machine/stm32, nrf: implement machine.Flash

Implements the machine.Flash interface using the same definition as the tinyfs BlockDevice.

This implementation covers the stm32f4, stm32l4, stm32wlx, nrf51, nrf52, and nrf528xx processors.
Этот коммит содержится в:
Ron Evans 2023-02-27 13:55:38 +01:00 коммит произвёл GitHub
родитель 8bf94b9231
коммит 6e1b8a54aa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 817 добавлений и 0 удалений

57
src/examples/flash/main.go Обычный файл
Просмотреть файл

@ -0,0 +1,57 @@
package main
import (
"machine"
"time"
)
var (
err error
message = "1234567887654321123456788765432112345678876543211234567887654321"
)
func main() {
time.Sleep(3 * time.Second)
// Print out general information
println("Flash data start: ", machine.FlashDataStart())
println("Flash data end: ", machine.FlashDataEnd())
println("Flash data size, bytes:", machine.Flash.Size())
println("Flash write block size:", machine.Flash.WriteBlockSize())
println("Flash erase block size:", machine.Flash.EraseBlockSize())
println()
flash := machine.OpenFlashBuffer(machine.Flash, machine.FlashDataStart())
original := make([]byte, len(message))
saved := make([]byte, len(message))
// Read flash contents on start (data shall survive power off)
print("Reading data from flash: ")
_, err = flash.Read(original)
checkError(err)
println(string(original))
// Write the message to flash
print("Writing data to flash: ")
flash.Seek(0, 0) // rewind back to beginning
_, err = flash.Write([]byte(message))
checkError(err)
println(string(message))
// Read back flash contents after write (verify data is the same as written)
print("Reading data back from flash: ")
flash.Seek(0, 0) // rewind back to beginning
_, err = flash.Read(saved)
checkError(err)
println(string(saved))
println()
}
func checkError(err error) {
if err != nil {
for {
println(err.Error())
time.Sleep(time.Second)
}
}
}

144
src/machine/flash.go Обычный файл
Просмотреть файл

