STM32 内蔵Flashメモリ データ保存 Fault

STM32のようなワンチップマイコンの内蔵Flashメモリにデータを書き込む際は、注意する事があります。
Flashメモリは、一般的にpage, block, deviceという単位ブロックで表現されてます。
Flash消去(Erase), 書き込み(Program)の実行中は、同じblockの読み出し(Read)が出来ません。FAULTします。
出来ることなら消去と書き込み中は、割り込み禁止にするのが良いのですが、システム的にそうする事で他のタスクに影響が出るため、割り込み禁止しない場合、動作する可能性のプログラム(関数や割り込みハンドラ)にram_function属性を付けて、RAM動作するように配置する必要があります。
逆の表現すると、データ通信中に希にデータを取りこぼす事象の場合、割り込み禁止状態が影響している場合がよくあります。どちらも見つけにくいbugです。
設計フェーズで懸念していると見つけやすいですよ。

Android ビジネスカレンダー2 導入

Androidアプリのビジネスカレンダー2(Business Calendar 2)をインストールしてみた。
祝日設定が分かり辛かった事と仕事用カレンダー追加が分かり辛かったので操作手順をメモしておきます。
ビジネスカレンダーアプリインストール後に月表示(左画面)、祝祭日と仕事カレンダを追加した月表示(右画面)に成ります。

インストール直後 予定入力後

Googleカレンダは使ってないので、ノー・スケジュールですwww
STEP1:まず、左上のハンバーガ(メニュー)をタッチしてメニューを表示する。そして[設定]メニューではなく、[カレンダーとタスクリスト]をタッチします。※ここにあるのが分からなかった。

STEP2:[祝祭日を追加する]をタッチして、[日本]を選択する。
日本祝祭日がリストと右下に追加されます。これで月予定に表示されます。

ついでに右下に[仕事]用のカレンダーも追加してみよう。カレンダー追加するだけでは、表示されないんだよね。。。
これも予想外なのでメモしておきます。
STEP1:まずは、ローカルカレンダーを追加する。

STEP2:タスクも関連付けするために[タスクリスト]も追加する。

STEP3:最後に[グループ]を追加すると、[仕事]カレンダが追加される。

月カレンダに”Webネタ収集”を期間で[Job]予定を入力して、”Web公開”を[Jon]タスクリストで入力してみた。

STM32F103C8T6 マイコン

STM32F103C8T6 が載った基板を購入したので、まずはST社からSTM32CubeF1をダウンロードして開発環境を整えてみます。
流用できるプロジェクトファイルは、STM32F103RB-Nucleoでイケます。main関数は基板固有コードばかりなので空関数にしますが、
使用するソースファイルやヘッダファイル、コンパイルオプションは、まずは同じでOKです。後から精査しましょう。

STM32CubeF1内のDrivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.hに使用するマイコン名を#defineするように記載されていますが、私はコンパイルオプション(-Dプリプロセッサ定義)でSTM32F103xBを指定します。(何故なら敢えてオリジナルファイルを変更しなくても対応可能な方法だから、ソースの変更管理しなくて済みます。
私の統合開発環境(IDE)は、IAR社のEWARMを使用しているから、iarフォルダ下にプロジェクトファイルがあります。このプロジェクトファイルをEWARMで開けば、必要なソースファイルが分かりますが、ココにも載せておきます。本サイトのSTM32F303K8でも取り上げています。

---STM32Cube_F1 フォルダ構成---
STM32Cube_FW_F1_V1.6.0
+Drivers
 +CMSIS
 |+Device
 ||+ST
 || +STM32F1xx
 ||  +Include   :includeパスにセットするフォルダ
 ||  +Source
 ||   +Templates   :以下(2)にコピーされている
 ||    +iar
 |+Include
 +STM32F1xx_HAL_Driver
  +Inc           :includeパスにセットするフォルダ
  +Src           :以下(1)参照
+Projects
 +STM32F103RB-Nucleo
  +Templates_LL
   +EWARM       :IAR用ファイルのフォルダ
   +Inc         :includeパスにセットするフォルダ
   +Src         :以下(3)参照
+Middlewares
 +Third_Party
  +FreeRTOS      :リアルタイムOS


---(1)Drivers\STM32F1xx_HAL_Driver\Src---
stm32f1xx_hal_XXXX.c  :HALドライバ
stm32f1xx_ll_XXXX.c   :LLドライバ

---(2)Projects\STM32F103RB-Nucleo\Templates_LL\EWARM---
startup_stm32f103xb.s  :ベクタテーブル、リセットハンドラ
stm32f103xb_flash.icf  :IARリンカの設定ファイル

---(3)Projects\STM32F103RB-Nucleo\Templates_LL\Src---
main.c
stm32f1xx_it.c     :割り込みハンドラ(無くても良い。各ハンドラはweakでstartup_stm32f103xb.sに定義済み)
system_stm32f1xx.c :boot初期処理

