// send cnt bits to LCD controller, only low bits of "bits" are used // use timer4 to get 600kHz clock for driving WR/ signal // timer is used in one-pulse mode and is started after each WR/ change, // so there will always be minimal required length of pulse also // when interrupted by some interrupt static void lcd_send_bits(u8 cnt, u16 bits) { // shift bits to high-bits bits <<= (u8)(16 - cnt); WR0; BSET(TIM4_CR1, 0); // start timer WR0; // optimize hack do { if (bits & 0x8000) { DATA1; } else { DATA0; } bits <<= 1; // to next bit while (!BCHK(TIM4_SR, 0)); // wait for timer WR1; BRES(TIM4_SR, 0); // clear intr flag BSET(TIM4_CR1, 0); // start timer if (!--cnt) break; while (!BCHK(TIM4_SR, 0)); // wait for timer WR0; BRES(TIM4_SR, 0); // clear intr flag BSET(TIM4_CR1, 0); // start timer } while (1); while (!BCHK(TIM4_SR, 0)); // wait for timer BRES(TIM4_SR, 0); // clear intr flag }
/* TIM3 overflow interrupt set next prescaler, output compare and overflow values to preload registers */ @interrupt void ppm_interrupt(void) { BRES(TIM3_SR1, 0); // erase interrupt flag if (ppm_channel2) { if (ppm_channel2 == 2) { // will be setting channel1 servo, so we are now generating // SYNC signal in HW // wakeup CALC task to compute new PPM values // values for ARR registers will be updated after calculate done // (and when we are still generating SYNC pulse) awake(CALC); } // set servo channel TIM3_PSCR = PPM_PSC_SERVO; TIM3_CCR2H = hi8(PPM_300US_SERVO); TIM3_CCR2L = lo8(PPM_300US_SERVO); TIM3_ARRH = ppm_values[ppm_channel2]; ppm_channel2++; TIM3_ARRL = ppm_values[ppm_channel2]; if (++ppm_channel2 > channels2) { // next to SYNC pulse ppm_channel2 = 0; } return; } // set SYNC signal TIM3_PSCR = PPM_PSC_SYNC; TIM3_CCR2H = hi8(PPM_300US_SYNC); TIM3_CCR2L = lo8(PPM_300US_SYNC); TIM3_ARRH = ppm_values[0]; TIM3_ARRL = ppm_values[1]; ppm_channel2 = 2; // to first channel (step 2 bytes) }
static void read_ADC(void) { u16 *buf = adc_buffer[adc_buffer_pos]; u8 dead; ADC_NEWVAL(0); ADC_NEWVAL(1); ADC_NEWVAL(2); adc_battery_last = ADC_DB3R; adc_buffer_pos++; adc_buffer_pos &= 3; BRES(ADC_CSR, 7); // remove EOC flag BSET(ADC_CR1, 0); // start new conversion // average battery voltage and check battery low // ignore very low, which means that it is supplied from SWIM connector adc_battery_filt = adc_battery_filt * (ADC_BAT_FILT - 1); // splitted - compiler hack adc_battery_filt = (adc_battery_filt + (ADC_BAT_FILT / 2)) / ADC_BAT_FILT + adc_battery_last; adc_battery = (u16)((adc_battery_filt + (ADC_BAT_FILT / 2)) / ADC_BAT_FILT); // start checking battery after 5s from power on if (time_sec >= 5) { // wakeup task only when something changed if (adc_battery > 50 && adc_battery < battery_low_raw) { // bat low if (!menu_battery_low) { menu_battery_low = 1; awake(MENU); } } else { // bat OK, but apply some hysteresis to not switch quickly ON/OFF if (menu_battery_low && adc_battery > battery_low_raw + 100) { menu_battery_low = 0; awake(MENU); } } } // wakeup MENU task when showing battery or at calibrate if (menu_wants_adc) awake(MENU); // reset inactivity timer when some steering or throttle applied dead = cg.steering_dead_zone; if (dead < 20) dead = 20; // use some minimal dead zone for this check if (adc_steering_last < (cg.calib_steering_mid - dead) || adc_steering_last > (cg.calib_steering_mid + dead)) reset_inactivity_timer(); else { dead = cg.throttle_dead_zone; if (dead < 20) dead = 20; // use some minimal dead zone for this check if (adc_throttle_last < (cg.calib_throttle_mid - dead) || adc_throttle_last > (cg.calib_throttle_mid + dead)) reset_inactivity_timer(); } }
// set number of channels void ppm_set_channels(u8 n) { // disable PPM generation till new values will not be set ppm_enabled = 0; BRES(TIM3_CR1, 0); // disable timer BSET(PD_ODR, 0); // set PPM pin to 1 // start with generating 20ms SYNC signal TIM3_CCR2H = hi8(PPM_300US_SYNC); TIM3_CCR2L = hi8(PPM_300US_SYNC); TIM3_ARRH = hi8(PPM_MUL_SYNC * 20); TIM3_ARRL = lo8(PPM_MUL_SYNC * 20); channels = n; channels2 = (u8)(n << 1); // also 2* value for compare in ppm_interrupt }