@ -0,0 +1,144 @@
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l4 || stm32wlx
package machine
import (
"errors"
"io"
"unsafe"
)
//go:extern __flash_data_start
var flashDataStart [0]byte
//go:extern __flash_data_end
var flashDataEnd [0]byte
// Return the start of the writable flash area, aligned on a page boundary. This
// is usually just after the program and static data.
func FlashDataStart() uintptr {
pagesize := uintptr(eraseBlockSize())
return (uintptr(unsafe.Pointer(&flashDataStart)) + pagesize - 1) &^ (pagesize - 1)
}
// Return the end of the writable flash area. Usually this is the address one
// past the end of the on-chip flash.
func FlashDataEnd() uintptr {
return uintptr(unsafe.Pointer(&flashDataEnd))
}
var (
errFlashCannotErasePage = errors.New("cannot erase flash page")
errFlashInvalidWriteLength = errors.New("write flash data must align to correct number of bits")
errFlashNotAllowedWriteData = errors.New("not allowed to write flash data")
errFlashCannotWriteData = errors.New("cannot write flash data")
errFlashCannotReadPastEOF = errors.New("cannot read beyond end of flash data")
errFlashCannotWritePastEOF = errors.New("cannot write beyond end of flash data")
)
// BlockDevice is the raw device that is meant to store flash data.
type BlockDevice interface {
// ReadAt reads the given number of bytes from the block device.
io.ReaderAt
// WriteAt writes the given number of bytes to the block device.
io.WriterAt
// Size returns the number of bytes in this block device.
Size() int64
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
WriteBlockSize() int64
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
// It must be a power of two, and may be as small as 1. A typical size is 4096.
EraseBlockSize() int64
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
EraseBlocks(start, len int64) error
}
// FlashBuffer implements the ReadWriteCloser interface using the BlockDevice interface.
type FlashBuffer struct {
b BlockDevice
start uintptr
current uintptr
}
// OpenFlashBuffer opens a FlashBuffer.
func OpenFlashBuffer(b BlockDevice, address uintptr) *FlashBuffer {
return &FlashBuffer{b: b, start: address, current: address}
}
// Read data from a FlashBuffer.
func (fl *FlashBuffer) Read(p []byte) (n int, err error) {
fl.b.ReadAt(p, int64(fl.current))
fl.current += uintptr(len(p))
return len(p), nil
}
// Write data to a FlashBuffer.
func (fl *FlashBuffer) Write(p []byte) (n int, err error) {
// any new pages needed?
// NOTE probably will not work as expected if you try to write over page boundary
// of pages with different sizes.
pagesize := uintptr(fl.b.EraseBlockSize())
currentPageCount := (fl.current - fl.start + pagesize - 1) / pagesize
totalPagesNeeded := (fl.current - fl.start + uintptr(len(p)) + pagesize - 1) / pagesize
if currentPageCount == totalPagesNeeded {
// just write the data
n, err := fl.b.WriteAt(p, int64(fl.current))
if err != nil {
return 0, err
}
fl.current += uintptr(n)
return n, nil
}
// erase enough blocks to hold the data
page := fl.flashPageFromAddress(fl.start + (currentPageCount * pagesize))
fl.b.EraseBlocks(page, int64(totalPagesNeeded-currentPageCount))
// write the data
for i := 0; i < len(p); i += int(pagesize) {
var last int = i + int(pagesize)
if i+int(pagesize) > len(p) {
last = len(p)
}
_, err := fl.b.WriteAt(p[i:last], int64(fl.current))
if err != nil {
return 0, err
}
fl.current += uintptr(last - i)
}
return len(p), nil
}
// Close the FlashBuffer.
func (fl *FlashBuffer) Close() error {
return nil
}
// Seek implements io.Seeker interface, but with limitations.
// You can only seek relative to the start.
// Also, you cannot use seek before write operations, only read.
func (fl *FlashBuffer) Seek(offset int64, whence int) (int64, error) {
fl.current = fl.start + uintptr(offset)
return offset, nil
}
// calculate page number from address
func (fl *FlashBuffer) flashPageFromAddress(address uintptr) int64 {
return int64(address-memoryStart) / fl.b.EraseBlockSize()
}

Просмотреть файл

@ -3,7 +3,9 @@
package machine package machine
import ( import (
"bytes"
"device/nrf" "device/nrf"
"encoding/binary"
"runtime/interrupt" "runtime/interrupt"
"unsafe" "unsafe"
) )
@ -382,3 +384,109 @@ func ReadTemperature() int32 {
nrf.TEMP.EVENTS_DATARDY.Set(0) nrf.TEMP.EVENTS_DATARDY.Set(0)
return temp return temp
} }
const memoryStart = 0x0
// compile-time check for ensuring we fulfill BlockDevice interface
var _ BlockDevice = flashBlockDevice{}
var Flash flashBlockDevice
type flashBlockDevice struct {
}
// ReadAt reads the given number of bytes from the block device.
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotReadPastEOF
}
data := unsafe.Slice((*byte)(unsafe.Pointer(FlashDataStart()+uintptr(off))), len(p))
copy(p, data)
return len(p), nil
}
// WriteAt writes the given number of bytes to the block device.
// Only double-word (64 bits) length data can be programmed. See rm0461 page 78.
// If the length of p is not long enough it will be padded with 0xFF bytes.
// This method assumes that the destination is already erased.
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotWritePastEOF
}
address := FlashDataStart() + uintptr(off)
padded := f.pad(p)
waitWhileFlashBusy()
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Wen)
defer nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Ren)
for j := 0; j < len(padded); j += int(f.WriteBlockSize()) {
// write word
*(*uint32)(unsafe.Pointer(address)) = binary.LittleEndian.Uint32(padded[j : j+int(f.WriteBlockSize())])
address += uintptr(f.WriteBlockSize())
waitWhileFlashBusy()
}
return len(padded), nil
}
// Size returns the number of bytes in this block device.
func (f flashBlockDevice) Size() int64 {
return int64(FlashDataEnd() - FlashDataStart())
}
const writeBlockSize = 4
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
func (f flashBlockDevice) WriteBlockSize() int64 {
return writeBlockSize
}
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
// It must be a power of two, and may be as small as 1. A typical size is 4096.
func (f flashBlockDevice) EraseBlockSize() int64 {
return eraseBlockSize()
}
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
address := FlashDataStart() + uintptr(start*f.EraseBlockSize())
waitWhileFlashBusy()
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Een)
defer nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Ren)
for i := start; i < start+len; i++ {
nrf.NVMC.ERASEPAGE.Set(uint32(address))
waitWhileFlashBusy()
address += uintptr(f.EraseBlockSize())
}
return nil
}
// pad data if needed so it is long enough for correct byte alignment on writes.
func (f flashBlockDevice) pad(p []byte) []byte {
paddingNeeded := f.WriteBlockSize() - (int64(len(p)) % f.WriteBlockSize())
if paddingNeeded == 0 {
return p
}
padding := bytes.Repeat([]byte{0xff}, int(paddingNeeded))
return append(p, padding...)
}
func waitWhileFlashBusy() {
for nrf.NVMC.GetREADY() != nrf.NVMC_READY_READY_Ready {
}
}