外部クロックHSE(8MHz)を用いて動作させるために必要なファイル一覧を以下に記載します。
ちなみにCMSISのLLドライバを使用しました。

---STM32Cube_F1 外部クロックで動作させるために必要なファイル一覧---
main.c
startup_stm32f103xb.s
system_stm32f1xx.c
(stm32_assert.h)
stm32f103x8.icf
stm32f1xx_ll_gpio.c
stm32f1xx_ll_rcc.c

STM32マイコンは、Power ONで内部クロックHSI(8MHz)により動作します。従って、ベクタテーブルのリセットハンドラさえあればmain()迄実行され、main(){while(1);}だけの空転関数でも動作します。
しかし、次のmain()ではFreeRTOSを盛り込み、外部クロックHSE(8MHz)を用いて動作するように設定した実装例も載せておきます。省電力のため動作クロックはSYSCLK 48MHz、HCLK 24MHzに落としてます。

---main.c---
#include "stm32f1xx_ll_rcc.h"
#include "stm32f1xx_ll_system.h"
#include "stm32f1xx_ll_gpio.h"
#include "stm32f1xx_ll_bus.h"
#include "FreeRTOS.h"	//FreeRTOS用
#include "task.h"	//FreeRTOS用

void SystemClock_Config(void);
void createOsResource(void);	//FreeRTOS用

/* テストピン(LED1)定義 */
#define LED1_GPIO		GPIOC
#define LED1_PIN		LL_GPIO_PIN_13

/**
 * @brief   Main program
 * @param   None
 * @retval  None
 */
int main(void)
{
	/* setup system clocks */
	SystemClock_Config();

	/* テストピン(LED1)をEnable */
	LL_APB2_GRP1_EnableClock( LL_APB2_GRP1_PERIPH_GPIOC );
#if 0	// LL_GPIO_Init()にBUGあり
	{
	LL_GPIO_InitTypeDef  pin_cfg;
		/* ポートCをEnableにする */
		LL_GPIO_DeInit(LED1_GPIO);

		/* テストピン(LED1)をセットアップする */
		pin_cfg.Pin		= LED1_PIN;
		pin_cfg.Mode		= LL_GPIO_MODE_OUTPUT;
		pin_cfg.Speed		= LL_GPIO_SPEED_FREQ_LOW;
		pin_cfg.OutputType	= LL_GPIO_OUTPUT_PUSHPULL;
		pin_cfg.Pull		= LL_GPIO_PULL_UP;
		LL_GPIO_Init(LED1_GPIO, &pin_cfg);
	}
#else
	/* Pin Mode configuration */
	LL_GPIO_SetPinMode( LED1_GPIO, LED1_PIN, LL_GPIO_MODE_OUTPUT );
	/* Pull-up Pull down resistor configuration*/
	LL_GPIO_SetPinPull( LED1_GPIO, LED1_PIN, LL_GPIO_PULL_UP );
	/* Speed mode configuration */
	LL_GPIO_SetPinSpeed( LED1_GPIO, LED1_PIN, LL_GPIO_SPEED_FREQ_LOW );
	/* Output mode configuration*/
	LL_GPIO_SetPinOutputType( LED1_GPIO, LED1_PIN, LL_GPIO_OUTPUT_PUSHPULL );
#endif

	/* OSリソース生成 */
	createOsResource();	//FreeRTOS用

	/* Start the scheduler */
	vTaskStartScheduler();	//FreeRTOS用

	while(1);
}

/**
 * @brief   System Clock Configuration
 *          The system Clock is configured as follow : 
 *             System Clock source            = PLL (HSE)
 *             SYSCLK(Hz)                     = 48000000
 *             HCLK(Hz)                       = 24000000
 *             AHB Prescaler                  = 2
 *             APB1 Prescaler                 = 1
 *             APB2 Prescaler                 = 1
 *             HSE Frequency(Hz)              = 8000000
 *             PLL_MUL                        = 6
 *             Flash Latency(WS)              = 1
 * @param   None
 * @retval  None
 */
