ハードウェアタイマーに基づくソフトウェアタイマー

一般化する

ハードウェアタイマーは非常に正確で、ソフトウェアタイマーにはとにかく遅延があります。これは主に正確なタイミングが不要な場合に使用され、ソフトウェアタイミングはシングルチップマイクロコンピューターのリソースを浪費します。
カーディング
言及タイマー、私たちは多かれ少なかれハードウェアタイマーにさらされていますが、時にはリソースの制約のために、必然的にソフトウェアタイマーを使用することが表示されますが、タイマーを開始する必要性を強調しますハードウェアタイマーから、ソフトウェアタイマーデバイスはその基礎から拡張されました。

ハードウェアタイマー

1.一般的なハードウェアタイマーはCPU内に統合されており、ハードウェアタイマーの動作周波数を設定するようにプログラムできる外部ハードウェアタイマーチップを使用できるものもあります。ハードウェアタイマーが動作周波数を設定したら、ハードウェアタイマーをアップロードするだけです。定期的にCPUに割り込み信号を出力します。これをクロック割り込みと呼びます。Linuxカーネルは、クロック割り込みに対応するサービスプログラムを実装しています。このサービスプログラムは、クロック割り込みサービス機能とも呼ばれます。ハードウェアタイマーが定期的にクロック割り込みを生成するためです。 CPUの場合、対応する割り込みサービスルーチンがカーネルによって定期的に呼び出されます。

2.ハードウェア遅延は、タイマー/カウンターチップ、またはマイクロコントローラー内のタイマー/カウンターを使用します。実際、正確な低周波数を取得するのは、水晶振動子の分周です(分周係数はプログラム可能に設定できます)。信号を循環させ、この周期信号(10msなど)を使用して割り込みをトリガーし、10msごとにタイマー割り込みサービスルーチンを呼び出します。タイマー割り込みサービスプログラムにカウント変数を追加すると、任意のタイミングを取得できます。10msが稼働していない場合、マイクロコントローラーは他のプログラムを実行し、10msが稼働しているときに自動的に割り込みサービスルーチンに入り、タイミングタスクを処理します。ソフトウェアの遅延のようにブロックされることはありません。

ソフトウェアタイマー

ソフトウェアタイマーはハードウェアタイマーに基づくタイマーです。何千ものソフトウェアタイマーをハードウェアタイマーでシミュレートできるため、より多くのタイマーを使用する必要があるときにプログラムが制限されることはありません。ハードウェアリソースの不足はソフトウェアの利点です。タイマー、つまり数に制限はありません。ただし、ソフトウェアタイマーはプログラムによって実装されるため、その運用と保守には一定量のCPUリソースを消費する必要があり、精度もハードウェアタイマーよりも劣ります。
一般
的な実装方法は、ハードウェアタイマーを介して固定クロックビートを生成することです。ハードウェアタイマーが中断されるたびに、グローバルタイムスタンプがインクリメントされます。各ソフトウェアタイマーは有効期限を節約します。実行中のすべてのソフトウェアタイマーを定期的にスキャンする必要があります。各有効期限はグローバルクロックマークと比較され、対応するソフトウェアタイマーが期限切れかどうかが判断され、対応するソフトウェアタイマーが期限切れになると、対応するコールバック関数が実行され、タイマーが閉じられます。

割り込みサービスはカーネルモードで実行されるため、グローバル変数を定義するためにvolatileを使用する必要があります。前のブログでもその使用法について説明しました。明らかに3つの状況があります。そうしないと、タイマーが失敗します。といった:

volatile uint32_t whiole_cnt;
void tickCnt_Update(void)
{
    
    
    whiole_cnt++;
}

必要なコールバック関数を設定します:argv、argcはそのパラメーターです:

typedef void callback(void *argv ,uint16_t argc);

実行が開始されると、whiole_cntは1ずつ増加し続け、各ソフトウェアタイマーは有効期限を記録します。whiole_cntが有効期限よりも大きい限り、タイマーが期限切れになったことを意味します。
上記のコードは理解できますが、現時点でタイミングが必要な場合、何を決定する必要がありますか?

typedef struct time
{
    
    
	unsigned char state;//此软件定时的状态
	unsigned char mode;//单次计时还是循环计时
	uint32_t timer_cnt;//此软件计时值与whiole_cnt比较
	uint32_t period;//循环计时时时间间隔
	callback *cb;//回调函数指针
	void *argv;//回调函数参数,根据实际情况可增可减
	uint16_t argc;//回调函数参数,根据实际情况可增可减
}_timer;

