From 6feecc5122dd76b358c86e257d2a7802f64f745a Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 19 Jul 2016 14:34:11 +0300 Subject: [PATCH] Fix analogWrite (#2299) * Fix analogWrite Move to NMI interrupt Rework the whole ISR to take less and more constant cycles Current defaults run 10bits/1KHz@160MHz and 9bit/1KHz@80MHz (soft mapped) * astyle and remove STORE_ATTR --- cores/esp8266/core_esp8266_wiring_pwm.c | 263 ++++++++++++++---------- 1 file changed, 160 insertions(+), 103 deletions(-) diff --git a/cores/esp8266/core_esp8266_wiring_pwm.c b/cores/esp8266/core_esp8266_wiring_pwm.c index 8d97895ff..8297e21ee 100644 --- a/cores/esp8266/core_esp8266_wiring_pwm.c +++ b/cores/esp8266/core_esp8266_wiring_pwm.c @@ -24,132 +24,189 @@ #include "eagle_soc.h" #include "ets_sys.h" +#ifndef F_CPU +#define F_CPU 800000000L +#endif + +struct pwm_isr_table { + uint8_t len; + uint16_t steps[17]; + uint32_t masks[17]; +}; + +struct pwm_isr_data { + struct pwm_isr_table tables[2]; + uint8_t active;//0 or 1, which table is active in ISR +}; + +static struct pwm_isr_data _pwm_isr_data; + uint32_t pwm_mask = 0; uint16_t pwm_values[17] = {0,}; uint32_t pwm_freq = 1000; uint32_t pwm_range = PWMRANGE; +uint8_t pwm_steps_changed = 0; uint32_t pwm_multiplier = 0; -uint16_t pwm_steps[17]; -uint8_t pwm_steps_len = 0; -uint32_t pwm_steps_mask[17]; -int pwm_sort_array(uint16_t a[], uint16_t al){ - uint16_t i, j; - for (i = 1; i < al; i++) { - uint16_t tmp = a[i]; - for (j = i; j >= 1 && tmp < a[j-1]; j--) - a[j] = a[j-1]; - a[j] = tmp; - } - int bl = 1; - for(i = 1; i < al; i++){ - if(a[i] != a[i-1]) a[bl++] = a[i]; - } - return bl; +int pwm_sort_array(uint16_t a[], uint16_t al) +{ + uint16_t i, j; + for (i = 1; i < al; i++) { + uint16_t tmp = a[i]; + for (j = i; j >= 1 && tmp < a[j-1]; j--) { + a[j] = a[j-1]; + } + a[j] = tmp; + } + int bl = 1; + for(i = 1; i < al; i++) { + if(a[i] != a[i-1]) { + a[bl++] = a[i]; + } + } + return bl; } -uint32_t pwm_get_mask(uint16_t value){ - uint32_t mask = 0; - int i; - for(i=0; i<17; i++){ - if((pwm_mask & (1 << i)) != 0 && pwm_values[i] == value) mask |= (1 << i); - } - return mask; +uint32_t pwm_get_mask(uint16_t value) +{ + uint32_t mask = 0; + int i; + for(i=0; i<17; i++) { + if((pwm_mask & (1 << i)) != 0 && pwm_values[i] == value) { + mask |= (1 << i); + } + } + return mask; } -void prep_pwm_steps(){ - if(pwm_mask == 0){ - pwm_steps_len = 0; - return; - } +void prep_pwm_steps() +{ + if(pwm_mask == 0) { + return; + } - int pwm_temp_steps_len = 0; - uint16_t pwm_temp_steps[17]; - uint32_t pwm_temp_masks[17]; + int pwm_temp_steps_len = 0; + uint16_t pwm_temp_steps[17]; + uint32_t pwm_temp_masks[17]; + uint32_t range = pwm_range; - int i; - for(i=0; i<17; i++){ - if((pwm_mask & (1 << i)) != 0 && pwm_values[i] != 0) pwm_temp_steps[pwm_temp_steps_len++] = pwm_values[i]; - } - pwm_temp_steps[pwm_temp_steps_len++] = pwm_range; - pwm_temp_steps_len = pwm_sort_array(pwm_temp_steps, pwm_temp_steps_len) - 1; - for(i=0; i0; i--){ - pwm_temp_steps[i] = pwm_temp_steps[i] - pwm_temp_steps[i-1]; - } - ETS_FRC1_INTR_DISABLE(); - pwm_steps_len = pwm_temp_steps_len; - ets_memcpy(pwm_steps, pwm_temp_steps, (pwm_temp_steps_len + 1) * 2); - ets_memcpy(pwm_steps_mask, pwm_temp_masks, pwm_temp_steps_len * 4); - pwm_multiplier = ESP8266_CLOCK/(pwm_range * pwm_freq); - ETS_FRC1_INTR_ENABLE(); + if((F_CPU / ESP8266_CLOCK) == 1) { + range /= 2; + } + + int i; + for(i=0; i<17; i++) { + if((pwm_mask & (1 << i)) != 0 && pwm_values[i] != 0) { + pwm_temp_steps[pwm_temp_steps_len++] = pwm_values[i]; + } + } + pwm_temp_steps[pwm_temp_steps_len++] = range; + pwm_temp_steps_len = pwm_sort_array(pwm_temp_steps, pwm_temp_steps_len) - 1; + for(i=0; i0; i--) { + pwm_temp_steps[i] = pwm_temp_steps[i] - pwm_temp_steps[i-1]; + } + + pwm_steps_changed = 0; + struct pwm_isr_table *table = &(_pwm_isr_data.tables[!_pwm_isr_data.active]); + table->len = pwm_temp_steps_len; + ets_memcpy(table->steps, pwm_temp_steps, (pwm_temp_steps_len + 1) * 2); + ets_memcpy(table->masks, pwm_temp_masks, pwm_temp_steps_len * 4); + pwm_multiplier = ESP8266_CLOCK/(range * pwm_freq); + pwm_steps_changed = 1; } -void ICACHE_RAM_ATTR pwm_timer_isr(){ - static uint8_t current_step = 0; - static uint8_t stepcount = 0; - static uint16_t steps[17]; - static uint32_t masks[17]; - if(current_step < stepcount){ - T1L = (pwm_steps[current_step+1] * pwm_multiplier); - TEIE |= TEIE1; - if(masks[current_step] & 0xFFFF) GPOC = masks[current_step] & 0xFFFF; - if(masks[current_step] & 0x10000) GP16O = 0; - current_step++; - } else { - current_step = 0; - stepcount = 0; - if(pwm_mask == 0) return; - T1L = (pwm_steps[current_step] * pwm_multiplier); - TEIE |= TEIE1; - if(pwm_mask & 0xFFFF) GPOS = pwm_mask & 0xFFFF; - if(pwm_mask & 0x10000) GP16O = 1; - stepcount = pwm_steps_len; - memcpy(steps, pwm_steps, (stepcount + 1) * 2); - memcpy(masks, pwm_steps_mask, stepcount * 4); - } +void ICACHE_RAM_ATTR pwm_timer_isr() //103-138 +{ + struct pwm_isr_table *table = &(_pwm_isr_data.tables[_pwm_isr_data.active]); + static uint8_t current_step = 0; + TEIE &= ~TEIE1;//14 + T1I = 0;//9 + if(current_step < table->len) { //20/21 + if(table->masks[current_step] & 0xFFFF) { + GPOC = table->masks[current_step] & 0xFFFF; //15/21 + } + if(table->masks[current_step] & 0x10000) { + GP16O = 0; //6/13 + } + current_step++;//1 + } else { + current_step = 0;//1 + if(pwm_mask == 0) { //12 + table->len = 0; + return; + } + if(pwm_mask & 0xFFFF) { + GPOS = pwm_mask & 0xFFFF; //11 + } + if(pwm_mask & 0x10000) { + GP16O = 1; //5/13 + } + if(pwm_steps_changed) { //12/21 + _pwm_isr_data.active = !_pwm_isr_data.active; + table = &(_pwm_isr_data.tables[_pwm_isr_data.active]); + pwm_steps_changed = 0; + } + } + T1L = (table->steps[current_step] * pwm_multiplier);//23 + TEIE |= TEIE1;//13 } -void pwm_start_timer(){ - timer1_disable(); - timer1_attachInterrupt(pwm_timer_isr); - timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); - timer1_write(1); +void pwm_start_timer() +{ + timer1_disable(); + ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); + ETS_FRC_TIMER1_NMI_INTR_ATTACH(pwm_timer_isr); + timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); + timer1_write(1); } -extern void __analogWrite(uint8_t pin, int value) { - bool start_timer = false; - if(value == 0){ - pwm_mask &= ~(1 << pin); - prep_pwm_steps(); - digitalWrite(pin, LOW); - if(pwm_mask == 0) timer1_disable(); - return; - } - if((pwm_mask & (1 << pin)) == 0){ - if(pwm_mask == 0) start_timer = true; - pwm_mask |= (1 << pin); - pinMode(pin, OUTPUT); - digitalWrite(pin, LOW); - } - pwm_values[pin] = value % (pwm_range + 1); - prep_pwm_steps(); - if(start_timer){ - pwm_start_timer(); - } +extern void __analogWrite(uint8_t pin, int value) +{ + bool start_timer = false; + if(value == 0) { + pwm_mask &= ~(1 << pin); + prep_pwm_steps(); + digitalWrite(pin, LOW); + if(pwm_mask == 0) { + ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); + timer1_disable(); + timer1_isr_init(); + } + return; + } + if((pwm_mask & (1 << pin)) == 0) { + if(pwm_mask == 0) { + memset(&_pwm_isr_data, 0, sizeof(struct pwm_isr_data*)); + start_timer = true; + } + pwm_mask |= (1 << pin); + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + } + if((F_CPU / ESP8266_CLOCK) == 1) { + value = (value+1) / 2; + } + pwm_values[pin] = value % (pwm_range + 1); + prep_pwm_steps(); + if(start_timer) { + pwm_start_timer(); + } } -extern void __analogWriteFreq(uint32_t freq){ - pwm_freq = freq; - prep_pwm_steps(); +extern void __analogWriteFreq(uint32_t freq) +{ + pwm_freq = freq; + prep_pwm_steps(); } -extern void __analogWriteRange(uint32_t range){ - pwm_range = range; - prep_pwm_steps(); +extern void __analogWriteRange(uint32_t range) +{ + pwm_range = range; + prep_pwm_steps(); } extern void analogWrite(uint8_t pin, int val) __attribute__ ((weak, alias("__analogWrite")));