mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 11:37:46 +00:00
4c3680635e
The UART handleInterrupt handler unconditionally read RDR on every interrupt without checking which flag triggered it. On newer STM32 USART peripherals (U5, L4, L5, L0, G0, F7, WL), RXNEIE enables interrupts for both RXFNE (data ready) and ORE (overrun error). Unlike older families (F1, F4), ORE is not cleared by reading the data register, it must be explicitly cleared via the ICR register. When an overrun occurred (e.g. serial data arriving while ADC busy-waits in Get()), ORE would trigger the interrupt, the handler would fire without clearing it, and the interrupt would re-trigger immediately, causing an infinite interrupt storm that locks up the CPU. Fix by: - Checking RXFNE/RXNE (bit 5) before reading data from RDR - Clearing ORE (bit 3) via ICR on newer peripherals when set - Adding errClearReg field to UART struct, set to &Bus.ICR in setRegisters() for all ICR-capable families - Preserving the SR+DR clearing sequence for older F1/F4 families Signed-off-by: deadprogram <ron@hybridgroup.com>
556 lines
15 KiB
Go
556 lines
15 KiB
Go
//go:build stm32wlx
|
|
|
|
package machine
|
|
|
|
// Peripheral abstraction layer for the stm32wle5
|
|
|
|
import (
|
|
"device/stm32"
|
|
"errors"
|
|
"internal/binary"
|
|
"math/bits"
|
|
"runtime/interrupt"
|
|
"runtime/volatile"
|
|
"unsafe"
|
|
)
|
|
|
|
var deviceIDAddr = []uintptr{0x1FFF7590, 0x1FFF7594, 0x1FFF7598}
|
|
|
|
const (
|
|
AF0_SYSTEM = 0
|
|
AF1_TIM1_2_LPTIM1 = 1
|
|
AF2_TIM1_2 = 2
|
|
AF3_SPIS2_TIM1_LPTIM3 = 3
|
|
AF4_I2C1_2_3 = 4
|
|
AF5_SPI1_SPI2S2 = 5
|
|
AF6_RF = 6
|
|
AF7_USART1_2 = 7
|
|
AF8_LPUART1 = 8
|
|
AF12_COMP1_2_TIM1 = 12
|
|
AF13_DEBUG = 13
|
|
AF14_TIM2_16_17_LPTIM2 = 14
|
|
AF15_EVENTOUT = 15
|
|
)
|
|
|
|
const (
|
|
SYSCLK = 48e6
|
|
APB1_TIM_FREQ = SYSCLK
|
|
APB2_TIM_FREQ = SYSCLK
|
|
)
|
|
|
|
func CPUFrequency() uint32 {
|
|
return SYSCLK
|
|
}
|
|
|
|
const (
|
|
PA0 = portA + 0
|
|
PA1 = portA + 1
|
|
PA2 = portA + 2
|
|
PA3 = portA + 3
|
|
PA4 = portA + 4
|
|
PA5 = portA + 5
|
|
PA6 = portA + 6
|
|
PA7 = portA + 7
|
|
PA8 = portA + 8
|
|
PA9 = portA + 9
|
|
PA10 = portA + 10
|
|
PA11 = portA + 11
|
|
PA12 = portA + 12
|
|
PA13 = portA + 13
|
|
PA14 = portA + 14
|
|
PA15 = portA + 15
|
|
|
|
PB0 = portB + 0
|
|
PB1 = portB + 1
|
|
PB2 = portB + 2
|
|
PB3 = portB + 3
|
|
PB4 = portB + 4
|
|
PB5 = portB + 5
|
|
PB6 = portB + 6
|
|
PB7 = portB + 7
|
|
PB8 = portB + 8
|
|
PB9 = portB + 9
|
|
PB10 = portB + 10
|
|
PB11 = portB + 11
|
|
PB12 = portB + 12
|
|
PB13 = portB + 13
|
|
PB14 = portB + 14
|
|
PB15 = portB + 15
|
|
|
|
PC0 = portC + 0
|
|
PC1 = portC + 1
|
|
PC2 = portC + 2
|
|
PC3 = portC + 3
|
|
PC4 = portC + 4
|
|
PC5 = portC + 5
|
|
PC6 = portC + 6
|
|
PC7 = portC + 7
|
|
PC8 = portC + 8
|
|
PC9 = portC + 9
|
|
PC10 = portC + 10
|
|
PC11 = portC + 11
|
|
PC12 = portC + 12
|
|
PC13 = portC + 13
|
|
PC14 = portC + 14
|
|
PC15 = portC + 15
|
|
|
|
PH3 = portH + 3
|
|
)
|
|
|
|
func (p Pin) getPort() *stm32.GPIO_Type {
|
|
switch p / 16 {
|
|
case 0:
|
|
return stm32.GPIOA
|
|
case 1:
|
|
return stm32.GPIOB
|
|
case 2:
|
|
return stm32.GPIOC
|
|
case 7:
|
|
return stm32.GPIOH
|
|
default:
|
|
panic("machine: unknown port")
|
|
}
|
|
}
|
|
|
|
// enableClock enables the clock for this desired GPIO port.
|
|
func (p Pin) enableClock() {
|
|
switch p / 16 {
|
|
case 0:
|
|
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOAEN)
|
|
case 1:
|
|
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOBEN)
|
|
case 2:
|
|
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOCEN)
|
|
case 7:
|
|
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOHEN)
|
|
default:
|
|
panic("machine: unknown port")
|
|
}
|
|
}
|
|
|
|
// Enable peripheral clock
|
|
func enableAltFuncClock(bus unsafe.Pointer) {
|
|
switch bus {
|
|
// APB1ENR1
|
|
case unsafe.Pointer(stm32.LPTIM1): // LPTIM1 clock enable
|
|
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_LPTIM1EN)
|
|
case unsafe.Pointer(stm32.DAC): // DAC clock enable
|
|
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_DAC1EN)
|
|
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
|
|
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_I2C3EN)
|
|
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
|
|
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_I2C2EN)
|
|
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
|
|
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_I2C1EN)
|
|
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
|
|
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_USART2EN)
|
|
case unsafe.Pointer(stm32.SPI2): // SPI2S2 clock enable
|
|
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_SPI2S2EN)
|
|
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
|
|
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_WWDGEN)
|
|
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
|
|
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_TIM2EN)
|
|
// APB1ENR2
|
|
case unsafe.Pointer(stm32.LPTIM3): // LPTIM3 clock enable
|
|
stm32.RCC.APB1ENR2.SetBits(stm32.RCC_APB1ENR2_LPTIM3EN)
|
|
case unsafe.Pointer(stm32.LPTIM2): // LPTIM2 clock enable
|
|
stm32.RCC.APB1ENR2.SetBits(stm32.RCC_APB1ENR2_LPTIM2EN)
|
|
case unsafe.Pointer(stm32.LPUART): // LPUART clock enable
|
|
stm32.RCC.APB1ENR2.SetBits(stm32.RCC_APB1ENR2_LPUART1EN)
|
|
//APB2ENR
|
|
case unsafe.Pointer(stm32.TIM17): // TIM17 clock enable
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM17EN)
|
|
case unsafe.Pointer(stm32.TIM16): // TIM16 clock enable
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM16EN)
|
|
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
|
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
|
case unsafe.Pointer(stm32.TIM1): // TIM1 clock enable
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN)
|
|
case unsafe.Pointer(stm32.ADC): // ADC clock enable
|
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADCEN)
|
|
|
|
}
|
|
}
|
|
|
|
func handlePinInterrupt(pin uint8) {
|
|
if stm32.EXTI.PR1.HasBits(1 << pin) {
|
|
// Writing 1 to the pending register clears the
|
|
// pending flag for that bit
|
|
stm32.EXTI.PR1.Set(1 << pin)
|
|
|
|
callback := pinCallbacks[pin]
|
|
if callback != nil {
|
|
callback(interruptPins[pin])
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p Pin) registerInterrupt() interrupt.Interrupt {
|
|
pin := uint8(p) % 16
|
|
|
|
switch pin {
|
|
case 0:
|
|
return interrupt.New(stm32.IRQ_EXTI0, func(interrupt.Interrupt) { handlePinInterrupt(0) })
|
|
case 1:
|
|
return interrupt.New(stm32.IRQ_EXTI1, func(interrupt.Interrupt) { handlePinInterrupt(1) })
|
|
case 2:
|
|
return interrupt.New(stm32.IRQ_EXTI2, func(interrupt.Interrupt) { handlePinInterrupt(2) })
|
|
case 3:
|
|
return interrupt.New(stm32.IRQ_EXTI3, func(interrupt.Interrupt) { handlePinInterrupt(3) })
|
|
case 4:
|
|
return interrupt.New(stm32.IRQ_EXTI4, func(interrupt.Interrupt) { handlePinInterrupt(4) })
|
|
case 5:
|
|
return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(5) })
|
|
case 6:
|
|
return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(6) })
|
|
case 7:
|
|
return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(7) })
|
|
case 8:
|
|
return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(8) })
|
|
case 9:
|
|
return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(9) })
|
|
case 10:
|
|
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(10) })
|
|
case 11:
|
|
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(11) })
|
|
case 12:
|
|
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(12) })
|
|
case 13:
|
|
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(13) })
|
|
case 14:
|
|
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(14) })
|
|
case 15:
|
|
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(15) })
|
|
}
|
|
|
|
return interrupt.Interrupt{}
|
|
}
|
|
|
|
// -- SPI ----------------------------------------------------------------------
|
|
|
|
type SPI struct {
|
|
Bus *stm32.SPI_Type
|
|
AltFuncSelector uint8
|
|
}
|
|
|
|
func (spi *SPI) config8Bits() {
|
|
// Set rx threshold to 8-bits, so RXNE flag is set for 1 byte
|
|
// (common STM32 SPI implementation does 8-bit transfers only)
|
|
spi.Bus.CR2.SetBits(stm32.SPI_CR2_FRXTH)
|
|
}
|
|
|
|
func (spi *SPI) configurePins(config SPIConfig) {
|
|
config.SCK.ConfigureAltFunc(PinConfig{Mode: PinModeSPICLK}, spi.AltFuncSelector)
|
|
config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector)
|
|
config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector)
|
|
}
|
|
|
|
func (spi *SPI) getBaudRate(config SPIConfig) uint32 {
|
|
var clock uint32
|
|
|
|
// We keep this switch and separate management of SPI Clocks
|
|
// for future improvement of system/bus clocks and prescalers
|
|
switch spi.Bus {
|
|
case stm32.SPI1:
|
|
clock = CPUFrequency()
|
|
case stm32.SPI2, stm32.SPI3:
|
|
clock = CPUFrequency()
|
|
}
|
|
|
|
// limit requested frequency to bus frequency and min frequency (DIV256)
|
|
freq := config.Frequency
|
|
if min := clock / 256; freq < min {
|
|
freq = min
|
|
} else if freq > clock {
|
|
freq = clock
|
|
}
|
|
|
|
// calculate the exact clock divisor (freq=clock/div -> div=clock/freq).
|
|
// truncation is fine, since it produces a less-than-or-equal divisor, and
|
|
// thus a greater-than-or-equal frequency.
|
|
// divisors only come in consecutive powers of 2, so we can use log2 (or,
|
|
// equivalently, bits.Len - 1) to convert to respective enum value.
|
|
div := bits.Len32(clock/freq) - 1
|
|
|
|
// but DIV1 (2^0) is not permitted, as the least divisor is DIV2 (2^1), so
|
|
// subtract 1 from the log2 value, keeping a lower bound of 0
|
|
if div < 0 {
|
|
div = 0
|
|
} else if div > 0 {
|
|
div--
|
|
}
|
|
|
|
// finally, shift the enumerated value into position for SPI CR1
|
|
return uint32(div) << stm32.SPI_CR1_BR_Pos
|
|
}
|
|
|
|
//---------- I2C related code
|
|
|
|
// Gets the value for TIMINGR register
|
|
func (i2c *I2C) getFreqRange(br uint32) uint32 {
|
|
// This is a 'magic' value calculated by STM32CubeMX
|
|
// for 48Mhz PCLK1.
|
|
// TODO: Do calculations based on PCLK1
|
|
switch br {
|
|
case 10 * KHz:
|
|
return 0x9010DEFF
|
|
case 100 * KHz:
|
|
return 0x20303E5D
|
|
case 400 * KHz:
|
|
return 0x2010091A
|
|
case 500 * KHz:
|
|
return 0x00201441
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
//---------- UART related code
|
|
|
|
// Configure the UART.
|
|
func (uart UART) configurePins(config UARTConfig) {
|
|
// enable the alternate functions on the TX and RX pins
|
|
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector)
|
|
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
|
|
}
|
|
|
|
// UART baudrate calc based on the bus and clockspeed
|
|
// NOTE: keep this in sync with the runtime/runtime_stm32wle5.go clock init code
|
|
func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
|
|
var br uint32
|
|
uartClock := CPUFrequency() // No Prescaler configuration
|
|
br = uint32((uartClock + baudRate/2) / baudRate)
|
|
return (br)
|
|
}
|
|
|
|
// Register names vary by ST processor, these are for STM L5
|
|
func (uart *UART) setRegisters() {
|
|
uart.rxReg = &uart.Bus.RDR
|
|
uart.txReg = &uart.Bus.TDR
|
|
uart.statusReg = &uart.Bus.ISR
|
|
uart.txEmptyFlag = stm32.USART_ISR_TXFNF //(TXFNF == TXE == bit 7, but depends alternate RM0461/1094)
|
|
uart.errClearReg = &uart.Bus.ICR
|
|
}
|
|
|
|
//---------- 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_LPTIM1}}},
|
|
TimerChannel{Pins: []PinFunction{{PA9, AF1_TIM1_2_LPTIM1}}},
|
|
TimerChannel{Pins: []PinFunction{{PA10, AF1_TIM1_2_LPTIM1}}},
|
|
TimerChannel{Pins: []PinFunction{{PA11, AF1_TIM1_2_LPTIM1}}},
|
|
},
|
|
busFreq: APB2_TIM_FREQ,
|
|
}
|
|
TIM2 = TIM{
|
|
EnableRegister: &stm32.RCC.APB1ENR1,
|
|
EnableFlag: stm32.RCC_APB1ENR1_TIM2EN,
|
|
Device: stm32.TIM2,
|
|
Channels: [4]TimerChannel{
|
|
TimerChannel{Pins: []PinFunction{{PA0, AF1_TIM1_2_LPTIM1}, {PA5, AF1_TIM1_2_LPTIM1}, {PA15, AF1_TIM1_2_LPTIM1}}},
|
|
TimerChannel{Pins: []PinFunction{{PA1, AF1_TIM1_2_LPTIM1}, {PB3, AF1_TIM1_2_LPTIM1}}},
|
|
TimerChannel{Pins: []PinFunction{{PA2, AF1_TIM1_2_LPTIM1}, {PB10, AF1_TIM1_2_LPTIM1}}},
|
|
TimerChannel{Pins: []PinFunction{{PA3, AF1_TIM1_2_LPTIM1}, {PB11, AF1_TIM1_2_LPTIM1}}},
|
|
},
|
|
busFreq: APB1_TIM_FREQ,
|
|
}
|
|
TIM16 = TIM{
|
|
EnableRegister: &stm32.RCC.APB2ENR,
|
|
EnableFlag: stm32.RCC_APB2ENR_TIM16EN,
|
|
Device: stm32.TIM16,
|
|
Channels: [4]TimerChannel{
|
|
TimerChannel{Pins: []PinFunction{{PA6, AF14_TIM2_16_17_LPTIM2}}},
|
|
TimerChannel{Pins: []PinFunction{}},
|
|
TimerChannel{Pins: []PinFunction{}},
|
|
TimerChannel{Pins: []PinFunction{}},
|
|
},
|
|
busFreq: APB2_TIM_FREQ,
|
|
}
|
|
TIM17 = TIM{
|
|
EnableRegister: &stm32.RCC.APB2ENR,
|
|
EnableFlag: stm32.RCC_APB2ENR_TIM17EN,
|
|
Device: stm32.TIM17,
|
|
Channels: [4]TimerChannel{
|
|
TimerChannel{Pins: []PinFunction{{PA7, AF1_TIM1_2_LPTIM1}, {PB9, AF1_TIM1_2_LPTIM1}}},
|
|
TimerChannel{Pins: []PinFunction{}},
|
|
TimerChannel{Pins: []PinFunction{}},
|
|
TimerChannel{Pins: []PinFunction{}},
|
|
},
|
|
busFreq: APB2_TIM_FREQ,
|
|
}
|
|
)
|
|
|
|
func (t *TIM) registerUPInterrupt() interrupt.Interrupt {
|
|
switch t {
|
|
case &TIM1:
|
|
return interrupt.New(stm32.IRQ_TIM1_UP, TIM1.handleUPInterrupt)
|
|
case &TIM2:
|
|
return interrupt.New(stm32.IRQ_TIM2, TIM2.handleUPInterrupt)
|
|
case &TIM16:
|
|
return interrupt.New(stm32.IRQ_TIM16, TIM16.handleUPInterrupt)
|
|
case &TIM17:
|
|
return interrupt.New(stm32.IRQ_TIM17, TIM17.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 &TIM16:
|
|
return interrupt.New(stm32.IRQ_TIM16, TIM16.handleOCInterrupt)
|
|
case &TIM17:
|
|
return interrupt.New(stm32.IRQ_TIM17, TIM17.handleOCInterrupt)
|
|
}
|
|
|
|
return interrupt.Interrupt{}
|
|
}
|
|
|
|
func (t *TIM) enableMainOutput() {
|
|
t.Device.BDTR.SetBits(stm32.TIM_BDTR_MOE)
|
|
}
|
|
|
|
func initRNG() {
|
|
stm32.RCC.AHB3ENR.SetBits(stm32.RCC_AHB3ENR_RNGEN)
|
|
|
|
// Enable RNG with config.A (See RM0453 22.6.2)
|
|
stm32.RNG.CR.Set(0x40F00D40) // RNG Config. A
|
|
stm32.RNG.HTCR.Set(0x17590ABC) // MAGIC NUMBER
|
|
stm32.RNG.HTCR.Set(0x0000AA74) // HTCR VALUE
|
|
stm32.RNG.CR.Set(0x00F00D4C) // CONFIG A + RNG_EN=1 + IE=1
|
|
}
|
|
|
|
//----------
|
|
|
|
type arrtype = uint32
|
|
type psctype = uint32
|
|
type arrRegType = volatile.Register32
|
|
|
|
const (
|
|
ARR_MAX = 0x10000
|
|
PSC_MAX = 0x10000
|
|
)
|
|
|
|
//---------- Flash related code
|
|
|
|
const eraseBlockSizeValue = 2048
|
|
|
|
// eraseBlock of the passed in block number
|
|
func eraseBlock(block uint32) error {
|
|
waitUntilFlashDone()
|
|
|
|
// check if operation is allowed.
|
|
if stm32.FLASH.GetSR_PESD() != 0 {
|
|
return errFlashCannotErasePage
|
|
}
|
|
|
|
// clear any previous errors
|
|
stm32.FLASH.SR.SetBits(0x3FA)
|
|
|
|
// page erase operation
|
|
stm32.FLASH.SetCR_PER(1)
|
|
defer stm32.FLASH.SetCR_PER(0)
|
|
|
|
// set the address to the page to be written
|
|
stm32.FLASH.SetCR_PNB(block)
|
|
defer stm32.FLASH.SetCR_PNB(0)
|
|
|
|
// start the page erase
|
|
stm32.FLASH.SetCR_STRT(1)
|
|
|
|
waitUntilFlashDone()
|
|
|
|
if err := checkError(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const writeBlockSize = 8
|
|
|
|
func writeFlashData(address uintptr, data []byte) (int, error) {
|
|
if len(data)%writeBlockSize != 0 {
|
|
return 0, errFlashInvalidWriteLength
|
|
}
|
|
|
|
waitUntilFlashDone()
|
|
|
|
// check if operation is allowed
|
|
if stm32.FLASH.GetSR_PESD() != 0 {
|
|
return 0, errFlashNotAllowedWriteData
|
|
}
|
|
|
|
// clear any previous errors
|
|
stm32.FLASH.SR.SetBits(0x3FA)
|
|
|
|
for j := 0; j < len(data); j += writeBlockSize {
|
|
// start page write operation
|
|
stm32.FLASH.SetCR_PG(1)
|
|
|
|
// write first word using double-word high order word
|
|
*(*uint32)(unsafe.Pointer(address)) = binary.LittleEndian.Uint32(data[j : j+writeBlockSize/2])
|
|
|
|
address += writeBlockSize / 2
|
|
|
|
// write second word using double-word low order word
|
|
*(*uint32)(unsafe.Pointer(address)) = binary.LittleEndian.Uint32(data[j+writeBlockSize/2 : j+writeBlockSize])
|
|
|
|
waitUntilFlashDone()
|
|
|
|
if err := checkError(); err != nil {
|
|
return j, err
|
|
}
|
|
|
|
// end flash write
|
|
stm32.FLASH.SetCR_PG(0)
|
|
address += writeBlockSize / 2
|
|
}
|
|
|
|
return len(data), nil
|
|
}
|
|
|
|
func waitUntilFlashDone() {
|
|
for stm32.FLASH.GetSR_BSY() != 0 {
|
|
}
|
|
|
|
for stm32.FLASH.GetSR_CFGBSY() != 0 {
|
|
}
|
|
}
|
|
|
|
var (
|
|
errFlashPGS = errors.New("errFlashPGS")
|
|
errFlashSIZE = errors.New("errFlashSIZE")
|
|
errFlashPGA = errors.New("errFlashPGA")
|
|
errFlashWRP = errors.New("errFlashWRP")
|
|
errFlashPROG = errors.New("errFlashPROG")
|
|
)
|
|
|
|
func checkError() error {
|
|
switch {
|
|
case stm32.FLASH.GetSR_PGSERR() != 0:
|
|
return errFlashPGS
|
|
case stm32.FLASH.GetSR_SIZERR() != 0:
|
|
return errFlashSIZE
|
|
case stm32.FLASH.GetSR_PGAERR() != 0:
|
|
return errFlashPGA
|
|
case stm32.FLASH.GetSR_WRPERR() != 0:
|
|
return errFlashWRP
|
|
case stm32.FLASH.GetSR_PROGERR() != 0:
|
|
return errFlashPROG
|
|
}
|
|
|
|
return nil
|
|
}
|