ジャンプベクタ方式による疑似マルチタスク
マルチタスク・マルチスレッドの無い組み込みプログラムでの疑似マルチタスクの実装
考え方としてはシンプルで、ジャンプベクタ(関数ポインタ)を定義して、このa_vectの中身を処理の進み具合に合わせて書き換えて行けば処理を進めることが出来る。
ポイントは各関数の中では待たないこと。行うべきことがあれば行ってジャンプベクタを書き換える。何等かの条件成立を待つところでは、条件が成り立つまでは何もせずに抜けて条件が成立したらジャンプベクタを書き換える。
ジャンプベクタ(関数ポインタ)を複数準備すれば、並行して複数の処理を行うことが出来る。
以下はTM1638ボードによるボタン入力を受けて分岐、再度ボタン入力があったらアイドルループに戻る例。
ここでは a_vectの1タスクのみであるが、複数の関数ポインタを定義してそれぞれ異なる処理を持たせることが出来る。ここでは void型引数なしを選択したが、戻り値型、引数はシステムの必要に応じて決定すればよい。
#include <MsTimer2.h>
#include <TM1638plus.h>
#define LED_BUILTIN (13)
#define INTERVAL (5) //Timer Interval
#define CHATCOUNT (10) //チャタリング除去回数
// GPIO I/O pins on the Arduino connected to strobe, clock, data,
//pick on any I/O you want.
#define STROBE_TM 4 // strobe = GPIO connected to strobe line of module
#define CLOCK_TM 6 // clock = GPIO connected to clock line of module
#define DIO_TM 7 // data = GPIO connected to data line of module
bool high_freq = false; //default false,, If using a high freq CPU > ~100 MHZ set to true.
//Constructor object (GPIO STB , GPIO CLOCK , GPIO DIO, use high freq MCU)
TM1638plus tm(STROBE_TM, CLOCK_TM , DIO_TM, high_freq);
volatile uint8_t buttonBuf = 0;
void (*a_vect)(void); //Jump Vector 本稿の主題
//タイマー割りこみハンドラ
//チャタリング除去付きボタン情報更新
void buttonUpdate()
{
static uint8_t cindex = 0;
static uint8_t bArray[CHATCOUNT]={0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55 };
uint8_t btn = tm.readButtons();
bool b = true;
bArray[cindex]=btn;
for( int i = 1; i <CHATCOUNT;i++){
int index= (cindex+i)%CHATCOUNT;
b &= (btn == bArray[index]);
}
if( b ){
buttonBuf = btn;
}
cindex++;
cindex %= CHATCOUNT;
}
//表示をスペースでクリア
void clearDisplay()
{
tm.displayText(" ");
}
//いずれかのボタンが押されるのを待つ
//ボタンが押されたら開放待ちへ
void func11(void)
{
digitalWrite(LED_BUILTIN, (buttonBuf != 0));
/* buttons contains a byte with values of button s8s7s6s5s4s3s2s1
HEX : Switch no : Binary
0x01 : S1 Pressed 0000 0001
0x02 : S2 Pressed 0000 0010
0x04 : S3 Pressed 0000 0100
0x08 : S4 Pressed 0000 1000
0x10 : S5 Pressed 0001 0000
0x20 : S6 Pressed 0010 0000
0x40 : S7 Pressed 0100 0000
0x80 : S8 Pressed 1000 0000
*/
if( buttonBuf != 0 ){
Serial.write("func11 change vect\n");
a_vect = func12; //ジャンプベクタ更新
}
return;
}
//ボタン開放を待つ。
//解放されたらアイドルループへ
void func12(void)
{
if( buttonBuf ==0){
Serial.write("func12 change vect\n");
a_vect = idlefunc; //ジャンプベクタ更新
}
}
//ボタン1押下時処理
void funcS1(void)
{
Serial.write("funcS1\n");
a_vect = func11; //ジャンプベクタ更新
}
//ボタン2押下時処理
void funcS2(void)
{
Serial.write("funcS2\n");
a_vect = func11; //ジャンプベクタ更新
}
//ボタン3押下時処理
void funcS3(void)
{
Serial.write("funcS3\n");
a_vect = func11; //ジャンプベクタ更新
}
//ボタン4押下時処理
void funcS4(void)
{
Serial.write("funcS4\n");
a_vect = func11; //ジャンプベクタ更新
}
//アイドルループ
//ボタン1~4が押されるのを待つ
void idlefunc(void)
{
/* buttons contains a byte with values of button s8s7s6s5s4s3s2s1
HEX : Switch no : Binary
0x01 : S1 Pressed 0000 0001
0x02 : S2 Pressed 0000 0010
0x04 : S3 Pressed 0000 0100
0x08 : S4 Pressed 0000 1000
0x10 : S5 Pressed 0001 0000
0x20 : S6 Pressed 0010 0000
0x40 : S7 Pressed 0100 0000
0x80 : S8 Pressed 1000 0000
*/
if( buttonBuf&0x0f)
{
char tmp[32];
sprintf(tmp, "%02x", buttonBuf);
Serial.write( tmp );
Serial.write( "-idlefunc Change Vect\n" );
}
switch( buttonBuf)
{ //ボタン入力によってジャンプベクタ更新
case 0x01: a_vect = funcS1; break;
case 0x02: a_vect = funcS2; break;
case 0x04: a_vect = funcS3; break;
case 0x08: a_vect = funcS4; break;
}
return;
}
//Arduino Setup
void setup() {
// put your setup code here, to run once:
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
tm.displayBegin();
clearDisplay();
a_vect = idlefunc;
MsTimer2::set(INTERVAL, buttonUpdate); //タイマーハンドラでボタン情報更新
MsTimer2::start();
delay(100);
}
//Arduino loop
//ジャンプベクタ=タスクが複数あれば複数記述
void loop() {
a_vect();
}