Project_OKI’s diary

エンジニアの勉強ブログ

組み込み基礎知識(ベクタ/ベクタテーブル/フェッチ/プリフェッチ)

ベクタとフェッチの意味

1.目的

  • STM32のマイコンのデータシートを参照すると、ベクタとフェッチという言葉が出てくる。今回は、その言葉の意味について記載する。
  •  ベクタについて理解する
  •  フェッチの意味について理解する。

 

目次

2. ベクタとは

 ・イベント割り込みが発生した時に、プログラムの実行を制御する為の、

  特定のメモリアドレス(メモリの位置)のこと。

 ・特定のイベントや割り込みが処理される為の、目的地を示す参照ポインタとなっている。

 

 ・例えば、割り込み要求が発生すると、マイコンは、割り込みベクタテーブルと呼ばれる特定のメモリ領域を参照する。

 ・Coretex-M3では、このメモリ領域にアドレスを記述し、このアドレスにジャンプし、そこに配置された割り込みなどを実行する。

 

3. ベクタテーブルとは

 ・ベクタアドレスを格納するテーブルの事をベクタテーブルと呼ぶ。

 ・通常、固定長のメモリ領域に配置される。

 ・関連つけられたの開始アドレスが記載される。

 

 

STM32F103のリファレンスマニュアルのベクタテーブルを参照すると下記のようになっている。

参照データシート:リファレンス マニュアル (日本語) | STM32, STM8ファミリはSTの32bit/8bit汎用マイクロコントローラ製品

 

 ・下記のようにそれぞれに位置が割り振られ、その位置に、優先度、機能、アドレスが割り当てられている。

 

 ・例えば、DMA1_Channel1のイベント割り込みが発生したとすると、

  ベクタの位置11を参照し、アドレス0x0000_006Cを取得する。

 ・このアドレスにジャンプし、このアドレスに記載されている処理を実行する。

 

 


 ・STM32では、マイコン側で決めてあるが、

  通常、開発者は、割り込みや例外に対応する処理ルーチンを適切なベクタアドレスに配置する必要がある。

  これにより、割り込みや例外処理が効率的に行われ、リアルタイム性や信頼性が確保される。

 

 ・この内容は、データシートの理解や、フォルトエラーなど発生時に必要な知識である。

 

参考:ベクタテーブル | APS|組み込み業界専門メディア

https://chat.openai.com/?model=text-davinci-002-render-sha

 

 

4. フェッチとは

 ・プログラムの命令をメモリから所得する処理のこと。

 

 ・マイコンは、メモリ内の特定のアドレスから次の命令をフェッチして実行する。

 ・フェッチは、プログラムカウンタ(Program Counter)と呼ばれる特殊なレジスタを使用して行われる。

 ・プログラムカウンタは、次に実行する命令のメモリアドレスを保持し、フェッチされる次の命令の場所を指定する。
 

 ・割り込みや例外などのイベントが発生すると、

  プロセッサは対応するベクタを参照し、

  そのイベントの処理に必要なアドレスを取得する。

 

 ・ベクタテーブル内のアドレスからフェッチされた命令が実行されることで、

  イベントや割り込みが適切に処理される。
 

 ・ベクタはイベントや割り込みの処理の目的地を示す。

 ・フェッチは次の命令をメモリから取得して実行するための処理を指す。

 ・これらの概念は、プログラムの制御フローとイベントハンドリングの重要な要素である。

 

例えば、RM0008に、下記のような文言がある。

 ・「CPU はアドレス 0x0000 0000 からスタック最上位の値をフェッチし、0x0000 0004から始まるブートメモリからコードの実行を開始します。」

 

 これについて説明すると。

 ・CPUアドレス0x0000 0000は、メモリ上の特定の位置を示す。

 ・スタックは、一時的なデータを保存する為の領域のことを示す。

 ・スタック最上位の値を取得し処理することにより、以前に保存した重要な情報やアドレスを取得することが出来る。

 ・「0x0000 0004」から始まるブートメモリは、コンピュータシステムの起動時に使用されるメモリ領域で、特定のメモリ領域を指す。

  ここからプログラムの実行が開始される。

 (ブートメモリは通常、システムの起動時に実行されるコードやデータを含む特殊な領域)

 

 つまり、0x0000 0000からスタックに保存してある最上位の値を処理することによって、以前の情報を読み出し、0x0000 0004から始まるブートメモリから、コードを実行し始めるということ。

 

この0x0000 0000などのアドレスについては、リファレンスマニュアルの4.Memory mappingを参照

下記に、一部を示す。

 

5. プリフェッチとは

 ・CPUがデータをあらかじめキャッシュメモリに読み出しておく機能のこと。

 ・データの読み出し命令があった場合、逐次読み出しに比べ処理応答速度を大幅に短縮できる。

 

 

参考:

フェッチとは (fetch): - IT用語辞典バイナリ

プリフェッチとは (prefetch): - IT用語辞典バイナリ

 

 

関連記事

過去の記事:

知らないと損するお金の話(ふるさと納税、確定申告とワンストップ納税どっちが得?) - Project_OKI’s diary

C言語基礎知識6(関数) - Project_OKI’s diary

C言語基礎知識7(構造体1) - Project_OKI’s diary

C言語基礎知識8(enum:列挙型) - Project_OKI’s diary

C言語基礎知識9(typedef) - Project_OKI’s diary

C言語基礎知識10(extern宣言) - Project_OKI’s diary

C言語基礎知識11(#define) - Project_OKI’s diary

 

組み込みC言語

STM32マイコン30(AD変換と移動平均)

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変換)
 (a) AdcApi_BattAdInfInit
   AD変換に使用するTAdBattInf構造体の初期化を行う。
 (b) AdcApi_BattAdStart
    AD変換の開始を行う関数。
   メイン処理で実行され、この処理が実行されるとAD変換を開始する。
 (c) AdcApi_BattAdChkReStart
   割り込み処理で20ms毎に実行する。
  AD変換を行うフラグを確認し、AD変換中の場合、AD変換を開始する。
 (d) AdcApi_BattAdInt
  AD変換完了割り込み時(HAL_ADC_ConvCpltCallback)に、実行する。
  AD変換結果をバッファに格納し、保持する。
  AD変換値が10個溜まったら、AD移動平均処理を許可し、AD変換を格納するバッファのバッファ位置を0に戻す。
 (e) AdcApi_BattAdGetMoveAve
  AD移動平均値の取得を行う関数。
  AD変換出力許可フラグがtrueになった時実行される。
  AD変換値を格納しているバッファの値を全て足し、
  それをデータ数で割ることにより、移動平均値を算出する。
  AD変換のデータ数が0だとエラーになるため、
  0の場合は割り算が起こらないようにしている。
  AD移動平均値を返しす。
 
 (f) HAL_ADC_ConvCpltCallback
 AD変換が終了した時に自動的に呼び出されるHALで用意された割り込み関数
 AD変換が終ったらGBattDatBuf[0]の値を読み込み、
 それをAD変換値格納バッファに格納している。
 
 

3.関連記事

関連記事一覧:
 
組み込みC言語

 

 

STM32マイコン29(CubeIDEの出力されるフォルダ構成の選択)

STM32 (CubeIDEのフォルダ構成の選択)

1.本日の内容

 (1) CubeIDEで出力されるフォルダ構成は2種類ある。

   そのフォルダ構成の違いについて記載する。

 参考:https://www.st.com/resource/ja/user_manual/um1718-stm32cubemx-for-stm32-configuration-and-initialization-c-code-generation-stmicroelectronics.pdf

 

・目次

 

2.内容

(1) フォルダ構成の設定方法

 ・CubeIDE及びCubeMXの

  Project Managerタブ→ Application Structure で下記2つの設定が出来る。

  ・Advanced (デフォルト設定)

  ・Basic

 

(2) 出力フォルダ構成

 ・Advancedに設定して出力する場合

 ※デフォルト設定では、この形式になるように設定されている。

 

 ・Basicに設定して出力する場合

 

 

 ・Advancedの出力           ・Basicの出力

この比較から分かるように、大きな違いは、main.cファイルの位置である。

Advancedの場合、SrcとIncフォルダがCoreフォルダの中に入る。

 
 

3.関連記事

関連記事一覧:
 
組み込みC言語

 

 

STM32マイコン28(LCDに数値を表示する)

STM32 (LCDに取得した数値を表示)

1.本日の内容

 (1) STM32マイコン(nucleof401re)を使用して、

   LCD(ACM1602)に取得した数値を表示する。

  文字列は、そのまま出力することが出来るが、LCDに数値を表示する場合、

  取得した数値を文字列に変換する必要がある。

  取得した数値を文字列に変換し、その値をLCDに出力する方法について

  記載する。

 

・目次

 

2.内容

(1) やる内容の詳細

 ・CubeMXでI2C通信の設定を行う。

 ・nucleof401REとLCD(ACM1602)を接続する。

 ・プログラム起動時に、I2C通信を行い、LCDの制御を行う。

 ・int型の数値を宣言する。

 ・数値を文字列に変換する。

 ・変換した数値をLCDに表示する。

 

