このような急ぎのタイムクリティカルなタスクを解決するには、ハードウェアのいずれかに基づいてソフトウェアタイマーを適用することが可能であり、必要です。 しかし、まず最初に...
序文の代わりに
Googleにこれについてどう思うか尋ねてください。
ためらうことなく、検索エンジンは次のようなものを生成します。
これらの記事は、アルゴリズムの実装の出発点として役立ちました。
まえがき
PCS開発者として、私はしばしばさまざまな企業のPLCをプログラミングしています。 ソフトウェアタイマー用のライブラリは、開発環境のすべてのPLC用に準備されています。 それらのほとんどすべてに同じ機能があります。 PLCプログラムでのタイマーの使用は多くの種類のタスクで必要です。それらすべてを説明するのは意味がないので、ACS TPからいくつかの例を示します。これらの例はこのモジュールに「大まかに移植」されます。
ミニTK
このモジュールではどのようなタイマーが必要ですか? 4つのタイプを選択しました。
- 遅延タイマー
- オフディレイタイマー
- サイクルタイマー
- シングルタイマー
最初の2つのタイマーは、産業用制御システムとPLCプログラミングから明らかに移行されました。 最後の2つは、MKのハードウェアタイマーの論理的な拡張です。 タイマーの各タイプを個別に検討してください。
遅延タイマー
このようなタイマーの一般的な例は、時間リレーの実装です。 高(アクティブ)レベル信号がタイマーの開始(制御)入力に到着し、タイマーは時間のカウントを素直に開始します。その後、入力にアクティブレベルがあると、出力もアクティブ状態に移行します。 タイマー入力で信号を削除するとすぐに、出力も非アクティブになります。 以下にタイミングチャートを示します。
MKの世界では、長いキーストロークなどを検出するために「ドライ」コンタクトのアンチバウンスを提供する必要がある場合、このタイマーはアプリケーションを見つけます。 このタイマーの範囲は明らかにこれに限定されません。
オフディレイタイマー
タイマーは基本的に最初のものと同じですが、その逆も論理的です。 アクティブ入力、アクティブおよびタイマー出力中。 入力がロー(アクティブではない)になるとすぐに、タイマーはカウントダウンを開始し、最後に出力もゼロにリセットします。 これが彼のタイミングチャートです。
これは、「親」と同時にではなく、少し後にタスクを停止する必要がある場合に発生します。 MKをプログラミングするとき、このタイプのタイマーは誰かにとっては異様に思えるかもしれませんが、彼らが言うように、そうするように。
サイクルタイマー
まあ、すべてが些細です。 このタイマーの各オーバーフローは、イベントをトリガーする必要があり、それに応じて、このタスクまたはそのルーチンタスクを実行します。 慣性環境センサーへの問い合わせ、LEDの点滅、MCが「順序どおり」であることを示す、さまざまなフィルターの等時間間隔の編成など 下のタイミングチャート。
シングルタイマー
このタイプのタイマーは、トリガーされた後に自動的にオフになる(停止する)ことを除いて、周期的なコピーのほぼ完全なコピーです。 つまり、彼らはそれを開始し、その遅延をカウントし、フラグを設定し(イベントを示します)、それ自体を停止しました。 これが彼のチャートです。
現在、これらはすべてコードに実装されています。
SwTimerモジュール
モジュールの名前はそれ自体を表しています。 このモジュールは、ヘッダーとソートの2つのファイルで構成されています。
ヘッダーを考慮してください:
#ifndef SW_TIMER_H_ #define SW_TIMER_H_ #define SwTimerCount 64 // /* */ typedef enum { SWTIMER_MODE_EMPTY, SWTIMER_MODE_WAIT_ON, SWTIMER_MODE_WAIT_OFF, SWTIMER_MODE_CYCLE, SWTIMER_MODE_SINGLE } SwTimerMode; /* */ typedef struct { unsigned LocalCount:32; // unsigned Count:24; // unsigned Mode:3; // unsigned On:1; // unsigned Reset:1; // unsigned Off:1; // unsigned Out:1; // unsigned Status:1; // }SW_TIMER; #if (SwTimerCount>0) volatile SW_TIMER TIM[SwTimerCount]; // #endif void SwTimerWork(volatile SW_TIMER* TIM, unsigned char Count); // void OnSwTimer(volatile SW_TIMER* TIM, SwTimerMode Mode, unsigned int SwCount); // unsigned char GetStatusSwTimer(volatile SW_TIMER* TIM); // #endif /* SW_TIMER_H_ */
このヘッダーには、アレイ内のソフトウェアタイマーの数を示す定義が含まれます。 Enumは、タイマー動作モードの「意識的な」記述のために宣言されています。 ソフトウェアタイマーの構造は次のとおりです。 注目したいのは、タイマー自体が24ビットであることだけです。 この構造では、ソフトウェアタイマーが8バイトのスペースを占有できます。 1 msのハードウェアタイマーオーバーフローを持つ24ビットにより、4.66時間または16,777秒の遅延を達成できます。 それで十分です。
機能はほとんどありません。
モジュール全体の動作を保証する主な機能:
void SwTimerWork(volatile SW_TIMER* TIM, unsigned char Count);
この関数は、ハードウェアタイマーがいっぱいになったときに呼び出す必要があります。 モジュールのアルゴリズム全体を整理します。 コードを見てください:
void SwTimerWork(volatile SW_TIMER* TIM, unsigned char Count){ unsigned short i=0; for (i=0; i<Count; i++){ if (TIM->Mode==SWTIMER_MODE_EMPTY) { TIM++; continue; } if (TIM->Mode==SWTIMER_MODE_WAIT_ON){ // if (TIM->On){ if (TIM->LocalCount>0) TIM->LocalCount--; else { TIM->Out=1; TIM->Status=1; } } else { TIM->Out=0; TIM->LocalCount=TIM->Count-1; } } if (TIM->Mode==SWTIMER_MODE_WAIT_OFF){ // if (TIM->On){ TIM->Out=1; TIM->Status=1; TIM->LocalCount=TIM->Count-1; } else { if (TIM->LocalCount>0) TIM->LocalCount--; else TIM->Out=0; } } if (TIM->Mode==SWTIMER_MODE_CYCLE){ if (TIM->Off){ if (TIM->On){ TIM->Off=0; if (TIM->LocalCount>0) TIM->LocalCount--; } } else{ if (TIM->LocalCount>0) { TIM->LocalCount--; TIM->Out=0; } else { TIM->Out=1; TIM->Status=1; TIM->LocalCount=TIM->Count-1; } } if (TIM->Reset){ TIM->LocalCount=TIM->Count-1; TIM->Out=0; TIM->Status=0; } } if (TIM->Mode==SWTIMER_MODE_SINGLE){ if (TIM->Off){ if (TIM->On){ TIM->Off=0; if (TIM->LocalCount>0) TIM->LocalCount--; } } else{ if (TIM->LocalCount>0) { TIM->LocalCount--; TIM->Out=0; } else { TIM->Out=1; TIM->Status=1; TIM->LocalCount=TIM->Count-1; TIM->Off=1; } } if (TIM->Reset){ TIM->LocalCount=TIM->Count-1; TIM->Out=0; TIM->Status=0; } } TIM++; } }
ループでは、タイマーの配列全体を調べます。 タイマーが空= EMPTYの場合、次のタイマーに進みます。 タイマーの動作モードに応じて、独自のロジックが編成されます。
この関数の呼び出しは、ハードウェアタイマーの割り込みと、フラグによるメインプログラムのループの両方から構成できます。
STM32の割り込みの例を次に示します。
/* USER CODE BEGIN 0 */ #include "sw_timer.h" /* USER CODE END 0 */ void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); HAL_SYSTICK_IRQHandler(); /* USER CODE BEGIN SysTick_IRQn 1 */ SwTimerWork(TIM,SwTimerCount); /* USER CODE END SysTick_IRQn 1 */ }
しかし、メインループから:
/* USER CODE BEGIN 0 */ extern uint8_t FlagSwTimer; /* USER CODE END 0 */ void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); HAL_SYSTICK_IRQHandler(); /* USER CODE BEGIN SysTick_IRQn 1 */ FlagSwTimer=1; /* USER CODE END SysTick_IRQn 1 */ } /* */ while(1){ if (FlagSwTimer){ #if (SwTimerCount>0) // sw_timer.c SwTimerWork(TIM,SwTimerCount); #endif FlagSwTimer=0; }
その他の機能:
void OnSwTimer(volatile SW_TIMER* TIM, SwTimerMode Mode, unsigned int SwCount);
この関数は、特定のタイマーの動作モードを設定し、必要な遅延を設定します。
unsigned char GetStatusSwTimer(volatile SW_TIMER* TIM);
タイマーステータスのステータスを読み取ります。 指定されたタイマーが空の場合、-1を返します。
申込み
#define I_DI_READ_0() HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) // OnSwTimer(&TIM[0],SWTIMER_MODE_WAIT_OFF,1000); OnSwTimer(&TIM[1],SWTIMER_MODE_SINGLE,1000); OnSwTimer(&TIM[2],SWTIMER_MODE_WAIT_ON,1000); OnSwTimer(&TIM[3],SWTIMER_MODE_CYCLE,1000); TIM[3].Off=0; TIM[3].On=1; // TIM[1].Off=0; TIM[1].On=1; //
いくつかのタイマーを異なるモードで初期化します。 メインループでは、自由裁量で使用します。
while (1){ TIM[2].On=I_DI_READ_0(); // , HAL_GPIO_WritePin(GPIOD,GPIO_PIN_15,TIM[2].Out); // , TIM[0].On=I_DI_READ_0(); // , HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14,TIM[0].Out); // , if (GetStatusSwTimer(&TIM[3])){ HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_13); // , } if (GetStatusSwTimer(&TIM[1])){ HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_12); // , } TIM[3].Reset=I_DI_READ_0(); // , }
サイクリックまたはシングルタイマーを停止する必要がある場合は、有効化ビットをリセットし、無効化ビットを設定する必要があります。
TIM[3].On=0; TIM[3].Off=1;
Onビットを設定して再度有効にします。
ステータスの読み取りは、関数として、またはビットを読み取ることで直接行うことができます。
if (GetStatusSwTimer(&TIM[1])) // if (TIM[3].Status){ // TIM[3].Status=0; // , // , /*UserCode*/ }
タイマーが不要になった場合、タイマーを停止するだけでなく、削除する、つまりEMPTYモードに切り替えると、タイマーの配列の処理機能の実行時間を短縮できます。 これを行うには、SWTIMER_MODE_EMPTYモードでタイマー準備関数を呼び出します。 または、これを直接示します。
OnSwTimer(&TIM[3],SWTIMER_MODE_EMPTY,0); // TIM[3].Mode=SWTIMER_MODE_EMPTY; //
最初の2つのタイマーと2番目の2つのタイマーは、不要な機能などを生成しないように、1つの構造で意味がわずかに異なります。 ソフトウェアタイマーモジュールは、ここからダウンロードできます 。
この記事は、 STM32レッスンの改訂資料です。 STM32F4プログラミングレッスン。 レッスン番号4。ソフトウェアマルチタスクタイマーSTM32F4。 私の著者。
このソフトウェアマルチタスクタイマーモジュールの機能を示すビデオ:
それがおそらくすべてです。