2014年1月26日日曜日

STM32F0 Value line Discovery(2) デモプログラムの中身を確認する

初めに

STM32F0のデモプログラムは何をやっているのか確認します。

統合開発環境やメーカーからのライブラリのおかげで、簡単にプログラムが作れるようになったのは良いのですが、最初からお膳立てされれているので逆に何をやっているのか理解が進まない事が有ります。今日はデモプログラムが何をやっているのか確認してみます。

main.c

マイコンの電源が投入されるとまずmain.cの先頭からプログラムが実行されます。(厳密には違いますけど、今のところはそう考えましょう)

インクルードファイル

#include "main.h"

ここでは、外部のヘッダファイルをインクルードしています。main.hの中身はあとで確認する事とします。

変数定義

static __IO uint32_t TimingDelay; uint8_t BlinkSpeed = 0;

二つの変数(TimingDelay,BlinkSpeed)を定義しています。 static は静的変数と言うのもので、メモリ上の(どこかに)常に確保され、どの関数からも読み書きができる変数です。

次の __IO は意味が解りません。 右クリックして宣言に移動してみると、core_cm0.hと言うファイルの中で、

#define __IO volatile /*!< Defines 'read / write' permissions */

volatile の別名として使っている事が解りました。 volatileと言うのは、最適化をしないでね、と言うもので組み込みマイコンでは良く使います。 しかし、普通にvolatileと書いてくれればよいものを...

main関数

変数定義

main関数で定義された変数はmain関数内で有効となります。

RCC_ClocksTypeDef RCC_Clocks;

RCC_ClocksTypeDef というのは、以下の様にstm32f0xx_rcc.hで定義されています。

typedef struct { uint32_t SYSCLK_Frequency; uint32_t HCLK_Frequency; uint32_t PCLK_Frequency; uint32_t ADCCLK_Frequency; uint32_t CECCLK_Frequency; uint32_t I2C1CLK_Frequency; uint32_t USART1CLK_Frequency; }RCC_ClocksTypeDef;

IOの初期化

STM_EVAL_LEDInit(LED3); STM_EVAL_LEDInit(LED4); STM_EVAL_PBInit(BUTTON_USER, BUTTON_MODE_GPIO);

LED3,4を出力に、USERボタンを入力に設定しています。

1msタイマの設定

RCC_GetClocksFreq(&RCC_Clocks); SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000);

RCC_GetClocksFreqで現在のクロック情報を読み出し、その値を 1/1000 して SysTick_Configに渡します。

systickとはARMコア共通のタイマ割り込みで、デフォルトはCPUの動作周波数(RCC_Clocks.HCLK_Frequency)となっています。 STM32F0の場合は48MHzですので、systickに48E6を書き込むと、ちょうど1秒でカウントダウンが完了します 。 このプログラムでは、ちょうど1ms毎に割り込みをかけたいので、48E6/1000を設定するわけです。

メインループ

ボタンのチェック

if(STM_EVAL_PBGetState(BUTTON_USER)== SET)

STM_EVAL_PBGetStateでボタンの状態を取得し押されれているか条件分岐します。

ブリンクスピードの変更

BlinkSpeed++; if(BlinkSpeed == 3) { BlinkSpeed = 0; }

ブリンクスピードをインクリメントしています。

Delay

Delay(1000);

随所にこのDelayがあります。待ち時間タイマと言う事は解るのですが、詳しく見てみましょう。 delay()は、mainの後ろの方で定義されています。

void Delay(__IO uint32_t nTime) { TimingDelay = nTime; while(TimingDelay != 0); }

ここでは、TimingDelayと言うグローバル変数(一番最初に宣言しましたよね)にタイマ値を代入し、 それがゼロにならない限りループする。というルーチンになっています。でもTimingDelayはどこでカウントダウンされるのでしょうか?

ここで、先ほどのsystickが出てきます。 systickは1/1000秒毎に割り込みをする設定をしたのですから、ここで何かをしているはずです。 stm32f0xx_it.cを見ると、以下のコードが有ります。