void SystemClock_Config(void)
{
#if 1
//	LL_APB2_GRP1_EnableClock( LL_APB2_GRP1_PERIPH_GPIOD );
//	LL_RCC_EnableIT_HSERDY(); 
	LL_RCC_HSE_EnableCSS(); 
	/* Enable HSE oscillator */
//	LL_RCC_HSE_EnableBypass();
	LL_RCC_HSE_Enable();
	while( LL_RCC_HSE_IsReady() != 1 );
#endif
	/* Set FLASH latency */
	LL_FLASH_SetLatency( LL_FLASH_LATENCY_2 );

	/* Set PLLSRC & PLLMUL */
#if 1
	LL_RCC_PLL_ConfigDomain_SYS( LL_RCC_PLLSOURCE_HSE_DIV_1, LL_RCC_PLL_MUL_6 );
#else
	LL_RCC_PLL_ConfigDomain_SYS( LL_RCC_PLLSOURCE_HSI_DIV_2, LL_RCC_PLL_MUL_12 );
#endif
	LL_RCC_PLL_Enable();
	while( LL_RCC_PLL_IsReady() != 1 );

	/* Set AHB prescaler */
	LL_RCC_SetAHBPrescaler( LL_RCC_SYSCLK_DIV_2 );
	/* Select SYSCLK by PLLCLK */
	LL_RCC_SetSysClkSource( LL_RCC_SYS_CLKSOURCE_PLL );
	while( LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL );

	/* Set APB1 & APB2 prescaler */
	LL_RCC_SetAPB1Prescaler( LL_RCC_APB1_DIV_1 );
	LL_RCC_SetAPB2Prescaler( LL_RCC_APB2_DIV_1 );

	/* Set SYSTICK to 10ms */
	SysTick_Config( 48000000 / 100 );

	/* Set CMSIS variable */
	SystemCoreClock = 48000000;
}

/**
  * @brief  TICKフック of FreeRTOS
  * @param  None
  * @retval None
  */
#define TICK_HOOKCNT  20		//= 200ms=20 * os tick=10ms
int16_t   iHookCnt = TICK_HOOKCNT;
void vApplicationTickHook( void )
{
	if(--iHookCnt <= 0){
		iHookCnt = TICK_HOOKCNT;
		/* テストピン(LED1)にトグル出力 */
		LL_GPIO_TogglePin(LED1_GPIO, LED1_PIN);
	}
}

/**
  * @brief  空タスク
  * @param  None
  * @retval None
  */
void XXXXTask( void * pvParameters )
{
	for( ;; );
}

/**
  * @brief  create FreeRTOS resorce.
  *         タスクやメッセージキューを生成する
  * @param  None
  * @retval None
  */
