diff --git a/Makefile b/Makefile index 52da5204..af61eb40 100644 --- a/Makefile +++ b/Makefile @@ -381,6 +381,8 @@ ifneq ($(STM32), 0) @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1 @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/pwm + @$(MD5SUM) test.hex endif ifneq ($(AVR), 0) $(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial diff --git a/src/examples/pwm/stm32f4disco.go b/src/examples/pwm/stm32f4disco.go new file mode 100644 index 00000000..baa34e99 --- /dev/null +++ b/src/examples/pwm/stm32f4disco.go @@ -0,0 +1,13 @@ +// +build stm32f4disco + +package main + +import "machine" + +var ( + // These pins correspond to LEDs on the discovery + // board + pwm = &machine.TIM4 + pinA = machine.PD12 + pinB = machine.PD13 +) diff --git a/src/machine/machine_stm32_moder_gpio.go b/src/machine/machine_stm32_moder_gpio.go index 2b3da819..ec4a7c17 100644 --- a/src/machine/machine_stm32_moder_gpio.go +++ b/src/machine/machine_stm32_moder_gpio.go @@ -35,7 +35,7 @@ const ( PinInputAnalog PinMode = 11 // for PWM - // TBD + PinModePWMOutput PinMode = 12 ) // Define several bitfields that have different names across chip families but @@ -135,6 +135,13 @@ func (p Pin) ConfigureAltFunc(config PinConfig, altFunc uint8) { port.OSPEEDR.ReplaceBits(gpioOutputSpeedLow, gpioOutputSpeedMask, pos) port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos) p.SetAltFunc(altFunc) + + // PWM + case PinModePWMOutput: + port.MODER.ReplaceBits(gpioModeAlternate, gpioModeMask, pos) + port.OSPEEDR.ReplaceBits(gpioOutputSpeedHigh, gpioOutputSpeedMask, pos) + port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos) + p.SetAltFunc(altFunc) } } diff --git a/src/machine/machine_stm32_tim.go b/src/machine/machine_stm32_tim.go new file mode 100644 index 00000000..160e00ae --- /dev/null +++ b/src/machine/machine_stm32_tim.go @@ -0,0 +1,334 @@ +// +build stm32f4 + +package machine + +import ( + "device/stm32" + "runtime/interrupt" + "runtime/volatile" +) + +type TimerCallback func() +type ChannelCallback func(channel uint8) + +type PinFunction struct { + Pin Pin + AltFunc uint8 +} + +type TimerChannel struct { + Pins []PinFunction +} + +type TIM struct { + EnableRegister *volatile.Register32 + EnableFlag uint32 + Device *stm32.TIM_Type + Channels [4]TimerChannel + UpInterrupt interrupt.Interrupt + OCInterrupt interrupt.Interrupt + + wraparoundCallback TimerCallback + channelCallbacks [4]ChannelCallback + + busFreq uint64 +} + +// Configure enables and configures this PWM. +func (t *TIM) Configure(config PWMConfig) error { + // Enable device + t.EnableRegister.SetBits(t.EnableFlag) + + err := t.setPeriod(config.Period, true) + if err != nil { + return err + } + + // Auto-repeat + t.Device.EGR.SetBits(stm32.TIM_EGR_UG) + + // Enable the timer + t.Device.CR1.SetBits(stm32.TIM_CR1_CEN | stm32.TIM_CR1_ARPE) + + return nil +} + +func (t *TIM) Count() uint32 { + return uint32(t.Device.CNT.Get()) +} + +// SetWraparoundInterrupt configures a callback to be called each +// time the timer 'wraps-around'. +// +// For example, if `Configure(PWMConfig{Period:1000000})` is used, +// to set the timer period to 1ms, this callback will be called every +// 1ms. +func (t *TIM) SetWraparoundInterrupt(callback TimerCallback) error { + // Disable this interrupt to prevent race conditions + //t.UpInterrupt.Disable() + + // Ensure the interrupt handler for Update events is registered + t.UpInterrupt = t.registerUPInterrupt() + + // Clear update flag + t.Device.SR.ClearBits(stm32.TIM_SR_UIF) + + t.wraparoundCallback = callback + t.UpInterrupt.SetPriority(0xc1) + t.UpInterrupt.Enable() + + // Enable the hardware interrupt + t.Device.DIER.SetBits(stm32.TIM_DIER_UIE) + + return nil +} + +// Sets a callback to be called when a channel reaches it's set-point. +// +// For example, if `t.Set(ch, t.Top() / 4)` is used then the callback will +// be called every quarter-period of the timer's base Period. +func (t *TIM) SetMatchInterrupt(channel uint8, callback ChannelCallback) error { + t.channelCallbacks[channel] = callback + + // Ensure the interrupt handler for Output Compare events is registered + t.OCInterrupt = t.registerOCInterrupt() + + // Clear the interrupt flag + t.Device.SR.ClearBits(stm32.TIM_SR_CC1IF_Match << channel) + + // Enable the interrupt + t.OCInterrupt.SetPriority(0xc1) + t.OCInterrupt.Enable() + + // Enable the hardware interrupt + t.Device.DIER.SetBits(stm32.TIM_DIER_CC1IE << channel) + + return nil +} + +// SetPeriod updates the period of this PWM peripheral. +// To set a particular frequency, use the following formula: +// +// period = 1e9 / frequency +// +// If you use a period of 0, a period that works well for LEDs will be picked. +// +// SetPeriod will not change the prescaler, but also won't change the current +// value in any of the channels. This means that you may need to update the +// value for the particular channel. +// +// Note that you cannot pick any arbitrary period after the PWM peripheral has +// been configured. If you want to switch between frequencies, pick the lowest +// frequency (longest period) once when calling Configure and adjust the +// frequency here as needed. +func (t *TIM) SetPeriod(period uint64) error { + return t.setPeriod(period, false) +} + +func (t *TIM) setPeriod(period uint64, updatePrescaler bool) error { + var top uint64 + if period == 0 { + top = ARR_MAX + } else { + top = (period / 1000) * (t.busFreq / 1000) / 1000 + } + + var psc uint64 + if updatePrescaler { + if top > ARR_MAX*PSC_MAX { + return ErrPWMPeriodTooLong + } + + // Select the minimum PSC that scales the ARR value into + // range to maintain precision in ARR for changing frequencies + // later + psc = ceil(top, ARR_MAX) + top = top / psc + + t.Device.PSC.Set(uint32(psc - 1)) + } else { + psc = uint64(t.Device.PSC.Get()) + 1 + top = top / psc + + if top > ARR_MAX { + return ErrPWMPeriodTooLong + } + } + + t.Device.ARR.Set(arrtype(top - 1)) + return nil +} + +// Top returns the current counter top, for use in duty cycle calculation. It +// will only change with a call to Configure or SetPeriod, otherwise it is +// constant. +// +// The value returned here is hardware dependent. In general, it's best to treat +// it as an opaque value that can be divided by some number and passed to +// pwm.Set (see pwm.Set for more information). +func (t *TIM) Top() uint32 { + return uint32(t.Device.ARR.Get()) + 1 +} + +// Channel returns a PWM channel for the given pin. +func (t *TIM) Channel(pin Pin) (uint8, error) { + + for chi, ch := range t.Channels { + for _, p := range ch.Pins { + if p.Pin == pin { + p.Pin.ConfigureAltFunc(PinConfig{Mode: PinModePWMOutput}, p.AltFunc) + return uint8(chi), nil + } + } + } + + return 0, ErrInvalidOutputPin +} + +// Set updates the channel value. This is used to control the channel duty +// cycle. For example, to set it to a 25% duty cycle, use: +// +// t.Set(ch, t.Top() / 4) +// +// ch.Set(0) will set the output to low and ch.Set(ch.Top()) will set the output +// to high, assuming the output isn't inverted. +func (t *TIM) Set(channel uint8, value uint32) { + t.enableMainOutput() + + ccr := t.channelCCR(channel) + ccmr, offset := t.channelCCMR(channel) + + // Disable interrupts whilst programming to prevent spurious OC interrupts + mask := interrupt.Disable() + + // Set the PWM to Mode 1 (active below set value, inactive above) + // Preload is disabled so we can change OC value within one update period. + var ccmrVal uint32 + ccmrVal |= PWM_MODE1 << stm32.TIM_CCMR1_Output_OC1M_Pos + ccmr.ReplaceBits(ccmrVal, 0xFF, offset) + + // Set the compare value + ccr.Set(arrtype(value)) + + // Enable the channel (if not already) + t.Device.CCER.ReplaceBits(stm32.TIM_CCER_CC1E, 0xD, channel*4) + + // Force update + t.Device.EGR.SetBits(stm32.TIM_EGR_CC1G << channel) + + // Reset Interrupt Flag + t.Device.SR.ClearBits(stm32.TIM_SR_CC1IF << channel) + + // Restore interrupts + interrupt.Restore(mask) +} + +// Unset disables a channel, including any configured interrupts. +func (t *TIM) Unset(channel uint8) { + // Disable interrupts whilst programming to prevent spurious OC interrupts + mask := interrupt.Disable() + + // Disable the channel + t.Device.CCER.ReplaceBits(0, 0xD, channel*4) + + // Reset to zero value + ccr := t.channelCCR(channel) + ccr.Set(0) + + // Disable the hardware interrupt + t.Device.DIER.ClearBits(stm32.TIM_DIER_CC1IE << channel) + + // Clear the interrupt flag + t.Device.SR.ClearBits(stm32.TIM_SR_CC1IF << channel) + + // Restore interrupts + interrupt.Restore(mask) +} + +// SetInverting sets whether to invert the output of this channel. +// Without inverting, a 25% duty cycle would mean the output is high for 25% of +// the time and low for the rest. Inverting flips the output as if a NOT gate +// was placed at the output, meaning that the output would be 25% low and 75% +// high with a duty cycle of 25%. +func (t *TIM) SetInverting(channel uint8, inverting bool) { + // Enable the channel (if not already) + + var val = uint32(0) + if inverting { + val |= stm32.TIM_CCER_CC1P + } + + t.Device.CCER.ReplaceBits(val, stm32.TIM_CCER_CC1P_Msk, channel*4) +} + +func (t *TIM) handleUPInterrupt(interrupt.Interrupt) { + if t.Device.SR.HasBits(stm32.TIM_SR_UIF) { + // clear the update flag + t.Device.SR.ClearBits(stm32.TIM_SR_UIF) + + if t.wraparoundCallback != nil { + t.wraparoundCallback() + } + } +} + +func (t *TIM) handleOCInterrupt(interrupt.Interrupt) { + if t.Device.SR.HasBits(stm32.TIM_SR_CC1IF) { + if t.channelCallbacks[0] != nil { + t.channelCallbacks[0](0) + } + } + if t.Device.SR.HasBits(stm32.TIM_SR_CC2IF) { + if t.channelCallbacks[1] != nil { + t.channelCallbacks[1](1) + } + } + if t.Device.SR.HasBits(stm32.TIM_SR_CC3IF) { + if t.channelCallbacks[2] != nil { + t.channelCallbacks[2](2) + } + } + if t.Device.SR.HasBits(stm32.TIM_SR_CC4IF) { + if t.channelCallbacks[3] != nil { + t.channelCallbacks[3](3) + } + } + + // Reset interrupt flags + t.Device.SR.ClearBits(stm32.TIM_SR_CC1IF | stm32.TIM_SR_CC2IF | stm32.TIM_SR_CC3IF | stm32.TIM_SR_CC4IF) +} + +func (t *TIM) channelCCR(channel uint8) *arrRegType { + switch channel { + case 0: + return &t.Device.CCR1 + case 1: + return &t.Device.CCR2 + case 2: + return &t.Device.CCR3 + case 3: + return &t.Device.CCR4 + } + + return nil +} + +func (t *TIM) channelCCMR(channel uint8) (reg *volatile.Register32, offset uint8) { + switch channel { + case 0: + return &t.Device.CCMR1_Output, 0 + case 1: + return &t.Device.CCMR1_Output, 8 + case 2: + return &t.Device.CCMR2_Output, 0 + case 3: + return &t.Device.CCMR2_Output, 8 + } + + return nil, 0 +} + +//go:inline +func ceil(num uint64, denom uint64) uint64 { + return (num + denom - 1) / denom +} diff --git a/src/machine/machine_stm32f4.go b/src/machine/machine_stm32f4.go index a0b00a81..3e615660 100644 --- a/src/machine/machine_stm32f4.go +++ b/src/machine/machine_stm32f4.go @@ -6,6 +6,8 @@ package machine import ( "device/stm32" + "runtime/interrupt" + "runtime/volatile" "unsafe" ) @@ -95,8 +97,56 @@ const ( PE14 = portE + 14 PE15 = portE + 15 - PH0 = portH + 0 - PH1 = portH + 1 + PF0 = portF + 0 + PF1 = portF + 1 + PF2 = portF + 2 + PF3 = portF + 3 + PF4 = portF + 4 + PF5 = portF + 5 + PF6 = portF + 6 + PF7 = portF + 7 + PF8 = portF + 8 + PF9 = portF + 9 + PF10 = portF + 10 + PF11 = portF + 11 + PF12 = portF + 12 + PF13 = portF + 13 + PF14 = portF + 14 + PF15 = portF + 15 + + PH0 = portH + 0 + PH1 = portH + 1 + PH2 = portH + 2 + PH3 = portH + 3 + PH4 = portH + 4 + PH5 = portH + 5 + PH6 = portH + 6 + PH7 = portH + 7 + PH8 = portH + 8 + PH9 = portH + 9 + PH10 = portH + 10 + PH11 = portH + 11 + PH12 = portH + 12 + PH13 = portH + 13 + PH14 = portH + 14 + PH15 = portH + 15 + + PI0 = portI + 0 + PI1 = portI + 1 + PI2 = portI + 2 + PI3 = portI + 3 + PI4 = portI + 4 + PI5 = portI + 5 + PI6 = portI + 6 + PI7 = portI + 7 + PI8 = portI + 8 + PI9 = portI + 9 + PI10 = portI + 10 + PI11 = portI + 11 + PI12 = portI + 12 + PI13 = portI + 13 + PI14 = portI + 14 + PI15 = portI + 15 ) func (p Pin) getPort() *stm32.GPIO_Type { @@ -227,3 +277,271 @@ func enableAltFuncClock(bus unsafe.Pointer) { stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN) } } + +//---------- Timer related code + +var ( + TIM1 = TIM{ + EnableRegister: &stm32.RCC.APB2ENR, + EnableFlag: stm32.RCC_APB2ENR_TIM1EN, + Device: stm32.TIM1, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PA8, AF1_TIM1_2}, {PE9, AF1_TIM1_2}}}, + TimerChannel{Pins: []PinFunction{{PA9, AF1_TIM1_2}, {PE11, AF1_TIM1_2}}}, + TimerChannel{Pins: []PinFunction{{PA10, AF1_TIM1_2}, {PE13, AF1_TIM1_2}}}, + TimerChannel{Pins: []PinFunction{{PA11, AF1_TIM1_2}, {PE14, AF1_TIM1_2}}}, + }, + busFreq: APB2_TIM_FREQ, + } + + TIM2 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM2EN, + Device: stm32.TIM2, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PA0, AF1_TIM1_2}, {PA5, AF1_TIM1_2}, {PA15, AF1_TIM1_2}}}, + TimerChannel{Pins: []PinFunction{{PA1, AF1_TIM1_2}, {PB3, AF1_TIM1_2}}}, + TimerChannel{Pins: []PinFunction{{PA2, AF1_TIM1_2}, {PB10, AF1_TIM1_2}}}, + TimerChannel{Pins: []PinFunction{{PA3, AF1_TIM1_2}, {PB11, AF1_TIM1_2}}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM3 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM3EN, + Device: stm32.TIM3, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PA6, AF2_TIM3_4_5}, {PB4, AF2_TIM3_4_5}, {PC6, AF2_TIM3_4_5}}}, + TimerChannel{Pins: []PinFunction{{PA7, AF2_TIM3_4_5}, {PB5, AF2_TIM3_4_5}, {PC7, AF2_TIM3_4_5}}}, + TimerChannel{Pins: []PinFunction{{PB0, AF2_TIM3_4_5}, {PC8, AF2_TIM3_4_5}}}, + TimerChannel{Pins: []PinFunction{{PB1, AF2_TIM3_4_5}, {PC9, AF2_TIM3_4_5}}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM4 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM4EN, + Device: stm32.TIM4, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PB6, AF2_TIM3_4_5}, {PD12, AF2_TIM3_4_5}}}, + TimerChannel{Pins: []PinFunction{{PB7, AF2_TIM3_4_5}, {PD13, AF2_TIM3_4_5}}}, + TimerChannel{Pins: []PinFunction{{PB8, AF2_TIM3_4_5}, {PD14, AF2_TIM3_4_5}}}, + TimerChannel{Pins: []PinFunction{{PB9, AF2_TIM3_4_5}, {PD15, AF2_TIM3_4_5}}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM5 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM5EN, + Device: stm32.TIM5, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PH10, AF2_TIM3_4_5}}}, + TimerChannel{Pins: []PinFunction{{PH11, AF2_TIM3_4_5}}}, + TimerChannel{Pins: []PinFunction{{PH12, AF2_TIM3_4_5}}}, + TimerChannel{Pins: []PinFunction{{PI0, AF2_TIM3_4_5}}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM6 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM6EN, + Device: stm32.TIM6, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM7 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM7EN, + Device: stm32.TIM7, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM8 = TIM{ + EnableRegister: &stm32.RCC.APB2ENR, + EnableFlag: stm32.RCC_APB2ENR_TIM8EN, + Device: stm32.TIM8, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PC6, AF3_TIM8_9_10_11}, {PI5, AF3_TIM8_9_10_11}}}, + TimerChannel{Pins: []PinFunction{{PC7, AF3_TIM8_9_10_11}, {PI6, AF3_TIM8_9_10_11}}}, + TimerChannel{Pins: []PinFunction{{PC8, AF3_TIM8_9_10_11}, {PI7, AF3_TIM8_9_10_11}}}, + TimerChannel{Pins: []PinFunction{{PC9, AF3_TIM8_9_10_11}, {PI2, AF3_TIM8_9_10_11}}}, + }, + busFreq: APB2_TIM_FREQ, + } + + TIM9 = TIM{ + EnableRegister: &stm32.RCC.APB2ENR, + EnableFlag: stm32.RCC_APB2ENR_TIM9EN, + Device: stm32.TIM9, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PA2, AF3_TIM8_9_10_11}, {PE5, AF3_TIM8_9_10_11}}}, + TimerChannel{Pins: []PinFunction{{PA3, AF3_TIM8_9_10_11}, {PE6, AF3_TIM8_9_10_11}}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB2_TIM_FREQ, + } + + TIM10 = TIM{ + EnableRegister: &stm32.RCC.APB2ENR, + EnableFlag: stm32.RCC_APB2ENR_TIM10EN, + Device: stm32.TIM10, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PB8, AF3_TIM8_9_10_11}, {PF6, AF3_TIM8_9_10_11}}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB2_TIM_FREQ, + } + + TIM11 = TIM{ + EnableRegister: &stm32.RCC.APB2ENR, + EnableFlag: stm32.RCC_APB2ENR_TIM11EN, + Device: stm32.TIM11, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PB9, AF3_TIM8_9_10_11}, {PF7, AF3_TIM8_9_10_11}}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB2_TIM_FREQ, + } + + TIM12 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM12EN, + Device: stm32.TIM12, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PB14, AF9_CAN1_CAN2_TIM12_13_14}, {PH6, AF9_CAN1_CAN2_TIM12_13_14}}}, + TimerChannel{Pins: []PinFunction{{PB15, AF9_CAN1_CAN2_TIM12_13_14}, {PH9, AF9_CAN1_CAN2_TIM12_13_14}}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM13 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM13EN, + Device: stm32.TIM13, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PA6, AF9_CAN1_CAN2_TIM12_13_14}, {PF8, AF9_CAN1_CAN2_TIM12_13_14}}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM14 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM14EN, + Device: stm32.TIM14, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PA7, AF9_CAN1_CAN2_TIM12_13_14}, {PF9, AF9_CAN1_CAN2_TIM12_13_14}}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB1_TIM_FREQ, + } +) + +func (t *TIM) registerUPInterrupt() interrupt.Interrupt { + switch t { + case &TIM1: + return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM1.handleUPInterrupt) + case &TIM2: + return interrupt.New(stm32.IRQ_TIM2, TIM2.handleUPInterrupt) + case &TIM3: + return interrupt.New(stm32.IRQ_TIM3, TIM3.handleUPInterrupt) + case &TIM4: + return interrupt.New(stm32.IRQ_TIM4, TIM4.handleUPInterrupt) + case &TIM5: + return interrupt.New(stm32.IRQ_TIM5, TIM5.handleUPInterrupt) + case &TIM6: + return interrupt.New(stm32.IRQ_TIM6_DAC, TIM6.handleUPInterrupt) + case &TIM7: + return interrupt.New(stm32.IRQ_TIM7, TIM7.handleUPInterrupt) + case &TIM8: + return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM8.handleUPInterrupt) + case &TIM9: + return interrupt.New(stm32.IRQ_TIM1_BRK_TIM9, TIM9.handleUPInterrupt) + case &TIM10: + return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM10.handleUPInterrupt) + case &TIM11: + return interrupt.New(stm32.IRQ_TIM1_TRG_COM_TIM11, TIM11.handleUPInterrupt) + case &TIM12: + return interrupt.New(stm32.IRQ_TIM8_BRK_TIM12, TIM12.handleUPInterrupt) + case &TIM13: + return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM13.handleUPInterrupt) + case &TIM14: + return interrupt.New(stm32.IRQ_TIM8_TRG_COM_TIM14, TIM14.handleUPInterrupt) + } + + return interrupt.Interrupt{} +} + +func (t *TIM) registerOCInterrupt() interrupt.Interrupt { + switch t { + case &TIM1: + return interrupt.New(stm32.IRQ_TIM1_CC, TIM1.handleOCInterrupt) + case &TIM2: + return interrupt.New(stm32.IRQ_TIM2, TIM2.handleOCInterrupt) + case &TIM3: + return interrupt.New(stm32.IRQ_TIM3, TIM3.handleOCInterrupt) + case &TIM4: + return interrupt.New(stm32.IRQ_TIM4, TIM4.handleOCInterrupt) + case &TIM5: + return interrupt.New(stm32.IRQ_TIM5, TIM5.handleOCInterrupt) + case &TIM6: + return interrupt.New(stm32.IRQ_TIM6_DAC, TIM6.handleOCInterrupt) + case &TIM7: + return interrupt.New(stm32.IRQ_TIM7, TIM7.handleOCInterrupt) + case &TIM8: + return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM8.handleOCInterrupt) + case &TIM9: + return interrupt.New(stm32.IRQ_TIM1_BRK_TIM9, TIM9.handleOCInterrupt) + case &TIM10: + return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM10.handleOCInterrupt) + case &TIM11: + return interrupt.New(stm32.IRQ_TIM1_TRG_COM_TIM11, TIM11.handleOCInterrupt) + case &TIM12: + return interrupt.New(stm32.IRQ_TIM8_BRK_TIM12, TIM12.handleOCInterrupt) + case &TIM13: + return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM13.handleOCInterrupt) + case &TIM14: + return interrupt.New(stm32.IRQ_TIM8_TRG_COM_TIM14, TIM14.handleOCInterrupt) + } + + return interrupt.Interrupt{} +} + +func (t *TIM) enableMainOutput() { + t.Device.BDTR.SetBits(stm32.TIM_BDTR_MOE) +} + +type arrtype = uint32 +type arrRegType = volatile.Register32 + +const ( + ARR_MAX = 0x10000 + PSC_MAX = 0x10000 +) diff --git a/src/machine/machine_stm32f405.go b/src/machine/machine_stm32f405.go index 92ad79f6..ae113dc5 100644 --- a/src/machine/machine_stm32f405.go +++ b/src/machine/machine_stm32f405.go @@ -13,6 +13,12 @@ func CPUFrequency() uint32 { return 168000000 } +// Internal use: configured speed of the APB1 and APB2 timers, this should be kept +// in sync with any changes to runtime package which configures the oscillators +// and clock frequencies +const APB1_TIM_FREQ = 42000000 * 2 +const APB2_TIM_FREQ = 84000000 * 2 + // Alternative peripheral pin functions const ( AF0_SYSTEM = 0 diff --git a/src/machine/machine_stm32f407.go b/src/machine/machine_stm32f407.go index 6ffafcd3..4c928376 100644 --- a/src/machine/machine_stm32f407.go +++ b/src/machine/machine_stm32f407.go @@ -12,6 +12,12 @@ func CPUFrequency() uint32 { return 168000000 } +// Internal use: configured speed of the APB1 and APB2 timers, this should be kept +// in sync with any changes to runtime package which configures the oscillators +// and clock frequencies +const APB1_TIM_FREQ = 42000000 * 2 +const APB2_TIM_FREQ = 84000000 * 2 + // Alternative peripheral pin functions const ( AF0_SYSTEM = 0