STM32 (AD変換と移動平均)
1.本日の内容
(1) STM32マイコン(nucleof401re)を使用して、
スイッチの長押し/短押しの判定を行う。
・目次
2.内容
(1) やる内容の詳細
・nucleof401REでAD変換を行う。
・AD変換を行う場合、全ての値を読み取ると、バラツキが大きく、誤差変動が大きくなる。
そのため、移動平均処理を行い、
AD変換10データの平均を出し、その値を出力する。
・最初にIDLEモードで起動しLEDを点灯
・スイッチ(青ボタン)を押すと、MODE1に遷移し、
LEDを消灯、AD変換を開始、その値をTeraTermに出力する。
(2) 使用部品
個数 | 部品名 | 型番 |
---|---|---|
1 | 接続ケーブル | ジャンパーワイヤ |
1 | mbed(マイコンボード) | nucleo f401RE |
1 | USBケーブル | USBミニBタイプ |
(2) CubeIDEの設定
ピン及び、基本設定として、CubeIDEで下記設定を行う。
・LEDのGPIO出力設定(PA5:GPIO_OUT)
・KEYのGPIO入力設定(PC13:GPIO_IN)
・タイマ割り込み設定(TIM3)
→1ms毎に割り込み発生
・デバック用にUART設定(UART2)
→9600BPS/s
・ AD変換をDMAで行う設定を行う。
CubeIDEの設定については下記を参考
・STM32マイコン24(DMAでAD変換) - Project_OKI’s diary
(今回は連続でAD変換するので、DMAはCircuitモードで行う)
(3) プログラムの作成
今回は、下記ファイルを作成する。
・AD変換作成用ファイル :adc_api.c/adc_api.h
→新規追加
→ 参考:STM32マイコン24(DMAでAD変換) - Project_OKI’s diary
・割り込み作成用ファイル :IntR.c/IntR.h
・メイン処理作成用ファイル :self_main.c/self_main.h
→STM32マイコン27(スイッチの短押し/長押し処理 )の記事のプログラムを一部修正
・メイン処理ファイル(自動生成):main.c/main.h
・stm32f4xx_it.c (割り込みについて記載)
・キー制御用ファイル :key_ctrl.c/key_ctrl.h
・led制御用ファイル :led.c/led.h
・printf用ファイル :printf.c/printf.h
・基本型設定用ファイル :typedef.h
・STM32レジスタ設定ファイル:STMSys.h
→STM32マイコン27(スイッチの短押し/長押し処理 ) と同じ
///adc_api.c
/** *********************************************** */ /** * @file IntR.c * @brief 割り込み制御ルーチン群 * @brief Compiler : CubeIDE * @brief CPU : STM32F401RE * @date $LastChangedDate : 2023/04/16 $ * @version $Revision : 1.00 $ */ /** *********************************************** */ /** *********************************************** */ /** インクルードファイル */ /** *********************************************** */ #include "typedef.h" ///< 基本型 #include "self_main.h" ///< システム #include "adc_api.h" ///< ADC制御 #include "main.h" ///< メイン処理 /** *********************************************** */ /** 外部定義変数 */ /** *********************************************** */ /** *********************************************** */ /** 内部定義変数 */ /** *********************************************** */ /** BATT-AD情報 **************************************** */ volatile TAdBattInf GAdBattInf; ///< BATT-AD情報 /** DMA(BATT-AD) **************************************** */ volatile USHORT GBattDatBuf[1]; ///< ADデータ格納バッファ extern ADC_HandleTypeDef hadc1; /** *********************************************** */ /** * AD情報の初期化 * @param void : None * @return void : None */ /** *********************************************** */ void AdcApi_BattAdInfInit( void ) { USHORT usCnt; ///< ループカウンタ GAdBattInf.eState = AD_ST_STOP; ///< AD変換停止中 GAdBattInf.bAdFin = false; ///< AD変換完了フラグOFF GAdBattInf.usAd = 0; ///< AD値初期化 GAdBattInf.bWatchReq = false; ///< BATT監視要求フラグOFF /** 移動平均制御情報の初期化 ********************************* */
/// データ数分のループ for( usCnt=0; usCnt < AD_BATT_MAVE_NUM_DAT; usCnt++ ) { GAdBattInf.usMAveBuf[usCnt] = 0; ///< 移動平均バッファをクリア } GAdBattInf.usWp = 0; ///< 書き込み位置 GAdBattInf.usNumDat = 0;///< データ数 } /** *********************************************** */ /** * BATT-AD AD変換開始 * @param void : None * @return void : None */ /** *********************************************** */ void AdcApi_BattAdStart( void ) { GAdBattInf.eState = AD_ST_STOP; ///< AD変換停止中 GAdBattInf.bAdFin = false; ///< AD変換完了フラグOFF ///ADC DMA転送停止と開始 HAL_ADC_Stop_DMA(&hadc1); ///< ADC DMA転送停止
/// ADC DMA転送開始 HAL_ADC_Start_DMA(&hadc1, (uint32_t *)GBattDatBuf, 2); GAdBattInf.eState = AD_ST_RUN; ///< AD変換中(開始) } /** *********************************************** */ /** * BATT-AD AD変換開始チェック(20ms毎) * @param void : None * @return void : None */ /** *********************************************** */ void AdcApi_BattAdChkReStart( void ) { if ( GAdBattInf.eState == AD_ST_RUN ) { ///< AD変換中 if ( GAdBattInf.bAdFin == true ) {///< AD変換完了フラグON? GAdBattInf.bAdFin = false;///< AD変換完了フラグOFF ///ADC DMA転送停止 HAL_ADC_Stop_DMA(&hadc1); ///ADC DMA転送開始 HAL_ADC_Start_DMA(&hadc1, (uint32_t *)GBattDatBuf, 1); } } } /** *********************************************** */ /** * DMA 転送完了割込み処理 * @param ADC_HandleTypeDef : hadc * @return void : None */ /** *********************************************** */ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { USHORT ad; if(hadc == &hadc1){ ad = GBattDatBuf[0]; AdcApi_BattAdInt(ad); ///< AD1 電圧の保存 } } /** *********************************************** */ /** * BATT-AD AD変換完了割込み(20ms毎) * @param void : None * @return void : None */ /** *********************************************** */ void AdcApi_BattAdInt( USHORT usAd ) { if ( GAdBattInf.eState == AD_ST_RUN ) { ///< AD変換中 GAdBattInf.usAd = usAd; ///< AD値を更新 /// AD値を移動平均バッファに格納 GAdBattInf.usMAveBuf[GAdBattInf.usWp] = GAdBattInf.usAd; /// バッファの最後の時 if ( GAdBattInf.usWp >= (AD_BATT_MAVE_NUM_DAT-1) ) { GAdBattInf.usWp = 0; ///< 書き込み位置を先頭へ GAdBattInf.bWatchReq = true;///< BATT監視要求フラグON } else { ///< バッファの最後でない GAdBattInf.usWp++; ///< 書き込み位置を更新 } /// データ数更新 GAdBattInf.usNumDat = ( GAdBattInf.usNumDat < AD_BATT_MAVE_NUM_DAT )?(GAdBattInf.usNumDat+1) : AD_BATT_MAVE_NUM_DAT; } GAdBattInf.bAdFin = true; ///< AD変換完了フラグON } /** *********************************************** */ /** * BATT-AD 移動平均値の取得 * @param void : None * @return USHORT : 移動平均値(1秒間) */ /** *********************************************** */ USHORT AdcApi_BattAdGetMoveAve( void ) { USHORT usCnt; ///< ループカウンタ USHORT usNumDat; ///< データ数 USHORT usMAve; ///< 移動平均値 usNumDat = GAdBattInf.usNumDat; ///< データ数を取得 usMAve = 0; ///< 移動平均値をクリア for( usCnt=0; usCnt < usNumDat; usCnt++ ) { ///< データ数分のループ usMAve += GAdBattInf.usMAveBuf[usCnt]; ///< バッファの値を加算 } if ( usNumDat == 0 ) { ///< データ数が0の時 usMAve = 0; ///< 移動平均値=0 } else { ///< データ数が0でない時 usMAve = ( usMAve / usNumDat ); ///< 移動平均値を更新 } return( usMAve ); ///< 移動平均値を返す } /** *********************************************** */ /** * AD監視要求の取得 * @param void : None * @return bool : BATT監視要求 */ /** *********************************************** */ bool AdcApi_GetWatchReq( void ) { return( GAdBattInf.bWatchReq ); ///< BATT監視要求を返す } /** *********************************************** */ /** * AD監視要求の設定 * @param bool : BATT監視要求 * @return void : None */ /** *********************************************** */ void AdcApi_SetWatchReq( bool bWatchReq ) { GAdBattInf.bWatchReq = bWatchReq; ///< BATT監視要求を設定 } /** *** ファイルの最後 ( adc_api.c ) ***************************** */
///adc_api.h
/** *********************************************** */ /** * @file IntR.c * @brief 割り込み制御ルーチン群 * @brief Compiler : CubeIDE * @brief CPU : STM32F401RE * @date $LastChangedDate : 2023/04/16 $ * @version $Revision : 1.00 $ */ /** *********************************************** */ #ifndef ADC_API_H ///< ADC_API_H 未定義? #define ADC_API_H ///< ADC_API_H 定義済み!! /** *********************************************** */ /** 定数値の定義 */ /** *********************************************** */ /** *********************************************** */ /** \defgroup ADCAPI_DEF ADC制御API処理用の定義 */ /** *********************************************** */ /*@{*/ #define MAX_AD_VAL (4095 ) ///< AD変換最大値(10ビット) #define MAX_AD_VOLT (3000 ) ///< AD変換最大電圧値[mV] /*@}*/ /** *********************************************** */ /** \defgroup ADC1SEQ0_DEF ADC1SEQ0(BATT)用の定義*/ /** *********************************************** */ /*@{*/ #define AD_BATT_NUM_CH (2)///< AD変換チャネル #define AD_BATT_MAVE_NUM_DAT (10)///< 移動平均データ数 #define AD_TIM_CNT (4)///< AD変換周期(5ms単位:5ms*4=20ms) /*@}*/ /** *********************************************** */ /** 列挙型の定義 */ /** *********************************************** */ /** *********************************************** */ /** * \defgroup AD_STATE AD動作ステートの定義 */ /** *********************************************** */ /*@{*/ /** 動作ステートの定義 ************************************** */ typedef enum { AD_ST_STOP=0, ///< AD変換停止中ステート AD_ST_RUN ///< AD変換中ステート } TAD_ST; /*@}*/ /** *********************************************** */ /** 構造体の定義 */ /** *********************************************** */ /** *********************************************** */ /** ADC1用 構造体の定義 */ /** *********************************************** */ typedef struct { TAD_ST eState; ///< 動作ステート bool bAdFin; ///< AD変換完了フラグ(true:ON) USHORT usAd; ///< AD値 bool bWatchReq; ///< BATT監視要求フラグ bool bWatchAcAdapterReq;///< ACアダプタ状態監視要求フラグ USHORT usMAveBuf[AD_BATT_MAVE_NUM_DAT];///< 移動平均バッファ USHORT usWp; ///< 書き込み位置 USHORT usNumDat; ///< データ数 } TAdBattInf; /** *********************************************** */ /** 外部変数の定義 */ /** *********************************************** */ /** AD1情報 **************************************** */ extern volatile TAdBattInf GAdBattInf; ///< ADC1情報 /** *********************************************** */ /** 関数のプロトタイプ宣言 */ /** *********************************************** */ extern void AdcApi_BattAdInfInit( void ); ///< AD情報の初期化 extern void AdcApi_BattAdStart( void ); ///< AD変換開始(初回用) extern void AdcApi_BattAdChkReStart( void );///< AD変換開始チェック(20ms毎) extern void AdcApi_BattAdInt( USHORT usAd );///< AD変換完了割込み(20ms毎) extern USHORT AdcApi_BattAdGetMoveAve( void );///< AD移動平均値(1秒間)の取得 extern bool AdcApi_GetWatchReq( void ); ///< AD監視要求の取得 extern void AdcApi_SetWatchReq( bool bWatchReq );///< AD監視要求の設定 #endif /***** ファイルの最後 ( adc_api.h ) ***************************** */
///self_main.c (メイン処理、main.cとは別に、自分で書き換えやすいように作成)
/** *********************************************** */ /** * @file self_main.c * @brief メイン処理 * @brief Compiler : CubeIDE * @brief CPU : STM32F401RE * @brief 変更内容 : AD変換追加 * @date $LastChangedDate : 2023/03/21 $ */ /** *********************************************** */ /** ************************************************************** */ /* インクルード ファイル */ /** ************************************************************** */ #include <stddef.h> ///< 標準定義 #include "typedef.h" ///< 基本型 #include "STMLib/STMSys.h" ///< STM32 基本制御 #include "IntR.h" ///< 割り込み #include "key_ctrl.h" ///< キー入力制御 #include "printf.h" ///< printf制御 #include "self_main.h" ///< selfメイン処理 #include "led.h" ///< LED制御 #include "adc_api.h" ///< adc制御 /** ************************************************************** */ /* 外部定義変数 */ /** ************************************************************** */ volatile TSysCtrlInf GSysCtrlInf; ///< システム制御情報 volatile TKeyCtrlInf GKeyCtrlInf; ///< キー制御情報 /** ************************************************************** */ /* 内部関数のプロトタイプ宣言 */ /** ************************************************************** */ void SysInit(void); ///<システム初期化 void MainLoop(void); ///<メインループ処理 /** ************************************************************** */ /** * メイン関数 * @param void : None * @return void : None */ /** ************************************************************** */ int self_main( void ) { /** 割込み禁止 *********************************************** */ __disable_irq(); ///<割込み禁止 /** 初期化 *************************************************** */ InitData(); ///< データ初期化 InitSystem(); ///< STM制御初期化 /** 割込み許可 *********************************************** */ __enable_irq(); ///<割込み許可 /** メインループ処理 ***************************************** */ MainLoop(); ///< メインループ return( 0 ); ///< 結果を返す } /** ************************************ */ /** * システム初期化 * @param void : None * @return void : None */ /** ************************************ */ void InitData( void ) { ///モード制御情報初期化 GSysCtrlInf.eCMode = INIT; GSysCtrlInf.eNMode = INIT; ///キー制御情報初期化 Key_Init(); ///AD変換情報初期化 AdcApi_BattAdInfInit(); } #if 1 /** ************************************ */ /** * メインループ * @param void : None * @return void : None */ /** ************************************ */ void MainLoop( void ) { ///キー監視開始 GKeyCtrlInf.key1_LongWatchEna = true; ///AD変換開始 AdcApi_BattAdStart(); while( 1 ) { ///< メインループ switch( GSysCtrlInf.eCMode ) { ///< 動作モード確認 case INIT: ///< IDLEモード選択 IdleMode_Proc(); ///< IDLEモードメイン処理 break; case LCD_FOR: ///< LCD表示モード選択 LCD_For_Proc(); ///< LCD表示モードメイン処理 break; case LCD_REV: ///< LCD表示反転モード選択 LCD_Rev_Proc(); ///< LCD反転表示モードメイン処理 break; default: ///< その他 GSysCtrlInf.eNMode = INIT;///< IDLEモードへ break; } /**現動作モードの更新 *********************************** */ if ( GSysCtrlInf.eCMode != GSysCtrlInf.eNMode ) {
/// 現動作モードを更新
GSysCtrlInf.eCMode = GSysCtrlInf.eNMode; } } } /** ************************************ */ /** * IDLEモード処理 * @param void : None * @return void : None */ /** ************************************ */ void IdleMode_Proc(){ UCHAR ucKey; ///< キー入力 Led1(HIGH); ///< LED1点灯 ucKey = Key_Proc(); ///< キー入力確認 ///キー入力確認 if(ucKey == KEY1_SHORT){ ///< KEY1短押し時 GSysCtrlInf.eNMode = LCD_FOR; ///< LCD表示モードに遷移する。 }else{ //何もしない。 } } /** ************************************ */ /** * LCD表示モードメイン処理 * @param void : None * @return void : None */ /** ************************************ */ void LCD_For_Proc(){ UCHAR ucKey; ///< キー入力 USHORT adValue; ///< 取得平均AD値 USHORT battValue; ///< 取得電圧値[mV] Led1(LOW); ///< LED1消灯 ///AD変換処理 if(GAdBattInf.bWatchReq == true){ ///< AD変換値出力許可フラグON時 GAdBattInf.bWatchReq = false; ///< AD変換値出力許可フラグOFF adValue = AdcApi_BattAdGetMoveAve();///< 平均AD値の取得 battValue = adValue*MAX_AD_VOLT/MAX_AD_VAL;///< 取得電圧値[mV] printf("battValue = %d[mV]\n",battValue); ///< 取得した電圧値を表示 } ucKey = Key_Proc(); ///< キー入力確認 if(ucKey == KEY1_SHORT){ ///< KEY1短押し時 GSysCtrlInf.eNMode = INIT; ///< IDLEモードへ遷移 }else if(ucKey == KEY1_LONG){ ///< KEY1長押し時 GSysCtrlInf.eNMode = LCD_REV; ////< LCD反転表示モードへ遷移 } } /** ************************************ */ /** * LCD反転表示モードメイン処理 * @param void : None * @return void : None */ /** ************************************ */ void LCD_Rev_Proc(){ UCHAR ucKey; ///< キー入力 GLEDCTRL.led1blif=true; ///< LED点滅フラグON(LED点滅開始) ucKey = Key_Proc(); ///< キー入力確認 printf("LCD_Rev_Proc:ucKey=%d\n",ucKey);///< 入力キーを表示 if(ucKey == KEY1_SHORT){ ///< KEY1短押し時 GLEDCTRL.led1blif=false; ///< LED点滅フラグOFF GSysCtrlInf.eNMode = INIT; ///< IDLEモードへ遷移 }else if(ucKey == KEY1_LONG){ ///< KEY1長押し時 GLEDCTRL.led1blif=false; ///< LED点滅フラグOFF GSysCtrlInf.eNMode = LCD_FOR;///< LCD表示モードへ遷移 } } #endif
///self_main.h (メイン処理のヘッダファイル)
/** *********************************************** */ /** * @file self_main.h * @brief STM32ソフトメイン * @brief Compiler : CubeIDE * @brief CPU : STM32F401RE */ /** *********************************************** */ #ifndef MAIN_H #define MAIN_H /** *********************************************** */ /* System Settings Define. */ /** *********************************************** */ //LED GPIO #define PORT_LED1 LED1_GPIO_Port #define BIT_LED1 LED1_Pin ///入力キー #define PORT_KEY1 KEY1_GPIO_Port #define BIT_KEY1 KEY1_Pin #define PORT_KEY2 KEY2_GPIO_Port #define BIT_KEY2 KEY2_Pin #define PORT_KEY3 KEY3_GPIO_Port #define BIT_KEY3 KEY3_Pin /** ************************************************************** */ /** 列挙型の定義 */ /** ************************************************************** */ //メイン動作モード typedef enum { INIT=0, ///< 初期モード LCD_FOR, ///< LCD電圧表示モード LCD_REV, ///< LCD電圧反転表示モード MODE3 ///< MODE3モード } TMMODE; /** ************************************************************** */ // 構造体の定義 /** ************************************************************** */ ///システム制御用構造体 typedef struct { /** メイン動作モード&ステート ******************************** */ TMMODE eCMode; ///< 現動作モード TMMODE eNMode; ///< 次動作モード } TSysCtrlInf; /** ************************************************************** */ /** 外部変数の定義 */ /** ************************************************************** */ /** システム制御情報 ********************************************* */ extern volatile TSysCtrlInf GSysCtrlInf; ///< システム制御情報 /** ************************************************************** */ /** 関数のプロトタイプ宣言 */ /** ************************************************************** */ extern int self_main(void ); ///< メイン処理 extern void InitData(void); ///< データ初期化処理 extern void SysInit(void); ///< システム初期化 extern void MainLoop(void ); ///< メインループ処理 extern void IdleMode_Proc(void); ///< 初期IDLEモード extern void LCD_For_Proc(void); ///< LCD表示モード extern void LCD_Rev_Proc(void); ///< LCD表示モード #endif
///IntR.c (割り込み用ファイルの作成)
/** *********************************************** */ /** * @file IntR.c * @brief 割り込み制御ルーチン群 * @brief Compiler : CubeIDE * @brief CPU : STM32F401RE * @date $LastChangedDate : 2023/03/21 $ * @version $Revision : 1.00 $ */ /** *********************************************** */ #include <stddef.h> #include "typedef.h" ///< 基本型 #include "STMLib/STMSys.h" ///< STMLib用 #include "key_ctrl.h" ///< キー入力制御 #include "IntR.h" ///< 割り込み制御 #include "main.h" ///< メイン処理 #include "self_main.h" ///< selfメイン処理 #include "adc_api.h" ///< AD変換制御 #include "led.h" ///< led制御 #include "printf.h" ///< printf制御 /** *********************************************** */ /** グローバル変数定義 */ /** *********************************************** */ ///構造体宣言 volatile TTimCtrlInf GTimCtrlInf; ///ハンドラ宣言 extern TIM_HandleTypeDef htim3; /** *********************************************** */ /** * TIM3割り込み処理(1msごとに割り込み) * @param void : None * @return void : None */ /** *********************************************** */ void Tim3_Int() { /// 5ms経過毎の処理 GTimCtrlInf.uc5msCnt++; ///< 5msカウンタを更新 GTimCtrlInf.uc20msCnt++; ///< 20msカウンタを更新 GTimCtrlInf.uc1sCnt++; ///< 1sカウンタを更新 /// 5ms時実行 if(GTimCtrlInf.uc5msCnt >= TIM5MS_CNT_VAL){ GTimCtrlInf.uc5msCnt = 0; ///<5msカウンタ初期化 Key_Input(); ///<キー入力制御 } ///20ms時実行(追加) if(GTimCtrlInf.uc20msCnt >= TIM20MS_CNT_VAL){ GTimCtrlInf.uc20msCnt = 0; ///< 20msカウンタ初期化 AdcApi_BattAdChkReStart(); ///< AD変換開始処理 } ///1s時実行 if(GTimCtrlInf.uc1sCnt >= TIM1S_CNT_VAL){ GTimCtrlInf.uc1sCnt = 0; ///< 1sカウンタ初期化 Led1Blink(); ///< LED1反転処理 } }
///IntR.h (割り込み用ヘッダファイル)
/** *********************************************** */ /** * @file IntR.h * @brief 割り込み制御ルーチン群 * @brief Compiler : CubeIDE * @brief CPU : STM32F401RE * @date $LastChangedDate : 2023/03/21 $ * @version $Revision : 1.00 $ */ /** *********************************************** */ #ifndef INTR_H #define INTR_H /** 100msタイマ値(5ms単位)用の定義 ******************************* */ #define TIM5MS_CNT_VAL (5)///< 5msタイマ値(1ms*5=5ms) /** 20msタイマ値(5ms単位)用の定義 ******************************* */ #define TIM20MS_CNT_VAL (20)///< 20msタイマ値(1ms*20=20ms) /** 1sタイマ値(100ms単位)用の定義 ******************************** */ #define TIM1S_CNT_VAL (1000) ///< 1sタイマ値(1ms*1000=1s) /** *********************************************** */ /* 構造体の定義 */ /** *********************************************** */ /// タイマ制御情報 構造体の定義 typedef struct { /** タイマ制御情報 ******************************************* */ UCHAR ucState; ///< 動作ステート UCHAR uc5msCnt; ///< 5msカウンタ UCHAR uc20msCnt; ///< 20msカウンタ UCHAR uc100msCnt; ///< 100msカウンタ UINT uc1sCnt; ///< 1s カウンタ } TTimCtrlInf; /** *********************************************** */ /* 外部変数の定義 */ /** *********************************************** */ extern volatile TTimCtrlInf GTimCtrlInf; ///< タイマ制御情報 /** *********************************************** */ /* プロトタイプ宣言 */ /** *********************************************** */ extern void Tim3_Int(); #endif
(4) プログラムの説明(AD変換)
3.関連記事
・STM32マイコン_2(UART通信:TeraTermにHellow表示) - Project_OKI’s diary
・STM32マイコン_3(UART通信:複数文字送信) - Project_OKI’s diary
・STM32マイコン_4(NVICの設定内容) - Project_OKI’s diary
・STM32マイコン_5(スイッチ入力とLED点灯、消灯) - Project_OKI’s diary
その他:
・CubeIDEの使い方(起動~デバック実行) - Project_OKI’s diary
C言語:
・組み込みの為のC言語基礎知識1(printf) - Project_OKI’s diary
・C言語基礎知識2(for分で処理を繰り返す) - Project_OKI’s diary
・C言語基礎知識3(配列) - Project_OKI’s diary