Просмотреть файл

@ -6,6 +6,12 @@ import (
"device/nrf" "device/nrf"
) )
const eraseBlockSizeValue = 1024
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
// Get peripheral and pin number for this GPIO pin. // Get peripheral and pin number for this GPIO pin.
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) { func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
return nrf.GPIO, uint32(p) return nrf.GPIO, uint32(p)

Просмотреть файл

@ -63,3 +63,9 @@ var (
PWM1 = &PWM{PWM: nrf.PWM1} PWM1 = &PWM{PWM: nrf.PWM1}
PWM2 = &PWM{PWM: nrf.PWM2} PWM2 = &PWM{PWM: nrf.PWM2}
) )
const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}

Просмотреть файл

@ -84,3 +84,9 @@ var (
PWM2 = &PWM{PWM: nrf.PWM2} PWM2 = &PWM{PWM: nrf.PWM2}
PWM3 = &PWM{PWM: nrf.PWM3} PWM3 = &PWM{PWM: nrf.PWM3}
) )
const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}

Просмотреть файл

@ -102,3 +102,9 @@ func (pdm *PDM) Read(buf []int16) (uint32, error) {
return uint32(len(buf)), nil return uint32(len(buf)), nil
} }
const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}

122
src/machine/machine_stm32_flash.go Обычный файл
Просмотреть файл

@ -0,0 +1,122 @@
//go:build stm32f4 || stm32l4 || stm32wlx
package machine
import (
"device/stm32"
"bytes"
"unsafe"
)
// compile-time check for ensuring we fulfill BlockDevice interface
var _ BlockDevice = flashBlockDevice{}
var Flash flashBlockDevice
type flashBlockDevice struct {
}
// ReadAt reads the given number of bytes from the block device.
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotReadPastEOF
}
data := unsafe.Slice((*byte)(unsafe.Pointer(FlashDataStart()+uintptr(off))), len(p))
copy(p, data)
return len(p), nil
}
// WriteAt writes the given number of bytes to the block device.
// Only double-word (64 bits) length data can be programmed. See rm0461 page 78.
// If the length of p is not long enough it will be padded with 0xFF bytes.
// This method assumes that the destination is already erased.
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotWritePastEOF
}
unlockFlash()
defer lockFlash()
return writeFlashData(FlashDataStart()+uintptr(off), f.pad(p))
}
// Size returns the number of bytes in this block device.
func (f flashBlockDevice) Size() int64 {
return int64(FlashDataEnd() - FlashDataStart())
}
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
func (f flashBlockDevice) WriteBlockSize() int64 {
return writeBlockSize
}
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
// It must be a power of two, and may be as small as 1. A typical size is 4096.
// TODO: correctly handle processors that have differently sized blocks
// in different areas of memory like the STM32F40x and STM32F1x.
func (f flashBlockDevice) EraseBlockSize() int64 {
return eraseBlockSize()
}
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
unlockFlash()
defer lockFlash()
for i := start; i < start+len; i++ {
if err := eraseBlock(uint32(i)); err != nil {
return err
}
}
return nil
}
// pad data if needed so it is long enough for correct byte alignment on writes.
func (f flashBlockDevice) pad(p []byte) []byte {
paddingNeeded := f.WriteBlockSize() - (int64(len(p)) % f.WriteBlockSize())
if paddingNeeded == 0 {
return p
}
padded := bytes.Repeat([]byte{0xff}, int(paddingNeeded))
return append(p, padded...)
}
const memoryStart = 0x08000000
func unlockFlash() {
// keys as described rm0461 page 76
var fkey1 uint32 = 0x45670123
var fkey2 uint32 = 0xCDEF89AB
// Wait for the flash memory not to be busy
for stm32.FLASH.GetSR_BSY() != 0 {
}
// Check if the controller is unlocked already
if stm32.FLASH.GetCR_LOCK() != 0 {
// Write the first key
stm32.FLASH.SetKEYR(fkey1)
// Write the second key
stm32.FLASH.SetKEYR(fkey2)
}
}
func lockFlash() {
stm32.FLASH.SetCR_LOCK(1)
}

