FreeRTOSでタイマーを使いたいときに
ソフトウェアタイマが使えるようですが
Tick Rate Hzがデフォルトだと1000で
xTimerCreateの引数を最小の1にしても1msタイマが最小になるかと思います。
今回10usタイマを作りたいのですが、
Tick Rate Hzを100000にすると、全く動作しなくなりました(何が起こってるのかすら分かってない)
//Tick Rate Hz = 1000 (default) // 1 = 1ms; // 10 = 10ms; // 100 = 100ms; // 1000 = 1000ms; //Tick Rate Hz = 10000 // 1 = 0.1ms; // 10 = 1ms; // 100 = 10ms; // 1000 = 100ms; xTimer = xTimerCreate("timer", 1, pdTRUE, (void*)0, vTimerCallback);
いろいろネットで検索していると
ソフトウェアタイマで10usは厳しいようなので
ハードウェアタイマを使うべきとあります。
そこで、今作成しているスレッドにE2StudioのStackのところでタイマを追加してやってみました。
結果ですが、
10usタイマのcallbackが呼ばれて、カウント動作はしているのですが
なぜか不定期にタイマー値が戻ってしまいます。
現在CAN の通信モニタを作っており、CANで受信したデータに10us単位のタイムスタンプをつけて、
EthernetのUDP通信でパソコンに送信しています。
下記はそのパソコンで受信した結果です。赤チェック部分のタイムスタンプがおかしい部分です。少し巻き戻った値になってしまっています。
マイコンC言語自体は15年ほどやっておりハードウェアタイマ割り込みなどは理解しているつもりですが
FreeRTOSやOSの知識はほとんどなく、今回はじめてFreeRTOSでの開発をしています。
単純にFSP + FreeRTOSで10usタイマを使用する場合、この使い方で良いのかが気になっております。
Stackの設定画面で追加したスレッドに、タイマ General PWMを追加して使うのは正しいやりかたなのでしょうか?
注意点等ありますでしょうか?
また原因が分かればご教授いただけると助かります。
callback_10us実行の時、優先度がFreeRTOSより上ならFreeRTOSはCPUが使えない状態になるので雰囲気的にはその瞬間、FreeRTOSから見たらCPUがなくなった状態と同義かなと・・・なお、callback_10us関数でtmr_10usにセマフォを使ってもうまくいかない気がします。そんなことするより、tmr_10us変数の値をFreeRTOSのタスクで参照する部分の先頭でローカル変数に値を一時コピーしてコピーの値に対して処理をしたらいい気がします…
10msタイマのコールバック関数
今、気付いたことが。
下記、タイムスタンプ部分(k_tmr_10usの値の16進数表示)
0x0003FF77
の次がなぜか
0x000312FF
になっている。
16bitから繰り上がるときにおかしくなってる。
なんでだろう。。。
きちんと繰り上がるときと、繰り上がらないときがある。
タイマ割り込みの使い方の問題ではない気がしてきました。
関係ないかもしれませんが参照部分を割り込み禁止にしてみては?
昔、「タイマ割り込み内でタイマ変数カウントアップ」「タイマ変数は32bit」とした時、
タイマ変数を参照する部分のアセンブラでは16bitずつコピーしていて、上位16bitを先にコピーするせいで、処理中に割り込みが入るとタイマ変数の値を正しく参照できないことがありました。
割り込み優先度でタイマ割り込みが待たされる時があるか、そもそも、FreeRTOSの割り込みハンドラで多重割り込みが禁止されていると思います。
ありがとうございます。
まだ解決はしてないのですが、
参照部分に問題があることが たった今分かりました。
k_tmr_10usの中身自体は正しくインクリメントされていますが
参照部分(Ethernetパケットにセットする部分)で何か問題が起こってるようです。
CANの受信スレッドと
Ethernetパケットへセットするスレッドが異なるせいか。。。
ちょっと初歩的なミスもありそうな気がしてドキドキしています。。。
もう少し調べてみます。
申し訳ございません!
Ethernetのパケットにセットする部分(1byteずつに分解) でやらかしておりました。
誤:
cString[4] = (uint8_t)((rcv_can_frame.ts_10us >> 24) & 0xFF);
cString[5] = (uint8_t)((rcv_can_frame.ts_10us >> 18) & 0xFF);
cString[6] = (uint8_t)((rcv_can_frame.ts_10us >> 8) & 0xFF);
cString[7] = (uint8_t)(rcv_can_frame.ts_10us & 0xFF);
正:
cString[5] = (uint8_t)((rcv_can_frame.ts_10us >> 16) & 0xFF);
解決しました。。。
10usタイマ、問題なく作動しました。
お騒がせしました
質問が少し変わってしまいますが
ハードウェアタイマ割り込み10usはOS管理外なので
OS管理のタスクは若干処理が遅れる
という考え方で合ってますでしょうか?
また、ハードウェアタイマ割り込み内でインクリメントしている変数を
タスク内で参照する場合に、セマフォを使って排他制御したほうがよいのでしょうか?
ハードウェアタイマ割り込みハンドラー
callback_10us()
{
take(セマフォ)
tmr_10us++
give(セマフォ)
}
参照部分
cString[4] = (uint8_t)((tmr_10us >> 24) & 0xFF);
cString[5] = (uint8_t)((tmr_10us >> 16) & 0xFF);
cString[6] = (uint8_t)((tmr_10us >> 8) & 0xFF);
cString[7] = (uint8_t)(tmr_10us & 0xFF);
callback_10us実行の時、優先度がFreeRTOSより上ならFreeRTOSはCPUが使えない状態になるので雰囲気的にはその瞬間、FreeRTOSから見たらCPUがなくなった状態と同義かなと・・・なお、callback_10us関数でtmr_10usにセマフォを使ってもうまくいかない気がします。そんなことするより、tmr_10us変数の値をFreeRTOSのタスクで参照する部分の先頭でローカル変数に値を一時コピーしてコピーの値に対して処理をしたらいい気がします。もし、外部の割り込みで複雑なデータ型がありアトミックな命令で処理が終わらない場合はその割り込みを禁止し、ローカルコピー、割り込み解除、ローカルコピー値に対してい処理をするみたい私なら実装します。