1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-22 21:23:07 +03:00

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
This commit is contained in:
Me No Dev 2016-07-19 14:34:11 +03:00 committed by Ivan Grokhotkov
parent 9c36ff9932
commit 6feecc5122

View File

@ -24,132 +24,189 @@
#include "eagle_soc.h" #include "eagle_soc.h"
#include "ets_sys.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; uint32_t pwm_mask = 0;
uint16_t pwm_values[17] = {0,}; uint16_t pwm_values[17] = {0,};
uint32_t pwm_freq = 1000; uint32_t pwm_freq = 1000;
uint32_t pwm_range = PWMRANGE; uint32_t pwm_range = PWMRANGE;
uint8_t pwm_steps_changed = 0;
uint32_t pwm_multiplier = 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){ int pwm_sort_array(uint16_t a[], uint16_t al)
uint16_t i, j; {
for (i = 1; i < al; i++) { uint16_t i, j;
uint16_t tmp = a[i]; for (i = 1; i < al; i++) {
for (j = i; j >= 1 && tmp < a[j-1]; j--) uint16_t tmp = a[i];
a[j] = a[j-1]; for (j = i; j >= 1 && tmp < a[j-1]; j--) {
a[j] = tmp; a[j] = a[j-1];
} }
int bl = 1; a[j] = tmp;
for(i = 1; i < al; i++){ }
if(a[i] != a[i-1]) a[bl++] = a[i]; int bl = 1;
} for(i = 1; i < al; i++) {
return bl; if(a[i] != a[i-1]) {
a[bl++] = a[i];
}
}
return bl;
} }
uint32_t pwm_get_mask(uint16_t value){ uint32_t pwm_get_mask(uint16_t value)
uint32_t mask = 0; {
int i; uint32_t mask = 0;
for(i=0; i<17; i++){ int i;
if((pwm_mask & (1 << i)) != 0 && pwm_values[i] == value) mask |= (1 << i); for(i=0; i<17; i++) {
} if((pwm_mask & (1 << i)) != 0 && pwm_values[i] == value) {
return mask; mask |= (1 << i);
}
}
return mask;
} }
void prep_pwm_steps(){ void prep_pwm_steps()
if(pwm_mask == 0){ {
pwm_steps_len = 0; if(pwm_mask == 0) {
return; return;
} }
int pwm_temp_steps_len = 0; int pwm_temp_steps_len = 0;
uint16_t pwm_temp_steps[17]; uint16_t pwm_temp_steps[17];
uint32_t pwm_temp_masks[17]; uint32_t pwm_temp_masks[17];
uint32_t range = pwm_range;
int i; if((F_CPU / ESP8266_CLOCK) == 1) {
for(i=0; i<17; i++){ range /= 2;
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; int i;
pwm_temp_steps_len = pwm_sort_array(pwm_temp_steps, pwm_temp_steps_len) - 1; for(i=0; i<17; i++) {
for(i=0; i<pwm_temp_steps_len; i++){ if((pwm_mask & (1 << i)) != 0 && pwm_values[i] != 0) {
pwm_temp_masks[i] = pwm_get_mask(pwm_temp_steps[i]); pwm_temp_steps[pwm_temp_steps_len++] = pwm_values[i];
} }
for(i=pwm_temp_steps_len; i>0; i--){ }
pwm_temp_steps[i] = pwm_temp_steps[i] - pwm_temp_steps[i-1]; pwm_temp_steps[pwm_temp_steps_len++] = range;
} pwm_temp_steps_len = pwm_sort_array(pwm_temp_steps, pwm_temp_steps_len) - 1;
ETS_FRC1_INTR_DISABLE(); for(i=0; i<pwm_temp_steps_len; i++) {
pwm_steps_len = pwm_temp_steps_len; pwm_temp_masks[i] = pwm_get_mask(pwm_temp_steps[i]);
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); for(i=pwm_temp_steps_len; i>0; i--) {
pwm_multiplier = ESP8266_CLOCK/(pwm_range * pwm_freq); pwm_temp_steps[i] = pwm_temp_steps[i] - pwm_temp_steps[i-1];
ETS_FRC1_INTR_ENABLE(); }
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(){ void ICACHE_RAM_ATTR pwm_timer_isr() //103-138
static uint8_t current_step = 0; {
static uint8_t stepcount = 0; struct pwm_isr_table *table = &(_pwm_isr_data.tables[_pwm_isr_data.active]);
static uint16_t steps[17]; static uint8_t current_step = 0;
static uint32_t masks[17]; TEIE &= ~TEIE1;//14
if(current_step < stepcount){ T1I = 0;//9
T1L = (pwm_steps[current_step+1] * pwm_multiplier); if(current_step < table->len) { //20/21
TEIE |= TEIE1; if(table->masks[current_step] & 0xFFFF) {
if(masks[current_step] & 0xFFFF) GPOC = masks[current_step] & 0xFFFF; GPOC = table->masks[current_step] & 0xFFFF; //15/21
if(masks[current_step] & 0x10000) GP16O = 0; }
current_step++; if(table->masks[current_step] & 0x10000) {
} else { GP16O = 0; //6/13
current_step = 0; }
stepcount = 0; current_step++;//1
if(pwm_mask == 0) return; } else {
T1L = (pwm_steps[current_step] * pwm_multiplier); current_step = 0;//1
TEIE |= TEIE1; if(pwm_mask == 0) { //12
if(pwm_mask & 0xFFFF) GPOS = pwm_mask & 0xFFFF; table->len = 0;
if(pwm_mask & 0x10000) GP16O = 1; return;
stepcount = pwm_steps_len; }
memcpy(steps, pwm_steps, (stepcount + 1) * 2); if(pwm_mask & 0xFFFF) {
memcpy(masks, pwm_steps_mask, stepcount * 4); 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(){ void pwm_start_timer()
timer1_disable(); {
timer1_attachInterrupt(pwm_timer_isr); timer1_disable();
timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL);
timer1_write(1); 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) { extern void __analogWrite(uint8_t pin, int value)
bool start_timer = false; {
if(value == 0){ bool start_timer = false;
pwm_mask &= ~(1 << pin); if(value == 0) {
prep_pwm_steps(); pwm_mask &= ~(1 << pin);
digitalWrite(pin, LOW); prep_pwm_steps();
if(pwm_mask == 0) timer1_disable(); digitalWrite(pin, LOW);
return; if(pwm_mask == 0) {
} ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL);
if((pwm_mask & (1 << pin)) == 0){ timer1_disable();
if(pwm_mask == 0) start_timer = true; timer1_isr_init();
pwm_mask |= (1 << pin); }
pinMode(pin, OUTPUT); return;
digitalWrite(pin, LOW); }
} if((pwm_mask & (1 << pin)) == 0) {
pwm_values[pin] = value % (pwm_range + 1); if(pwm_mask == 0) {
prep_pwm_steps(); memset(&_pwm_isr_data, 0, sizeof(struct pwm_isr_data*));
if(start_timer){ start_timer = true;
pwm_start_timer(); }
} 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){ extern void __analogWriteFreq(uint32_t freq)
pwm_freq = freq; {
prep_pwm_steps(); pwm_freq = freq;
prep_pwm_steps();
} }
extern void __analogWriteRange(uint32_t range){ extern void __analogWriteRange(uint32_t range)
pwm_range = range; {
prep_pwm_steps(); pwm_range = range;
prep_pwm_steps();
} }
extern void analogWrite(uint8_t pin, int val) __attribute__ ((weak, alias("__analogWrite"))); extern void analogWrite(uint8_t pin, int val) __attribute__ ((weak, alias("__analogWrite")));