· 

AVR128DB28でArduino(5)

 

進化した8bitAVRマイコン"AVR Dx"をArduinoにしてみる。Atmega328PのArduinoでリソースが足りない場合の選択肢としていんじゃないかなって。タイマーを使ってみる。さて、正確なタイミングで動作させたいことが多い業界にいるのでタイマーで割り込み起こして処理をするってのをやってみる。とはいえ割り込みハンドラ内で条件分岐とかしてしまうと結局条件によってタイミングが変わってしまうので、回避する工夫も考える。個人的に好きなのは関数ポインタの配列を作っておいて、処理を開始する前に全条件分岐の結果による処理内容を配列にぶっこんでおくってやつ。まぁタイマー設定時間に対して余裕がある場合は処理した後に次の条件分岐をやっておくって手もよく使う。
で、UNO R3ではArduinoっぽくない書き方でタイマーを使うことが多かったけど、AVR128DB28はAtmega328Pと比較してタイマーの構成がまぁまぁ大きく変わっていて、調べるのがめんどくさいのでまずはライブラリがないかどうか探してみると、Dx_TimerInterruptってのがあるらしいので、これでチャレンジする。
Arduinoのライブラリマネジャでdx_で検索すると、
って感じで見つかるので、Installする。で、本家サイトはこちら(https://github.com/khoih-prog/Dx_TimerInterrupt/tree/main)
で、使い方がびみょーに変わっていて、ヘッダをインクルードする前にいくつかの条件を#defineするってかんじらしい。本家サイト見ただけではわからんくてカルくはまったけど、Arduino IDEからexampleを開いてみたら、あーそういうことかいってかんじです。
で、こんな感じで
  1. #define USING_FULL_CLOCK true
  2. #define USING_HALF_CLOCK false
  3. #define USING_250KHZ false // Not supported now
  4. #define USE_TIMER_0 false
  5. #define USE_TIMER_1 true
  6. #define USE_TIMER_2 false // Normally used by millis(). Don't use
  7. #define USE_TIMER_3 false
  8. #define USE_TIMER_4 false
  9. #if USE_TIMER_0
  10.   #define CurrentTimer ITimer0
  11. #elif USE_TIMER_1
  12.   #define CurrentTimer ITimer1
  13. #elif USE_TIMER_2
  14.   #define CurrentTimer ITimer2
  15. #elif USE_TIMER_3
  16.   #define CurrentTimer ITimer3
  17. #elif USE_TIMER_4
  18.   #define CurrentTimer ITimer4
  19. #else
  20.   #error You must select one Timer
  21. #endif
  22. #include "Dx_TimerInterrupt.h"
  23. #define LED_PIN (19)
  24. #define TIMER1_INTERVAL_MS 500
  25. volatile bool PIN_STATE;
  26. void TimerHandler1(void){
  27.   digitalWrite(LED_PIN,PIN_STATE);
  28.   PIN_STATE=!PIN_STATE;
  29. }
  30. void setup(){
  31.   pinMode(LED_PIN, OUTPUT);
  32.   
  33.   Serial.begin(115200);
  34.   while (!Serial && millis() < 5000);
  35.   Serial.println(F("\r\nDx_TimerInterrupt test"));
  36.   CurrentTimer.init();
  37.   CurrentTimer.attachInterruptInterval(TIMER1_INTERVAL_MS, TimerHandler1);
  38. }
  39. void loop()
  40. {
  41. }
で、書き込んでみる。
残メモリーがめちゃ多いってのに超感激するのは、ちょっと前にAtmega328PでSRAM使用率90%を超えるようなソフトを作ったからである。で、
なにか変なキャラクターをおもらししているみたいだけど、とりあえずUARTも動いている。で、
、、、あれ、1msもずれている。まぁ気にせずに攻めていく。attachInterruptIntervalだとmsオーダーでしか設定できないけど、attachInterruptにすれば周波数で設定できるので、usオーダーもいけるはず、で、
  1. #define USING_FULL_CLOCK true
  2. #define USING_HALF_CLOCK false
  3. #define USING_250KHZ false // Not supported now
  4. #define USE_TIMER_0 false
  5. #define USE_TIMER_1 true
  6. #define USE_TIMER_2 false // Normally used by millis(). Don't use
  7. #define USE_TIMER_3 false
  8. #define USE_TIMER_4 false
  9. #if USE_TIMER_0
  10.   #define CurrentTimer ITimer0
  11. #elif USE_TIMER_1
  12.   #define CurrentTimer ITimer1
  13. #elif USE_TIMER_2
  14.   #define CurrentTimer ITimer2
  15. #elif USE_TIMER_3
  16.   #define CurrentTimer ITimer3
  17. #elif USE_TIMER_4
  18.   #define CurrentTimer ITimer4
  19. #else
  20.   #error You must select one Timer
  21. #endif
  22. #include "Dx_TimerInterrupt.h"
  23. #define LED_PIN (19)
  24. //#define TIMER1_INTERVAL_MS 500
  25. #define TIMER1_INTERVAL_HZ 20000.0
  26. volatile bool PIN_STATE;
  27. void TimerHandler1(void){
  28.   digitalWrite(LED_PIN,PIN_STATE);
  29.   PIN_STATE=!PIN_STATE;
  30. }
  31. void setup(){
  32.   pinMode(LED_PIN, OUTPUT);
  33.   
  34.   Serial.begin(115200);
  35.   while (!Serial && millis() < 5000);
  36.   Serial.println(F("\r\nDx_TimerInterrupt test"));
  37.   CurrentTimer.init();
  38.   //CurrentTimer.attachInterruptInterval(TIMER1_INTERVAL_MS, TimerHandler1);
  39.   CurrentTimer.attachInterrupt(TIMER1_INTERVAL_HZ, TimerHandler1);
  40. }
  41. void loop()
  42. {
  43. }
ってやって、10kHzの矩形波がでるはず。で、
まぁちゃんとできているようです。では任意のデータ(いわゆるNRZデータ)を送ってみます。タイマー割込ハンドラで都度判定すると、タイミングがおかしくなっちゃうので、タイマー開始前に動きを関数配列にぶっこんでおく。
  1. #define USING_FULL_CLOCK true
  2. #define USING_HALF_CLOCK false
  3. #define USING_250KHZ false // Not supported now
  4. #define USE_TIMER_0 false
  5. #define USE_TIMER_1 true
  6. #define USE_TIMER_2 false // Normally used by millis(). Don't use
  7. #define USE_TIMER_3 false
  8. #define USE_TIMER_4 false
  9. #if USE_TIMER_0
  10.   #define CurrentTimer ITimer0
  11. #elif USE_TIMER_1
  12.   #define CurrentTimer ITimer1
  13. #elif USE_TIMER_2
  14.   #define CurrentTimer ITimer2
  15. #elif USE_TIMER_3
  16.   #define CurrentTimer ITimer3
  17. #elif USE_TIMER_4
  18.   #define CurrentTimer ITimer4
  19. #else
  20.   #error You must select one Timer
  21. #endif
  22. #include "Dx_TimerInterrupt.h"
  23. #define LED_PIN (19)
  24. #define TIMER1_INTERVAL_HZ 20000.0
  25. volatile uint8_t send_pos;
  26. uint8_t send_pattern[]={0xAA,0xAA,0xCC,0x33,0x6A,0x0F,0x9C,0x51,0x55,0x55};
  27. uint8_t send_pattern_len=77;
  28. void (*funcarray[256])(void);
  29. void func0(void){
  30.   digitalWrite(LED_PIN,LOW);
  31. }
  32. void func1(void){
  33.   digitalWrite(LED_PIN,HIGH);
  34. }
  35. void Generate_funcarray(){
  36.   uint8_t i,j,k,sdat;
  37.   uint8_t nbyte,nbit;
  38.   nbyte=send_pattern_len>>3;
  39.   nbit=send_pattern_len-nbyte*8;
  40.   k=0;
  41.   for(i=0;i<nbyte;i++){
  42.     sdat=send_pattern[i];
  43.     for(j=0;j<8;j++){
  44.       if(sdat&0x80){
  45.         funcarray[k]=func1;
  46.       }else{
  47.         funcarray[k]=func0;
  48.       }
  49.       sdat=sdat<<1;
  50.       k=k+1;
  51.     }
  52.   }
  53.   if(nbit>0){
  54.     sdat=send_pattern[i];
  55.     for(j=0;j<nbit;j++){
  56.       if(sdat&0x80){
  57.         funcarray[k]=func1;
  58.       }else{
  59.         funcarray[k]=func0;
  60.       }
  61.       sdat=sdat<<1;
  62.       k=k+1;
  63.     }
  64.   }
  65. }
  66. void TimerHandler1(void){
  67.   funcarray[send_pos]();
  68.   send_pos=send_pos+1;
  69. }
  70. void setup(){
  71.   pinMode(LED_PIN, OUTPUT);
  72.   
  73.   Serial.begin(115200);
  74.   while (!Serial && millis() < 5000);
  75.   Serial.println(F("\r\nDx_TimerInterrupt test"));
  76.   Generate_funcarray();
  77.   send_pos=0;
  78.   CurrentTimer.init();
  79.   CurrentTimer.attachInterrupt(TIMER1_INTERVAL_HZ, TimerHandler1);
  80. }
  81. void loop()
  82. {
  83.   if(send_pos>=send_pattern_len){
  84.     CurrentTimer.pauseTimer();
  85.     delay(100);
  86.     send_pos=0;
  87.     CurrentTimer.setCount(0);
  88.     CurrentTimer.resumeTimer();
  89.   }
  90. }
こんな感じです。タイマ割込ハンドラ内ではあらかじめ決められた関数をCallして、そして関数配列用のカウンタをインクリメントするだけ。で、こうなる。
ちゃんと指定した通りのパタンが出てます。AVR128DB28だとSRAMが16kBもあるので結構長いパタンでも生成できそうなのがイイネ。

FusionPCBはこんな状況。
いつもよりちょっと遅いような。かつて一度だけあるけど、データ不備とかあるとメンドクサイよな。まぁ、すすまんなーと思いながら見ていると、突然、発送しましたメールが来ることもあるのでゆるーく待つ。そもそもこの値段で輸送込みで2週間弱で届くんだから、すごいことよ。