GPIO多重化機能1 - タイマーインプットキャプチャ

序文

ここに画像の説明を挿入
GPIOの汎用入出力機能とそのアナログタイミング機能については先ほど紹介しましたが、アナログタイミングでペリフェラルを駆動するとコード部分で若干のトラブルが発生したり、アナログでは実現できないタイミングや機能も存在することがわかります。タイミング。このとき、ペリフェラルを使用する必要がありますが、ここでのペリフェラルとは、チップ外のペリフェラルだけでなく、チップ内の内部および外部デバイスも指します。
これらのオンチップ周辺機器のほとんどは、タイマー入力キャプチャ、タイマー比較出力、SPI コントローラー、I2C コントローラーなど、オフチップとデータを交換する必要があります。
データ交換を必要とするこれらのオンチップ周辺機器は、フロント GPIO を仲介として使用する必要があります。GPIO はマイクロコントローラーの内部と外部の間の唯一の通信ブリッジであるため、これらのオンチップ周辺機器を使用する場合、GPIO を多重化モードとも呼ばれる特別なモードとして構成する必要があります。
![ここに画像の説明を挿入](https://img-blog.csdnimg.cn/e7ff1952b78443c09008e92099784d9b.png#pic_center =600*200)
STM32F103C8T6 のオンチップ ペリフェラルの数は、チップのマニュアルで確認できます。筆者のボードではタイマーを4つ、SPIを1つ、USARTを3つ、ADCを1つ使用していますが、具体的な使い方は後ほど紹介します。
ここに画像の説明を挿入

タイマインプットキャプチャ

前回の内容と繋がるため、今回はまずタイマーインプットキャプチャを使って赤外線受光管のデータを解析し、制御用の入力信号として利用する方法を紹介します。

赤外線受信データ解析

前回の記事では、HS0038 が受信する NEC フォーマットのデータ フレームについて説明しましたので、下図に示すようにスクリーンショットを撮ります
ここに画像の説明を挿入

ここに画像の説明を挿入

1. データ フレームの初期調整には、9ms のロー レベルと 4.5ms のハイ レベルで構成される同期ヘッダーがあります; 2.
ロジック "1" 信号とロジック "0" 信号の前半は両方とも 560us Low です。レベルでは、後半のハイレベル時間だけが異なり、ロジック「1」のハイレベル期間は1680us、ロジック「0」のハイレベル期間は560usです;これらの特性に従って、
data を使用するには、各立ち上がりエッジを特定し、各部分の高レベル期間を取得する必要があります。ハイレベル時間の差により、この時点での信号の同期先頭がまだ論理「0」であるか論理「1」であるかを知ることができる。
エッジを正確に特定し、レベルの持続時間を記録する必要があります。このプロセスに見覚えはありますか? 以前のシリーズでインプット キャプチャを導入したとき、ボタンが押された時間の長さを取得するためにインプット キャプチャを使用しました。超音波モジュールを通じて取得され、検出エッジとタイミングが加算されます。
ここでのインプット キャプチャ機能を知らない場合は、著者による紹介を参照してください。ここでは繰り返しません。
埋め込み学習ノート - 入力キャプチャ

アイデアを捉える

フレーム形式の特性に従って、赤外線リモコン信号を取得するためのプログラミングのアイデアを簡単に説明できます:
1. まず、ハードウェア接続に従って、対応するタイミング チャネルと GPIO を初期化する必要があります;
2. 対応するタイム ベースを構成します。ここで、より正確にカウントするには、データ処理にも便利な、タイマーのカウント周期を 1 回 1us に設定するのが最適です。具体的な設定方法はプログラミングの後半で紹介します;
3. インプットキャプチャのトリガモードと分周するかどうかのパラメータを設定します;
4. キャプチャするキャプチャ割り込みを設定します
。取得:
まず第一に、それを発生させる必要があります。 エッジ トリガのキャプチャ割り込みの場合、立ち上がりエッジ トリガの後に最初にカウント値をクリアし、0 からカウントを開始し、トリガ モードを立ち下がりエッジ トリガに切り替え、カウント値を返します。立ち下がりエッジがトリガされると、再び立ち上がりエッジのトリガに戻り、カウント値を判定および比較します。各信号の範囲と特性により、このときのデータ値を知ることができます。
ここに画像の説明を挿入
5. キャプチャ割り込みに加えて、受信完了の合図としてアップデート割り込みをオンにすることもできます。区別するために、ここでのアップデート割り込み時間は 10ms です。プロセス全体では、32 個のメッセージを受信した後にのみ、 -bit 信号の場合、カウント値がオーバーフローして更新が発生する可能性があります 割り込み、更新割り込みの発生は、データ処理が実行できることを意味します。
ここに画像の説明を挿入

プログラミングの練習

まず、アイデアに従って対応する GPIO を見つけます。回路図とデータシートから、GPIOA1 が使用され、TIM2 のチャネル 2 が多重化されていることがわかります。
ここに画像の説明を挿入

1. 時計を初期化する

内蔵ペリフェラルを使用する場合、まず対応するクロックをオンにします (ここでは GPIOA と TIM2 のクロックをオンにする必要があります)。マルチプレクス機能を使用しているため、マルチプレクス クロックをオンにする必要があります。
これまでの経験によれば、クロック関連の担当者はまずデータシートを見て、オンチップ周辺機器のフックアップ バスを見つけます。
ここに画像の説明を挿入
2 つのオンチップ ペリフェラルがそれぞれ APB2 と APB1 に実装されていることがわかります。そのため、必要なコードは次のとおりです。

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//使能定时器2时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);  //使能GPIOA外设能模块时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  //使能GPIOA AFIO复用功能模块时钟

上記のコードでは別の AFIO クロックがあることがわかりますが、これをオンにする理由は、GPIO の多重化機能を使用する必要があるため、このクロックをオンにする必要があるためです。
ここに画像の説明を挿入

2. GPIOの初期化

ここで注意すべき点は、GPIO の数が少ない C8T6 などの MCU の場合、下図のマッピング テーブルのような再マッピング設定が必要になることです。ここでの TIM2CH2 のデフォルトでは、PA1 を再マッピングする必要はありません。

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState) この関数は、再マッピング設定を実装するために特別に使用されます。とりあえずここでは使いませんが、後ほどお話します。
ここに画像の説明を挿入

そこで問題は、GPIO の初期化をどのように記述するかということです。
インプットキャプチャなので当然入力モードになりますが、M3のGPIO入力モードには多重入力モードはなく、ここではプルアップやプルダウンも必要なく、アナログ入力でもないのでそのまま入力してください。フローティング入力を直接選択します。
ここに画像の説明を挿入

 //设置该引脚为浮空输入功能,
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM2_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO

3. タイマーのクロック基準を設定します。

タイマのクロック基準は、プリスケーラ周波数、リロード値、タイマのカウント モードなどのタイマの基本パラメータを設定することです。これらの詳細については、著者による以前の組み込み研究シリーズを参照してください。注意事項 - 一般的なタイマー
ここでライブラリ関数を使用するという考え方は、実際には前の GPIO と似ています。まずライブラリ関数で stm32f10x_tim.h を見つけてから、対応する初期化構造体を見つけます。説明と後続のパラメータ マクロまたはパラメータ列挙によると、構造体のメンバーを順番に実行します。
ここに画像の説明を挿入
パラメータの割り当てが完了した後、対応する構造体の初期化関数を呼び出して、これらのパラメータを対応するレジスタに書き込む必要もあります。パラメータはタイマー番号と上記構造体のアドレスです。
ここに画像の説明を挿入

//初始化TIM2
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;//定义定时器结构体
	TIM_TimeBaseStructure.TIM_Period = arr-1; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc-1; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

4. インプットキャプチャパラメータを設定する

ここではタイマーのインプットキャプチャ機能を使用する必要があるため、再度ベースに基づいてさらに設定する必要があります。プロセスは上記と同じで、対応する構造体変数が見つかり、説明とパラメータマクロを参照します。割り当てて初期化する Rain パラメータの列挙。ここでは、物理チャンネル、トリガー信号タイプ、入力キャプチャ、およびマッピングチャンネルを設定する必要があります。
ここに画像の説明を挿入
同様に、関連するパラメータを設定した後、対応する初期化関数を呼び出してレジスタに書き込む必要もあります。
ここに画像の説明を挿入


	//输入捕获
	TIM_ICInitTypeDef TIM_ICInitStruct;//定时器输入捕获控制寄存器
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;//通道2
	TIM_ICInitStruct.TIM_ICFilter=0x00;//不使用滤波器
	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获
	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;//不分频
	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;// TI2FP2 输入捕获通道选择
	TIM_ICInit(TIM2,&TIM_ICInitStruct);

5. 割り込み構成

次に、割り込みの設定ですが、まずタイマーの関数を呼び出して割り込み信号を設定する必要がありますが、ここでは更新割り込みとキャプチャ割り込みの 2 種類を使用する必要があります。
ここに画像の説明を挿入
割り込み信号は上記でのみ指定されており、割り込みはまだ構成されていないため、
misc.h を見つける必要があります。このファイルには、対応するパラメーター構成構造体も含まれています。
ここに画像の説明を挿入
対応する初期化関数もあるので
ここに画像の説明を挿入
、コードは次のとおりです。

