Project_OKI’s diary

エンジニアの勉強ブログ

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言語: