github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_stm32_tim.go (about) 1 //go:build stm32 2 3 package machine 4 5 // The type alias `arrtype` should be defined to either uint32 or uint16 6 // depending on the size of that register in the MCU's TIM_Type structure. 7 8 import ( 9 "device/stm32" 10 "runtime/interrupt" 11 "runtime/volatile" 12 ) 13 14 const PWM_MODE1 = 0x6 15 16 type TimerCallback func() 17 type ChannelCallback func(channel uint8) 18 19 type PinFunction struct { 20 Pin Pin 21 AltFunc uint8 22 } 23 24 type TimerChannel struct { 25 Pins []PinFunction 26 } 27 28 type TIM struct { 29 EnableRegister *volatile.Register32 30 EnableFlag uint32 31 Device *stm32.TIM_Type 32 Channels [4]TimerChannel 33 UpInterrupt interrupt.Interrupt 34 OCInterrupt interrupt.Interrupt 35 36 wraparoundCallback TimerCallback 37 channelCallbacks [4]ChannelCallback 38 39 busFreq uint64 40 } 41 42 // Configure enables and configures this PWM. 43 func (t *TIM) Configure(config PWMConfig) error { 44 // Enable device 45 t.EnableRegister.SetBits(t.EnableFlag) 46 47 err := t.setPeriod(config.Period, true) 48 if err != nil { 49 return err 50 } 51 52 // Auto-repeat 53 t.Device.EGR.SetBits(stm32.TIM_EGR_UG) 54 55 // Enable the timer 56 t.Device.CR1.SetBits(stm32.TIM_CR1_CEN | stm32.TIM_CR1_ARPE) 57 58 return nil 59 } 60 61 func (t *TIM) Count() uint32 { 62 return uint32(t.Device.CNT.Get()) 63 } 64 65 // SetWraparoundInterrupt configures a callback to be called each 66 // time the timer 'wraps-around'. 67 // 68 // For example, if `Configure(PWMConfig{Period:1000000})` is used, 69 // to set the timer period to 1ms, this callback will be called every 70 // 1ms. 71 func (t *TIM) SetWraparoundInterrupt(callback TimerCallback) error { 72 // Disable this interrupt to prevent race conditions 73 //t.UpInterrupt.Disable() 74 75 // Ensure the interrupt handler for Update events is registered 76 t.UpInterrupt = t.registerUPInterrupt() 77 78 // Clear update flag 79 t.Device.SR.ClearBits(stm32.TIM_SR_UIF) 80 81 t.wraparoundCallback = callback 82 t.UpInterrupt.SetPriority(0xc1) 83 t.UpInterrupt.Enable() 84 85 // Enable the hardware interrupt 86 t.Device.DIER.SetBits(stm32.TIM_DIER_UIE) 87 88 return nil 89 } 90 91 // Sets a callback to be called when a channel reaches it's set-point. 92 // 93 // For example, if `t.Set(ch, t.Top() / 4)` is used then the callback will 94 // be called every quarter-period of the timer's base Period. 95 func (t *TIM) SetMatchInterrupt(channel uint8, callback ChannelCallback) error { 96 t.channelCallbacks[channel] = callback 97 98 // Ensure the interrupt handler for Output Compare events is registered 99 t.OCInterrupt = t.registerOCInterrupt() 100 101 // Clear the interrupt flag 102 t.Device.SR.ClearBits(stm32.TIM_SR_CC1IF << channel) 103 104 // Enable the interrupt 105 t.OCInterrupt.SetPriority(0xc1) 106 t.OCInterrupt.Enable() 107 108 // Enable the hardware interrupt 109 t.Device.DIER.SetBits(stm32.TIM_DIER_CC1IE << channel) 110 111 return nil 112 } 113 114 // SetPeriod updates the period of this PWM peripheral. 115 // To set a particular frequency, use the following formula: 116 // 117 // period = 1e9 / frequency 118 // 119 // If you use a period of 0, a period that works well for LEDs will be picked. 120 // 121 // SetPeriod will not change the prescaler, but also won't change the current 122 // value in any of the channels. This means that you may need to update the 123 // value for the particular channel. 124 // 125 // Note that you cannot pick any arbitrary period after the PWM peripheral has 126 // been configured. If you want to switch between frequencies, pick the lowest 127 // frequency (longest period) once when calling Configure and adjust the 128 // frequency here as needed. 129 func (t *TIM) SetPeriod(period uint64) error { 130 return t.setPeriod(period, false) 131 } 132 133 func (t *TIM) setPeriod(period uint64, updatePrescaler bool) error { 134 var top uint64 135 if period == 0 { 136 top = ARR_MAX 137 } else { 138 top = (period / 1000) * (t.busFreq / 1000) / 1000 139 } 140 141 var psc uint64 142 if updatePrescaler { 143 if top > ARR_MAX*PSC_MAX { 144 return ErrPWMPeriodTooLong 145 } 146 147 // Select the minimum PSC that scales the ARR value into 148 // range to maintain precision in ARR for changing frequencies 149 // later 150 psc = ceil(top, ARR_MAX) 151 top = top / psc 152 153 t.Device.PSC.Set(uint32(psc - 1)) 154 } else { 155 psc = uint64(t.Device.PSC.Get()) + 1 156 top = top / psc 157 158 if top > ARR_MAX { 159 return ErrPWMPeriodTooLong 160 } 161 } 162 163 t.Device.ARR.Set(arrtype(top - 1)) 164 return nil 165 } 166 167 // Top returns the current counter top, for use in duty cycle calculation. It 168 // will only change with a call to Configure or SetPeriod, otherwise it is 169 // constant. 170 // 171 // The value returned here is hardware dependent. In general, it's best to treat 172 // it as an opaque value that can be divided by some number and passed to 173 // pwm.Set (see pwm.Set for more information). 174 func (t *TIM) Top() uint32 { 175 return uint32(t.Device.ARR.Get()) + 1 176 } 177 178 // Channel returns a PWM channel for the given pin. 179 func (t *TIM) Channel(pin Pin) (uint8, error) { 180 181 for chi, ch := range t.Channels { 182 for _, p := range ch.Pins { 183 if p.Pin == pin { 184 t.configurePin(uint8(chi), p) 185 //p.Pin.ConfigureAltFunc(PinConfig{Mode: PinModePWMOutput}, p.AltFunc) 186 return uint8(chi), nil 187 } 188 } 189 } 190 191 return 0, ErrInvalidOutputPin 192 } 193 194 // Set updates the channel value. This is used to control the channel duty 195 // cycle. For example, to set it to a 25% duty cycle, use: 196 // 197 // t.Set(ch, t.Top() / 4) 198 // 199 // ch.Set(0) will set the output to low and ch.Set(ch.Top()) will set the output 200 // to high, assuming the output isn't inverted. 201 func (t *TIM) Set(channel uint8, value uint32) { 202 t.enableMainOutput() 203 204 ccr := t.channelCCR(channel) 205 ccmr, offset := t.channelCCMR(channel) 206 207 // Disable interrupts whilst programming to prevent spurious OC interrupts 208 mask := interrupt.Disable() 209 210 // Set the PWM to Mode 1 (active below set value, inactive above) 211 // Preload is disabled so we can change OC value within one update period. 212 var ccmrVal uint32 213 ccmrVal |= PWM_MODE1 << stm32.TIM_CCMR1_Output_OC1M_Pos 214 ccmr.ReplaceBits(ccmrVal, 0xFF, offset) 215 216 // Set the compare value 217 ccr.Set(arrtype(value)) 218 219 // Enable the channel (if not already) 220 t.Device.CCER.ReplaceBits(stm32.TIM_CCER_CC1E, 0xD, channel*4) 221 222 // Force update 223 t.Device.EGR.SetBits(stm32.TIM_EGR_CC1G << channel) 224 225 // Reset Interrupt Flag 226 t.Device.SR.ClearBits(stm32.TIM_SR_CC1IF << channel) 227 228 // Restore interrupts 229 interrupt.Restore(mask) 230 } 231 232 // Unset disables a channel, including any configured interrupts. 233 func (t *TIM) Unset(channel uint8) { 234 // Disable interrupts whilst programming to prevent spurious OC interrupts 235 mask := interrupt.Disable() 236 237 // Disable the channel 238 t.Device.CCER.ReplaceBits(0, 0xD, channel*4) 239 240 // Reset to zero value 241 ccr := t.channelCCR(channel) 242 ccr.Set(0) 243 244 // Disable the hardware interrupt 245 t.Device.DIER.ClearBits(stm32.TIM_DIER_CC1IE << channel) 246 247 // Clear the interrupt flag 248 t.Device.SR.ClearBits(stm32.TIM_SR_CC1IF << channel) 249 250 // Restore interrupts 251 interrupt.Restore(mask) 252 } 253 254 // SetInverting sets whether to invert the output of this channel. 255 // Without inverting, a 25% duty cycle would mean the output is high for 25% of 256 // the time and low for the rest. Inverting flips the output as if a NOT gate 257 // was placed at the output, meaning that the output would be 25% low and 75% 258 // high with a duty cycle of 25%. 259 func (t *TIM) SetInverting(channel uint8, inverting bool) { 260 // Enable the channel (if not already) 261 262 var val = uint32(0) 263 if inverting { 264 val |= stm32.TIM_CCER_CC1P 265 } 266 267 t.Device.CCER.ReplaceBits(val, stm32.TIM_CCER_CC1P_Msk, channel*4) 268 } 269 270 func (t *TIM) handleUPInterrupt(interrupt.Interrupt) { 271 if t.Device.SR.HasBits(stm32.TIM_SR_UIF) { 272 // clear the update flag 273 t.Device.SR.ClearBits(stm32.TIM_SR_UIF) 274 275 if t.wraparoundCallback != nil { 276 t.wraparoundCallback() 277 } 278 } 279 } 280 281 func (t *TIM) handleOCInterrupt(interrupt.Interrupt) { 282 if t.Device.SR.HasBits(stm32.TIM_SR_CC1IF) { 283 if t.channelCallbacks[0] != nil { 284 t.channelCallbacks[0](0) 285 } 286 } 287 if t.Device.SR.HasBits(stm32.TIM_SR_CC2IF) { 288 if t.channelCallbacks[1] != nil { 289 t.channelCallbacks[1](1) 290 } 291 } 292 if t.Device.SR.HasBits(stm32.TIM_SR_CC3IF) { 293 if t.channelCallbacks[2] != nil { 294 t.channelCallbacks[2](2) 295 } 296 } 297 if t.Device.SR.HasBits(stm32.TIM_SR_CC4IF) { 298 if t.channelCallbacks[3] != nil { 299 t.channelCallbacks[3](3) 300 } 301 } 302 303 // Reset interrupt flags 304 t.Device.SR.ClearBits(stm32.TIM_SR_CC1IF | stm32.TIM_SR_CC2IF | stm32.TIM_SR_CC3IF | stm32.TIM_SR_CC4IF) 305 } 306 307 func (t *TIM) channelCCR(channel uint8) *arrRegType { 308 switch channel { 309 case 0: 310 return &t.Device.CCR1 311 case 1: 312 return &t.Device.CCR2 313 case 2: 314 return &t.Device.CCR3 315 case 3: 316 return &t.Device.CCR4 317 } 318 319 return nil 320 } 321 322 func (t *TIM) channelCCMR(channel uint8) (reg *volatile.Register32, offset uint8) { 323 switch channel { 324 case 0: 325 return &t.Device.CCMR1_Output, 0 326 case 1: 327 return &t.Device.CCMR1_Output, 8 328 case 2: 329 return &t.Device.CCMR2_Output, 0 330 case 3: 331 return &t.Device.CCMR2_Output, 8 332 } 333 334 return nil, 0 335 } 336 337 //go:inline 338 func ceil(num uint64, denom uint64) uint64 { 339 return (num + denom - 1) / denom 340 }