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:
parent
9c36ff9932
commit
6feecc5122
@ -24,55 +24,84 @@
|
|||||||
#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;
|
uint16_t i, j;
|
||||||
for (i = 1; i < al; i++) {
|
for (i = 1; i < al; i++) {
|
||||||
uint16_t tmp = a[i];
|
uint16_t tmp = a[i];
|
||||||
for (j = i; j >= 1 && tmp < a[j-1]; j--)
|
for (j = i; j >= 1 && tmp < a[j-1]; j--) {
|
||||||
a[j] = a[j-1];
|
a[j] = a[j-1];
|
||||||
|
}
|
||||||
a[j] = tmp;
|
a[j] = tmp;
|
||||||
}
|
}
|
||||||
int bl = 1;
|
int bl = 1;
|
||||||
for(i = 1; i < al; i++) {
|
for(i = 1; i < al; i++) {
|
||||||
if(a[i] != a[i-1]) a[bl++] = a[i];
|
if(a[i] != a[i-1]) {
|
||||||
|
a[bl++] = a[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return bl;
|
return bl;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t pwm_get_mask(uint16_t value){
|
uint32_t pwm_get_mask(uint16_t value)
|
||||||
|
{
|
||||||
uint32_t mask = 0;
|
uint32_t mask = 0;
|
||||||
int i;
|
int i;
|
||||||
for(i=0; i<17; i++) {
|
for(i=0; i<17; i++) {
|
||||||
if((pwm_mask & (1 << i)) != 0 && pwm_values[i] == value) mask |= (1 << i);
|
if((pwm_mask & (1 << i)) != 0 && pwm_values[i] == value) {
|
||||||
|
mask |= (1 << i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void prep_pwm_steps(){
|
void prep_pwm_steps()
|
||||||
|
{
|
||||||
if(pwm_mask == 0) {
|
if(pwm_mask == 0) {
|
||||||
pwm_steps_len = 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;
|
||||||
|
|
||||||
|
if((F_CPU / ESP8266_CLOCK) == 1) {
|
||||||
|
range /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for(i=0; i<17; 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];
|
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[pwm_temp_steps_len++] = range;
|
||||||
pwm_temp_steps_len = pwm_sort_array(pwm_temp_steps, pwm_temp_steps_len) - 1;
|
pwm_temp_steps_len = pwm_sort_array(pwm_temp_steps, pwm_temp_steps_len) - 1;
|
||||||
for(i=0; i<pwm_temp_steps_len; i++) {
|
for(i=0; i<pwm_temp_steps_len; i++) {
|
||||||
pwm_temp_masks[i] = pwm_get_mask(pwm_temp_steps[i]);
|
pwm_temp_masks[i] = pwm_get_mask(pwm_temp_steps[i]);
|
||||||
@ -80,61 +109,87 @@ void prep_pwm_steps(){
|
|||||||
for(i=pwm_temp_steps_len; i>0; 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[i] = pwm_temp_steps[i] - pwm_temp_steps[i-1];
|
||||||
}
|
}
|
||||||
ETS_FRC1_INTR_DISABLE();
|
|
||||||
pwm_steps_len = pwm_temp_steps_len;
|
pwm_steps_changed = 0;
|
||||||
ets_memcpy(pwm_steps, pwm_temp_steps, (pwm_temp_steps_len + 1) * 2);
|
struct pwm_isr_table *table = &(_pwm_isr_data.tables[!_pwm_isr_data.active]);
|
||||||
ets_memcpy(pwm_steps_mask, pwm_temp_masks, pwm_temp_steps_len * 4);
|
table->len = pwm_temp_steps_len;
|
||||||
pwm_multiplier = ESP8266_CLOCK/(pwm_range * pwm_freq);
|
ets_memcpy(table->steps, pwm_temp_steps, (pwm_temp_steps_len + 1) * 2);
|
||||||
ETS_FRC1_INTR_ENABLE();
|
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
|
||||||
|
{
|
||||||
|
struct pwm_isr_table *table = &(_pwm_isr_data.tables[_pwm_isr_data.active]);
|
||||||
static uint8_t current_step = 0;
|
static uint8_t current_step = 0;
|
||||||
static uint8_t stepcount = 0;
|
TEIE &= ~TEIE1;//14
|
||||||
static uint16_t steps[17];
|
T1I = 0;//9
|
||||||
static uint32_t masks[17];
|
if(current_step < table->len) { //20/21
|
||||||
if(current_step < stepcount){
|
if(table->masks[current_step] & 0xFFFF) {
|
||||||
T1L = (pwm_steps[current_step+1] * pwm_multiplier);
|
GPOC = table->masks[current_step] & 0xFFFF; //15/21
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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(){
|
void pwm_start_timer()
|
||||||
|
{
|
||||||
timer1_disable();
|
timer1_disable();
|
||||||
timer1_attachInterrupt(pwm_timer_isr);
|
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_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE);
|
||||||
timer1_write(1);
|
timer1_write(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void __analogWrite(uint8_t pin, int value) {
|
extern void __analogWrite(uint8_t pin, int value)
|
||||||
|
{
|
||||||
bool start_timer = false;
|
bool start_timer = false;
|
||||||
if(value == 0) {
|
if(value == 0) {
|
||||||
pwm_mask &= ~(1 << pin);
|
pwm_mask &= ~(1 << pin);
|
||||||
prep_pwm_steps();
|
prep_pwm_steps();
|
||||||
digitalWrite(pin, LOW);
|
digitalWrite(pin, LOW);
|
||||||
if(pwm_mask == 0) timer1_disable();
|
if(pwm_mask == 0) {
|
||||||
|
ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL);
|
||||||
|
timer1_disable();
|
||||||
|
timer1_isr_init();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if((pwm_mask & (1 << pin)) == 0) {
|
if((pwm_mask & (1 << pin)) == 0) {
|
||||||
if(pwm_mask == 0) start_timer = true;
|
if(pwm_mask == 0) {
|
||||||
|
memset(&_pwm_isr_data, 0, sizeof(struct pwm_isr_data*));
|
||||||
|
start_timer = true;
|
||||||
|
}
|
||||||
pwm_mask |= (1 << pin);
|
pwm_mask |= (1 << pin);
|
||||||
pinMode(pin, OUTPUT);
|
pinMode(pin, OUTPUT);
|
||||||
digitalWrite(pin, LOW);
|
digitalWrite(pin, LOW);
|
||||||
}
|
}
|
||||||
|
if((F_CPU / ESP8266_CLOCK) == 1) {
|
||||||
|
value = (value+1) / 2;
|
||||||
|
}
|
||||||
pwm_values[pin] = value % (pwm_range + 1);
|
pwm_values[pin] = value % (pwm_range + 1);
|
||||||
prep_pwm_steps();
|
prep_pwm_steps();
|
||||||
if(start_timer) {
|
if(start_timer) {
|
||||||
@ -142,12 +197,14 @@ extern void __analogWrite(uint8_t pin, int value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void __analogWriteFreq(uint32_t freq){
|
extern void __analogWriteFreq(uint32_t freq)
|
||||||
|
{
|
||||||
pwm_freq = freq;
|
pwm_freq = freq;
|
||||||
prep_pwm_steps();
|
prep_pwm_steps();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void __analogWriteRange(uint32_t range){
|
extern void __analogWriteRange(uint32_t range)
|
||||||
|
{
|
||||||
pwm_range = range;
|
pwm_range = range;
|
||||||
prep_pwm_steps();
|
prep_pwm_steps();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user