void SysTick_Handler(void) { TimingDelay_Decrement(); }

SysTick_Handlerとは、systickタイマーがタイムアップした時に飛んでくる場所です。 systickタイマーは1/1000秒毎にタイムアップする設定をしましたから、 1ms毎にこのルーチンが(割り込みで)実行されます。 TimingDelay_Decrementとは、TimingDelayがゼロで無い限りデクリメントをする。というルーチンになっています。

つまり、メインルーチンでTimingDelayに値を書き込むと、割り込みタイマが ---勝手に!--- カウントダウンを行ってくれる。 と言う仕組みになっています。メインルーチンはその値がゼロかどうかを調べるだけで良いわけです。

この方法は、マイコンで正確なタイマーを作る時の定石となっています。このプログラムではタイマーは1個でしたが、 例えば、tim1,tim2,tim3....tim10 などと10個のグローバル変数を用意し、SysTick_Handlerでそれら変数をデクリメントする様にしておけば、 メインルーチンのループ時間に関係なく、正確な1ms単位のタイマーを10個用意する事が出来ます。 (もちろん、メインループが1ms以上かかってしまうとその分の遅れ時間は出ますが)

また、この方法はカウントダウン中に別の作業を行う事が出来ますので、 古典的なマルチタスク(原理が単純なので私は好きですが)はこの方法を応用しています。

今日はこの辺で。次はmain.hは何をやっているのか見てみましょう。

main.c

/* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private variables ---------------------------------------------------------*/ static __IO uint32_t TimingDelay; uint8_t BlinkSpeed = 0; int main(void) { RCC_ClocksTypeDef RCC_Clocks; /* Configure LED3 and LED4 on STM32F0308-Discovery */ STM_EVAL_LEDInit(LED3); STM_EVAL_LEDInit(LED4); /* Initialize User_Button on STM32F0308-Discovery */ STM_EVAL_PBInit(BUTTON_USER, BUTTON_MODE_GPIO); /* SysTick end of count event each 1ms */ RCC_GetClocksFreq(&RCC_Clocks); SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000); /* Initiate Blink Speed variable */ BlinkSpeed = 1; while(1) { /* Check if the user button is pressed */ if(STM_EVAL_PBGetState(BUTTON_USER)== SET) { /* BlinkSpeed: 1 -> 2 -> 0, then re-cycle */ /* Turn on LD4 Blue LED during 1s each time User button is pressed */ STM_EVAL_LEDOn(LED4); /* wait for 1s */ Delay(1000); /* Turn off LD4 Blue LED after 1s each time User button is pressed */ STM_EVAL_LEDOff(LED4); /* Increment the blink speed counter */ BlinkSpeed++; /* Default value for blink speed counter */ if(BlinkSpeed == 3) { BlinkSpeed = 0; } } /* Test on blink speed */ if(BlinkSpeed == 2) { /* LED3 toggles each 100 ms */ STM_EVAL_LEDToggle(LED3); /* maintain LED3 status for 100ms */ Delay(100); } else if(BlinkSpeed == 1) { /* LED3 toggles each 200 ms */ STM_EVAL_LEDToggle(LED3); /* maintain LED3 status for 200ms */ Delay(200); } else { /* LED3 Off */ STM_EVAL_LEDOff(LED3); } } } /** * @brief Inserts a delay time. * @param nTime: specifies the delay time length, in 1 ms. * @retval None */ void Delay(__IO uint32_t nTime) { TimingDelay = nTime; while(TimingDelay != 0); } /** * @brief Decrements the TimingDelay variable. * @param None * @retval None */ void TimingDelay_Decrement(void) { if (TimingDelay != 0x00) { TimingDelay--; } } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t* file, uint32_t line) { /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* Infinite loop */ while (1) {} } #endif /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

0 件のコメント:

コメントを投稿

コメントをどうぞ