Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
TimerBasedPwm.hxx
1#ifndef _DRIVERS_ESP8266_TIMERBASEDPWM_HXX_
2#define _DRIVERS_ESP8266_TIMERBASEDPWM_HXX_
3
4extern "C" {
5#include <eagle_soc.h>
6#include <gpio.h>
7}
8
9#include "utils/blinker.h"
10
11//Register definitions
12
13#define FRC1_LOAD READ_PERI_REG(PERIPHS_TIMER_BASEDDR + FRC1_LOAD_ADDRESS)
14#define FRC1_COUNT READ_PERI_REG(PERIPHS_TIMER_BASEDDR + FRC1_COUNT_ADDRESS)
15#define FRC1_CTRL READ_PERI_REG(PERIPHS_TIMER_BASEDDR + FRC1_CTRL_ADDRESS)
16#define FRC1_INTCLR READ_PERI_REG(PERIPHS_TIMER_BASEDDR + FRC1_INT_ADDRESS)
17
18#define FRC_CTL_INT_ACTIVE 0x100
19
20#define FRC_CTL_ENABLE 0x80
21#define FRC_CTL_AUTORELOAD 0x40
22#define FRC_CTL_DIV_1 0x00
23#define FRC_CTL_DIV_16 0x04
24#define FRC_CTL_DIV_256 0x0C
25#define FRC_CTL_INT_EDGE 0x00
26#define FRC_CTL_INT_LEVEL 0x01
27
28#define FRC2_LOAD READ_PERI_REG(PERIPHS_TIMER_BASEDDR + 0x20)
29#define FRC2_COUNT READ_PERI_REG(PERIPHS_TIMER_BASEDDR + 0x24)
30#define FRC2_CTRL READ_PERI_REG(PERIPHS_TIMER_BASEDDR + 0x28)
31#define FRC2_INTCLR READ_PERI_REG(PERIPHS_TIMER_BASEDDR + 0x2C)
32#define FRC2_ALARM READ_PERI_REG(PERIPHS_TIMER_BASEDDR + 0x30)
33
34
35#define GPIO_OUT_CLR GPIO_REG_READ(GPIO_OUT_W1TC_ADDRESS)
36#define GPIO_OUT_SET GPIO_REG_READ(GPIO_OUT_W1TS_ADDRESS)
37
38
39void isr_test() {
40 FRC1_INTCLR = 0;
41}
42
47public:
49 }
50
52 void __attribute__((noinline)) enable() {
53 ETS_FRC1_INTR_DISABLE();
54#pragma GCC diagnostic push
55#pragma GCC diagnostic ignored "-Wpmf-conversions"
56 ETS_FRC_TIMER1_INTR_ATTACH(&TimerBasedPwm::owned_isr_handler, this);
57 //ETS_FRC_TIMER1_NMI_INTR_ATTACH(reinterpret_cast<uint32_t>(&TimerBasedPwm::isr_handler));
58#pragma GCC diagnostic pop
59 }
60
62 static void ICACHE_RAM_ATTR isr_handler(void*) {
63 FRC1_INTCLR = 0;
64 }
65
68 if (isOn_) {
69 isOn_ = false;
70 FRC1_LOAD = clockOff_;
71 GPIO_OUT_CLR = gpioValue_;
72 } else {
73 isOn_ = true;
74 FRC1_LOAD = clockOn_;
75 GPIO_OUT_SET = gpioValue_;
76 }
77 }
78
83 void set_off() {
84 ETS_FRC1_INTR_DISABLE();
85 if (gpioValue_) {
86 GPIO_OUT_CLR = gpioValue_;
87 }
88 }
89
96 void old_set_state(int pin, long long nsec_period, long long nsec_on) {
97 enable();
98 ETS_FRC1_INTR_DISABLE();
99 gpioValue_ = 1<<pin;
100 if (nsec_on <= 0) {
101 GPIO_OUTPUT_SET(pin, 0);
102 return;
103 }
104 if (nsec_on >= nsec_period) {
105 GPIO_OUTPUT_SET(pin, 1);
106 return;
107 }
108 // CPU clock = 80 MHz, 12.5 nsec per clock.
109 clockOn_ = (nsec_on * 2) / 25;
110 long long nsec_off = nsec_period - nsec_on;
111 clockOff_ = (nsec_off * 2) / 25;
112 HASSERT(clockOn_ < (1<<23));
113 HASSERT(clockOff_ < (1<<23));
114 GPIO_OUTPUT_SET(pin, 0);
115 isOn_ = false;
116 FRC1_CTRL = FRC_CTL_ENABLE | FRC_CTL_DIV_1 | FRC_CTL_INT_EDGE;
117 FRC1_INTCLR = 0;
118 ETS_FRC1_INTR_ENABLE();
119 TM1_EDGE_INT_ENABLE();
120 FRC1_LOAD = 100; // will trigger interrupt immediately
121 TM1_EDGE_INT_ENABLE();
122 }
123
135 void pause(long long nsec_pause) {
136 ETS_FRC1_INTR_DISABLE();
137 unsigned clock_pause = (nsec_pause * 2) / 25;
138 HASSERT(clock_pause < (1<<23));
139 FRC1_INTCLR = 0;
140 ETS_FRC1_INTR_ENABLE();
141 TM1_EDGE_INT_ENABLE();
142 FRC1_LOAD = std::max(clock_pause, 1U);
143 }
144
146 static void ICACHE_RAM_ATTR new_isr_handler(void*) {
147 //if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) TEIE &= ~TEIE1;//edge int disable
148 FRC1_INTCLR = 0;
149 if (isOn_) {
150 GPIO_OUT_CLR = gpioValue_;
151 isOn_ = false;
152 FRC1_LOAD = clockOff_;
153 //TM1_EDGE_INT_ENABLE();
154 } else {
155 GPIO_OUT_SET = gpioValue_;
156 isOn_ = true;
157 FRC1_LOAD = clockOn_;
158 //TM1_EDGE_INT_ENABLE();
159 }
160 }
161
168 void set_state(int pin, long long nsec_period, long long nsec_on)
169 {
170 gpioValue_ = 1<<pin;
171
172 // CPU clock = 80 MHz, 12.5 nsec per clock.
173 clockOn_ = (nsec_on * 2) / 25;
174 long long nsec_off = nsec_period - nsec_on;
175 clockOff_ = (nsec_off * 2) / 25;
176 HASSERT(clockOn_ < (1<<23));
177 HASSERT(clockOff_ < (1<<23));
178
179 //timer1_disable();
180 ETS_FRC_TIMER1_INTR_ATTACH(&new_isr_handler, NULL);
181 ETS_FRC1_INTR_ENABLE();
182 //timer1_attachInterrupt(&TimerBasedPwm::new_isr_handler);
183 FRC1_CTRL = FRC_CTL_ENABLE | FRC_CTL_DIV_1 | FRC_CTL_INT_EDGE;
184 //timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE);
185 FRC1_LOAD = 1;
186 TM1_EDGE_INT_ENABLE();
187 //timer1_write(1);
188 }
189
190private:
192 static uint32_t gpioValue_;
195 static uint32_t clockOn_;
198 static uint32_t clockOff_;
200 static bool isOn_;
201};
202
203
204uint32_t TimerBasedPwm::gpioValue_ = 0;
208
209
210#endif // _DRIVERS_ESP8266_TIMERBASEDPWM_HXX_
Single-channel PWM implementation for the ESP8266 that uses the hardware timer resource.
void ICACHE_RAM_ATTR owned_isr_handler()
interrupt handler (to be called with a this pointer externally stored).
static uint32_t clockOn_
How many clock cycles we should wait after switching ON before swithing OFF.
void enable()
Turns on the PWM.
void pause(long long nsec_pause)
Pauses updating the PWM for a certain amount of time.
void set_off()
Turns off the previous output that may have been running.
static void ICACHE_RAM_ATTR new_isr_handler(void *)
New implementation of the interrupt handler.
static uint32_t clockOff_
How many clock cycles we should wait after switching OFF before swithing ON.
void set_state(int pin, long long nsec_period, long long nsec_on)
Sets the PWM parameters.
static uint32_t gpioValue_
Bit-shifted value to send to the GPIO output port.
void old_set_state(int pin, long long nsec_period, long long nsec_on)
Enables the PWM on a specific pin.
static void ICACHE_RAM_ATTR isr_handler(void *)
Interrupt handler.
static bool isOn_
1 if output is currently on, 0 if it is off.
#define ICACHE_RAM_ATTR
Declares (on the ESP8266) that the current function is executed often and should be placed in the ins...
Definition macros.h:225
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138