(2) 使用部品
個数 部品名 型番
1 接続ケーブル  ジャンパーワイヤ​​
1 mbed(マイコンボード) ​​ nucleo f401RE
1 USBケーブル USBミニBタイプ
1 ブレッドボード ​​ ​​EIC104j
1 LCD ACM1602NI-FLW-FBW-M01
2 カーボン抵抗(10kΩ) RD25S
1 多回転半固定ボリューム(1kΩ) 3296W-1-102LF

 

LCD(ACM1602)データシート:

https://akizukidenshi.com/download/ds/xiamen/ACM1602NI-FLW-FBW-M01_20190219.pdf

 

(3) 接続
nucleof401re側 LCD
pin番号 役割 pin番号 役割
GND GND 1 GND
3.3V 3.3V電源 2 VDD
3.3V(可変抵抗接続) 3.3V電源-可変抵抗 3 Vo
D15(PB8) I2C_SCL 4 I2C_SCL
D14(PB9) I2C_SDA 5 I2C_SDA
3.3V 3.3V電源 6 BL+(バックライト)
GND GND 7 BL-(バックライトGND)

※Voは、LCDのコンストラクタの為、可変抵抗で入力する電圧を変化させ、調整する。

 

 ・LCDのデータシートより

 

 接続は下記記事を参考にしました。 

ArduinoでI2C制御LCD ACM1602を使う(1) | wsnakのブログ

・上記記事では、SCDとSDAに5Vが接続されているが、3.3Vを使用。

 

(2) CubeMXの設定

 ・File→New ProjectでSTM32F401を選択し、新しいプロジェクトを作成する。

  (a) Pin設定画面の下記ピンを設定する。

    ・PB9をI2C1_SDA   (LCD制御用 I2C_SDA)

    ・PB8をI2C1_SCL          (LCD制御用 I2C_SCL)

    ・PA5をGPIO_Output     (LED点灯用)

    ・PC13をGPIO_EXTI13 (今回は使わないが、デバック用にあると良い)

  (b) それぞれのピンの名前を右クリック、Enter User Labelで

    画像の名前に変更する。(変更しなくてもよい。)

 

 

  (b) ConnectivityのI2C1を選択し、LCDの仕様に合わせて、下記設定を行う。

Mater Feature
  I2C Speed Mode Standard Mode
  I2C Clock Speed 70000
Slave Features
      Clock No Stretch Mode Disable
 Primary Address Length selection 7-bit
 Dual Address Acknowledged Disable
 Primary slave address 0
 Generala Call address detection Disabled

 

   下記に簡単に、それぞれの設定の意味を示す。

設定項目 説明
Master Features I2C通信のマスター側の設定項目
I2C Speed Mode

I2Cのスピードモード設定。

標準モード、Fastモードが選択可能

※今回はそこまで速度が必要ないので標準。

I2C Clock Speed

I2Cのクロック速度設定。Hz単位で設定可能

※データシートより100kHzに設定

Slave Features I2C通信のスレーブ側の設定項目
Clock No Stretch Mode スレーブデバイスによるクロックのストレッチを禁止するか選択可能。
Primary Address Length selection

プライマリアドレスのビット数を7ビット

または10ビットで設定可能。

Dual Address Acknowledged スレーブデバイスが2つのアドレスを認識するかどうか選択可能。
Primary slave address スレーブデバイスのプライマリアドレスを設定可能。
General Call address detection ジェネラルコールアドレスの受信を有効にするか選択可能。

※仕様上、LCDのI2Cクロックスピードは100kHzまで可能ですが。

線の長さなどによっては、そのスピードだと通信できない可能性があります。

その場合は、70kHzなどクロックスピードを落とすと通信出来るようになります。

 

 

 

  (c)NVICを設定

 

  (d) 割り込みの優先順位を下げる。

 

  (e) プロジェクト名を付け、Toolchain/IEDをTrueSTUDIOに設定

 

 

 (f)GENERATE CODEでコードを生成し、AtricTrueStdioを起動する。

  ※Open PROJECTをクリックすると、AtricTrueStdioが自動的に起動される。

 

 LED及びボタン設定に関しては下記記事を参照 

 ・STM32マイコン_6(スイッチ入力(INT割り込み)とLED出力) - Project_OKI’s diary

 

(3) プログラムの作成

動作:

 ・LCDの初期化

 ・LCDのカーソル位置を左上(0,0)に設定

 ・数値を表示

 

main.c
/* USER CODE BEGIN Includes */
#include	"stdio.h"	///< 標準関数
/* USER CODE END Includes */
/* USER CODE BEGIN PD */
//コマンド送信用定数
#define LCDADRESS 0xa0	//LCDのスレーブアドレス
#define CMDSEND 0x00	//コマンド送信
#define	DATASEND 0x80	//データ送信
/* USER CODE END PD */
/* USER CODE BEGIN 0 */

///コマンド送信用関数
///引数:送信コマンド
///返り値:無し
void writeCmd(uint8_t cmd)
{
	uint8_t buf[] = {CMDSEND,cmd};
	HAL_I2C_Master_Transmit(&hi2c1,LCDADRESS,buf,sizeof(buf),HAL_MAX_DELAY);
}

///データ送信用関数
///引数:送信データ
///返り値:無し
void writeData(uint8_t data)
{
	uint8_t buf[] = {DATASEND,data};
	HAL_I2C_Master_Transmit(&hi2c1 ,LCDADRESS , buf,sizeof(buf),HAL_MAX_DELAY);
}

///LCD初期化関数
///引数:無し
///返り値:無し
void Lcd_init(){
	writeCmd(0x01);		///画面クリア
	HAL_Delay(5);		///5msまち
	writeCmd(0x38);		///ファンクション設定
	HAL_Delay(5);		///5ms
	writeCmd(0x0f);		///ディスプレイON、CURSOR-ON、blinking-ON
	HAL_Delay(5);		///5msまち
	writeCmd(0x06);		///データ書き込み後アドレス加算モード設定
	HAL_Delay(5);		///5msまち
}

//LCD画面クリア
///引数:送信データ
///返り値:無し
void LCD_Clear(void){
	writeCmd(0x01);	//0x01コマンドを送信
	HAL_Delay(2);
}

///LCDのカーソル位置設定
///引数:uint8_t col:横の位置、uint8_t row:縦の位置(1段目、2段目)
///返り値:無し
void LCD_SetCursor(uint8_t col,uint8_t row)
{
	///
	uint8_t row_offsets[] = { 0x00, 0x40};
	if (row > 1) {		///2段目以上はない。
	row = 1;
	}
	writeCmd(0x80 | (col + row_offsets[row]));
}

//LCD文字出力
///引数:char str:送信する文字
///返り値:無し
void LCD_Print(char* str) { while (*str) { writeData((uint8_t)*str++); } } /* USER CODE END 0 */
/* USER CODE BEGIN 2 */
char str[255]; //文字列 uint8_t a; //数値を用意 a = 10; sprintf(str,"%d[mV]",a); ///< 数値を文字列に変換 Lcd_init(); //LCD初期化 LCD_SetCursor(0, 0); //カーソル位置を0,0に設定(1行目の左端) LCD_Print(str); //数値をLCDに出力
/* USER CODE END 2 */
 
(4) プログラムの説明
 (a) sprintf(str,"%d[mV]",a); ///< 数値を文字列に変換
  ・数値を文字列に置き換える関数として、sprintf関数を使用。
  ・これは、標準関数(stdio.h)に用意されている。
  ・a = 10 の数値をsprintfでstrに文字列として格納
  ・strをLCDに文字列として出力する。
 
  sprintf関数の詳細は下記記事を参照。
 
 (b) LCDの出力に関しては下記記事を参照
  ・C言語基礎知識30(数値を文字列に変換)
 

3.関連記事

関連記事一覧:
 
組み込みC言語:

 

 

STM32マイコン27(スイッチの短押し/長押し処理 )

STM32 (スイッチ長押し/短押し制御)

1.本日の内容

 (1) STM32マイコン(nucleof401re)を使用して、

   スイッチの長押し/短押しの判定を行う。

 

・目次

 

2.内容

(1) やる内容の詳細

 ・nucleof401REスイッチのスイッチ(青ボタン)の長押し/短押し制御を行う。

 ・長押し時:LEDの点滅/LED消灯

 ・短押し時:LED点灯/消灯

 を行う。

 

 スイッチを押した時の、状態の変化を状態遷移図で示す。

 

 これを元に、状態遷移表を作成する。

 

 ・MODE1:LED点灯

 ・MODE2:LED消灯

 ・MODE3:LED点滅

 状態遷移図及び状態遷移表については下記が参考になります。

  ・状態遷移表を使用した要求分析モデル:状態遷移表による設計手法(3)(1/3 ページ) - MONOist

  ・UMLステートマシンと状態遷移図 - astah-info

  ・設計 | PlantUMLによる状態遷移図の書き方, 状態遷移表からのテスト抽出方法 - わくわくBank

 

