PSoC

このページの目次

PSoCで周期処理を使用した擬似的なマルチタスクの実現

リアルタイムOSを使用しない擬似的なマルチタスクの実現

マイコンでいろいろな処理を同時に進めるときには、リアルタイムOSを使うことが多くなってきています。 但し、リアルタイムOSがない環境や、リアルタイムOSを使用するほどでもない状況では、それぞれの処理を時分割処理に刻んで並行処理する手法が使用される場合があります。

例えば、一定の周期で処理したい処理が3つあったとします。それらを仮にタスクA,B,Cとします。そうすると、以下のように一定の周期でタスクを実行できます。タスクそれぞれを別な処理のステートマシンにする事で、外部からは3つの異なる処理が同時に処理できます。

処理周期に対して一回あたりの全処理処理時間が終わった後の待機中の期間をアイドルタスクと呼び、この部分の割合をCPU処理余裕度と考えることができると思います。以下の図は、それを模式的にあらわしたもので時間軸は横方向、左から右に流れているつもりで書いています。

timeslice.gif

状態遷移

この処理を実現する場合、以下のアクティビティ図のような状態遷移で実現できると思います。

pseudomultitask_activity.gif

処理実装をしましょう

カウンターを準備します

いろいろな実現方法があるかと思いますが、私はCounter8を使用しました。まずは、Device EditorからCounter8を導入し、モジュール名は「Counter8_1」としました。

Selected User ModulesにCounter8のアイコンが出ていると思いますので、右クリックして「Place」を選択しましょう。これで配置されます。

PlaceCounter8.gif

Counter8のパラメータ設定

Counter8のパラメータは以下のように設定してみました。

User Module ParametersValue
ClockVC3
ClockSync?Sync to SysClk?
EnableHigh
CompareOut?None
TerminalCountOut?None
Period0
CompareValue0
CompareType?Less Than Or Equal
InterruptType?Compare True
InterruptAPIEnable
IntDispatchMode?ActiveStatus?
InvertEnable?Normal

ClockはVC3にしました。VC3の設定については後述します。

割り込み処理生成の有効化

次に、ツールバーのProject→Settingsタブ→Enable Interrupt generation controlのチェックを入れてください。

ProjectSettings.gif

Global ResourceのVC3の設定

Counterなどの周辺回路では、クロック源としてSysClk?をはじめとして各種使用可能です。PSoCでは、VC1〜VC3というクロック源があり、SysClk?を接続して、分周したものを周辺回路に供給する事が可能です。

今回は、十分遅いタイミングを生成してテストしたかったのでSysClk?をVC1、VC2、VC3を使用して分周しました。 とりあえず、以下のように設定してみました。

GlobalResource_VCx.gif

この設定では、元々のSysClk?(24MHz)が分周器VC1〜3に接続されています。

CounterClock_VC3.gif

したがって、Counter8には、366.2[Hz]が供給されることになります。

APIを生成し、カウンター割り込み処理を追加します

Config→Generate Applicationを選択して、Counter8のAPIを生成します。 次に、Counter8割り込みの割り込み処理を作成しましょう。割り込み処理はC言語で記載します。

static B Cyclic_WaitFlag;

#pragma interrupt_handler Counter8_1_INT
void Counter8_1_INT(void){
	Cyclic_WaitFlag = 0;
}

割り込み処理で、スタティック変数Cyclic_WaitFlag?を0にするよう記述します。また、コンパイラに割り込み処理である事を通知するため、#pragma interrupt_handlerで関数を登録します。

Counter8の割り込みサービスルーチンから、先ほど作成した関数がコールされるように設定する

libフォルダに「Counter8_1INT.asm」というファイルが生成されていると思います(モジュール名の設定によってはプリフィックス部の名称が変わります)。 これをエディタで開き、先ほどのC言語による割り込み処理ルーチンがコールされるよう変更します。

_Counter8_1_ISR:

   ;@PSoC_UserCode_BODY@ (Do not change this line.)
   ;---------------------------------------------------
   ; Insert your custom code below this banner
   ;---------------------------------------------------
   ;   NOTE: interrupt service routines must preserve
   ;   the values of the A and X CPU registers.

	ljmp	_Counter8_1_INT  ; ここを追加する!!!

   ;---------------------------------------------------
   ; Insert your custom code above this banner
   ;---------------------------------------------------
   ;@PSoC_UserCode_END@ (Do not change this line.)

   reti

PSoCの自動生成ファイルは、PSoC Designerで勝手に書き換えられてしまいますが、上記の

   ;---------------------------------------------------
   ; Insert your custom code below this banner
   ;---------------------------------------------------

