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 2024 ExpyDoc