//中断配置
	TIM_ITConfig(TIM2,TIM_IT_CC2|TIM_IT_Update,ENABLE);//更新中断和捕获中断

	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct);
	 //使能放到最后,避免写入失败。
	TIM_Cmd(TIM2, ENABLE);  //使能TIM2

6. インタラプトサービス機能

上記は単なる初期設定関数であり、実際の機能を実現するには割り込みサービス関数を使用する必要があります。まず、スタートアップ ファイルに移動して、対応する割り込みサービス関数名を取得する必要があります。
コード全体の考え方は、
1. 立ち上がりエッジ トリガを使用してカウンタをクリアし、トリガ モードを立ち下がりエッジ トリガに切り替え、カウント サイクルは 1 回 1us です。2. 立ち下がりエッジがトリガされると
、カウント値をカウントして比較し、同期ヘッダ、論理1か論理0かを判定する3つのタイプの機能に応じて比較します; 3.
下位の順序に従って、32ビットのデータを定義し、データビットを受信しますビット単位;
4. カウンタがオーバーフローした場合、つまりカウント値が 10000 に達した場合、つまり接触時間が 10ms の High レベルになった時点で受信完了と判断します。

//定时器2中断服务程序	 
void TIM2_IRQHandler(void)
{
    
     	
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
	{
    
    //10MS产生一次中断,整个传输中最长的高电平时间为同步码 的 4.5ms ;只有释放信号才可能进入更新中断	
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);	 
		if(Infrared_Receive.start)//接收到过同步头且产生了更新中断说明接收到完毕了
		{
    
    
			Infrared_Receive.start=0;
			Infrared_Receive.end =1;//接收完成的标志位
			
		}
	}
	if(TIM_GetITStatus(TIM2,TIM_IT_CC2)!=RESET)
	{
    
    	  
		TIM_ClearITPendingBit(TIM2,TIM_IT_CC2);	 
		//进入捕获中断,由于需要捕获比较的是高电平的时间,所以第一次捕获为上升沿,
		//检测是否是上升沿使用IO的输入电平来判断
		if(REMOTE_DATA)					//此时是高电平,说明是上升沿检测,
		{
    
    
			//为了统计高电平的时间,进入后需要将计数器的值清零计数一次是1US
			TIM_SetCounter(TIM2,0);  //表示计数值清零
			//切换为下降沿捕获
			TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling);	
		}
		else       //说明是下降沿的捕获中断,
		{
    
    
			//此时需要判断是否为起始(同步头)信号
			//获取此时计数器的值,也就是高电平的持续时间
			Infrared_Receive.ccr = TIM_GetCapture2(TIM2);//获取当前计数值
			TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);//切换为上升沿捕获
			
			if(Infrared_Receive.start == 1)     //如果已经接收过同步头
			{
    
    
				if(Infrared_Receive.ccr>300 && Infrared_Receive.ccr<800)//560us高电平说明是数据0
				{
    
    
						Infrared_Receive.data=Infrared_Receive.data>>1;//低位在前接收后的格式为:8位控制码反码+8位控制码+8位地址码反码+8位地址码
				}
				else if(Infrared_Receive.ccr>1300 && Infrared_Receive.ccr<1900)//1680us说明是数据1
				{
    
    
						Infrared_Receive.data=Infrared_Receive.data>>1;
						Infrared_Receive.data |=0x80000000;//接收数据为1时,给对应位补1
				}
			}
			else                           //没有接收到同步头,先判断是否有同步头 
			{
    
    
					if(Infrared_Receive.ccr>4000 && Infrared_Receive.ccr<5000)//检测到同步头信号
					{
    
    
						Infrared_Receive.start=1;
					}
			}
		}
	}    
}

7. データフレームの処理

32ビットデータを受信した後、そのフレームフォーマットに従って必要なデータを抽出できます

/************************************************
函数功能:红外获取控制码
函数名:Infrared_Get_Data
函数形参:None
函数返回值:无控制-1  有控制码则返回控制码
备注:  
**************************************************/
s8 Infrared_Get_Data(void)
{
    
    
	u8 data;  //控制码
	
	if(Infrared_Receive.end==1)//检测到数据接收完毕
	{
    
    
		Infrared_Receive.end=0;//清除标志位,方便下次接收
		
		data = (Infrared_Receive.data & 0x00ff0000) >> 16;  //获取控制码
		
		return data;	
	}
	return -1;
}

次に、この関数を呼び出して、さまざまな入力値に応じて対応する関数を実行できます。

効果を達成する

ここに画像の説明を挿入

要約する

赤外線入力信号の処理を実現するタイマーインプットキャプチャの紹介はこちら 記事に不備があればご指摘・修正をお願いします。

おすすめ

転載: blog.csdn.net/qq_41954556/article/details/131140258