Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 163 additions & 106 deletions cores/esp8266/core_esp8266_wiring_pwm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

int pwm_temp_steps_len = 0;
uint16_t pwm_temp_steps[17];
uint32_t pwm_temp_masks[17];

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; i<pwm_temp_steps_len; i++){
pwm_temp_masks[i] = pwm_get_mask(pwm_temp_steps[i]);
}
for(i=pwm_temp_steps_len; i>0; 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();
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];
uint32_t range = pwm_range;

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; i<pwm_temp_steps_len; i++) {
pwm_temp_masks[i] = pwm_get_mask(pwm_temp_steps[i]);
}
for(i=pwm_temp_steps_len; i>0; 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")));
Expand Down