Просмотреть файл

@ -6,6 +6,8 @@ package machine
import ( import (
"device/stm32" "device/stm32"
"encoding/binary"
"errors"
"math/bits" "math/bits"
"runtime/interrupt" "runtime/interrupt"
"runtime/volatile" "runtime/volatile"
@ -791,3 +793,142 @@ func (i2c *I2C) getSpeed(config I2CConfig) uint32 {
} }
} }
} }
//---------- Flash related code
// the block size actually depends on the sector.
// TODO: handle this correctly for sectors > 3
const eraseBlockSizeValue = 16384
// see RM0090 page 75
func sectorNumber(address uintptr) uint32 {
switch {
// 0x0800 0000 - 0x0800 3FFF
case address >= 0x08000000 && address <= 0x08003FFF:
return 0
// 0x0800 4000 - 0x0800 7FFF
case address >= 0x08004000 && address <= 0x08007FFF:
return 1
// 0x0800 8000 - 0x0800 BFFF
case address >= 0x08008000 && address <= 0x0800BFFF:
return 2
// 0x0800 C000 - 0x0800 FFFF
case address >= 0x0800C000 && address <= 0x0800FFFF:
return 3
// 0x0801 0000 - 0x0801 FFFF
case address >= 0x08010000 && address <= 0x0801FFFF:
return 4
// 0x0802 0000 - 0x0803 FFFF
case address >= 0x08020000 && address <= 0x0803FFFF:
return 5
// 0x0804 0000 - 0x0805 FFFF
case address >= 0x08040000 && address <= 0x0805FFFF:
return 6
case address >= 0x08060000 && address <= 0x0807FFFF:
return 7
case address >= 0x08080000 && address <= 0x0809FFFF:
return 8
case address >= 0x080A0000 && address <= 0x080BFFFF:
return 9
case address >= 0x080C0000 && address <= 0x080DFFFF:
return 10
case address >= 0x080E0000 && address <= 0x080FFFFF:
return 11
default:
return 0
}
}
// calculate sector number from address
// var sector uint32 = sectorNumber(address)
// see RM0090 page 85
// eraseBlock at the passed in block number
func eraseBlock(block uint32) error {
waitUntilFlashDone()
// clear any previous errors
stm32.FLASH.SR.SetBits(0xF0)
// set SER bit
stm32.FLASH.SetCR_SER(1)
defer stm32.FLASH.SetCR_SER(0)
// set the block (aka sector) to be erased
stm32.FLASH.SetCR_SNB(block)
defer stm32.FLASH.SetCR_SNB(0)
// start the page erase
stm32.FLASH.SetCR_STRT(1)
waitUntilFlashDone()
if err := checkError(); err != nil {
return err
}
return nil
}
const writeBlockSize = 2
// see RM0090 page 86
// must write data in word-length
func writeFlashData(address uintptr, data []byte) (int, error) {
if len(data)%writeBlockSize != 0 {
return 0, errFlashInvalidWriteLength
}
waitUntilFlashDone()
// clear any previous errors
stm32.FLASH.SR.SetBits(0xF0)
// set parallelism to x32
stm32.FLASH.SetCR_PSIZE(2)
for i := 0; i < len(data); i += writeBlockSize {
// start write operation
stm32.FLASH.SetCR_PG(1)
*(*uint16)(unsafe.Pointer(address)) = binary.BigEndian.Uint16(data[i : i+writeBlockSize])
waitUntilFlashDone()
if err := checkError(); err != nil {
return i, err
}
// end write operation
stm32.FLASH.SetCR_PG(0)
}
return len(data), nil
}
func waitUntilFlashDone() {
for stm32.FLASH.GetSR_BSY() != 0 {
}
}
var (
errFlashPGS = errors.New("errFlashPGS")
errFlashPGP = errors.New("errFlashPGP")
errFlashPGA = errors.New("errFlashPGA")
errFlashWRP = errors.New("errFlashWRP")
)
func checkError() error {
switch {
case stm32.FLASH.GetSR_PGSERR() != 0:
return errFlashPGS
case stm32.FLASH.GetSR_PGPERR() != 0:
return errFlashPGP
case stm32.FLASH.GetSR_PGAERR() != 0:
return errFlashPGA
case stm32.FLASH.GetSR_WRPERR() != 0:
return errFlashWRP
}
return nil
}