(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

 下記画像を参考

 (画像ではLED制御作成後、AD変換とLCD制御を追加予定の為、そのピン設定も含まれている)

 

 

 

 詳細は、下記記事を参照。

 下記には、CubeMXの設定について、それぞれ詳細が記載されている。

 CubeIDEも同じ設定を行う。

 

 LED及びボタン設定について

  ・STM32マイコン_5(スイッチ入力とLED点灯、消灯) - Project_OKI’s diary

 タイマ割り込みの設定について

  ・STM32マイコン_9(タイマ割り込み:LED点滅) - Project_OKI’s diary

 デバック用printfについて

  ・STM32マイコン_12(printfの導入) - Project_OKI’s diary

 

(3) プログラムの作成

 今回は、下記ファイルを作成する。

 ・割り込み作成用ファイル  :IntR.c/IntR.h

  →参考:STM32マイコン26(割り込み:TIM3_IRQHandler)

 ・キー制御用ファイル    :key_ctrl.c/key_ctrl.h

  →参考:STM32マイコン_16(レジスタ操作でスイッチ入力) - Project_OKI’s diary

 ・led制御用ファイル     :led.c/led.h

  →参考:STM32マイコン_10(レジスタ操作でLED点滅) - Project_OKI’s diary

        STM32マイコン_15(レジスタ操作でLED点滅2) - Project_OKI’s diary

 ・printf用ファイル      :printf.c/printf.h

  →参考:STM32マイコン_12(printfの導入) - Project_OKI’s diary

 ・メイン処理作成用ファイル :self_main.c/self_main.h

 ・基本型設定用ファイル   :typedef.h

 ・STM32レジスタ設定ファイル:STMSys.h

  →参考:STM32マイコン_16(レジスタ操作でスイッチ入力) - Project_OKI’s diary

 

今回のファイルは、JAVADOCの形式に従ってコメントを書く。

これで書くと、doxygenによって、コードからドキュメントを生成することが可能になる。

JAVADOCについて参考:

  ・いまさら聞けない「Javadoc」と「アノテーション」入門:【改訂版】Eclipseではじめるプログラミング(22)(1/4 ページ) - @IT

 

 

 プログラムのキー入力に関するざっくりとした流れ

 

全体的な関数の紐づけ

 

 

///main.c (CubeIDEによって自動生成)のユーザー書き込み部分

/* USER CODE BEGIN Includes */
#include "self_main.h"       
#include "printf.h"         
/* USER CODE END Includes */
  /* USER CODE BEGIN Init */
  setbuf(stdout,NULL);
  /* USER CODE END Init */
  /* USER CODE BEGIN 2 */
  self_main();
  /* USER CODE END 2 */

 

///stm32f4xx_it.c (割り込みについて記載)

/* USER CODE BEGIN Includes */
#include "typedef.h"			///< 基本型
#include "IntR.h"			///< 割り込み
/* USER CODE END Includes */
  /* USER CODE BEGIN TIM3_IRQn 1 */
  Tim3_Int();			///< Tim割り込み処理
  /* USER CODE END TIM3_IRQn 1 */

 

///self_main.c (メイン処理、main.cとは別に、自分で書き換えやすいように作成)

/** *********************************************** */
/**
 *	@file		self_main.c
 *	@brief		メイン処理
 *	@brief		Compiler 		: CubeIDE
 *	@brief		CPU			: STM32F401RE
 */
/** *********************************************** */

//インクルード ファイル
#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制御


// 外部定義変数
volatile TSysCtrlInf	GSysCtrlInf;	///< システム制御情報
volatile TKeyCtrlInf	GKeyCtrlInf;	///< キー制御情報

// 内部関数のプロトタイプ宣言  
 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(); } #if 1 /** ************************************ */ /** * メインループ * @param void : None * @return void : None */ /** ************************************ */ void MainLoop( void ) { ///キー監視開始 GKeyCtrlInf.key1_LongWatchEna = true; /** メインループ開始 ********************** */ 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; ///< キー入力 Led1(LOW); ///< LED1消灯 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; ///< キー入力 ledctrl.led1blif=true; ///< LED点滅フラグON(LED点滅開始) ucKey = Key_Proc(); ///< キー入力確認 if(ucKey == KEY1_SHORT){ ///< KEY1短押し時 ledctrl.led1blif=false; ///< LED点滅フラグOFF GSysCtrlInf.eNMode = INIT; ///< IDLEモードへ遷移 }else if(ucKey == KEY1_LONG){ ///< KEY1長押し時 ledctrl.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
///入力キー #define PORT_KEY1 KEY1_GPIO_Port ///< KEY1 入力ポート #define BIT_KEY1 KEY1_Pin ///< KEY1 入力ビット #define PORT_KEY2 KEY2_GPIO_Port ///< KEY2 入力ポート #define BIT_KEY2 KEY2_Pin ///< KEY2 入力ビット #define PORT_KEY3 KEY3_GPIO_Port ///< KEY3 入力ポート #define BIT_KEY3 KEY3_Pin ///< KEY3 入力ビット
///メイン動作モード定義 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 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  $
 */
/** *********************************************** */

#include "typedef.h"				///< 基本型
#include "STMLib/STMSys.h"			///< STMLib用
#include "key_ctrl.h"				///< キー入力制御
#include "IntR.h"				///< 割り込み制御
#include "main.h"				///< メイン処理
#include "led.h" ///< led制御 #include "printf.h" ///< printf制御 /** *************************************************** */ /** グローバル変数定 */ /** *************************************************** */ ///構造体宣言 volatile TTimCtrlInf GTimCtrlInf; ///ハンドラ宣言 extern TIM_HandleTypeDef htim3; /* ***************************************************** */ /** STM32 関数作成 */ /* ***************************************************** */  

