STM32 + レジスタを直接たたいてFLASHの書き込み読み込みをやってみる

2020年6月7日

STM32マイコンのペリフェラル関連記事を一覧にまとめました。

LLAPIには、Flash関連のプログラムが実装されていませんでした。したがって、レジスタを叩いて動かすということをしていきたいと思います。

HALAPIでも書いているので、HALを使用している方は以下に紹介する記事をみていただければと思います。リファレンスマニュアルについて読んだ点は同じだったのでレジスタの中身の確認からしていきます。

 




STM32CubeMXの設定

Flashに読み書きができているかどうかの確認を視覚的に行えるように、USARTを有効にしました。

 

STM32CubeMX USARTの設定

その後、ペリフェラルごとにファイルを分割するというところにチェックをいれて、コード生成をしたのち、printfを使用できる状態にしました。

LLAPIを使用したprintfの実装はこちらの記事を参考にしていただければと思います。

 

レジスタの確認を行う

KEYRレジスタ

RM0090 リファレンスマニュアルより引用

KEY1,KEY2を順番に書き込むことでFLASHレジスタへのアクセスが可能にするためのレジスタです。ここで、ロックを解除しないと書き込みができないということが読み取れます。

 

CRレジスタ

RM0090 リファレンスマニュアルより引用

RM0090 リファレンスマニュアルより引用

セクタ消去のセクタを設定したり、プログラミングのサイズを決めたりといろいろなことができるビットであることが伺えます。リファレンスマニュアルのセクタ消去や標準プログラミングのやり方でも出てきているので、マニュアルに沿って設定をしていきたいと思います。

 

SRレジスタ

RM0090 リファレンスマニュアルより引用

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またはコンタクトフォームから連絡を入れていただければ書くかもしれないです。リクエストお待ちしております。

 

参考文献

RM0090 リファレンスマニュアル