#define	XXXXTask_STACK_SIZE		configMINIMAL_STACK_SIZE
void createOsResource(void)
{
	static uint8_t ucParameterToPass;
	TaskHandle_t xHandle = NULL;

	// Create the task, storing the handle.
	xTaskCreate( XXXXTask, "TSKNAME", XXXXTask_STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
	configASSERT( xHandle );
}

FreeRTOSを盛り込むにあたり、startup_stm32f103xb.sの実装例も載せておきます。

        PUBWEAK SVC_Handler
	EXTERN  vPortSVCHandler		;☆追加
        SECTION .text:CODE:REORDER:NOROOT(1)
SVC_Handler
        B vPortSVCHandler		;☆変更

        PUBWEAK DebugMon_Handler
        SECTION .text:CODE:REORDER:NOROOT(1)
DebugMon_Handler
        B DebugMon_Handler

        PUBWEAK PendSV_Handler
	EXTERN  xPortPendSVHandler	;☆追加
        SECTION .text:CODE:REORDER:NOROOT(1)
PendSV_Handler
        B xPortPendSVHandler		;☆変更

        PUBWEAK SysTick_Handler
	EXTERN  xPortSysTickHandler	;☆追加
        SECTION .text:CODE:REORDER:NOROOT(1)
SysTick_Handler
        B xPortSysTickHandler		;☆変更

IAR社のEWARM

[ブログ]
EWARMのバージョンが8.11.1になっていたので使用させてもらったところ、アイコンが新しくなっり64bitOS対応版がありました。

少し使用した感想ですが、ビルドオプションが増えてたので、STのサンプルプロジェクトをダウンロードしようとしたのですがエラーが出てできななった(現在2017/8/15はダウンロード可能)。
デバッグ時のレジスタ窓から各コントローラのレジスタ一覧が無くなってて機能ダウン。レジスタ定義を入手すれば表示されそうだけど手間なので、旧バージョンv7.50でデバッグした(笑)

doxygenとPlantUMLの組み合わせ

■doxygenとPlantUMLの組み合わせ
doxygen のドキュメント生成ツールと PluntUML の作図ツールを組み合わせてみました。
言葉だけでは理解困難なケースや誤解を招くため、doxygen生成ドキュメントに図を取り入れよう。
その方法を簡単に記載する。
(1)まずは、doxygenのコンフィグファイルを生成してカスタマイズする。
(2)次にドキュメント生成元のソースファイルにPlantUMLの記載を入れる。
(3)最後にPlantUMLによる図の.pngファイルを生成する。

◆Doxyfileコンフィグファイル生成
doxygen-1.8.12> doxygen.exe -g

◆Doxyfileコンフィグファイル設定例
・関連する設定項目
DOXYFILE_ENCODING = CP932   #Doxyfileの文字コード指定:拡張SHIFT_JIS
OUTPUT_DIRECTORY = ./out_dir  #Doxygen出力フォルダ指定
INPUT = ./in_dir          #(入力)ソースファイルのフォルダ指定
INPUT_ENCODING = CP932    #(入力)ソースファイルの文字コード指定
HAVE_DOT = YES         #graphvizを使う場合 YES
DOT_PATH = ../PlantUML/graphviz-2.38/bin
PLANTUML_JAR_PATH = ../PlantUML

・その他設定項目
PROJECT_NAME = “NewProject”
PROJECT_NUMBER = Rev 0.10
PROJECT_BRIEF = プロジェクトの概要をココに記載する
BRIEF_MEMBER_DESC = NO
REPEAT_BRIEF = NO
STRIP_FROM_PATH = in_dir
STRIP_FROM_INC_PATH = in_dir
OPTIMIZE_OUTPUT_FOR_C = YES
IDL_PROPERTY_SUPPORT = NO
GENERATE_LATEX = NO

◆doxygenの.batファイルの内容
—————————
echo *** JREパス ***
set PATH=%PATH%;D:\User\070_Document\PlantUML\jre7\jre1.7.0\bin
echo *** Graphviz起動 ***
set GRAPHVIZ_DOT=D:\User\070_Document\PlantUML\graphviz-2.38\bin\dot.exe
echo *** doxygen起動 ***
doxygen.exe
—————————

◆ソースファイルsource.cの記載例
・source.cをin_dirフォルダ下に置き、doxygenを実行する
※この時点ではPlantUMLが出力するXXXX.pngは生成されない(PlantUMLは.batで動作させるため.pngが生成されない方が利便性が良い)
—————————
/**
* @brief 大分類ステートメントテーブル
*
* 大分類の状態テーブルである
* 大分類を細分化した状態は中分類の状態テーブルを参照の事
* @note
* @startuml XXXX.png
database LargeStateTable as ”
LARGE_S1

LARGE_S2

LARGE_S3

LARGE_S4

LARGE_STATE_MAX

* @enduml
*/
—————————

・doxygenで出力されたout_dir\htmlフォルダ下の.htmlファイル抜粋
 @startuml~@endumlの部分が、img src=”XXXX.png” に成っているから、
カレントフォルダにXXXX.pngが有れば(生成されれば)、図がドキュメントに表示される。

◆PlantUMLの.batファイルの内容
・PlantUML.batを実行すると、XXXX.puが読み込まれ XXXX.pngが生成される
文字コードは、doxygenにより UTF-8で出力されてるため -charset UTF-8を指定。
(javeへのPATHが通っていない場合、javaへの相対パスを記載した例)
—————————
echo *** Graphviz起動 ***
set GRAPHVIZ_DOT=D:\User\070_Document\PlantUML\graphviz-2.38\bin\dot.exe
echo *** PlantUML起動 ***
PlantUML\jre7\jre1.7.0\bin\java -jar PlantUML\plantuml.jar -v -charset UTF-8 doxygen-1.8.12\out_dir\html\*.pu
—————————

STM32F303K8のブートプログラム

[コンテンツ]
STM32F303K8内蔵クロック(HSI 8MHz)だけを使用するのであれば、ブートプログラムは IARのEWARMの場合はSTM32CubeF3 内のProjectsフォルダ下を利用すればmain関数まで実行できるようになります。
必要なファイルは以下の通り。
・startup_stm32f303x8.s :無修正で利用
・system_stm32f3xx.c :無修正で利用
・stm32f3xx_it.c :無修正で利用
・main.c :main関数内は空でも良い
・stm32f3xx_it.h
・stm32_assert.h
・main.h

次のステップとしては、クロックアップさせる事になると思います。
STM32CubeF3では、HALまたはLLペリフェラルドライバを提供しています。
HALドライバは、少々ソフトウェア的な作りとなっており、該当MCU以外の処理ロジックが含まれていて理解し辛いです。
LLドライバは、該当MCU以外の処理ロジックはコンパイルフラグにより排除できる形となっています。

ローカルな実装では、動作クロックはPLLを用いて48MHzにしました。
以下のようにmain関数でLLドライバを使用してます。

#include "stm32f3xx_ll.h"

int main(void)
{
	/* クロック設定の確認のためにクロックをMCOポート(18pin:PA8)に出力する */
	LL_RCC_ConfigMCO( LL_RCC_MCO1SOURCE_PLLCLK, LL_RCC_MCO1_DIV_1 );

	/* Configure the system clock to 48 MHz */
	SystemClock_Config();

	/* Infinite loop */
	while (1);
}

次のステップでFreeRTOSを実装します。
STM32CubeF3に含まれているver9.0とオフィシャル ver7.6の消費リソース具合を調べようと思ってます。
組み込みシステムで使用するRTOSの機能は、セマフォとメッセージがあれば事足りるからです。