/* ***************************************************** */
/** * システム初期化実行 * @param void : None * @return void : None */ /* ***************************************************** */
void InitSystem() { //Timer3割り込み開始 HAL_TIM_Base_Start_IT(&htim3); } /* ***************************************************** */
/** * TIM3割り込み処理(1msごとに割り込み) * @param void : None * @return void : None */ /* ***************************************************** */
void Tim3_Int() { /// 5ms経過毎の処理 GTimCtrlInf.uc5msCnt++; ///< 5msカウンタを更新 GTimCtrlInf.uc1sCnt++; ///< 1sカウンタを更新 /// 5ms時実行 if(GTimCtrlInf.uc5msCnt >= TIM5MS_CNT_VAL){ GTimCtrlInf.uc5msCnt = 0; ///<5msカウンタ初期化 Key_Input(); ///<キー入力制御 } ///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  $
 */
/** *********************************************** */

#ifndef INTR_H
#define INTR_H

/** *********************************************** */
/* defineの定義 */
/** *********************************************** */ #define TIM5MS_CNT_VAL ( 5 ) ///< 100msタイマ値(1ms*5=5ms) #define TIM1S_CNT_VAL ( 1000 ) ///< 1sタイマ値(1ms*1000=1s) /** *********************************************** */ /* 構造体の定義 */ /** *********************************************** */ /// タイマ制御情報 構造体の定義 typedef struct { /** タイマ制御情報 ***************** */ UCHAR uc5msCnt;  ///< 5msカウンタ UINT uc1sCnt; ///< 1s カウンタ } TTimCtrlInf; /** *********************************************** */ /* 外部変数の定義 */ /** *********************************************** */ extern volatile TTimCtrlInf GTimCtrlInf; ///< タイマ制御情報 /** *********************************************** */ /* プロトタイプ宣言 */ /** *********************************************** */ extern void Tim3_Int(); extern void InitSystem(); #endif

 

 ///key_ctrl.c (キー制御用ファイル)

/** *********************************************** */
/**
 *	@file		key_ctrl.c
 *	@brief		キー入力制御
 *	@brief		Compiler 		: CubeIDE
 *	@brief		CPU			: STM32F401RE
 *	@date		$LastChangedDate	: 2023/03/21  $
 */
/** *********************************************** */


/** *********************************************** */
/**		インクルードファイル	           */
/** *********************************************** */
#include "typedef.h"			///< 基本型
#include "key_ctrl.h"			///< キー入力制御API処理
#include "self_main.h"			///< メイン処理
#include "STMLib/STMSys.h"		///< STM32 基本制御

#include "printf.h"			///< printf制御(デバック用)

/** *********************************************** */
/**		内部関数のプロトタイプ宣言	    */
/** *********************************************** */
extern void	Key_DatSet( UCHAR ucDat ); ///< キー入力バッファデータ格納
extern UCHAR	Key_DatGet( void );	   ///< キー入力バッファからデータ取得

/**	************************************************************** */
/**
 *	キー入力情報の初期化
 *	@param		void			: None
 *	@return		void			: None
 */
/**	************************************************************** */
void		Key_Init( void )
{
	UCHAR		ucCnt;	 ///< ループカウンタ

	/**	キー入力状態の初期化**************************/
	GKeyCtrlInf.ucState = KEYS_IDLE;	///< キー入力ステート
	GKeyCtrlInf.ucInp = 0;			///< キー入力状態
	GKeyCtrlInf.ucPrev = 0;			///< キー入力状態(前回)
	GKeyCtrlInf.ucFix = 0;			///< キー入力状態(確定)
	GKeyCtrlInf.ucTimCnt = 0;		///< チャタリング除去用カウンタ

	/**キー入力リングバッファの初期化 ****************/
	GKeyCtrlInf.ucWp = 0;			///< ライト位置
	GKeyCtrlInf.ucRp = 0;			///< リード位置
///バッファサイズ分のループ
for( ucCnt=0; ucCnt < KEY_BUFF_SIZE; ucCnt++ ) {
GKeyCtrlInf.ucBuff[ucCnt] = 0; ///< 0クリア } /** キー長押し用カウンタ初期化*******************/ GKeyCtrlInf.Key1_OnTimCnt = 0; ///< キー1長押し時間カウンタ /** キー長押し検出フラグ初期化*******************/ GKeyCtrlInf.eKey1_Det =0; ///< キー1長押し検出フラグ初期化 } /** ************************************************************** */ /** * キー入力 * @param void : None * @return USHORT : キー入力値 */ /** ************************************************************** */ USHORT Key_In(void) { volatile USHORT usKey1; ///< KEY1入力用変数 USHORT usKey = 0; ///< KEY store usKey1 = KEY1_IN(); ///< KEY1入力 ///<キー入力データを取得 usKey = (usKey1<<KEY1SFT); return( usKey ); ///<キー入力を返す } /** ************************************************************** */ /** * キー入力制御(5ms毎に呼び出される) * @param void : None * @return void : None */ /** ************************************************************** */ void Key_Input( void ) { USHORT ucKey1; ///< 入力データ変数
ucKey1 = Key_In(); ///< キー入力ポート1をリード:ON時:BIT1_KEY1を取得 GKeyCtrlInf.ucInp = KEY_NONE; ///< キー入力状態をクリア

      /// KEY1ON時:KEYビットを設定 
GKeyCtrlInf.ucInp |= (ucKey1 & BIT_KEY1 )? KEY1_SET : KEY_NONE; if ( GKeyCtrlInf.ucState == KEYS_IDLE ) {///< キー入力状態変化:待ち時
            ///< キー入力状態変化時
  if ( GKeyCtrlInf.ucInp != GKeyCtrlInf.ucPrev ) { GKeyCtrlInf.ucPrev = GKeyCtrlInf.ucInp; ///< キー入力状態を更新 GKeyCtrlInf.ucTimCnt = 0;///< チャタリング除去カウンタをクリア GKeyCtrlInf.ucState = KEYS_CHG; ///< ステート=キー入力状態変化 }else { ///< キー入力状態変化なし時 /** キー長押し確認*********************/ if ( GKeyCtrlInf.ucFix & KEY1_SET ) { ///< KEY1 ON時
                           ///< カウント可能時
      if ( GKeyCtrlInf.Key1_OnTimCnt < 0xffff ) { GKeyCtrlInf.Key1_OnTimCnt++;///< ON時間を更新 } } } }else { GKeyCtrlInf.ucTimCnt++; ///< チャタリング除去カウンタを更新 if ( GKeyCtrlInf.ucInp != GKeyCtrlInf.ucPrev ) {
                       /// キー入力状態を更新   GKeyCtrlInf.ucPrev = GKeyCtrlInf.ucInp; GKeyCtrlInf.ucTimCnt = 0;///< 除去カウンタクリア }else if (GKeyCtrlInf.ucTimCnt >= KEY_CHATTER_REMOVE_TIM_CNT) { /** KEY1の状態変化確認 ******************/ if ( (GKeyCtrlInf.ucFix & KEY1_SET) != (GKeyCtrlInf.ucInp & KEY1_SET) ) { /** キーコード確認***********************/
                           ///< KEY1状態 OFF=>ON時
      if ( ( GKeyCtrlInf.ucFix & KEY1_SET ) == 0 ) { Key_DatSet( KEY1_ON ); ///< KEY1 ONコード格納 } else {///< KEY1状態 ON =>OFF Key_DatSet( KEY1_OFF ); ///< KEY1 OFFコード格納 } } /// キー入力状態(確定)を更新
/// ステート=キー入力状態変化待ち
/// チャタリング除去カウンタをクリア

    GKeyCtrlInf.ucFix = GKeyCtrlInf.ucInp; GKeyCtrlInf.ucState = KEYS_IDLE; GKeyCtrlInf.ucTimCnt = 0; } } } /** ************************************************************** */ /** * キー入力データの取得 * @param void : None * @return UCHAR : ==KEY_NONE: キー入力データなし<BR> * : !=KEY_NONE: キー入力コード */ /** ************************************************************** */ UCHAR Key_Proc( void ) { UCHAR ucKey; ///< キー入力 ucKey = Key_DatGet(); ///< キー入力データの取得 /** KEY1 ON/OFF確認 ***********************************/ if ( (ucKey == KEY1_ON) || (ucKey == KEY1_OFF) ) {
    GKeyCtrlInf.Key1_OnTimCnt = 0; ///< KEY1 ON時間(5ms単位)クリア } ///< キー入力なし時かつ
///< キー長短押し未検出かつ
///< 長押し時間経過時
if ( ( ucKey == KEY_NONE ) && ( GKeyCtrlInf.eKey1_Det == false ) && ( GKeyCtrlInf.Key1_OnTimCnt >= TIM_KEY_LONG ) ) { GKeyCtrlInf.eKey1_Det = ON; ///< キー長短押し検出 ucKey = KEY1_LONG;    ///< キー長押し検出 } /** KEY1 短押し検出 ************************/
/// KEY1 OFF時かつ
/// 長押し時間未経過時

  if ( ( ucKey == KEY1_OFF ) && ( GKeyCtrlInf.Key1_OnTimCnt < TIM_KEY_LONG ) ) { ///キー長短押し検出
/// キー長短押し未検出時
if ( GKeyCtrlInf.eKey1_Det == false ) { ucKey = KEY1_SHORT;///< キー短押しコードを格納 } else { ///< キー長短押し検出時
                            /// キー長短押し未検出に設定
GKeyCtrlInf.eKey1_Det = OFF; } } return( ucKey ); ///< キーデータを返す } /** ************************************************************** */ /** * キー入力バッファにデータを格納 * @param ucDat : キーデータ * @return void : None */ /** ************************************************************** */ void Key_DatSet( UCHAR ucDat ) { UCHAR ucWp; __disable_irq(); ///< 割込み禁止 /** 位置(次)の更新 *******************************/ ucWp = ( GKeyCtrlInf.ucWp + 1 ); ucWp = ( ucWp >= KEY_BUFF_SIZE )? 0 : ucWp; ///< 範囲チェック /** バッファフル確認 ************************************/ if ( ucWp == GKeyCtrlInf.ucRp ) { ///< バッファフル時 } else { ///< バッファに空きあり時
///< キーデータを格納
///< 位置を更新
GKeyCtrlInf.ucBuff[GKeyCtrlInf.ucWp] = ucDat; GKeyCtrlInf.ucWp = ucWp; } __enable_irq(); ///< 割込み許可 } /** ************************************************************** */ /** * キー入力バッファからデータを取得 * @param void : None * @return UCHAR : ==KEY_NONE: キー入力データなし<BR> * : !=KEY_NONE: キー入力データ */ /** ************************************************************** */ UCHAR Key_DatGet( void ) { UCHAR ucDat; ///< キーデータ /** キー入力データがない場合 *********************************/ if ( GKeyCtrlInf.ucWp == GKeyCtrlInf.ucRp ){ ///< キー入力データなし? ucDat = KEY_NONE; ///< キー入力データなし } /** キー入力データがある場合 *********************************/ else { ///<キー入力データあり時
///キー入力データを取得
/// 割込み禁止
/// 位置を更新
/// 割込み許可
ucDat = GKeyCtrlInf.ucBuff[GKeyCtrlInf.ucRp]; __disable_irq(); GKeyCtrlInf.ucRp++; GKeyCtrlInf.ucRp = ( GKeyCtrlInf.ucRp >= KEY_BUFF_SIZE )? 0 : GKeyCtrlInf.ucRp; __enable_irq(); } return( ucDat ); /// キーデータを返す } /** **** ファイルの最後 ( key_ctrl.c ) ************************** */

 

///key_ctrl.h (キー制御用ヘッダファイル)

/** *********************************************** */
/**
 *	@file		key_ctrl.h
 *	@brief		キー入力制御用ヘッダファイル
 *	@brief		Compiler 		: CubeIDE
 *	@brief		CPU			: STM32F401RE
 */
/** *********************************************** */

#ifndef		KEY_CTRL_H								///< KEY_CTRL_H 未定義?
#define		KEY_CTRL_H								///< KEY_CTRL_H 定義済み!!


/* *************************************************************** */
//		定数値の定義											
/* *************************************************************** */
/// KEY1 ON/OFF確認
#define
KEY1_IN() ( InGPIOBitR(PORT_KEY1,BIT_KEY1)) #define KEY1SFT ( 13 ) ///< KEY1 左シフト数

/// キー長押し時間(5ms * 400 = 2.0
#define TIM_KEY_LONG ( 400 ) s
/// チャタリング除去カウンタ値(5ms単位)*2 =10ms
#define KEY_CHATTER_REMOVE_TIM_CNT ( 2 ) /// キー入力コードの定数 #define KEY_NONE (0x00 ) ///< キー入力データなし #define KEY_OFF (0x80 ) ///< キーOFF
#define KEY1_SET (0x01 ) ///< KEY1 設定コード #define KEY1_ON (0x01 ) ///< キー1ON #define KEY1_OFF ( KEY1_ON | KEY_OFF ) ///< キー1OFF /// キー入力コード(長押し/短押し)の定数 #define KEY_LONG (0x20 ) ///< キー長押し #define KEY1_LONG ( KEY_LONG | KEY1_ON ) ///< キー1長押し検出 #define KEY_SHORT (0x40 ) ///< キー短押し #define KEY1_SHORT ( KEY_SHORT | KEY1_ON )///< キー1短押し検出 /// キー入力ステートの定数 #define KEYS_IDLE (0 ) ///< キー入力状態 変化待ち #define KEYS_CHG (1 ) ///< キー入力状態 変化 ///キー入力処理用定数 #define KEY_BUFF_SIZE (10 ) ///< キー入力リングバッファ サイズ /** ************************************************************** */ /** 構造体の定義 */ /** ************************************************************** */ ///キー入力制御情報 構造体の定義 typedef struct { /// キー入力状態 ********************************************* UCHAR ucState; ///< キー入力ステート UCHAR ucInp; ///< キー入力状態 UCHAR ucPrev; ///< キー入力状態(前回) UCHAR ucFix; ///< キー入力状態(確定) UCHAR ucTimCnt;///< チャタリング除去用カウンタ UCHAR ucBuff[KEY_BUFF_SIZE]; ///< キー入力バッファ UCHAR ucWp; ///< ライト位置
    UCHAR ucRp;
///< リード位置 USHORT Key1_OnTimCnt; ///< キー1 ON時間カウント(5ms) bool key1_LongWatchEna; ///< キー1長押し監視許可 bool eKey1_Det; ///< キー1長押し } TKeyCtrlInf; /** ******************************************************* */ /** 外部変数の定義 */ /** ******************************************************* */ extern volatile TKeyCtrlInf GKeyCtrlInf; ///< キー入力制御情報 構造体変数宣言 /** ****************************************************** */ /** 関数のプロトタイプ宣言 */ /** ****************************************************** */ extern void Key_Init( void ); ///< キー入力情報の初期化 extern void Key_Input( void );///< キー入力制御 extern UCHAR Key_Proc( void ); ///< キー入力データの取得 #endif /** **** ファイルの最後 ( key_ctrl.h ) ************************** */

///printf.c (出力デバック確認用ファイル)

/**	****************************** */
/**
 *	@file		printf.c
 *	@brief		printfを使用可能とする。
 *	@brief		Compiler 	: CubeIDE
 *	@brief		CPU		: STM32F401
 *	@date		$LastChangedDate: 2023/03/04  $
 *	@version	$Revision	: 1.00 $
 */
/**	******************************* */


/* ********************************* */
// インクルード ファイル
/* ********************************* */
#include "printf.h"

/** ******************************* */
//		外部定義変数	  
/** ******************************* */ ///UART2を使用 extern UART_HandleTypeDef huart2; /** ******************************** */ /** * 説明 * @param 引数(ucInitReq) : UARTを使用して、文字を送信する * @param int c : 出力文字 * @return void : None */ /** ******************************** */ int __io_putchar(int c) { if( c == '\n' ) { int b = '\r'; HAL_UART_Transmit(&huart2,(uint8_t*)&b, 1, 1); } HAL_UART_Transmit(&huart2,(uint8_t*)&c, 1, 1); return 0; } /* setbuf(stdout,NULL); をメイン関数に記載すること。 */ /* ** ファイルの最後 ( printf.c ) ** */

///printf.h (出力デバック用ヘッダファイル)

/**	****************************** */
/**
 *	@file		printf.h
 *	@brief		printfを使用可能とする。
 *	@brief		Compiler 	: CubeIDE
 *	@brief		CPU			: STM32F401RE
 *	@date		$LastChangedDate	: 2023/03/04 $
 *
 */
/**	****************************** */

#ifndef		PRINTF_H	///<PRINTF_H 未定義?
#define		PRINTF_H	///<PRINTF_H 定義済み!!


/**	****************************** */
/*		インクルード ファイル   */
/**	****************************** */
#include "main.h"
#include "stdio.h"

#endif																///<PRINTF_H 未定義 End
/* **** ファイルの最後 ( form.h ) ****************** */

///typedef.h (基本型ヘッダファイル)

/** *********************************************** */
/**
*	@file		typedef.h
*	@brief		基本型変数定義
 *	@brief		Compiler 		: CubeIDE
 *	@brief		CPU			: STM32F401RE
 */
/** *********************************************** */

#ifndef TYPEDEF_H
#define TYPEDEF_H

/**
 * \defgroup CompileOption #ifのコンパイルオプション用
 */
/*@{*/
#undef Disable
#define Disable	0xffff		///<使用しない場合に指定
/*@}*/


///BOOL型
#undef false
#undef true
typedef enum{false=0	///<為
	,true		///<真
	}bool;

///ONOFF型
typedef enum{OFF=0	///<OFF指定
	,ON		///<ON指定
	}ONOFF;

///SIGNAL型
typedef enum{LOW=0	///<信号をLOWにする
	,HIGH		///<信号をHIGHにする。
	}SIGNAL;

///UPDOWN型
typedef enum{UP=0	///<UP指定
	,DOWN		///<DOWN指定
	}UPDOWN;


///基本型定義
typedef signed char	SCHAR;		///<8bit変数 符号有り
typedef unsigned char	UCHAR;		///<8bit変数 符号無し
typedef signed short	SSHORT;		///<16bit変数 符号有り
typedef unsigned short	USHORT;		///<16bit変数 符号無し

#endif

///STMSys.h (STM32レジスタ設定ファイル)

/** *********************************************** */
/**
* @file STMSys.h * @brief STM32 基本制御ルーチン * @brief Compiler : CubeIDE * @brief CPU : STM32F401RE */ /** *********************************************** */ #ifndef STMSYS_H #define STMSYS_H /* *************************************************************** */ /** STMSys Settings Define. */ /* *************************************************************** */ #include "main.h" ///< メイン処理制御 /* *************************************************************** */ /** STM32 GPIO Control Define. */ /* *************************************************************** */ ///BIT指定のGPIO出力 ///base:GPIOのベース pin:ピンBIT data:0=OFF/1=ON #define OutGPIOBit(port,pin,data) (port->ODR=((port->ODR&~(pin))|((data==0) ? 0 : (pin)))) ///BIT指定のGPIO出力(反転) ///base:GPIOのベース pin:ピンBIT data:0=OFF/1=ON #define OutGPIOBitR(port,pin,data) (port->ODR=((port->ODR&~(pin))|(data==0) ? (pin) : 0)))
///base:GPIOのベース pin:ピンBIT指定 戻り:0/1
#define InGPIOBitR(port,pin) ((((port->IDR)&(pin))==0)?0:1)
///BIT指定のGPIO入力(反転) ///base:GPIOのベース pin:ピンBIT指定 戻り:0/1 #define InGPIOBitR(port,pin) ((((port->IDR)&(pin))==0)?1:0) #endif

 

(4) プログラムの説明(キー入力)
 (a) #define KEY1_IN() ( InGPIOBitR(PORT_KEY1,BIT_KEY1))
  key_ctrl.hにて作成されている、これにより、KEY1の入力を確認する。
  nucleof401のスイッチは、デフォルトで3.3Vでプルアップ(High)されており、
  スイッチを押すと、GNDに接続され、LOW(0V)となる。
 
 (b)InGPIOBitR は、STMSys.h (STM32レジスタ設定ファイル)にて作成されている。
  #defineによって、 
  ((((port->IDR)&(pin))==0)?1:0) を InGPIOBitR(port,pin) に置き換えている。
  この動作については、下記を参照
  指定したpinが0の場合、1を返し、pinが1だったら0を返している。
 
 (c) Key_In(void)関数について
   Key_Input関数によって呼び出される。
    usKey1 = KEY1_IN();
    usKey = ( usKey1<<KEY1SFT | (usKey2 << KEY2SFT) | (usKey3 << KEY3SFT) );
   KEY1_IN()で取り込んだ値(1 or 0)をusKey1に取り込む。
 
   usKey1<<KEY1SFTによって、1<<13 となる。
   これは、KEY1がPC13の為、1<<13とすることにより、
   KEY1_GPIO_Portと同じ値になる。
   このusKey = usKey1<<KEY1SFT の値を返す。
 
 (d)  key_ctrl.c の Key_Input関数 について
   タイマ割り込みによって5ms毎に呼び出される。
   (Tim3_Int();によって呼び出される。)
 
   ucKey1 = Key_In(); 
   GKeyCtrlInf.ucInp = KEY_NONE; ///< キー入力状態をクリア
   GKeyCtrlInf.ucInp |= (ucKey1 & BIT_KEY1 )? KEY1_SET : KEY_NONE;
     BIT_KEY1は、KEY1_GPIO_Portのこと。
 
   つまり、
   ucKey1 & BIT_KEY1というのは、
   ucKey1 = KEY1_GPIO_Port 又は それ以外の値が入力されるので、
   
   ・ucKey1がKEY1_GPIO_Portの時
    ucKey1 & BIT_KEY1は1(true)になる。
   ・ucKey1がKEY1_GPIO_Port以外の時
    ucKey1 & BIT_KEY1は0(false)となる。
   
   ・ucKey1 & BIT_KEY1が1(true)の時
    GKeyCtrlInf.ucInp |=KEY1_SET (KEY1 設定コードが設定)
   ・ucKey1 & BIT_KEY1が0(false)の時
    GKeyCtrlInf.ucInp |=KEY1_SET : KEY_NONE (KEY入力なし 設定コードが設定)
    が設定される。
    参考:
     のビット演算子と条件演算子の項目を参照
 
 (e) GKeyCtrlInf.ucInp != GKeyCtrlInf.ucPrev
   GKeyCtrlInf.ucPrevは、一回前に実行した時のKEY設定コードが入る。
  1回前のKEY設定コードと今回のKEY設定コードを比較することにより、
  KEYが変化したかどうかを確認している。
 
  KEY設定コードが変化している場合、下記初期化を行う。
  ・GKeyCtrlInf.ucPrev = GKeyCtrlInf.ucInp; ///< キー入力状態を更新
  ・GKeyCtrlInf.ucTimCnt = 0;      ///< チャタリング除去カウンタをクリア
  ・GKeyCtrlInf.ucState = KEYS_CHG;   ///< ステート=キー入力状態変化
  
 (f) KEYが変化した場合、下記処理によって、チャタリングの確認を行う。
  KEYが押されたままで、変化しない間、
  チャタリング除去カウンタを+1し続け、それが設定した除去期間を超えた場合、
  チャタリング除去が出来たものとして、次の処理に進む。
  それ以外の場合は、
  ・チャタリング除去カウンタの更新と
  ・キー入力状態の変化の確認 のみ行う。
 
  GKeyCtrlInf.ucTimCnt++;  ///< チャタリング除去カウンタを更新 
  if ( GKeyCtrlInf.ucInp != GKeyCtrlInf.ucPrev ) {/// キー入力状態を更新  
  /// 除去カウンタクリア 
  GKeyCtrlInf.ucPrev = GKeyCtrlInf.ucInp; GKeyCtrlInf.ucTimCnt = 0;
  }
  /// チャタリング除去期間経過時
  else if (GKeyCtrlInf.ucTimCnt >= KEY_CHATTER_REMOVE_TIM_CNT) {
 
 (g) if ( (GKeyCtrlInf.ucFix & KEY1_SET) != (GKeyCtrlInf.ucInp & KEY1_SET) ) {
  GKeyCtrlInf.ucFixは確定している現在のキーの状態を確認している。
  GKeyCtrlInf.ucFix と KEY1_SETが同じだった場合1、
  GKeyCtrlInf.ucFix と KEY1_SETが違う場合、0 となり、
  それを、GKeyCtrlInf.ucInp & KEY1_SETと比較している。
 
  これは、
  ・キー1が押されてない状態から押された状態に変化したのか。
  ・キー1が押された状態から、押されてない状態に変化したのか。
  というのを確認している。
  
  (h) if ( ( GKeyCtrlInf.ucFix & KEY1_SET ) == 0 ) 
   これがtureの時、
   KEY1の状態が OFF からONに変化したことになるので、
   Key_DatSet( KEY1_ON ); によって、キー入力バッファにKEY1_ONコードを格納
    falseの時
   KEY1の状態が ON からOFFに変化したことになるので、
   Key_DatSet( KEY1_OFF); によって、キー入力バッファにKEY1_OFFコードを格納
 
以下に、上記動作の変数の状態変化について記載する。
下記表の名前省略及び表している数値
 ucFix         :GKeyCtrlInf.ucFix →現在のボタンの確定している状態を表す。
 ucInp        :GKeyCtrlInf.ucInp →今のボタンを読み込んだ状態を表す。
 ucPrev      :GKeyCtrlInf.ucPrev  →1回前に関数が実行された時のボタンの状態を表す。

 BIT_KEY1    :0x01<<13
 KEY1_SET    :0x01
 ucFixが0    :KEY入力無し
 ucFixがKEY1_SET:KEY1が押されている状態
 

 

 (i)if ( GKeyCtrlInf.ucFix & KEY1_SET ) { ///< KEY1 ON時

          ///< カウント可能時
       if ( GKeyCtrlInf.Key1_OnTimCnt < 0xffff ) {
    GKeyCtrlInf.Key1_OnTimCnt++; ///< ON時間を更新
   この処理により、
  GKeyCtrlInf.ucFixがKEY1_SETの間、KEY1長押し時間カウントを+1し続ける。
  また、0xffff以上に値が大きくならないようにしている。
 
 
 (j) Key_Proc( void )
  これは、メイン処理で実行される。
  ucKey = Key_DatGet(); ///< キー入力データの取得
  でキーバッファに格納されているキーコードを取得する。
 
  長押しされている場合、キーが押されていない状態の場合は、KEY_NONE
  キー1の状態が変化した場合は、KEY1_ON又は、KEY1_OFFが返される。
  (今回はKEY1のみの為)
  メイン処理の ucKey = Key_Proc(); に入る。
 
 (l) if(ucKey == KEY1_SHORT){
  メイン処理の(j)で格納されたucKeyの値によって、if文で動作を変更する。
  今回は、モードの切り替えにより、LEDの状態を変化させている。
 
 
 

3.関連記事

関連記事一覧:
 
組み込みC言語:

 

 

STM32マイコン26(割り込み:TIM3_IRQHandler)

 

割り込み:TIM3_IRQHandler

1.本日の内容

 (1) STM32マイコン(nucleof401RE)を使用しLEDの点灯/消灯を制御する。

 (2) 割り込み処理を行う場合、下記2つの関数がある。

  ・void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

  ・void TIM3_IRQHandler(void)

  TIM3_IRQHandlerの使い方と、2つの関数の違いについて説明する。

 

 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

 の実装方法などは下記を参照

 ・STM32マイコン20(割り込み処理部分を別ファイルに作成) - Project_OKI’s diary

 ・STM32マイコン_9(タイマ割り込み:LED点滅) - Project_OKI’s diary

 

・目次

 

2.内容

​​(1) やる内容の詳細​

 ・void TIM3_IRQHandler(void)を使用し、割り込み処理を実装する。

 ・割り込み処理によって、LEDを1秒ごとに点灯/消灯を繰り返す。

 ・TIM3_IRQHandlerHAL_TIM_PeriodElapsedCallbackの違いについて説明する。

  (メリットデメリットなど)

 

(2) CubeMXの設定

 CubeMXの設定は、下記記事と同じ。

 →STM32マイコン_9(タイマ割り込み:LED点滅) - Project_OKI’s diary

 

(3) プログラムの作成

typedef.hファイルの作成

/**
*	@file		typedef.h
*	@brief		基本型変数定義
*/

#ifndef TYPEDEF_H
#define TYPEDEF_H

///BOOL型(ない場合のみ)
#undef false
#undef true
typedef enum{false=0	///<為
	,true		///<真
	}bool;

///ONOFF型
typedef enum{OFF=0	///<OFF指定
	,ON		///<ON指定
	}ONOFF;

///SIGNAL型
typedef enum{LOW=0	///<信号をLOWにする
	,HIGH		///<信号をHIGHにする。
	}SIGNAL;

///基本型定義
typedef signed char	SCHAR;		///<8bit変数 符号有り
typedef unsigned char	UCHAR;		///<8bit変数 符号無し
typedef signed short	SSHORT;		///<16bit変数 符号有り
typedef unsigned short	USHORT;		///<16bit変数 符号無し
typedef signed int	SINT;		///<int型 符号有り(8bitサイズ)
typedef unsigned int	UINT;		///<int型 符号無し(8bitサイズ)
typedef signed long	SLONG;		///<32bit変数 符号有り
typedef unsigned long	ULONG;		///<32bit変数 符号無し

#endif

 

led.cファイルの作成

/**	******************************************************* */
/**
 *	@file		led.c
 *	@brief		LED制御処理
 *	@brief		Compiler 	: Atollic True Studio for STM32 9.0.0
 *	@brief		CPU			: STM32F401
 */
/**	******************************************************* */

/* ************************************************************ */
//		インクルード ファイル																			
/* ************************************************************* */
#include "main.h"
#include "led.h"    	//LED制御ヘッダファイルのインクルード

/** ************************************************************ */
/**		外部定義変数																						*/
/** ************************************************************ */
	LEDCTRL 		ledctrl;   //LED制御構造体

/** ******************************************************* */
//		関数の作成																						
/** *************************************************** */

/**	*********************************************** */
/**
*	MODE1 LED点灯/消灯処理(LED1を点灯/消灯(反転)する処理)
*	@param	void        	: none
*	@return	void		: none
*/ /** ************************************************ */ void Led1Blink() { ///LED点滅フラグ確認 if(ledctrl.led1blif == true){ if(Led1State == HIGH){ ///LEDがHIGHの時 Led1(LOW); ///LED1をON }else{ ///LED1がLOWの時 Led1(HIGH); ///LED1をOFF } } } /* **** ファイルの最後 ( led.c ) ******************************** */

 

led.hファイルの作成

/**	******************************************** */
/**
 *	@file		led.h
 *	@brief		LED制御ヘッダ
 *	@brief		Compiler 	: Atollic True Studio for STM32 9.0.0
 *	@brief		CPU			: STM32F401RE
 *
 */
/**	******************************************** */

///多重インクルード防止
#ifndef LED_H ///<LED未定義?
#define LED_H ///<LED_H 定義済み!!

/* ****************************************************** */
//		インクルード ファイル																			
/**************************************************** */
#include "typedef.h"	//基本型変数定義ヘッダファイルのインクルード

/* **************************************************** */
//		定数値の定義																					
/* **************************************************** */

/* ************************************************** */
//		式、関数の置き換え																						
/* **************************************************** */
///Bit変換:出力
#define OutGPIOBit(port,pin,data)	(port->ODR=((port->ODR&~(pin))|((data==0) ? 0 : (pin))))	///GPIO設定
///Bit変換:入力
#define InGPIO(port,pin) ((((port->IDR)&(pin))==0)?0:1)

///LED ON/OFF関数の作成
#define Led1(dt)	OutGPIOBit(LED1_GPIO_Port,LED1_Pin,dt)
///LEDステート確認用変数
#define Led1State	InGPIO(LED1_GPIO_Port,LED1_Pin)					

/* ****************************************************** */
//	構造体の定義																					
/* ************************************************ */
//MODE LEDステート管理用構造体
typedef struct{
	//LED動作ステート
	USHORT		led1blicout;///LED点滅時間カウント用変数
	bool		led1blif;   ///LED1点滅ステート:true:LED点滅 false:LED点滅しない
} LEDCTRL;

/* **************************************************** */
//		外部変数の定義																					
/* ********************************************* */
extern LEDCTRL 		ledctrl;	///LED制御構造体

/* ********************************************** */
//	関数のプロトタイプ宣言																				
/* ********************************************* */
extern void Led1Blink();	///LED1を点灯/消灯(反転)関数

#endif ///<LED_H 未定義 End

/* **** ファイルの最後 ( led.h ) ************************* */

 

main.cファイルの作成

/* USER CODE BEGIN Includes */
#include "typedef.h"
#include "led.h"
/* USER CODE END Includes */
  /* USER CODE BEGIN 2 */
  //TIM3開始
  HAL_TIM_Base_Start_IT(&htim3);

  //LED点滅開始
  ledctrl.led1blif = true;

  /* USER CODE END 2 */

 

stm32f4xx_it.cファイル (割り込みについて記載、CubeMXで自動生成される。)

ここに、TIM3_IRQHandlerが自動的に作成される。

TIM3_IRQHandlerのUSER CODEに自分が作成した割り込みの関数などを実行する。

/* USER CODE BEGIN Includes */
#include "typedef.h"					///< 基本型
#include "IntR.h"					///< 割り込み
/* USER CODE END Includes */
  /* USER CODE BEGIN TIM3_IRQn 1 */
  Tim3_Int();			///< Tim割り込み処理
  /* USER CODE END TIM3_IRQn 1 */

 

IntR.cファイルの作成

/** *********************************************** */
/** 
 *	@file		IntR.c
 *	@brief		割り込み制御
 *	@brief		Compiler 		: CubeIDE
 *	@brief		CPU			: STM32F401RE
 */
/** *********************************************** */

#include "typedef.h"		///< 基本型
#include "IntR.h"		///< 割り込み制御
#include "main.h"		///< メイン処理
#include "led.h"		///< led制御

/**************************************************************** */
/**          グローバル変数定義                                   */
/**************************************************************** */
///構造体宣言
volatile TTimCtrlInf		GTimCtrlInf;
///ハンドラ宣言
extern TIM_HandleTypeDef htim3;

/* *************************************************************** */
/**		STM32 関数作成				          */
/* *************************************************************** */

/**	************************************************************** */
/**
 *	TIM3割り込み処理(1msごとに割り込み)
 *	@param		void			: None
 *	@return		void			: None
 */
/**	************************************************************** */
void Tim3_Int()
{
	///1ms経過毎の処理
	GTimCtrlInf.uc1sCnt++;		///< 1sカウンタを更新

	///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  $
 */
/** *********************************************** */

#ifndef INTR_H
#define INTR_H

#define	TIM1S_CNT_VAL	1000  ///< 1sタイマ値(1ms*1000=1s)

/** *********************************************** */
/*		構造体の定義			    */
/** *********************************************** */
///	タイマ制御情報 構造体の定義
typedef struct {
	UINT	uc1sCnt;///< 1s カウンタ
} TTimCtrlInf;

/** *********************************************** */
/*		外部変数の定義			    */
/** *********************************************** */
extern volatile TTimCtrlInf	GTimCtrlInf;	///< タイマ制御情報

/** *********************************************** */
/*		プロトタイプ宣言		   */
/** *********************************************** */
extern void Tim3_Int();
#endif

 

 

(4) プログラムの説明
 (a) 基本は下記記事と同じ
   を参照
   Led1Blinkは、LEDが点灯してたら、LEDを消灯。
   LEDが消灯していたらLEDを点灯する関数。
 
 (b) #define OutGPIOBit(port,pin,data) (port->ODR=*1|((data==0) ? 0 : (pin)))) ///GPIO設定 
  #define Led1(dt) OutGPIOBit(LED1_GPIO_Port,LED1_Pin,dt)
  これで、レジスタ操作により、LEDのON/OFFを行っている。
 
 (c) HAL_TIM_Base_Start_IT(&htim3);
  TIM3開始関数。これにより、設定した秒数でタイマ割り込みが行われる。
  今回は、tim3を1秒に1回実行している。
 
 (d)  Tim3_Int();
   今回割り込みを行う関数。
   自動生成された、TIM3_IRQHandlerUSER CODE BEGIN TIM3_IRQn 1
   に記載することで、1秒に1回、割り込み処理が実行される。
 
   ledctrl.led1blifをLED点滅フラグを確認し、trueの場合、下記動作を実行する。
   LEDが点灯していたらLEDを消灯
   LEDが消灯していたらLEDを点灯する。
 
  とほぼ同じで、どちらも1秒に1回割り込み処理を実行している。
 
(5) TIM3_IRQHandlerHAL_TIM_PeriodElapsedCallbackの違い
 ・TIM3_IRQHandler
 タイマー3の割り込みハンドラーであり、直接割り込みを処理する関数。
 この関数を使用すると、より低レベルの割り込み処理を実装することができる。
 割り込みハンドラーは、プロセッサが割り込みを受け取った際に、
 直接呼び出されるため、割り込みに対する最速の応答が必要な場合に使用される。
 
HAL_TIM_PeriodElapsedCallback
 HALドライバで呼び出されるコールバック関数。
 割り込み処理が行われると、HALドライバにより自動的に呼び出される。
 割り込みハンドラーの処理が終了した後に呼び出されるため、
 割り込みに対する迅速な応答は必要ない場合に使用される。
 
 使い分けとしては、

 以下の場合は、HALドライバより、TIM3_IRQHandlerに記載した方が良い。

 

  ・割り込みが発生して、すぐにレジスタ値を読み込んで処理をする必要がある場合。(DMAなど)
  ・割り込みが発生した際に直ちにデータを受信する必要がある場合
  ・特定のシリアル通信モードに移行するために特定のレジスタに特定の値を書き込む必要がある場合

それ以外だと基本は、HALドライバを使用した方が、コード変更が容易になり、
他のCPUへの応用もききやすい。

 
 下記記事は、TIM3の処理手順の中身を追っているので、参考になる。
 

3.関連記事

関連記事一覧:
 
組み込みC言語

 

*1:port->ODR&~(pin

CubeIDE AD変換の設定

STM32 (DMAでAD変換)

1.本日の内容

 (1) CubeIDEのAD変換の設定項目についてまとめました。

   AD変換の設定を行う際の参考にしてください。

  ※使用マイコン:nucleof401

 

・目次

 

2.内容

​​(1) 設定内容

項目 説明
Clock Prescaler ADCのクロック周波数を設定する
 PCLK2 divided by 2/4/6/8 PCLK2を2/4/6/8で分割する。
Resolution 変換結果のビット幅を設定する
 12bits/10bit/8bit/6bit ADCの変換分解能を設定。下記に詳細を示す。
Data Alignment 変換結果のアラインメント方法を設定する
 Right alignment 右寄せ
 Left alignment 左寄せ
Scan Conversion Mode 複数チャンネルをスキャン変換するかどうかを設定する
 Enable 設定を有効にする。
 Disable 設定を無効にする。
Continuous Conversion Mode 変換を継続的に行うかどうかを設定する
 Enable 設定を有効にする。
 Disable 設定を無効にする。
Discontinuous Conversion Mode 途中で変換を一時停止するかどうかを設定する
 Enable 設定を有効にする。
 Disable 設定を無効にする。
DMA Continuous Requests 変換終了時にDMAを継続的にリクエストするかどうかを設定する。
 Enable 設定を有効にする。
 Disable 設定を無効にする。
End Of Conversion Selection 変換終了時のシーケンスを選択する
 EOC flag at the end of single channel vonversion 単一チャンネル変換後にEOCフラグを立てる
 EOS flag at the end of all conversions すべてのチャンネル変換後にEOSフラグを立てる
ADC_Regular_ConversionMode 通常変換モードの設定をする
Number Of Conversion 変換するチャンネル数を設定する
External Trigger Conversion Source トリガソースを設定する
 Regular Conversion launched by software ソフトウェアプログラムによってADC変換を開始するトリガソース
 Timer 1 Capture Compare 1event タイマー1(TIMER1)が特定の値に達した時、ADC変換を開始するトリガーソース
 Timer 1 Capture Compare 2event タイマー1(TIMER1)が特定の値に達した時、ADC変換を開始するトリガーソース
 ・・・
External Trigger Conversion Edge トリガのエッジを設定する
 Trigger detection on the rising edge トリガの立ち上がりエッジ検出時に、ADCを開始する。
 Trigger detection on the falilin edge トリガの立下りエッジ検出時に、ADCを開始する。
 Trigger detection on both the rising and fallin. トリガの立ち上がり、立下り両方検出時に、ADCを開始する。
Rank 変換するチャンネルの順序を設定する
 Channel 変換するチャンネルを選択する。
 Sampling Time サンプリング時間を設定する。:3,15,28,56,84,112,144,480
ADC_Injected_ConversionMode インジェクション変換モードの設定をする
Number Of Conversions インジェクション変換するチャンネル数を設定する
External Trigger Source トリガソースを設定する
External Trigger Edge トリガのエッジを設定する
Injected Conversion Mode インジェクション変換モードの設定
  Discontinous Mode 複数変換時、1つの変換が完了する度にADC変換を停止、変換間の時間を空ける。
  Auto Injected Mode 自動的に切り替え、複数のインジェクションチャンネルで変換を行う
WatchDog アナログウォッチドッグモードを有効にするかどうかを設定する
 Enable Analog WatchDog Mode アナログウィッチモードを有効にする。

 

​​(2) 設定の詳細内容

 (a) Data Alignment (データアライメント)

  ・コンピュータのメモリ内でデータを格納する際に、
   どのようにアドレスを割り当てるかを決定する方法です。
  ・右寄せ又は左寄せの設定を行います。

  アライメントとは

  ・メモリやストレージなどの記憶装置にデータを書き込む際、

  書き込むデータの大きさや先頭の位置(アドレス)を、

  装置の管理単位の整数倍になるように調節すること。

 

  例:32ビットの整数を4バイト単位でアライメントする場合
    先頭アドレスは4の倍数になるように割り当てられます。

 

右寄せの場合

  ・変換結果は下位ビットから順にメモリに格納。
左寄せの場合

  ・変換結果は上位ビットから順にメモリに格納。


データアライメントの設定

  ・データを処理する際にどのようにアクセスするかに影響を与えます。

  ・一般的に8ビットの符号なし整数データを変換する場合、

   変換後のデータの桁数も8ビットになるため、

   Data Alignmentはそのままで構いません。

  ・16ビットの符号付き整数データを変換する場合、

   変換後のデータの桁数が16ビットになるため、

   Data Alignmentを設定する必要があります。

 

・参考:データ型のアラインメントとは何か,なぜ必要なのか?

メモリマップ | APS|組み込み業界専門メディア

STM32H7でキャッシュ一貫性を保持したDMA転送(Memory-to-Peripheral) | ごはんたべよ

 

 (b) EOC flag at the end of single channel vonversion

  ・シングルチャンネル変換が完了したときに、ADCがフラグをセットします。

 

 (c) EOC flag at the end of all channel vonversion

  ・複数チャネル変換ですべてのアナログ入力ピンの変換が完了した後に、

   ADCがフラグをセットします。

  ・これらのフラグを使用することで、マイコンは変換が完了するまで待機し、

   変換データを読み取ることができます。

   これにより、正確で信頼性の高いAD変換が可能になります。

 

 (d) Regular Conversion launched by software

  ・マイコン内のソフトウェアプログラムによってトリガされる

   ADCの変換モードのことです。
  ・ソフトウェアプログラムがADC変換を開始するためにトリガ信号を送信します。


  ・このトリガー信号が送信されることで、ADCが変換を開始し、

   変換結果をレジスタに格納します。
  ・このモードは、タイマ等の外部トリガソースを使用しない場合に選択されます。

 

  ・Regular Conversion launched by softwareを使用する場合、
   ADCを起動するためにHAL_ADC_Start()関数を使用します。
   この関数は、ADCコンバータを開始し指定したチャンネルで変換を開始します。


  ・変換結果が利用可能になると、HAL_ADC_PollForConversion()関数などを使用して

   結果を取得することができます。


  ・HAL_ADC_Stop()関数を使用してADCを停止する必要があります。

   ※DMAで使用する場合は、HAL_ADC_Start()ではなく、

    HAL_ADC_Start_DMAを使用します。
    DMAを使用して変換を開始し、変換が完了するとDMA割り込みを発生させます。

 

 (e) Timer 1 Capture Compare 1event

  ・タイマ1(TIMER1)が特定の値に達したときにADC変換を開始するトリガーソースの1つです。
  ・TIMER1は、マイクロコントローラ内のタイマー/カウンターの1つであり、   

   Capture/Compare機能を備えています。

  

  ・Capture:タイマ1の計算値を外部からの入力パルスを

    トリガとして、レジスタに取りこみます。

 

  ・Compare:レジスタに設定れた値とタイマ1の計算値を比較し、

     一致すると、割り込み要求を発生させます。

  この機能により、タイマーが特定の値に達すると、

  タイマ1のCapture Compareレジスタ1(CCR1)で定義されたイベントが発生します。

 

  このイベントは、ADC変換をトリガーするのに使用できます。
  具体的には、CCR1レジスタに設定された値と一致すると、
  タイマー1 Capture Compare 1eventが発生し、ADC変換が開始されます。

 

  ・1eventは、タイマー1のキャプチャ/コンペアユニットのCCR1が一定の値に達した  

   ときに発生するイベントを指します。
  ・2eventは、CCR2が一定の値に達したときに発生するイベントを指します。

参考:電子工作室

キャプチャ・コンペア

 

 (e) Sampling Time

  ・アナログ入力ピンからアナログ信号を取り込む時間の長さを設定します。
  ・この時間は、アナログ信号がキャプチャされ、

   デジタル値に変換されるための時間です。

 

  ・STM32のADCは、サンプリング時間を1.5サイクルから239.5サイクルの範囲で設定できます。
  ・サンプリング時間が長いほど、より正確な変換結果を得ることができますが、

   変換速度が低下します。


  ・サンプリング時間が短い場合、変換速度が向上しますが、

   精度が低下する可能性があります。

 

 (f) ADC_Injected_ConversionMode

  ・ADC Regular Conversionは、ADCが連続的に入力信号をサンプリングし、デジタ

   ル信号に変換する方法です。
  ・この変換モードでは、1つのアナログチャネルに対して1回の変換が行われます。

  ・変換を完了するまでに複数のサンプルが必要な場合でも、

   同じチャネルで実行する必要があります。

 

  ・ADC Injected Conversionは、

   ADCが複数のアナログチャネルから1回の変換を行う方法です。


   この変換モードでは、ADCによって定義された複数のチャネルが、

   同時にまたは順次変換されます。

 

  ・チャネルごとに変換結果を取得できます。
   例えば、ADC Injected Conversionを使用すると、

   複数のセンサーからの信号を同時に変換できます。

   これにより、処理速度が向上し、システムの全体的な精度が向上します。

 

 (f) Discontinuous Mode

  ・ADCが複数の変換を行う際に、1つの変換が完了するたびにADC変換を停止することで、変換間の時間を空けるモードです。

  ・これにより、ADC変換によるノイズが減少し、精度の高い変換を実現できます。

 

  ・例えば、4つのチャンネルを持つADCで、Discontinuous Modeを使用する場合、

   1つの変換が完了すると、ADCは変換を停止し、指定された時間(Discontinuous Modeの設定値)を待ってから次の変換を開始します。

   これにより、ADC変換間の時間を空けることができます。

 

  ・Discontinuous Modeは、通常、オペアンプを使用してシグナルを増幅する必要がある場合や、シグナルが不安定な場合に使用されます。

   このモードを使用することで、シグナルのノイズが減少し、より正確な変換結果を得ることができます。

 

 (g) Auto Injected Mode

  ・変換シーケンスを開始するためのトリガが必要です。
  ・トリガは、ソフトウェアトリガー、外部トリガー、またはレギュラーチャンネル変換の完了トリガーなど、複数のオプションから選択できます。

  ・トリガが発生すると、ADCはインジェクションチャンネル変換を開始し、

   自動的にインジェクションチャンネルを切り替えながら変換を行います。

  ・最後のインジェクションチャンネル変換が完了すると、

   ADCが変換シーケンスの終了を通知するフラグが立ちます。

 

  ・変換結果はDMA転送を使用して転送することができます。
  ・複数のインジェクションチャンネル変換結果を取得するには、

   JDR1、JDR2、JDR3、JDR4といった複数のレジスタを使用します。

 

 (h) Enable Analog WatchDog Mode

  ・ADCが取得したアナログ信号の値が、あらかじめ設定した上限値と下限値の範囲外になった場合に、割り込みまたはDMA転送をトリガーする機能です。

  ・これを有効にすると、入力信号が範囲外になった場合にすばやく対応することができます。

 

  ・このモードを有効にするには、ADCのクロックを設定、チャンネルを構成、

   ADCのアナログウォッチドッグモードを有効にする必要があります。

 

  具体的には、次の手順を実行します。

  (a) ADCのクロックを設定する。
  (b) 変換するチャンネルを構成する。
  (c) ADCのアナログウォッチドッグモードを有効にする。
  (d) アナログウォッチドッグレジスター(AWD)の上限値と下限値を設定する。
  (e) ADCを有効にする。
 
 

3.関連記事

関連記事一覧:
 
組み込みC言語