STM32 + レジスタを直接たたいてFLASHの書き込み読み込みをやってみる
STM32マイコンのペリフェラル関連記事を一覧にまとめました。
LLAPIには、Flash関連のプログラムが実装されていませんでした。したがって、レジスタを叩いて動かすということをしていきたいと思います。
HALAPIでも書いているので、HALを使用している方は以下に紹介する記事をみていただければと思います。リファレンスマニュアルについて読んだ点は同じだったのでレジスタの中身の確認からしていきます。
STM32CubeMXの設定
Flashに読み書きができているかどうかの確認を視覚的に行えるように、USARTを有効にしました。
その後、ペリフェラルごとにファイルを分割するというところにチェックをいれて、コード生成をしたのち、printfを使用できる状態にしました。
LLAPIを使用したprintfの実装はこちらの記事を参考にしていただければと思います。
レジスタの確認を行う
KEYRレジスタ
KEY1,KEY2を順番に書き込むことでFLASHレジスタへのアクセスが可能にするためのレジスタです。ここで、ロックを解除しないと書き込みができないということが読み取れます。
CRレジスタ
セクタ消去のセクタを設定したり、プログラミングのサイズを決めたりといろいろなことができるビットであることが伺えます。リファレンスマニュアルのセクタ消去や標準プログラミングのやり方でも出てきているので、マニュアルに沿って設定をしていきたいと思います。
SRレジスタ
FLASHの作業中の状態を表示してくれるレジスタです。使用するので確認をしました。
ソースコードを実装する
今回は、stm32f405xx.hでmacro定義をされているものを上手く使用して書いていきました。定義されていないものは自分で定義を行っています。
また、電圧などに依存しないように8byte書き込みでの実装をしてみました。
#ifndef __FLASH_H
#define __FLASH_H
#include <stdint.h>
#include "main.h"
#define FLASH_KEY1 0x45670123U
#define FLASH_KEY2 0xCDEF89ABU
// flash use address ( sector11 )
extern const uint32_t start_address; //sentor11 start address
extern const uint32_t end_adress;
__STATIC_INLINE void FLASH_Lock(void)
{
FLASH->CR |= FLASH_CR_LOCK;
}
__STATIC_INLINE void FLASH_Unlock(void)
{
FLASH->KEYR = FLASH_KEY1;
FLASH->KEYR = FLASH_KEY2;
}
void FLASH_WaitBusy(void);
void FLASH_Erease(void);
void FLASH_WriteByte(uint32_t address, uint8_t data);
void FLASH_ReadData(uint32_t address, uint8_t* data, uint32_t size);
void FLASH_WriteData(uint32_t address, uint8_t* data, uint32_t size);
#endif /* __FLASH_H */
#include "flash.h"
#include <string.h>
//1byteずつの書き込み
#define FLASH_TYPEPROGRAM_BYTE 0x00000000U
//0x1011 <<3 より 0x1011000
#define FLASH_SENTOR11 0x58
// flash use address ( sector11 )
const uint32_t start_address = 0x80E0000; //sentor11 start address
const uint32_t end_adress = 0x80FFFFF;
void FLASH_WaitBusy(void)
{
while( ( (FLASH->SR & FLASH_SR_BSY) == FLASH_SR_BSY) == 1 );
}
void FLASH_Erease(void)
{
FLASH->CR |= FLASH_CR_SER;
FLASH->CR |= FLASH_SENTOR11 & FLASH_CR_SNB_Msk;
FLASH->CR |= FLASH_CR_STRT;
FLASH_WaitBusy();
}
void FLASH_WriteByte(uint32_t address, uint8_t data)
{
FLASH->CR &= ~(FLASH_CR_PSIZE);
FLASH->CR |= FLASH_TYPEPROGRAM_BYTE;
FLASH->CR |= FLASH_CR_PG;
*(__IO uint8_t*)address = data;
FLASH_WaitBusy();
FLASH->CR &= ~(FLASH_CR_PG);
}
void FLASH_WriteData(uint32_t address, uint8_t* data, uint32_t size)
{
FLASH_Unlock();
FLASH_Erease();
do {
FLASH_WriteByte(address, *data);
}while(++address, ++data, --size);
FLASH_Lock();
}
void FLASH_ReadData(uint32_t address, uint8_t* data, uint32_t size)
{
memcpy(data, (uint8_t*)address, size);
}
こんな感じで実装しました。セクタ11を消去してセクタ11に書き込みを行っています。例によって構造体での書き込みで実装をしました。毎回フラッシュ領域をすべて消去していますがビットが1の場合は消去せずともデータの書き込みが可能なので、上手く扱うことで毎回セクタ消去しなくても使えるかもしれないです。
おわりに
LLAPIではFlashのプログラミングにAPIが提供されていなかったのでレジスタを叩いてみました。リファレンスマニュアルを読めばちゃんと動くものが実装できるということが体感できたと思います。次は、LLAPIのUSARTを使用してscanfの実装について書いていきたいと思います。
リクエスト等がありましたら、Twitterまたはコンタクトフォームから連絡を入れていただければ書くかもしれないです。リクエストお待ちしております。