Просмотреть файл

@ -4,6 +4,8 @@ package machine
import ( import (
"device/stm32" "device/stm32"
"encoding/binary"
"errors"
"runtime/interrupt" "runtime/interrupt"
"runtime/volatile" "runtime/volatile"
"unsafe" "unsafe"
@ -543,3 +545,104 @@ func initRNG() {
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN) stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN)
stm32.RNG.CR.SetBits(stm32.RNG_CR_RNGEN) stm32.RNG.CR.SetBits(stm32.RNG_CR_RNGEN)
} }
//---------- Flash related code
const eraseBlockSizeValue = 2048
// see RM0394 page 83
// eraseBlock of the passed in block number
func eraseBlock(block uint32) error {
waitUntilFlashDone()
// 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 page to be erased
stm32.FLASH.SetCR_PNB(block)
// start the page erase
stm32.FLASH.SetCR_START(1)
waitUntilFlashDone()
if err := checkError(); err != nil {
return err
}
return nil
}
const writeBlockSize = 8
// see RM0394 page 84
// It is only possible to program double word (2 x 32-bit data).
func writeFlashData(address uintptr, data []byte) (int, error) {
if len(data)%writeBlockSize != 0 {
return 0, errFlashInvalidWriteLength
}
waitUntilFlashDone()
// 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 low order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j+writeBlockSize/2 : j+writeBlockSize])
address += writeBlockSize / 2
// write second word using double-word high order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j : j+writeBlockSize/2])
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 {
}
}
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
}

Просмотреть файл

@ -6,6 +6,8 @@ package machine
import ( import (
"device/stm32" "device/stm32"
"encoding/binary"
"errors"
"math/bits" "math/bits"
"runtime/interrupt" "runtime/interrupt"
"runtime/volatile" "runtime/volatile"
@ -424,3 +426,115 @@ const (
ARR_MAX = 0x10000 ARR_MAX = 0x10000
PSC_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 low order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j+writeBlockSize/2 : j+writeBlockSize])
address += writeBlockSize / 2
// write second word using double-word high order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j : j+writeBlockSize/2])
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
}

Просмотреть файл

@ -69,3 +69,7 @@ _heap_start = _ebss;
_heap_end = ORIGIN(RAM) + LENGTH(RAM); _heap_end = ORIGIN(RAM) + LENGTH(RAM);
_globals_start = _sdata; _globals_start = _sdata;
_globals_end = _ebss; _globals_end = _ebss;
/* For the flash API */
__flash_data_start = LOADADDR(.data) + SIZEOF(.data);
__flash_data_end = ORIGIN(FLASH_TEXT) + LENGTH(FLASH_TEXT);