これは、単一のソフトウェアタイマーの基本的な状況です。複数のタイマーが必要な場合は、次のように実行でき
ます。2つのタイマー配列を定義します

#define NUM 2
_timer TIME[NUM];

上記は単なるフレームワークであり、後で使用する初期化関数に影響を与えないように、最初に初期化する必要があり
ます。

void TIME_NUM_INIT(void)
{
    
    
	int i=0; 
	for( i ;i<NUM;i++)//循环初始化结构体
	{
    
    
		TIME[i].timer_cnt=0;
		TIME[i].period=0;
		TIME[i].state=stop;
		TIME[i].mode=once;
		TIME[i].cb=NULL;
		TIME[i].argv=NULL;
		TIME[i].argc=0;
	}
}

TIME [i] .stateには3つの状態があります。

typedef enum state
{
    
    
	stop=1,
	runing,
	time_out
}timer_status;

TIME [i] .modeには2つの状態があります。

typedef enum mode
{
    
    
	once=1,
	on_once
}timer_mode;

初期化後、実際のタイマーはまだ表示されていません。次のステップは、構造をカプセル化し、別のタイマーを作成することです。

void TIME_START(uint8_t id,uint32_t timer,uint32_t delay,unsigned char mode,void *cb,void *argv,uint16_t argc)
{
    
    
	TIME[id].timer_cnt=timer;
	TIME[id].period=delay;
	TIME[id].mode=mode;
	TIME[id].cb=cb;
	TIME[id].argv=argv;
	TIME[id].argc=argc;
	TIME[id].state=runing;
}

定義されたNUM値に従って、それらを個別に初期化し、初期化後に実行できます。たとえば、500ミリ秒のソフトウェアタイマーの時間を計り、タイマーの割り込み時間が10ミリ秒の場合、50回の割り込みが必要です。

TIME_START(0,50,0,once,Time ,"12345",15);

時間はコールバック関数ポインタです:
argv、argcはそのパラメータです

int *Time(void *argv,uint16_t argc)
{
    
    
	printf("运行状态:%d",time_state(0));
	printf("%s,%d\r\n",argv,argc);
	
}

初期化が完了したら、ソフトウェアタイマーを常にスキャンする必要があります。主な理由は、タイマー割り込みの変数と比較して、時間が経過しているかどうかを判断することです。スキャンされた関数は次のようになります。

void check_time(void)
{
    
    
	uint16_t i=0;
	  for(i;i<NUM;i++)
			{
    
    
				switch(TIME[i].state)
				{
    
    
					case stop:
						break;
						
					case runing:	
						if(TIME[i].timer_cnt<=whiole_cnt)
						{
    
    
							TIME[i].state=time_out;
							TIME[i].cb(TIME[i].argv,TIME[i].argc);
						}
						break;
						
					case time_out:
						if(TIME[i].mode==once)
						{
    
    
							TIME[i].state=stop;
						}
						else
						{
    
    
							TIME[i].timer_cnt=whiole_cnt+TIME[i].period;
							TIME[i].state=runing;
						}
						break;
					default:break;		
				}
			}
}

main関数でwhile(1)を定義し、常にスキャンして効果を実現します。
主な機能main.c

int main(void)
{
    
    
	
	LED_Init();
	delay_init();
	timer_init();
	usart_init(115200);
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
   
	TIME_NUM_INIT();//初始化

	TIME_START(0,2,2,on_once,Time ,"12345",15);//定时器1
	TIME_START(1,4,2,once,Time ,"6789",30);//定时器2初始化
	
	 while(1)
	 {
    
     
		 check_time();
		 if(TIME[1].state==stop)//如果软件定时器1定时时间到,LED0反转
		 {
    
    
			  LED0=!LED0;
		 }
	 }
}

コールバック関数を忘れないでください!

総括する

モードに関係なく、タイマーが切れた後にコールバック関数が実行されます。関数の定義は次のとおりです。パラメーターポインターargvはvoidポインター型であり、さまざまなタイプのパラメーターを渡すのに便利です。

おすすめ

転載: blog.csdn.net/weixin_42271802/article/details/106062854