Browse Source

Using ESP8266_pwm_new

fastled
Xose Pérez 7 years ago
parent
commit
c990cc4c11
5 changed files with 568 additions and 40 deletions
  1. +9
    -1
      code/espurna/config/general.h
  2. +9
    -8
      code/espurna/config/hardware.h
  3. +69
    -31
      code/espurna/light.ino
  4. +448
    -0
      code/espurna/pwm.c
  5. +33
    -0
      code/espurna/pwm.h

+ 9
- 1
code/espurna/config/general.h View File

@ -498,9 +498,17 @@ PROGMEM const char* const custom_reset_string[] = {
#endif #endif
#ifndef LIGHT_MAX_PWM #ifndef LIGHT_MAX_PWM
#define LIGHT_MAX_PWM 4095 // Maximum PWM value
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
#define LIGHT_MAX_PWM 256
#endif
#if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER
#define LIGHT_MAX_PWM 1000
#endif #endif
#endif // LIGHT_MAX_PWM
#ifndef LIGHT_LIMIT_PWM #ifndef LIGHT_LIMIT_PWM
#define LIGHT_LIMIT_PWM LIGHT_MAX_PWM // Limit PWM to this value (prevent 100% power) #define LIGHT_LIMIT_PWM LIGHT_MAX_PWM // Limit PWM to this value (prevent 100% power)
#endif #endif


+ 9
- 8
code/espurna/config/hardware.h View File

@ -465,6 +465,7 @@
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels // Channels
#define LIGHT_CHANNELS 1
#define LIGHT_CH1_PIN 12 #define LIGHT_CH1_PIN 12
#define LIGHT_CH1_INVERSE 0 #define LIGHT_CH1_INVERSE 0
@ -516,9 +517,9 @@
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels // Channels
#define LIGHT_CHANNELS 2
#define LIGHT_CH1_PIN 12 // Cold white #define LIGHT_CH1_PIN 12 // Cold white
#define LIGHT_CH2_PIN 14 // Warm white #define LIGHT_CH2_PIN 14 // Warm white
#define LIGHT_CH1_INVERSE 0 #define LIGHT_CH1_INVERSE 0
#define LIGHT_CH2_INVERSE 0 #define LIGHT_CH2_INVERSE 0
@ -689,11 +690,11 @@
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels // Channels
#define LIGHT_CHANNELS 4
#define LIGHT_CH1_PIN 14 // RED #define LIGHT_CH1_PIN 14 // RED
#define LIGHT_CH2_PIN 5 // GREEN #define LIGHT_CH2_PIN 5 // GREEN
#define LIGHT_CH3_PIN 12 // BLUE #define LIGHT_CH3_PIN 12 // BLUE
#define LIGHT_CH4_PIN 13 // WHITE #define LIGHT_CH4_PIN 13 // WHITE
#define LIGHT_CH1_INVERSE 0 #define LIGHT_CH1_INVERSE 0
#define LIGHT_CH2_INVERSE 0 #define LIGHT_CH2_INVERSE 0
#define LIGHT_CH3_INVERSE 0 #define LIGHT_CH3_INVERSE 0
@ -713,11 +714,11 @@
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels // Channels
#define LIGHT_CHANNELS 4
#define LIGHT_CH1_PIN 5 // RED #define LIGHT_CH1_PIN 5 // RED
#define LIGHT_CH2_PIN 12 // GREEN #define LIGHT_CH2_PIN 12 // GREEN
#define LIGHT_CH3_PIN 13 // BLUE #define LIGHT_CH3_PIN 13 // BLUE
#define LIGHT_CH4_PIN 15 // WHITE #define LIGHT_CH4_PIN 15 // WHITE
#define LIGHT_CH1_INVERSE 0 #define LIGHT_CH1_INVERSE 0
#define LIGHT_CH2_INVERSE 0 #define LIGHT_CH2_INVERSE 0
#define LIGHT_CH3_INVERSE 0 #define LIGHT_CH3_INVERSE 0
@ -815,12 +816,12 @@
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels // Channels
#define LIGHT_CHANNELS 5
#define LIGHT_CH1_PIN 15 // RED #define LIGHT_CH1_PIN 15 // RED
#define LIGHT_CH2_PIN 13 // GREEN #define LIGHT_CH2_PIN 13 // GREEN
#define LIGHT_CH3_PIN 12 // BLUE #define LIGHT_CH3_PIN 12 // BLUE
#define LIGHT_CH4_PIN 14 // WHITE1 #define LIGHT_CH4_PIN 14 // WHITE1
#define LIGHT_CH5_PIN 4 // WHITE2 #define LIGHT_CH5_PIN 4 // WHITE2
#define LIGHT_CH1_INVERSE 0 #define LIGHT_CH1_INVERSE 0
#define LIGHT_CH2_INVERSE 0 #define LIGHT_CH2_INVERSE 0
#define LIGHT_CH3_INVERSE 0 #define LIGHT_CH3_INVERSE 0
@ -837,11 +838,11 @@
#define DUMMY_RELAY_COUNT 1 #define DUMMY_RELAY_COUNT 1
// Channels // Channels
#define LIGHT_CHANNELS 4
#define LIGHT_CH1_PIN 12 // RED #define LIGHT_CH1_PIN 12 // RED
#define LIGHT_CH2_PIN 14 // GREEN #define LIGHT_CH2_PIN 14 // GREEN
#define LIGHT_CH3_PIN 13 // BLUE #define LIGHT_CH3_PIN 13 // BLUE
#define LIGHT_CH4_PIN 15 // WHITE #define LIGHT_CH4_PIN 15 // WHITE
#define LIGHT_CH1_INVERSE 0 #define LIGHT_CH1_INVERSE 0
#define LIGHT_CH2_INVERSE 0 #define LIGHT_CH2_INVERSE 0
#define LIGHT_CH3_INVERSE 0 #define LIGHT_CH3_INVERSE 0
@ -1074,9 +1075,9 @@
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// Channels // Channels
#define LIGHT_CHANNELS 2
#define LIGHT_CH1_PIN 0 #define LIGHT_CH1_PIN 0
#define LIGHT_CH2_PIN 2 #define LIGHT_CH2_PIN 2
#define LIGHT_CH1_INVERSE 0 #define LIGHT_CH1_INVERSE 0
#define LIGHT_CH2_INVERSE 0 #define LIGHT_CH2_INVERSE 0
@ -1094,12 +1095,12 @@
#define DUMMY_RELAY_COUNT 1 #define DUMMY_RELAY_COUNT 1
// Channels // Channels
#define LIGHT_CHANNELS 5
#define LIGHT_CH1_PIN 12 // RED #define LIGHT_CH1_PIN 12 // RED
#define LIGHT_CH2_PIN 14 // GREEN #define LIGHT_CH2_PIN 14 // GREEN
#define LIGHT_CH3_PIN 13 // BLUE #define LIGHT_CH3_PIN 13 // BLUE
#define LIGHT_CH4_PIN 15 // WHITE1 #define LIGHT_CH4_PIN 15 // WHITE1
#define LIGHT_CH5_PIN 5 // WHITE2 #define LIGHT_CH5_PIN 5 // WHITE2
#define LIGHT_CH1_INVERSE 0 #define LIGHT_CH1_INVERSE 0
#define LIGHT_CH2_INVERSE 0 #define LIGHT_CH2_INVERSE 0
#define LIGHT_CH3_INVERSE 0 #define LIGHT_CH3_INVERSE 0
@ -1144,11 +1145,11 @@
#define DUMMY_RELAY_COUNT 1 #define DUMMY_RELAY_COUNT 1
// Channels // Channels
#define LIGHT_CHANNELS 4
#define LIGHT_CH1_PIN 13 // RED #define LIGHT_CH1_PIN 13 // RED
#define LIGHT_CH2_PIN 12 // GREEN #define LIGHT_CH2_PIN 12 // GREEN
#define LIGHT_CH3_PIN 14 // BLUE #define LIGHT_CH3_PIN 14 // BLUE
#define LIGHT_CH4_PIN 2 // WHITE #define LIGHT_CH4_PIN 2 // WHITE
#define LIGHT_CH1_INVERSE 0 #define LIGHT_CH1_INVERSE 0
#define LIGHT_CH2_INVERSE 0 #define LIGHT_CH2_INVERSE 0
#define LIGHT_CH3_INVERSE 0 #define LIGHT_CH3_INVERSE 0


+ 69
- 31
code/espurna/light.ino View File

@ -13,6 +13,13 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <vector> #include <vector>
#if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER
#define PWM_CHANNEL_NUM_MAX LIGHT_CHANNELS
extern "C" {
#include "pwm.h"
}
#endif
Ticker colorTicker; Ticker colorTicker;
typedef struct { typedef struct {
unsigned char pin; unsigned char pin;
@ -29,25 +36,26 @@ unsigned int _brightness = LIGHT_MAX_BRIGHTNESS;
my9291 * _my9291; my9291 * _my9291;
#endif #endif
// Gamma Correction lookup table for gamma=2.8 and 12 bit (4095) full scale
// Gamma Correction lookup table (8 bit)
// TODO: move to PROGMEM // TODO: move to PROGMEM
const unsigned short gamma_table[LIGHT_MAX_VALUE+1] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11,
12, 13, 15, 16, 17, 18, 20, 21, 23, 25, 26, 28, 30, 32, 34, 36,
38, 40, 43, 45, 48, 50, 53, 56, 59, 62, 65, 68, 71, 75, 78, 82,
85, 89, 93, 97, 101, 105, 110, 114, 119, 123, 128, 133, 138, 143, 149, 154,
159, 165, 171, 177, 183, 189, 195, 202, 208, 215, 222, 229, 236, 243, 250, 258,
266, 273, 281, 290, 298, 306, 315, 324, 332, 341, 351, 360, 369, 379, 389, 399,
409, 419, 430, 440, 451, 462, 473, 485, 496, 508, 520, 532, 544, 556, 569, 582,
594, 608, 621, 634, 648, 662, 676, 690, 704, 719, 734, 749, 764, 779, 795, 811,
827, 843, 859, 876, 893, 910, 927, 944, 962, 980, 998,1016,1034,1053,1072,1091,
1110,1130,1150,1170,1190,1210,1231,1252,1273,1294,1316,1338,1360,1382,1404,1427,
1450,1473,1497,1520,1544,1568,1593,1617,1642,1667,1693,1718,1744,1770,1797,1823,
1850,1877,1905,1932,1960,1988,2017,2045,2074,2103,2133,2162,2192,2223,2253,2284,
2315,2346,2378,2410,2442,2474,2507,2540,2573,2606,2640,2674,2708,2743,2778,2813,
2849,2884,2920,2957,2993,3030,3067,3105,3143,3181,3219,3258,3297,3336,3376,3416,
3456,3496,3537,3578,3619,3661,3703,3745,3788,3831,3874,3918,3962,4006,4050,4095 };
const unsigned char gamma_table[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6,
6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11,
12, 12, 13, 13, 14, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19,
19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28,
29, 30, 30, 31, 32, 33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40,
41, 42, 43, 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 71,
72, 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 86, 87, 88, 89,
91, 92, 93, 94, 96, 97, 98, 100, 101, 102, 104, 105, 106, 108, 109, 110,
112, 113, 115, 116, 118, 119, 121, 122, 123, 125, 126, 128, 130, 131, 133, 134,
136, 137, 139, 140, 142, 144, 145, 147, 149, 150, 152, 154, 155, 157, 159, 160,
162, 164, 166, 167, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 187, 189,
191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221,
223, 225, 227, 229, 231, 233, 235, 238, 240, 242, 244, 246, 248, 251, 253, 255
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// UTILS // UTILS
@ -197,9 +205,10 @@ void _fromMireds(unsigned long mireds) {
unsigned int _toPWM(unsigned long value, bool bright, bool gamma, bool reverse) { unsigned int _toPWM(unsigned long value, bool bright, bool gamma, bool reverse) {
value = constrain(value, 0, LIGHT_MAX_VALUE); value = constrain(value, 0, LIGHT_MAX_VALUE);
if (bright) value *= ((float) _brightness / LIGHT_MAX_BRIGHTNESS); if (bright) value *= ((float) _brightness / LIGHT_MAX_BRIGHTNESS);
unsigned int pwm = gamma ? gamma_table[value] : map(value, 0, LIGHT_MAX_VALUE, 0, LIGHT_LIMIT_PWM);
if (reverse) pwm = LIGHT_LIMIT_PWM - pwm;
return pwm;
if (gamma) value = gamma_table[value];
if (LIGHT_MAX_VALUE != LIGHT_LIMIT_PWM) value = map(value, 0, LIGHT_MAX_VALUE, 0, LIGHT_LIMIT_PWM);
if (reverse) value = LIGHT_LIMIT_PWM - value;
return value;
} }
// Returns a PWM valule for the given channel ID // Returns a PWM valule for the given channel ID
@ -252,13 +261,11 @@ void _lightProviderUpdate() {
if (_lightState) { if (_lightState) {
float ratio = (float) LIGHT_MAX_VALUE / LIGHT_MAX_PWM;
unsigned int red = _toPWM(0) * ratio;
unsigned int green = _toPWM(1) * ratio;
unsigned int blue = _toPWM(2) * ratio;
unsigned int white = _toPWM(3) * ratio;
unsigned int warm = _toPWM(4) * ratio;
unsigned int red = _toPWM(0);
unsigned int green = _toPWM(1);
unsigned int blue = _toPWM(2);
unsigned int white = _toPWM(3)
unsigned int warm = _toPWM(4);
_my9291->setColor((my9291_color_t) { red, green, blue, white, warm }); _my9291->setColor((my9291_color_t) { red, green, blue, white, warm });
_my9291->setState(true); _my9291->setState(true);
@ -274,8 +281,9 @@ void _lightProviderUpdate() {
#if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER #if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER
for (unsigned int i=0; i < _channels.size(); i++) { for (unsigned int i=0; i < _channels.size(); i++) {
analogWrite(_channels[i].pin, _toPWM(i));
pwm_set_duty(_toPWM(i), i);
} }
pwm_start();
#endif #endif
@ -571,6 +579,30 @@ void _lightAPISetup() {
} }
#if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER
unsigned long getIOMux(unsigned long gpio) {
unsigned long muxes[16] = {
PERIPHS_IO_MUX_GPIO0_U, PERIPHS_IO_MUX_U0TXD_U, PERIPHS_IO_MUX_GPIO2_U, PERIPHS_IO_MUX_U0RXD_U,
PERIPHS_IO_MUX_GPIO4_U, PERIPHS_IO_MUX_GPIO5_U, PERIPHS_IO_MUX_SD_CLK_U, PERIPHS_IO_MUX_SD_DATA0_U,
PERIPHS_IO_MUX_SD_DATA1_U, PERIPHS_IO_MUX_SD_DATA2_U, PERIPHS_IO_MUX_SD_DATA3_U, PERIPHS_IO_MUX_SD_CMD_U,
PERIPHS_IO_MUX_MTDI_U, PERIPHS_IO_MUX_MTCK_U, PERIPHS_IO_MUX_MTMS_U, PERIPHS_IO_MUX_MTDO_U
};
return muxes[gpio];
}
unsigned long getIOFunc(unsigned long gpio) {
unsigned long funcs[16] = {
FUNC_GPIO0, FUNC_GPIO1, FUNC_GPIO2, FUNC_GPIO3,
FUNC_GPIO4, FUNC_GPIO5, FUNC_GPIO6, FUNC_GPIO7,
FUNC_GPIO8, FUNC_GPIO9, FUNC_GPIO10, FUNC_GPIO11,
FUNC_GPIO12, FUNC_GPIO13, FUNC_GPIO14, FUNC_GPIO15
};
return funcs[gpio];
}
#endif
void lightSetup() { void lightSetup() {
#ifdef LIGHT_ENABLE_PIN #ifdef LIGHT_ENABLE_PIN
@ -608,11 +640,17 @@ void lightSetup() {
_channels.push_back((channel_t) {LIGHT_CH5_PIN, LIGHT_CH5_INVERSE, 0}); _channels.push_back((channel_t) {LIGHT_CH5_PIN, LIGHT_CH5_INVERSE, 0});
#endif #endif
analogWriteRange(LIGHT_MAX_PWM+1);
analogWriteFreq(LIGHT_PWM_FREQUENCY);
uint32 pwm_duty_init[PWM_CHANNEL_NUM_MAX];
uint32 io_info[PWM_CHANNEL_NUM_MAX][3];
for (unsigned int i=0; i < _channels.size(); i++) { for (unsigned int i=0; i < _channels.size(); i++) {
pwm_duty_init[i] = 0;
io_info[i][0] = getIOMux(_channels[i].pin);
io_info[i][1] = getIOFunc(_channels[i].pin);
io_info[i][2] = _channels[i].pin;
pinMode(_channels[i].pin, OUTPUT); pinMode(_channels[i].pin, OUTPUT);
} }
pwm_init(LIGHT_MAX_PWM, pwm_duty_init, PWM_CHANNEL_NUM_MAX, io_info);
pwm_start();
#endif #endif


+ 448
- 0
code/espurna/pwm.c View File

@ -0,0 +1,448 @@
/*
* Copyright (C) 2016 Stefan Brüns <stefan.bruens@rwth-aachen.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Set the following three defines to your needs */
#ifndef SDK_PWM_PERIOD_COMPAT_MODE
#define SDK_PWM_PERIOD_COMPAT_MODE 0
#endif
#ifndef PWM_MAX_CHANNELS
#define PWM_MAX_CHANNELS 8
#endif
#define PWM_DEBUG 0
#define PWM_USE_NMI 0
/* no user servicable parts beyond this point */
#define PWM_MAX_TICKS 0x7fffff
#if SDK_PWM_PERIOD_COMPAT_MODE
#define PWM_PERIOD_TO_TICKS(x) (x * 0.2)
#define PWM_DUTY_TO_TICKS(x) (x * 5)
#define PWM_MAX_DUTY (PWM_MAX_TICKS * 0.2)
#define PWM_MAX_PERIOD (PWM_MAX_TICKS * 5)
#else
#define PWM_PERIOD_TO_TICKS(x) (x)
#define PWM_DUTY_TO_TICKS(x) (x)
#define PWM_MAX_DUTY PWM_MAX_TICKS
#define PWM_MAX_PERIOD PWM_MAX_TICKS
#endif
#include <c_types.h>
#include <pwm.h>
#include <eagle_soc.h>
#include <ets_sys.h>
// from SDK hw_timer.c
#define TIMER1_DIVIDE_BY_16 0x0004
#define TIMER1_ENABLE_TIMER 0x0080
struct pwm_phase {
uint32_t ticks; ///< delay until next phase, in 200ns units
uint16_t on_mask; ///< GPIO mask to switch on
uint16_t off_mask; ///< GPIO mask to switch off
};
/* Three sets of PWM phases, the active one, the one used
* starting with the next cycle, and the one updated
* by pwm_start. After the update pwm_next_set
* is set to the last updated set. pwm_current_set is set to
* pwm_next_set from the interrupt routine during the first
* pwm phase
*/
typedef struct pwm_phase (pwm_phase_array)[PWM_MAX_CHANNELS + 2];
static pwm_phase_array pwm_phases[3];
static struct {
struct pwm_phase* next_set;
struct pwm_phase* current_set;
uint8_t current_phase;
} pwm_state;
static uint32_t pwm_period;
static uint32_t pwm_period_ticks;
static uint32_t pwm_duty[PWM_MAX_CHANNELS];
static uint16_t gpio_mask[PWM_MAX_CHANNELS];
static uint8_t pwm_channels;
// 3-tuples of MUX_REGISTER, MUX_VALUE and GPIO number
typedef uint32_t (pin_info_type)[3];
struct gpio_regs {
uint32_t out; /* 0x60000300 */
uint32_t out_w1ts; /* 0x60000304 */
uint32_t out_w1tc; /* 0x60000308 */
uint32_t enable; /* 0x6000030C */
uint32_t enable_w1ts; /* 0x60000310 */
uint32_t enable_w1tc; /* 0x60000314 */
uint32_t in; /* 0x60000318 */
uint32_t status; /* 0x6000031C */
uint32_t status_w1ts; /* 0x60000320 */
uint32_t status_w1tc; /* 0x60000324 */
};
static struct gpio_regs* gpio = (struct gpio_regs*)(0x60000300);
struct timer_regs {
uint32_t frc1_load; /* 0x60000600 */
uint32_t frc1_count; /* 0x60000604 */
uint32_t frc1_ctrl; /* 0x60000608 */
uint32_t frc1_int; /* 0x6000060C */
uint8_t pad[16];
uint32_t frc2_load; /* 0x60000620 */
uint32_t frc2_count; /* 0x60000624 */
uint32_t frc2_ctrl; /* 0x60000628 */
uint32_t frc2_int; /* 0x6000062C */
uint32_t frc2_alarm; /* 0x60000630 */
};
static struct timer_regs* timer = (struct timer_regs*)(0x60000600);
static void ICACHE_RAM_ATTR
pwm_intr_handler(void)
{
if ((pwm_state.current_set[pwm_state.current_phase].off_mask == 0) &&
(pwm_state.current_set[pwm_state.current_phase].on_mask == 0)) {
pwm_state.current_set = pwm_state.next_set;
pwm_state.current_phase = 0;
}
do {
// force write to GPIO registers on each loop
asm volatile ("" : : : "memory");
gpio->out_w1ts = (uint32_t)(pwm_state.current_set[pwm_state.current_phase].on_mask);
gpio->out_w1tc = (uint32_t)(pwm_state.current_set[pwm_state.current_phase].off_mask);
uint32_t ticks = pwm_state.current_set[pwm_state.current_phase].ticks;
pwm_state.current_phase++;
if (ticks) {
if (ticks >= 16) {
// constant interrupt overhead
ticks -= 9;
timer->frc1_int &= ~FRC1_INT_CLR_MASK;
WRITE_PERI_REG(&timer->frc1_load, ticks);
return;
}
ticks *= 4;
do {
ticks -= 1;
// stop compiler from optimizing delay loop to noop
asm volatile ("" : : : "memory");
} while (ticks > 0);
}
} while (1);
}
/**
* period: initial period (base unit 1us OR 200ns)
* duty: array of initial duty values, may be NULL, may be freed after pwm_init
* pwm_channel_num: number of channels to use
* pin_info_list: array of pin_info
*/
void ICACHE_FLASH_ATTR
pwm_init(uint32_t period, uint32_t *duty, uint32_t pwm_channel_num,
uint32_t (*pin_info_list)[3])
{
int i, j, n;
pwm_channels = pwm_channel_num;
if (pwm_channels > PWM_MAX_CHANNELS)
pwm_channels = PWM_MAX_CHANNELS;
for (i = 0; i < 3; i++) {
for (j = 0; j < (PWM_MAX_CHANNELS + 2); j++) {
pwm_phases[i][j].ticks = 0;
pwm_phases[i][j].on_mask = 0;
pwm_phases[i][j].off_mask = 0;
}
}
pwm_state.current_set = pwm_state.next_set = 0;
pwm_state.current_phase = 0;
uint32_t all = 0;
// PIN info: MUX-Register, Mux-Setting, PIN-Nr
for (n = 0; n < pwm_channels; n++) {
pin_info_type* pin_info = &pin_info_list[n];
PIN_FUNC_SELECT((*pin_info)[0], (*pin_info)[1]);
gpio_mask[n] = 1 << (*pin_info)[2];
all |= 1 << (*pin_info)[2];
if (duty)
pwm_set_duty(duty[n], n);
}
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, all);
GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, all);
pwm_set_period(period);
#if PWM_USE_NMI
ETS_FRC_TIMER1_NMI_INTR_ATTACH(pwm_intr_handler);
#else
ETS_FRC_TIMER1_INTR_ATTACH(pwm_intr_handler, NULL);
#endif
TM1_EDGE_INT_ENABLE();
timer->frc1_int &= ~FRC1_INT_CLR_MASK;
timer->frc1_ctrl = 0;
pwm_start();
}
__attribute__ ((noinline))
static uint8_t ICACHE_FLASH_ATTR
_pwm_phases_prep(struct pwm_phase* pwm)
{
uint8_t n, phases;
uint16_t off_mask = 0;
for (n = 0; n < pwm_channels + 2; n++) {
pwm[n].ticks = 0;
pwm[n].on_mask = 0;
pwm[n].off_mask = 0;
}
phases = 1;
for (n = 0; n < pwm_channels; n++) {
uint32_t ticks = PWM_DUTY_TO_TICKS(pwm_duty[n]);
if (ticks == 0) {
pwm[0].off_mask |= gpio_mask[n];
} else if (ticks >= pwm_period_ticks) {
pwm[0].on_mask |= gpio_mask[n];
} else {
if (ticks < (pwm_period_ticks/2)) {
pwm[phases].ticks = ticks;
pwm[0].on_mask |= gpio_mask[n];
pwm[phases].off_mask = gpio_mask[n];
} else {
pwm[phases].ticks = pwm_period_ticks - ticks;
pwm[phases].on_mask = gpio_mask[n];
pwm[0].off_mask |= gpio_mask[n];
}
phases++;
}
}
pwm[phases].ticks = pwm_period_ticks;
// bubble sort, lowest to hightest duty
n = 2;
while (n < phases) {
if (pwm[n].ticks < pwm[n - 1].ticks) {
struct pwm_phase t = pwm[n];
pwm[n] = pwm[n - 1];
pwm[n - 1] = t;
if (n > 2)
n--;
} else {
n++;
}
}
#if PWM_DEBUG
int t = 0;
for (t = 0; t <= phases; t++) {
ets_printf("%d @%d: %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
}
#endif
// shift left to align right edge;
uint8_t l = 0, r = 1;
while (r <= phases) {
uint32_t diff = pwm[r].ticks - pwm[l].ticks;
if (diff && (diff <= 16)) {
uint16_t mask = pwm[r].on_mask | pwm[r].off_mask;
pwm[l].off_mask ^= pwm[r].off_mask;
pwm[l].on_mask ^= pwm[r].on_mask;
pwm[0].off_mask ^= pwm[r].on_mask;
pwm[0].on_mask ^= pwm[r].off_mask;
pwm[r].ticks = pwm_period_ticks - diff;
pwm[r].on_mask ^= mask;
pwm[r].off_mask ^= mask;
} else {
l = r;
}
r++;
}
#if PWM_DEBUG
for (t = 0; t <= phases; t++) {
ets_printf("%d @%d: %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
}
#endif
// sort again
n = 2;
while (n <= phases) {
if (pwm[n].ticks < pwm[n - 1].ticks) {
struct pwm_phase t = pwm[n];
pwm[n] = pwm[n - 1];
pwm[n - 1] = t;
if (n > 2)
n--;
} else {
n++;
}
}
// merge same duty
l = 0, r = 1;
while (r <= phases) {
if (pwm[r].ticks == pwm[l].ticks) {
pwm[l].off_mask |= pwm[r].off_mask;
pwm[l].on_mask |= pwm[r].on_mask;
pwm[r].on_mask = 0;
pwm[r].off_mask = 0;
} else {
l++;
if (l != r) {
struct pwm_phase t = pwm[l];
pwm[l] = pwm[r];
pwm[r] = t;
}
}
r++;
}
phases = l;
#if PWM_DEBUG
for (t = 0; t <= phases; t++) {
ets_printf("%d @%d: %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
}
#endif
// transform absolute end time to phase durations
for (n = 0; n < phases; n++) {
pwm[n].ticks =
pwm[n + 1].ticks - pwm[n].ticks;
// subtract common overhead
pwm[n].ticks--;
}
pwm[phases].ticks = 0;
// do a cyclic shift if last phase is short
if (pwm[phases - 1].ticks < 16) {
for (n = 0; n < phases - 1; n++) {
struct pwm_phase t = pwm[n];
pwm[n] = pwm[n + 1];
pwm[n + 1] = t;
}
}
#if PWM_DEBUG
for (t = 0; t <= phases; t++) {
ets_printf("%d +%d: %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
}
ets_printf("\n");
#endif
return phases;
}
void ICACHE_FLASH_ATTR
pwm_start(void)
{
pwm_phase_array* pwm = &pwm_phases[0];
if ((*pwm == pwm_state.next_set) ||
(*pwm == pwm_state.current_set))
pwm++;
if ((*pwm == pwm_state.next_set) ||
(*pwm == pwm_state.current_set))
pwm++;
uint8_t phases = _pwm_phases_prep(*pwm);
// all with 0% / 100% duty - stop timer
if (phases == 1) {
if (pwm_state.next_set) {
#if PWM_DEBUG
ets_printf("PWM stop\n");
#endif
timer->frc1_ctrl = 0;
ETS_FRC1_INTR_DISABLE();
}
pwm_state.next_set = NULL;
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, (*pwm)[0].on_mask);
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, (*pwm)[0].off_mask);
return;
}
// start if not running
if (!pwm_state.next_set) {
#if PWM_DEBUG
ets_printf("PWM start\n");
#endif
pwm_state.current_set = pwm_state.next_set = *pwm;
pwm_state.current_phase = phases - 1;
ETS_FRC1_INTR_ENABLE();
RTC_REG_WRITE(FRC1_LOAD_ADDRESS, 0);
timer->frc1_ctrl = TIMER1_DIVIDE_BY_16 | TIMER1_ENABLE_TIMER;
return;
}
pwm_state.next_set = *pwm;
}
void ICACHE_FLASH_ATTR
pwm_set_duty(uint32_t duty, uint8_t channel)
{
if (channel > PWM_MAX_CHANNELS)
return;
if (duty > PWM_MAX_DUTY)
duty = PWM_MAX_DUTY;
pwm_duty[channel] = duty;
}
uint32_t ICACHE_FLASH_ATTR
pwm_get_duty(uint8_t channel)
{
if (channel > PWM_MAX_CHANNELS)
return 0;
return pwm_duty[channel];
}
void ICACHE_FLASH_ATTR
pwm_set_period(uint32_t period)
{
pwm_period = period;
if (pwm_period > PWM_MAX_PERIOD)
pwm_period = PWM_MAX_PERIOD;
pwm_period_ticks = PWM_PERIOD_TO_TICKS(period);
}
uint32_t ICACHE_FLASH_ATTR
pwm_get_period(void)
{
return pwm_period;
}
uint32_t ICACHE_FLASH_ATTR
get_pwm_version(void)
{
return 1;
}
void ICACHE_FLASH_ATTR
set_pwm_debug_en(uint8_t print_en)
{
(void) print_en;
}

+ 33
- 0
code/espurna/pwm.h View File

@ -0,0 +1,33 @@
#ifndef __PWM_H__
#define __PWM_H__
/*pwm.h: function and macro definition of PWM API , driver level */
/*user_light.h: user interface for light API, user level*/
/*user_light_adj: API for color changing and lighting effects, user level*/
/*NOTE!! : DO NOT CHANGE THIS FILE*/
/*SUPPORT UP TO 8 PWM CHANNEL*/
//#define PWM_CHANNEL_NUM_MAX 8
struct pwm_param {
uint32 period;
uint32 freq;
uint32 duty[PWM_CHANNEL_NUM_MAX]; //PWM_CHANNEL<=8
};
/* pwm_init should be called only once, for now */
void pwm_init(uint32 period, uint32 *duty,uint32 pwm_channel_num,uint32 (*pin_info_list)[3]);
void pwm_start(void);
void pwm_set_duty(uint32 duty, uint8 channel);
uint32 pwm_get_duty(uint8 channel);
void pwm_set_period(uint32 period);
uint32 pwm_get_period(void);
uint32 get_pwm_version(void);
void set_pwm_debug_en(uint8 print_en);
#endif

Loading…
Cancel
Save