○シーケンス制御みたいな物を書いてみる


本来なら ラダー言語 で行う所をC言語で書いてみます

装置の制御を行うプログラムでは、通常のパソコン上でのプログラムとは違う考え方が必要になってきます
たとえば、”複数の処理を平行する、沢山のタイマーを使う、処理の延滞は許されない”をパソコン上でのプログラムの考え方で書くと、コードが煩雑になり、混沌としてくるでしょう。
そのため、装置制御のプログラムでは、サイクル・カウンタ・モード・割り込み、という考え方を使います

サイクルとはプログラム全体をループで囲っておき無限ループ状にします、
ループ内での処理で入力待ち、待機、停止は一切行なわせず、常にループを回り続けさせることにより
複数の処理を平行に行うことが可能になります

カウンタとは定期的にカウントアップする変数であり、それを使い時間を管理します。
時間の測定を開始するには、カウンタ変数をリセットし、その変数の値を周期的に監視することにより時間を測定します。
通常は正確にカウントアップするためにタイマ割り込みを利用するのですが、パソコン上でのアプリケーションでは難しいため今回はサイクルによりカウントアップします

モードとは現在の制御の進行状況を表すもので、プログラムの実行行の位置とは関係なく進行状況を表します
複数のモード変数を持つことにより、複数の制御の進行状況を管理することができます。

割り込みとは実行中の処理を一時中断し、新たに発生した処理を優先して行なわせるため機能です
しかしハードウエアに直接アクセスが難しいパソコン上でのアプリケーションのため今回は使用しません




■次のような処理を想定してみます





▼上記処理をモードで区切ります



モード0でCTRLキーを監視し、押された場合次のモードに移行します。
モード1で画面表示し次のモードに移行します。
モード2でカウンタをリセットし次のモードに移行します。
モード3でカウンタを監視し時間を測定します。”カウンタ>5秒”が成立した時点で次のモードに移行します。
モード4で画面表示し次のモードに移行します。
モード5でALTキーを監視し、押された場合次のモードに移行します。
モード6で画面表示しモード0に移行します。



■並列処理を確認するためにもう一つ処理を追加する




▼処理をモードで区切ります



モード0でカウンタをリセットし次のモードに移行します。
モード1でカウンタを監視し時間を測定します。”カウンタ>1秒”が成立した時点で次のモードに移行します。
モード2で画面表示しモード0に移行します。



■上記処理をサイクルとカウンタとモードを使いプログラムにしてみます



#include <stdio.h>
#include <windows.h>

unsigned int sq1_timer;
unsigned int sq1_mode;

void sequence1(){
	if(0==sq1_mode){
		if(GetAsyncKeyState (VK_CONTROL)){
			//CTRLキーが押されている
			sq1_mode=1;
		}
	}else if(1==sq1_mode){
		printf("CTRLキーが押されました\n");
		sq1_mode=2;
	}else if(2==sq1_mode){
		sq1_timer=0;//タイマーの初期化
		sq1_mode=3;
	}else if(3==sq1_mode){
		if(500<=sq1_timer){
			sq1_mode=4;//5秒以上経過していた場合
		}
	}else if(4==sq1_mode){
		printf("CTRLキーが押されてから約5秒経過しました\n");
		sq1_mode=5;
	}else if(5==sq1_mode){
		if(GetAsyncKeyState (VK_MENU)){
			//ALTキーが押されている
			sq1_mode=6;
		}
	}else if(6==sq1_mode){
		printf("ALTキーが押されました\n");
		sq1_mode=0;
	}
	return;
}


unsigned int sq2_timer;
unsigned int sq2_mode;

void sequence2(){
	if(0==sq2_mode){
		sq2_timer=0;//タイマーの初期化
		sq2_mode=1;
	}else if(1==sq2_mode){
		if(100<=sq2_timer){
			sq2_mode=2;//1秒以上経過していた場合
		}
	}else if(2==sq2_mode){
		printf("A\n");
		sq2_mode=0;
	}
	return;
}


int main(){
	while(1){
		Sleep(10);//10ms スキャンタイミング

		//1サイクルタイミングごとにカウントアップするカウンタ
		if(sq1_timer<(unsigned int)-1) sq1_timer++;	
		if(sq2_timer<(unsigned int)-1) sq2_timer++;
		
		sequence1();
		sequence2();
	}
	return 0;
}




▼実行結果


A
A
CTRLキーが押されました
A
A
A
A
A
CTRLキーが押されてから約5秒経過しました
A
ALTキーが押されました
A
A
A
A
CTRLキーが押されました
A
A
A
A
A
CTRLキーが押されてから約5秒経過しました
A


二つの処理が遅延なく並列して動作していることが確認できます
このような考え方でプログラムを作成することにより沢山の処理を並列して実行することが出来ます



■カウンタでの時間測定について


定期的にカウントアップする変数があるとして、その変数により時間を測定するには、


▼方法1

測定開始とともにカウンタをリセットして、測定終了時にカウンタの値を見て経過時間を確認する。
カウンタがオーバフロー時にはカウントアップを停止する。
測定可能時間はカウンタがオーバーフローするまでの時間

測定のイメージとしては、測定開始時点でカウンタがリセットされるため、
測定終了時点のカウンタの値がそのまま経過時間になります。

測定開始 0x00
測定終了 0x10
経過時間 0x10


▼方法2

測定開始とともにカウンタの値を取得して、測定終了時にカウンタの値を引いて経過時間を確認する。
カウンタがオーバーフローしてもカウントアップを続ける。
測定可能時間はカウンタの値を取得した時点からカウンタが一周するまでの時間
方法1と、カウンタの測定可能時間は同一になります。

測定のイメージとしては測定終了時間から測定開始時間を引くことにより経過時間が算出されます。

測定開始 0x10
測定終了 0x20
経過時間 0x10=0x20-0x10

計測途中でカウンタがオーバーフローしたとしても正しい時間が算出できます。

測定開始 0xFE
測定終了 0x0E
経過時間 0x10=0x0E-0xFE


方法1は全体をシンプルに設計することができ、方法2は一つのカウンタで複数の時間を計測することができます。


▲トップページ > プログラミングの実験