AVR用スレッドの実装
B3 suno
親 ide syu-he-
目次
概要
スレッドの実装の問題点・解決策
実装
デモ
まとめ
背景・目的
貧弱なプロセッサでも使い場所はまだまだある
簡単な処理にはスペック的に十分
安い
複数の機能を実装すると機能がまざる
それぞれの機能をスレッドとして実装すれば
スレッド単位で再利用できる
計算量が多い処理は割り込みで処理できない
貧弱なプロセッサで動作するスレッドを実装する
スレッドとは
プログラムの実行の単位
(ASCII24デジタル用語辞典)
CPUは複数の処理を同時には行えない
並行して実行できる
メモリ空間を共有している
頻繁に処理内容を切り換えて実行している
スレッド固有の情報
プログラムカウンタ – 実行中のプログラム
スタックレジスタ – 現在のスタックの位置
汎用レジスタ
使ったチップ
Atmel AT90s2313
速さ 4MHz
プログラム領域 2KB
メモリ 128byte SDRAM
汎用レジスタ 8bit * 32
開発環境
WinAVR(avr-gcc)
AVR用のCコンパイラ
AVRStudio
AVR用アセンブラ+シミュレータ
スレッドの実装の問題点
とにかくメモリが小さい
メモリ128 byte、汎用レジスタが32本
1スレッドごとに汎用レジスタ保存用に32 byte
1スレッドごとにスタックレジスタを保存
1スレッドごとにスタック領域1つ
動的なメモリ確保はできない
そんな余裕なし
解決策
コンパイル時にスレッド数を決める
静的にメモリを確保
スタック領域
スレッド情報保存用構造体
実装
実装方針
スレッドの初期化
コンテキストスイッチ
動かしてみたコード
実装方針
スタックレジスタ
汎用レジスタ
静的に予約したメモリ空間に退避
プログラムカウンタ
静的に予約したメモリ空間に退避
スタック上に保存
割り込みハンドラからのreturnで元のコードに戻る
スレッド切り換えのタイミング
内部のカウンタによる割り込み時
スレッドの初期化
char createThread
(void (*entrypoint)(char),char arg)
第一引数に指定した関数が第二引数のchar
を引数に呼び出される
すぐ実行されるわけではなく、スレッドの切り
換えはタイマーで行われる
成功で1,失敗で0
スレッドの初期化の手順
スレッド情報の初期化
実行中のスレッドに追加
スレッド情報の初期化
Threadinfo構造体の初期化
スタックの末尾にエントリーポイントのアドレ
スをセット
Threadinfo
汎用レジスタの24番にエントリーポイントの関数
の引数をセット
スタックレジスタをスタックの後ろから5バイト目に
セット
エンディアンが逆なのに注意
汎用レジスタ
関数への引数(r24)
stackarea
スタック
次のスレッドの構造体
スタックレジスタ
エントリーポイント
実行中のスレッドに追加
新しいThreadinfo構造体を実行中のスレッ
ドのリストに加える
汎用レジスタ
汎用レジスタ
汎用レジスタ
汎用レジスタ
汎用レジスタ
スレッド切り換え
SIGNAL (SIG_OVERFLOW1)
内部のタイマーのオーバーフローでスレッド切
り換え
スレッド切り替えの手順
r26-r31の値を静的領域に退避
currentinfoをcurrentinfo->nextに
スタックレジスタの入れ替え
r0-r27を入れ替え
r28-r31を入れ替え
r26-r31の値を静的領域に退避
レジスタの入れ替えの作業用レジスタとし
てr26-31を利用する
上書きしてはいけないのでメモリに退避す
る
レジスタ
char tmpregisterarea[6]
r26-31
r26-31
currentinfoをcurrentinfo->nextに
currentinfoは現在実行中のスレッド構造
体を指す
nextは次に実行するスレッドの構造体を指す
汎用レジスタ
Threadinfo *currentinfo
汎用レジスタ
スタックレジスタの入れ替え
現在のスタックレジスタの値と次のスレッド
のスタックレジスタの値を入れ替える
r26,27はこれ以降入れ替え作業用には使
わないのでレジスタに戻す
スタックレジスタ
レジスタ
(*currentinfo)
char tmpregisterarea[6]
r26-31
threads[n]->stack
r0-r27を入れ替え
次のスレッドのレジスタ保存領域からr0の
値をr28にロード
現在のr0の値をレジスタ保存領域にストア
r28にロードされている次のスレッドのr0の
値をr0にコピー
レジスタ
*currentinfo
r28-r31を入れ替え
次のスレッドのr28をレジスタ保存領域から
ロードしてpushする
退避していた前のスレッドのr28をロードし、
レジスタ保存領域にストア
繰り返す
まとめてpop レジスタ
スタック
(*currentinfo)
char tmpregisterarea[6]
r28-31
作ったもの
LEDを点滅させるプログラム
プライマリスレッドと別のスレッド2つがLED
を点滅させる
計3つのLEDが点滅
実際動かしたコード
LEDを点滅させる
引数で操作する
PORTBのピンを
指定する
void ticklight(char pin){
int loop1=0;
for(;;){
loop1++
if(!loop){
if(bit_is_clear(PORTB, pin)) {
sbi(PORTB, port);//off
} else {
cbi(PORTB, pin);//on
}
}
}
}
}
スレッド生成コード
引数を変えることでそれぞれの担当する
LEDを決めている
int main (void){
ioinit ();
sei ();
createThread((void (*)(char))tickLight,0);
createThread((void (*)(char))tickLight,1);
tickLight(2);
}
デモ
実機
LEDが点滅します
シミュレータ
まとめ
at90s2313上でスレッドの実装を行った
LEDを点滅させるプログラムを並行させて
動かした
感想
電気回路は難しい
未だによくわからない
低スペックだとスレッドをいっぱい作るのは
厳しい
メモリが超ギリギリ
プログラムサイズもちょっと大きい
割り込みでできるところは割り込みですべき
© Copyright 2026 ExpyDoc