|
| 1 | +//go:build esp32 |
| 2 | + |
| 3 | +// PWM on the classic ESP32 uses the LEDC peripheral. |
| 4 | +// |
| 5 | +// LEDC here has two halves: a high-speed one and a low-speed one. This file uses |
| 6 | +// the high-speed half. It gives 4 timers (PWM0-PWM3) and 8 channels. |
| 7 | +// |
| 8 | +// The code that is the same on every ESP32 chip is in machine_esp32xx_pwm.go. |
| 9 | +// This file adds only the parts that are different here. |
| 10 | +// |
| 11 | +// Three things are different from the ESP32-C3 and the ESP32-S3: |
| 12 | +// |
| 13 | +// - The clock is switched on in DPORT. The newer chips renamed that block to |
| 14 | +// SYSTEM. This chip also has no LEDC.CONF_CLK_EN bit. |
| 15 | +// - Register names start with HS, for example HSTIMER0_CONF and HSCH0_CONF0. |
| 16 | +// - There is no PARA_UP bit. On the C3 and S3 you set PARA_UP to say "apply my |
| 17 | +// changes now". Here the channel applies them by itself, at the end of the |
| 18 | +// period it is in. That is what makes it "high speed". |
| 19 | +// See the ESP32 TRM, chapter "LED PWM Controller". |
| 20 | + |
| 21 | +package machine |
| 22 | + |
| 23 | +import "device/esp" |
| 24 | + |
| 25 | +// The GPIO matrix can send an internal signal to almost any pin. Each signal has |
| 26 | +// a number. High-speed LEDC channel 0 is number 71, and the rest follow on from |
| 27 | +// there, so channels 0 to 7 are 71 to 78. |
| 28 | +// (From Espressif's soc/gpio_sig_map.h: LEDC_HS_SIG_OUT0_IDX.) |
| 29 | +const LEDC_HS_SIG_OUT0_IDX = 71 |
| 30 | + |
| 31 | +const ledcChannelsESP32 = 8 |
| 32 | + |
| 33 | +var ( |
| 34 | + PWM0 = &LEDCPWM{SigOutBase: LEDC_HS_SIG_OUT0_IDX, NumChannels: ledcChannelsESP32, timerNum: 0} |
| 35 | + PWM1 = &LEDCPWM{SigOutBase: LEDC_HS_SIG_OUT0_IDX, NumChannels: ledcChannelsESP32, timerNum: 1} |
| 36 | + PWM2 = &LEDCPWM{SigOutBase: LEDC_HS_SIG_OUT0_IDX, NumChannels: ledcChannelsESP32, timerNum: 2} |
| 37 | + PWM3 = &LEDCPWM{SigOutBase: LEDC_HS_SIG_OUT0_IDX, NumChannels: ledcChannelsESP32, timerNum: 3} |
| 38 | +) |
| 39 | + |
| 40 | +// enableClock turns the LEDC hardware on and picks its clock. |
| 41 | +func (pwm *LEDCPWM) enableClock() { |
| 42 | + // Every peripheral starts switched off, to save power. Turn the LEDC clock |
| 43 | + // on, then take it out of reset. These registers are in DPORT on this chip. |
| 44 | + // The C3 and S3 call the same block SYSTEM. |
| 45 | + esp.DPORT.SetPERIP_RST_EN_LEDC_RST(1) |
| 46 | + esp.DPORT.SetPERIP_CLK_EN_LEDC_CLK_EN(1) |
| 47 | + esp.DPORT.SetPERIP_RST_EN_LEDC_RST(0) |
| 48 | + |
| 49 | + // Use APB_CLK, which runs at 80MHz. This chip has no CONF_CLK_EN bit, so |
| 50 | + // there is nothing more to switch on. |
| 51 | + esp.LEDC.SetCONF_APB_CLK_SEL(1) |
| 52 | +} |
| 53 | + |
| 54 | +// setTimerConf writes the resolution and the divider that Configure worked out |
| 55 | +// into one timer. |
| 56 | +// |
| 57 | +// Watch TICK_SEL. It is 1 here, not 0. On this chip: |
| 58 | +// |
| 59 | +// 1 = APB_CLK, 80MHz <- what we want |
| 60 | +// 0 = REF_TICK, 1MHz |
| 61 | +// |
| 62 | +// The low-speed timers on the C3 and S3 use the opposite meaning, so their code |
| 63 | +// writes 0. Writing 0 here would make every frequency 80 times too slow. |
| 64 | +// |
| 65 | +// These timers have no PARA_UP bit. The short reset pulse at the end is what |
| 66 | +// makes the new values take effect. |
| 67 | +func (pwm *LEDCPWM) setTimerConf(dutyRes uint8, divReg uint32) { |
| 68 | + switch pwm.timerNum { |
| 69 | + case 0: |
| 70 | + esp.LEDC.SetHSTIMER0_CONF_DUTY_RES(uint32(dutyRes)) |
| 71 | + esp.LEDC.SetHSTIMER0_CONF_DIV_NUM(divReg) |
| 72 | + esp.LEDC.SetHSTIMER0_CONF_TICK_SEL(1) |
| 73 | + esp.LEDC.SetHSTIMER0_CONF_PAUSE(0) |
| 74 | + esp.LEDC.SetHSTIMER0_CONF_RST(1) |
| 75 | + esp.LEDC.SetHSTIMER0_CONF_RST(0) |
| 76 | + case 1: |
| 77 | + esp.LEDC.SetHSTIMER1_CONF_DUTY_RES(uint32(dutyRes)) |
| 78 | + esp.LEDC.SetHSTIMER1_CONF_DIV_NUM(divReg) |
| 79 | + esp.LEDC.SetHSTIMER1_CONF_TICK_SEL(1) |
| 80 | + esp.LEDC.SetHSTIMER1_CONF_PAUSE(0) |
| 81 | + esp.LEDC.SetHSTIMER1_CONF_RST(1) |
| 82 | + esp.LEDC.SetHSTIMER1_CONF_RST(0) |
| 83 | + case 2: |
| 84 | + esp.LEDC.SetHSTIMER2_CONF_DUTY_RES(uint32(dutyRes)) |
| 85 | + esp.LEDC.SetHSTIMER2_CONF_DIV_NUM(divReg) |
| 86 | + esp.LEDC.SetHSTIMER2_CONF_TICK_SEL(1) |
| 87 | + esp.LEDC.SetHSTIMER2_CONF_PAUSE(0) |
| 88 | + esp.LEDC.SetHSTIMER2_CONF_RST(1) |
| 89 | + esp.LEDC.SetHSTIMER2_CONF_RST(0) |
| 90 | + case 3: |
| 91 | + esp.LEDC.SetHSTIMER3_CONF_DUTY_RES(uint32(dutyRes)) |
| 92 | + esp.LEDC.SetHSTIMER3_CONF_DIV_NUM(divReg) |
| 93 | + esp.LEDC.SetHSTIMER3_CONF_TICK_SEL(1) |
| 94 | + esp.LEDC.SetHSTIMER3_CONF_PAUSE(0) |
| 95 | + esp.LEDC.SetHSTIMER3_CONF_RST(1) |
| 96 | + esp.LEDC.SetHSTIMER3_CONF_RST(0) |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +// chanOp does the work for one channel, numbered 0 to 7. It either sets the |
| 101 | +// channel up, changes its duty, or flips its idle level. |
| 102 | +// |
| 103 | +// Each channel has its own registers, and each register has its own generated |
| 104 | +// setter function. There is no array to index, so this has to be a long switch. |
| 105 | +// The C3 and S3 files have the same shape. |
| 106 | +// |
| 107 | +// DUTY_SCALE is set to 0. LEDC can fade slowly from one duty to the next; 0 |
| 108 | +// turns that off, so the duty changes in a single step. |
| 109 | +func (pwm *LEDCPWM) chanOp(ch uint8, op ledcChanOp, duty uint32, inverting bool) { |
| 110 | + invVal := uint32(0) |
| 111 | + if inverting { |
| 112 | + invVal = 1 |
| 113 | + } |
| 114 | + switch ch { |
| 115 | + case 0: |
| 116 | + switch op { |
| 117 | + case ledcChanOpInit: |
| 118 | + esp.LEDC.SetHSCH0_CONF0_TIMER_SEL(uint32(pwm.timerNum)) |
| 119 | + esp.LEDC.SetHSCH0_CONF0_SIG_OUT_EN(1) |
| 120 | + esp.LEDC.SetHSCH0_CONF0_IDLE_LV(0) |
| 121 | + esp.LEDC.SetHSCH0_HPOINT_HPOINT(0) |
| 122 | + esp.LEDC.SetHSCH0_DUTY_DUTY(0) |
| 123 | + esp.LEDC.SetHSCH0_CONF1_DUTY_SCALE(0) |
| 124 | + esp.LEDC.SetHSCH0_CONF1_DUTY_CYCLE(1) |
| 125 | + esp.LEDC.SetHSCH0_CONF1_DUTY_NUM(1) |
| 126 | + esp.LEDC.SetHSCH0_CONF1_DUTY_INC(1) |
| 127 | + esp.LEDC.SetHSCH0_CONF1_DUTY_START(1) |
| 128 | + case ledcChanOpSetDuty: |
| 129 | + esp.LEDC.SetHSCH0_DUTY_DUTY(duty) |
| 130 | + esp.LEDC.SetHSCH0_CONF1_DUTY_CYCLE(1) |
| 131 | + esp.LEDC.SetHSCH0_CONF1_DUTY_NUM(1) |
| 132 | + esp.LEDC.SetHSCH0_CONF1_DUTY_INC(1) |
| 133 | + esp.LEDC.SetHSCH0_CONF1_DUTY_START(1) |
| 134 | + esp.LEDC.SetHSCH0_CONF0_SIG_OUT_EN(1) |
| 135 | + case ledcChanOpSetInvert: |
| 136 | + esp.LEDC.SetHSCH0_CONF0_IDLE_LV(invVal) |
| 137 | + } |
| 138 | + case 1: |
| 139 | + switch op { |
| 140 | + case ledcChanOpInit: |
| 141 | + esp.LEDC.SetHSCH1_CONF0_TIMER_SEL(uint32(pwm.timerNum)) |
| 142 | + esp.LEDC.SetHSCH1_CONF0_SIG_OUT_EN(1) |
| 143 | + esp.LEDC.SetHSCH1_CONF0_IDLE_LV(0) |
| 144 | + esp.LEDC.SetHSCH1_HPOINT_HPOINT(0) |
| 145 | + esp.LEDC.SetHSCH1_DUTY_DUTY(0) |
| 146 | + esp.LEDC.SetHSCH1_CONF1_DUTY_SCALE(0) |
| 147 | + esp.LEDC.SetHSCH1_CONF1_DUTY_CYCLE(1) |
| 148 | + esp.LEDC.SetHSCH1_CONF1_DUTY_NUM(1) |
| 149 | + esp.LEDC.SetHSCH1_CONF1_DUTY_INC(1) |
| 150 | + esp.LEDC.SetHSCH1_CONF1_DUTY_START(1) |
| 151 | + case ledcChanOpSetDuty: |
| 152 | + esp.LEDC.SetHSCH1_DUTY_DUTY(duty) |
| 153 | + esp.LEDC.SetHSCH1_CONF1_DUTY_CYCLE(1) |
| 154 | + esp.LEDC.SetHSCH1_CONF1_DUTY_NUM(1) |
| 155 | + esp.LEDC.SetHSCH1_CONF1_DUTY_INC(1) |
| 156 | + esp.LEDC.SetHSCH1_CONF1_DUTY_START(1) |
| 157 | + esp.LEDC.SetHSCH1_CONF0_SIG_OUT_EN(1) |
| 158 | + case ledcChanOpSetInvert: |
| 159 | + esp.LEDC.SetHSCH1_CONF0_IDLE_LV(invVal) |
| 160 | + } |
| 161 | + case 2: |
| 162 | + switch op { |
| 163 | + case ledcChanOpInit: |
| 164 | + esp.LEDC.SetHSCH2_CONF0_TIMER_SEL(uint32(pwm.timerNum)) |
| 165 | + esp.LEDC.SetHSCH2_CONF0_SIG_OUT_EN(1) |
| 166 | + esp.LEDC.SetHSCH2_CONF0_IDLE_LV(0) |
| 167 | + esp.LEDC.SetHSCH2_HPOINT_HPOINT(0) |
| 168 | + esp.LEDC.SetHSCH2_DUTY_DUTY(0) |
| 169 | + esp.LEDC.SetHSCH2_CONF1_DUTY_SCALE(0) |
| 170 | + esp.LEDC.SetHSCH2_CONF1_DUTY_CYCLE(1) |
| 171 | + esp.LEDC.SetHSCH2_CONF1_DUTY_NUM(1) |
| 172 | + esp.LEDC.SetHSCH2_CONF1_DUTY_INC(1) |
| 173 | + esp.LEDC.SetHSCH2_CONF1_DUTY_START(1) |
| 174 | + case ledcChanOpSetDuty: |
| 175 | + esp.LEDC.SetHSCH2_DUTY_DUTY(duty) |
| 176 | + esp.LEDC.SetHSCH2_CONF1_DUTY_CYCLE(1) |
| 177 | + esp.LEDC.SetHSCH2_CONF1_DUTY_NUM(1) |
| 178 | + esp.LEDC.SetHSCH2_CONF1_DUTY_INC(1) |
| 179 | + esp.LEDC.SetHSCH2_CONF1_DUTY_START(1) |
| 180 | + esp.LEDC.SetHSCH2_CONF0_SIG_OUT_EN(1) |
| 181 | + case ledcChanOpSetInvert: |
| 182 | + esp.LEDC.SetHSCH2_CONF0_IDLE_LV(invVal) |
| 183 | + } |
| 184 | + case 3: |
| 185 | + switch op { |
| 186 | + case ledcChanOpInit: |
| 187 | + esp.LEDC.SetHSCH3_CONF0_TIMER_SEL(uint32(pwm.timerNum)) |
| 188 | + esp.LEDC.SetHSCH3_CONF0_SIG_OUT_EN(1) |
| 189 | + esp.LEDC.SetHSCH3_CONF0_IDLE_LV(0) |
| 190 | + esp.LEDC.SetHSCH3_HPOINT_HPOINT(0) |
| 191 | + esp.LEDC.SetHSCH3_DUTY_DUTY(0) |
| 192 | + esp.LEDC.SetHSCH3_CONF1_DUTY_SCALE(0) |
| 193 | + esp.LEDC.SetHSCH3_CONF1_DUTY_CYCLE(1) |
| 194 | + esp.LEDC.SetHSCH3_CONF1_DUTY_NUM(1) |
| 195 | + esp.LEDC.SetHSCH3_CONF1_DUTY_INC(1) |
| 196 | + esp.LEDC.SetHSCH3_CONF1_DUTY_START(1) |
| 197 | + case ledcChanOpSetDuty: |
| 198 | + esp.LEDC.SetHSCH3_DUTY_DUTY(duty) |
| 199 | + esp.LEDC.SetHSCH3_CONF1_DUTY_CYCLE(1) |
| 200 | + esp.LEDC.SetHSCH3_CONF1_DUTY_NUM(1) |
| 201 | + esp.LEDC.SetHSCH3_CONF1_DUTY_INC(1) |
| 202 | + esp.LEDC.SetHSCH3_CONF1_DUTY_START(1) |
| 203 | + esp.LEDC.SetHSCH3_CONF0_SIG_OUT_EN(1) |
| 204 | + case ledcChanOpSetInvert: |
| 205 | + esp.LEDC.SetHSCH3_CONF0_IDLE_LV(invVal) |
| 206 | + } |
| 207 | + case 4: |
| 208 | + switch op { |
| 209 | + case ledcChanOpInit: |
| 210 | + esp.LEDC.SetHSCH4_CONF0_TIMER_SEL(uint32(pwm.timerNum)) |
| 211 | + esp.LEDC.SetHSCH4_CONF0_SIG_OUT_EN(1) |
| 212 | + esp.LEDC.SetHSCH4_CONF0_IDLE_LV(0) |
| 213 | + esp.LEDC.SetHSCH4_HPOINT_HPOINT(0) |
| 214 | + esp.LEDC.SetHSCH4_DUTY_DUTY(0) |
| 215 | + esp.LEDC.SetHSCH4_CONF1_DUTY_SCALE(0) |
| 216 | + esp.LEDC.SetHSCH4_CONF1_DUTY_CYCLE(1) |
| 217 | + esp.LEDC.SetHSCH4_CONF1_DUTY_NUM(1) |
| 218 | + esp.LEDC.SetHSCH4_CONF1_DUTY_INC(1) |
| 219 | + esp.LEDC.SetHSCH4_CONF1_DUTY_START(1) |
| 220 | + case ledcChanOpSetDuty: |
| 221 | + esp.LEDC.SetHSCH4_DUTY_DUTY(duty) |
| 222 | + esp.LEDC.SetHSCH4_CONF1_DUTY_CYCLE(1) |
| 223 | + esp.LEDC.SetHSCH4_CONF1_DUTY_NUM(1) |
| 224 | + esp.LEDC.SetHSCH4_CONF1_DUTY_INC(1) |
| 225 | + esp.LEDC.SetHSCH4_CONF1_DUTY_START(1) |
| 226 | + esp.LEDC.SetHSCH4_CONF0_SIG_OUT_EN(1) |
| 227 | + case ledcChanOpSetInvert: |
| 228 | + esp.LEDC.SetHSCH4_CONF0_IDLE_LV(invVal) |
| 229 | + } |
| 230 | + case 5: |
| 231 | + switch op { |
| 232 | + case ledcChanOpInit: |
| 233 | + esp.LEDC.SetHSCH5_CONF0_TIMER_SEL(uint32(pwm.timerNum)) |
| 234 | + esp.LEDC.SetHSCH5_CONF0_SIG_OUT_EN(1) |
| 235 | + esp.LEDC.SetHSCH5_CONF0_IDLE_LV(0) |
| 236 | + esp.LEDC.SetHSCH5_HPOINT_HPOINT(0) |
| 237 | + esp.LEDC.SetHSCH5_DUTY_DUTY(0) |
| 238 | + esp.LEDC.SetHSCH5_CONF1_DUTY_SCALE(0) |
| 239 | + esp.LEDC.SetHSCH5_CONF1_DUTY_CYCLE(1) |
| 240 | + esp.LEDC.SetHSCH5_CONF1_DUTY_NUM(1) |
| 241 | + esp.LEDC.SetHSCH5_CONF1_DUTY_INC(1) |
| 242 | + esp.LEDC.SetHSCH5_CONF1_DUTY_START(1) |
| 243 | + case ledcChanOpSetDuty: |
| 244 | + esp.LEDC.SetHSCH5_DUTY_DUTY(duty) |
| 245 | + esp.LEDC.SetHSCH5_CONF1_DUTY_CYCLE(1) |
| 246 | + esp.LEDC.SetHSCH5_CONF1_DUTY_NUM(1) |
| 247 | + esp.LEDC.SetHSCH5_CONF1_DUTY_INC(1) |
| 248 | + esp.LEDC.SetHSCH5_CONF1_DUTY_START(1) |
| 249 | + esp.LEDC.SetHSCH5_CONF0_SIG_OUT_EN(1) |
| 250 | + case ledcChanOpSetInvert: |
| 251 | + esp.LEDC.SetHSCH5_CONF0_IDLE_LV(invVal) |
| 252 | + } |
| 253 | + case 6: |
| 254 | + switch op { |
| 255 | + case ledcChanOpInit: |
| 256 | + esp.LEDC.SetHSCH6_CONF0_TIMER_SEL(uint32(pwm.timerNum)) |
| 257 | + esp.LEDC.SetHSCH6_CONF0_SIG_OUT_EN(1) |
| 258 | + esp.LEDC.SetHSCH6_CONF0_IDLE_LV(0) |
| 259 | + esp.LEDC.SetHSCH6_HPOINT_HPOINT(0) |
| 260 | + esp.LEDC.SetHSCH6_DUTY_DUTY(0) |
| 261 | + esp.LEDC.SetHSCH6_CONF1_DUTY_SCALE(0) |
| 262 | + esp.LEDC.SetHSCH6_CONF1_DUTY_CYCLE(1) |
| 263 | + esp.LEDC.SetHSCH6_CONF1_DUTY_NUM(1) |
| 264 | + esp.LEDC.SetHSCH6_CONF1_DUTY_INC(1) |
| 265 | + esp.LEDC.SetHSCH6_CONF1_DUTY_START(1) |
| 266 | + case ledcChanOpSetDuty: |
| 267 | + esp.LEDC.SetHSCH6_DUTY_DUTY(duty) |
| 268 | + esp.LEDC.SetHSCH6_CONF1_DUTY_CYCLE(1) |
| 269 | + esp.LEDC.SetHSCH6_CONF1_DUTY_NUM(1) |
| 270 | + esp.LEDC.SetHSCH6_CONF1_DUTY_INC(1) |
| 271 | + esp.LEDC.SetHSCH6_CONF1_DUTY_START(1) |
| 272 | + esp.LEDC.SetHSCH6_CONF0_SIG_OUT_EN(1) |
| 273 | + case ledcChanOpSetInvert: |
| 274 | + esp.LEDC.SetHSCH6_CONF0_IDLE_LV(invVal) |
| 275 | + } |
| 276 | + case 7: |
| 277 | + switch op { |
| 278 | + case ledcChanOpInit: |
| 279 | + esp.LEDC.SetHSCH7_CONF0_TIMER_SEL(uint32(pwm.timerNum)) |
| 280 | + esp.LEDC.SetHSCH7_CONF0_SIG_OUT_EN(1) |
| 281 | + esp.LEDC.SetHSCH7_CONF0_IDLE_LV(0) |
| 282 | + esp.LEDC.SetHSCH7_HPOINT_HPOINT(0) |
| 283 | + esp.LEDC.SetHSCH7_DUTY_DUTY(0) |
| 284 | + esp.LEDC.SetHSCH7_CONF1_DUTY_SCALE(0) |
| 285 | + esp.LEDC.SetHSCH7_CONF1_DUTY_CYCLE(1) |
| 286 | + esp.LEDC.SetHSCH7_CONF1_DUTY_NUM(1) |
| 287 | + esp.LEDC.SetHSCH7_CONF1_DUTY_INC(1) |
| 288 | + esp.LEDC.SetHSCH7_CONF1_DUTY_START(1) |
| 289 | + case ledcChanOpSetDuty: |
| 290 | + esp.LEDC.SetHSCH7_DUTY_DUTY(duty) |
| 291 | + esp.LEDC.SetHSCH7_CONF1_DUTY_CYCLE(1) |
| 292 | + esp.LEDC.SetHSCH7_CONF1_DUTY_NUM(1) |
| 293 | + esp.LEDC.SetHSCH7_CONF1_DUTY_INC(1) |
| 294 | + esp.LEDC.SetHSCH7_CONF1_DUTY_START(1) |
| 295 | + esp.LEDC.SetHSCH7_CONF0_SIG_OUT_EN(1) |
| 296 | + case ledcChanOpSetInvert: |
| 297 | + esp.LEDC.SetHSCH7_CONF0_IDLE_LV(invVal) |
| 298 | + } |
| 299 | + } |
| 300 | +} |
0 commit comments