から

   ;---------------------------------------------------
   ; Insert your custom code above this banner
   ;---------------------------------------------------

のバナーの間はユーザーが変更しても変更内容は保持されるようです。 ここでは、C言語の割り込み処理ルーチンは、Counter8_1_INT()でしたので、上記のように追加して下さい。C言語関数はアセンブラ展開された時先頭に「_」(アンダーバー)が付くようですのでアンダーバー付きで指定します。

周期毎に全てのタスク処理をコールする呼び元を作成する

処理の追加・削除が行いやすいよう、関数テーブルに関数を追加すると、その関数全てがテーブルの定義順にシーケンシャルに実行できるようにしてみます。今回は全て同じ周期でコールされますが、処理によってスケジューリングできるように工夫しても面白そうですね。

また、このスタイルでプログラムを作成する時には、絶対処理中で長い待ち処理(例えばfor文ループなど)を入れてはいけません。待ちもひとつの状態として処理し、他の処理の実行を阻害しないようにします。

// void function(void)関数型
typedef void (*VFP)(void);

void DEBUG_MessageOutput(void);
void DEBUG_EchoBack(void);

const VFP Task_FuncTable[] = {
	DEBUG_MessageOutput,
	DEBUG_EchoBack
};

/** タスクを実行する
 * @note この関数を実行する度にスケジューリングしている全関数をシーケンシャルにコールする
 */
void TASK_Execute(void){
	int i;

	for(i=0;i<ARRAY_LEN(Task_FuncTable);i++){
		(*(Task_FuncTable[i]))();
	}
}

この例では周期処理として、DEBUG_MessageOutput関数とDEBUG_EchoBack?という関数を登録しています。次は、これらの関数を一定周期でコールする処理を作ります。

メイン処理に周期処理を実装

では、メイン処理から定期的に各処理の呼び元をコールするようループを作成し、ループの最後で次の処理周期フラグが立つのを待ちます。

	// 周期処理開始
	while(1){

		// 周期タスクの実行
		TASK_Execute();

		// 次の周期タスク実行タイミングまでの待ち処理
		{
			B wait_flag;

			while(1){
				Counter8_1_DisableInt();
				wait_flag = Cyclic_WaitFlag;
				Counter8_1_EnableInt();

				if(wait_flag == 0){
					Counter8_1_DisableInt();
					Cyclic_WaitFlag = 1;
					Counter8_1_EnableInt();
					break;
				}
			}
		}
	}

Cyclic_WaitFlag?変数は割り込み内部でセットされる変数ですので、変数へのアクセス時はCounter8割り込みを停止させています。

実行結果

上記の例では、Debug用のふたつの関数を登録しました。それらの関数の役割は以下のようになっています。

DEBUG_MessageOutput()関数デバッグメッセージをUSBUARTから出力
DEBUG_EchoBack?()関数USBUARTからデータを入力したらメッセージを出力

したがって実行し、適当にTeraTermでキーインすると以下のような表示が出ます。

Count : 206(Hello)[RAM:R A M!]123
Count : 207(Hello)[RAM:R A M!]123
Count : 208(Hello)[RAM:R A M!]123

Input messageDUMP{0D 00 00 00 00 00 00 00 00 00 }
Count : 209(Hello)[RAM:R A M!]123
Count : 210(Hello)[RAM:R A M!]123
Count : 211(Hello)[RAM:R A M!]123
Count : 212(Hello)[RAM:R A M!]123

Input messageDUMP{0D 00 00 00 00 00 00 00 00 00 }
Count : 213(Hello)[RAM:R A M!]123
Count : 214(Hello)[RAM:R A M!]123

Input messageDUMP{0D 00 00 00 00 00 00 00 00 00 }
Count : 215(Hello)[RAM:R A M!]123

プロジェクトファイル一式ダウンロード

ソースコード一式はこちらでダウンロードして下さい。 なお、CY8C24794-24LFXI(PastelMagic?さんのところのボード等)用です。 vcフォルダは、そーすをVisualStudio2005の開発環境で編集する目的で作成したソリューションファイルです。

Access count : 8015 Today's access count : 1


添付ファイル: filepsoc_multitask_usbuart_sample.zip 697件 [詳細] fileCounterClock_VC3.gif 533件 [詳細] fileGlobalResource_VCx.gif 515件 [詳細] fileProjectSettings.gif 475件 [詳細] filePlaceCounter8.gif 483件 [詳細] filepseudomultitask_activity.gif 597件 [詳細] filetimeslice.gif 506件 [詳細]

トップ   編集 凍結解除 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2007-01-03 (水) 09:33:55 (4638d)