2024年8月17日土曜日

BLEを下から見ていく(2)

 

だいぶ一般的になっているBLEについて。すごく概念的なことはあちこちに書いてある。が、下から上まで(むしろ下だけ)知りたいのに、資料少なすぎ。なので自力で調べる無茶な挑戦。第2回=アドバタイズ波形を復号する

さて、前回アドバタイズ波形を観測してAccess Addressっぽいの得られたので、残り全部を数字に変えていく。
今回のBLEモジュールは1Mbpsでデータを送っているはずで、サンプリングが56MS/sなので、56回に1回データを取得すればいいことになる。で、その56カウントのゼロをどこから開始するのかってのが課題となるけど、まぁ業界の人はささっとやっちゃうんだろう。で、方針は
・適当にカウントを初めて、エッジが来たところでカウンタをゼロにする
・カウンタがTOP値になったらカウンタをゼロにする
・カウンタのTOP値は56-1なので(ゼロもカウントするから)、その半分の値int((56-1)/2)で値を保存する※
(※実際はint((56-1)/2)になる配列番号を抽出して、その番号の値を取り出す...numpyらしいやり方)
で、コードはこんな感じ(前回のコードに対して、2値化を反転して、値を抽出するってのを追加した)
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from scipy import signal
  4.  
  5. f_sym=1e6 #
  6. rx=np.load('o_rx_t.npy')
  7. t=np.load('o_t.npy')
  8. #rx=rx[235500:248000]
  9. #t=t[235500:248000]
  10. #rx=rx[208500:228500]
  11. #t=t[208500:228500]
  12. rx=rx[32800:54800]
  13. t=t[32800:54800]
  14. t=t-t[0]
  15. t_0=t[:-1:]
  16. t_1=t[1::]
  17. t_samp=np.mean(t_1-t_0)
  18. f_samp=1/t_samp
  19. print(f_samp/1e6) # should be 56MHz
  20.  
  21. # フィルター係数生成
  22. freq_cutoff=f_sym*2
  23. w_cutoff=freq_cutoff/(f_samp/2)
  24. b,a=signal.butter(1,w_cutoff,'lowpass')
  25.  
  26. # フィルター適用
  27. rx=signal.lfilter(b,a,rx)
  28.  
  29. # demod
  30. rx_t0=rx[:-1:]
  31. rx_t1=rx[1::]
  32. rx_dem=-1*np.angle(rx_t1/rx_t0)
  33. rx_dem=np.append(0,rx_dem)
  34. # normalization
  35. rx_dem=rx_dem/(f_sym/f_samp*np.pi*1)
  36. # digitization
  37. rx_dig=np.where(rx_dem>0,0,1)
  38.  
  39. # counter top value
  40. counter_top=56-1
  41. counter_half=int(counter_top/2)
  42. cnt_list=[]
  43. cnt_list.append(int(0))
  44. for i in range(rx_dig.size-1):
  45.     dig_pres=rx_dig[i+1]
  46.     dig_prev=rx_dig[i]
  47.     if (dig_pres!=dig_prev):
  48.         cnt_list.append(int(0))
  49.     else:
  50.         if (cnt_list[i]<counter_top):
  51.             cnt_list.append(cnt_list[i]+int(1))
  52.         else:
  53.             cnt_list.append(int(0))
  54. cnt=np.array(cnt_list)
  55. smp_pos=np.where(cnt==counter_half)[0]
  56. rd_dig_smp=rx_dig[smp_pos]
  57. print(rd_dig_smp)
  58.  
  59. fig=plt.figure()
  60. fig.add_subplot(3,1,1)
  61. plt.plot(np.real(rx))
  62. plt.plot(np.imag(rx))
  63. fig.add_subplot(3,1,2)
  64. plt.plot(rx_dem)
  65. plt.ylim(-1,1)
  66. fig.add_subplot(3,1,3)
  67. plt.plot(rx_dig)
  68. plt.plot(smp_pos,rd_dig_smp,linestyle='',marker='.')
  69. plt.ylim(-0.5,1.5)
  70. fig.tight_layout()
  71. plt.show()
で、コンソールへの出力はこうなる
[1 1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 1 0 1 0 1 1 0 1 1 1 1 1 0 1 1 0 0
 1 0 0 0 1 0 1 1 1 0 0 0 1 1 0 1 1 0 0 0 1 1 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0
 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 0 1
 1 0 0 1 1 1 0 1 1 1 0 0 0 0 0 1 1 0 0 1 1 1 0 1 0 0 0 1 0 0 1 1 0 1 0 1 0
 0 0 1 0 0 1 0 0 1 1 1 1 0 0 1 0 0 1 0 1 1 0 0 1 0 0 1 0 1 1 1 0 1 0 0 0 0
 1 0 0 0 0 0 1 0 0 0 1 1 1 0 1 1 0 0 0 1 1 1 0 0 0 1 0 0 0 1 0 0 0 1 0 1 1
 0 0 1 1 0 1 1 0 1 1 1 0 1 0 1 1 0 1 1 0 1 0 1 0 0 1 0 1 1 0 0 1 1 0 0 1 0
 1 0 1 0 0 1 1 1 1 0 1 0 0 0 1 0 0 1 0 0 0 0 1 0 1 1 1 1 1 0 0 0 1 1 1 0 1
 1 1 1 1 0 0 0 1 1 0 1 1 0 0 0 1 0 1 0 0 1 0 1 1 0 1 1 0 1 0 1 0 1 0 0 0 1
 1 0 0 1 0 0 1 0 1 0 1 0 0 0 1 1 0 1 0 1 0 0]
で、グラフはこうなる
一番下のグラフが2値化した波形と、そのどの時点を値として取り込んだのかを示していて、拡大すると、こうなっている
まぁうまくいってそう。で、これをさらに8bitごとにLSBファーストで16進数化すればいいんだけど、どこまでがプリアンブルなのかとかを自動検出するコードを書くのがメンドクサイ(今回はプリアンブルとAccess Addressの相関が最大となる位置を探すだけなんだけど、それすらメンドクサイおじさんなのです)ので、この後は人力でやる。ちなみに8bitのことを1byteっていうことがあるけど、Bluetoothの世界では1octっていうらしい。byteはシステム依存で1byte=8bitとは限らないかららしい。
で、人力でしようとしていたんだけど、どうにもこうにも仕様と合わない、、、クッソー何か間違っているかもしれない。
上図の赤線部がどうもPDU Typeらしい。というのも、右側はD1R32のソフトをiBeaconに書き換えた後なので、ADV_SCAN_IND=0110になっているんだと思う。で、左はSCAN_REQ=0011なのかなと。が、それ以外がちっともわからん。新しいモジュールが来たら考える。、、、さっそく挫折しそう߹ㅁ߹)スマソ

あぁ夏休みが終わるぅ(鬱)。明日から年間最長の平常運転期間が始まるので、精神的にはしんどい。ともかく巨大地震が来なくてよかった。

2024年8月16日金曜日

BLEを下から見ていく(1)

だいぶ一般的になっているBLEについて。すごく概念的なことはあちこちに書いてある。が、下から上まで(むしろ下だけ)知りたいのに、資料少なすぎ。なので自力で調べる無茶な挑戦。第1回=アドバタイズを出して、それを観測する。

本当はありとあらゆる資料を読むのが正解だとはわかっている。が、手を動かさないとぜんっぜん頭に入らない初老のおじさんなのです(ていうか、だいたいの資料が英語で、で、英語がわからんだけ๐·°(৹˃̵﹏˂̵৹)°·๐)。
まずは、WeMos D1R32を使ってアドバタイズを出してみる。WeMos D1R32については、こちら(https://sunday-engineer.jimdofree.com/2023/09/03/wemos-d1r32%E3%83%A1%E3%83%A2/)に取り上げている。で、Exampleからこいつ
を開いて書き込む。
そすっと、
こんな感じで、なにかペリフェラルとして動作していそうな感じがする。で、どんなのを出しているのかをAdalm-Plutoで観測する。Adalm-Plutoについては以前に触れてみたことがある(
ADALM-PLUTO(購入からセットアップまで)
ADALM-PLUTO(pythonで)(1)
ADALM-PLUTO(MATLABで動作確認)
ADALM-PLUTO(pythonで)(2)
ADALM-PLUTO(pythonで)(3)
)
で、まぁ気持ちを新たに、うちはLinuxなので、こちら(https://pysdr.org/content/pluto.html)を参考にしてインストール。Windowsだとこのへん(https://wiki.analog.com/resources/tools-software/linux-software/pyadi-iio)かな。通常やるっぽいんだけど、SSHでAdalm-Plutoに入って対応周波数の拡大はやっておく。で、こんなコードで動かしてみる。
  1. import numpy as np
  2. import adi
  3. import matplotlib.pyplot as plt
  4. sample_rate = 56e6 # Hz
  5. center_freq = 2402e6 # Hz
  6. num_samps = 1000000 # number of samples per call to rx()
  7. sdr = adi.Pluto("ip:192.168.2.1")
  8. sdr.sample_rate = int(sample_rate)
  9. # Config Rx
  10. sdr.rx_lo = int(center_freq)
  11. #sdr.rx_rf_bandwidth = int(sample_rate)
  12. sdr.rx_rf_bandwidth = int(2e6)
  13. sdr.rx_buffer_size = num_samps
  14. sdr.gain_control_mode_chan0 = 'manual'
  15. sdr.rx_hardwaregain_chan0 = 0.0 # dB, increase to increase the receive gain, but be careful not to saturate the ADC
  16. # Clear buffer just to be safe
  17. for i in range (0, 10):
  18.     raw_data = sdr.rx()
  19. # Receive samples
  20. rx_samples = sdr.rx()
  21. print(rx_samples)
  22. t=np.linspace(0,1/sample_rate*num_samps,num_samps)
  23. np.save('o_t',t)
  24. np.save('o_rx_t',rx_samples)
  25. # Calculate power spectral density (frequency domain version of signal)
  26. psd = np.abs(np.fft.fftshift(np.fft.fft(rx_samples)))**2
  27. psd_dB = 10*np.log10(psd)
  28. f = np.linspace(sample_rate/-2, sample_rate/2, len(psd))
  29. # Plot time domain
  30. plt.figure(0)
  31. plt.plot(np.real(rx_samples))
  32. plt.plot(np.imag(rx_samples))
  33. plt.xlabel("Time")
  34. # Plot freq domain
  35. plt.figure(1)
  36. plt.plot(f/1e6, psd_dB)
  37. plt.xlabel("Frequency [MHz]")
  38. plt.ylabel("PSD")
  39. plt.show()
まぁ、ちょうど出しているところが引っかかってくれるかどうかで、何度かトライしてみて、こんな感じになる(見たいところを拡大済み)。
搬送波周波数+/-周波数変位幅にピークが出ないのはGFSKの性質なんだろう(局初周波数=搬送波周波数で直行復調済み波形での計算結果なので0が搬送波周波数)。
で、読み取ったデータを保存するようにしてあるので、これを解析してみる。
解析は、かつてFSKを調べるときに使ったコード(https://sunday-engineer.jimdofree.com/2020/07/22/%E3%83%91%E3%83%AB%E3%82%B9%E3%81%A7%E7%9B%B4%E4%BA%A4%E5%BE%A9%E8%AA%BF-5-numpy%E3%81%A7%E3%82%84%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B/)を参考に、こんなかんじで、
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from scipy import signal
  4. f_sym=1e6 #
  5. rx=np.load('o_rx_t.npy')
  6. t=np.load('o_t.npy')
  7. rx=rx[208500:228500]
  8. t=t[208500:228500]
  9. t=t-t[0]
  10. t_0=t[:-1:]
  11. t_1=t[1::]
  12. t_samp=np.mean(t_1-t_0)
  13. f_samp=1/t_samp
  14. print(f_samp/1e6) # should be 56MHz
  15. # フィルター係数生成
  16. freq_cutoff=f_sym*2
  17. w_cutoff=freq_cutoff/(f_samp/2)
  18. b,a=signal.butter(1,w_cutoff,'lowpass')
  19.  
  20. # フィルター適用
  21. rx=signal.lfilter(b,a,rx)
  22. # demod
  23. rx_t0=rx[:-1:]
  24. rx_t1=rx[1::]
  25. rx_dem=-1*np.angle(rx_t1/rx_t0)
  26. rx_dem=np.append(0,rx_dem)
  27. # normalization
  28. rx_dem=rx_dem/(f_sym/f_samp*np.pi*1)
  29. fig=plt.figure()
  30. fig.add_subplot(2,1,1)
  31. plt.plot(t*1e6,np.real(rx))
  32. plt.plot(t*1e6,np.imag(rx))
  33. fig.add_subplot(2,1,2)
  34. plt.plot(rx_dem)
  35. plt.ylim(-1,1)
  36. plt.show()
ってすると、
はて、これがなんなのか、、、まず復調波形が見にくいので2値化する。
# digitization
rx_dig=np.where(rx_dem>0,1,0)
ってのを追加してプロットするわけだ。すると
こうなる。これを拡大して波形からいってきっとNRZで読んでいけばいいんだろうとして読んでいく、、、が、何となく前から気にはなっていたけど、上のpythonコードで復調すると、正負が逆になっちゃうので、それも考慮してって、、、
ってな感じで、パワポでビットレートと思われる周期で平行線を書きまくってそれにあわせて数字を書くって方法で読み取っていったら、図中のようなコードが読み取れて、これってのが
Bluetooth Core Specificationのこれにあたるっぽいぞ。よしきた。ちなみにビット順序がひっくり返ってるやんってのは、これ
によるわけだ(と思う)。仕様書上はMSBを左に書いてあるけど、LSBファーストで送信するぜってことだと思う。
ところで、いつもイラっとするアルファベット言語圏の省略造語について、Bluetooth Core Specificationに"LIST OF ACRONYMS AND ABBREVIATIONS"っていう項があるので、ギリ許すことにする。絶望的にたくさんあるがこいつらをおぼえておかんといかん。

ところで、この実験に使っているWeMos D1R32はだいぶ古いものなので、最近のものをいくつか発注した。これ(https://akizukidenshi.com/catalog/g/g117454/)と、これ(https://akizukidenshi.com/catalog/g/g117341/)。どちらも大した値段ではないけど、無駄遣いになる可能性もあるっちゃーある。、、、投資ってそんなもんだよね。それほど大したことのない台風で秋月もお休みしたようで、まだぜんっぜん発送手配が進んでないっぽいけど。
しかし、BLEの通信を下から紐解いていくのはまぁまぁしんどい。このテーマの続編を書けるかどうか、挫折するかもしれないって心配。だいたいの人がスマホやらRaspberryPiとかで情報をとるってのはやってるんだけど、その情報がどうやって飛んできたのかってことまで掘っている記事はないんよね。今の世の中そんなものなのかな。

記事と全く関係ないけど、FusionPCBに発注したP板は昨夜OCSに持ち込まれたらしい。順調です。P板設計がごく一般的な大人のたしなみになる日も近い、、、なわけないか。

2024年8月12日月曜日

AVR128DB28のGPIOについて

 

AVR128DB28のGPIOの制御について毎度のことながら調べてみる。

以前の調査はこちら(https://sunday-engineer.jimdofree.com/2022/06/19/arduino%E3%81%AEgpio%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/)。Arduino的な書き方をすると、
  1. void setup(void){
  2.     pinMode(17,OUTPUT);
  3.     pinMode(18,OUTPUT);
  4.     pinMode(19,OUTPUT);
  5. }
  6. void loop(void){
  7.     digitalWrite(17,HIGH);
  8.     digitalWrite(18,HIGH);
  9.     digitalWrite(19,HIGH);
  10.     digitalWrite(17,LOW);
  11.     digitalWrite(18,LOW);
  12.     digitalWrite(19,LOW);
  13. }
まぁ、こうなる。そすっと、
で、以前Arduino UNO R3でやった時より、立ち上がり立下りとも3us遅い、、、なんやそれ。まぁ気を取り直して。データシートによると、
これだけのことで出力を制御できるっぽい。で、
  1. void setup(void){
  2.   PORTD.DIR|=0xE0;
  3. }
  4. void loop(void){
  5.   PORTD.OUT|=0xE0;
  6.   PORTD.OUT&=~0xE0;
  7. }
ってすると、
となるので、おおって思うけど、それでも実はAtmega328Pより遅い。で、AVR128DB28にはVirtualPortって機能があるので、こちらもやってみる。データシート読んでも何かさっぱりわからんのだけど、どうもシングルサイクルビットアクセスができるようにしたものっぽい。まぁやってみるだよ。で、
  1. void setup(void){
  2.   VPORTD.DIR|=0xE0;
  3. }
  4. void loop(void){
  5.   VPORTD.OUT|=0xE0;
  6.   VPORTD.OUT&=~0xE0;
  7. }
こういうコード。PORTDがVPORTDになっただけ。で、
立ち上がりエッジから立ち上がりエッジを比較すると、
まぁこんなもんか。AVR DXは公式で24MHzまでクロックを早くできるので、その点ではAtmega(上限20MHz)よりは有利なんだろうと思うことにする。そして、実は上の結果は48MHzのATSAMD21G18よりも早い。まぁいんじゃない?

AVR128DB28でのお遊びは一旦このくらいにしておこうと思う。次回はP板が入荷してから。いやしかし、まぁ良いデバイスだと思う。いろんなイミでちょうど良いデバイス。

あぁーこの休みに色々勉強せんといかんと思いつつもなかなかやる気が出ない。せめて軽い内容でブログを更新してみたってかんじ。ところで、仕事があるときは日々どんどん就寝時間が遅くなって土日で寝まくってなんとかリセットするって感じだけど、長期連休ではどんどん就寝時間が早まっていくのは、やっぱ、齢のせいだよなー

2024年8月11日日曜日

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週間弱で届くんだから、すごいことよ。

2024年8月10日土曜日

AVR128DB28でArduino(4)

進化した8bitAVRマイコン"AVR Dx"をArduinoにしてみる。Atmega328PのArduinoでリソースが足りない場合の選択肢としていんじゃないかなって。FusionPCBに発注する。

さて、今回は本当に発注してしまう。回路なんちゃらとかAWなんちゃらとかはやってない(仕事じゃないので)。ミスがあっても完全に自己責任(そりゃそうだ)。まぁ環境には優しくないけど、連射したっていいじゃないか、にんげんだもの。
まずは発注用のデータ(ガーバーってやつ)を作成していく。KiCADからちゃちゃっとできちゃう。
で、ドリルファイルも作成。
で、できたファイルたちをzipにまとめて、こんな感じに。
で、FusionPCB(https://www.fusionpcb.jp/)にGo。
今すぐ発注を押す。
ガーバーファイルを追加を押して、作成したzipファイルを選択する。
基板サイズが設計した通りならまぁ間違いない。で、念のためビューアで見てみる。両面表示するとややこしいので、片面ずつ見てみる。
まぁあってそう。後はカートに追加ってして、普通の通販サイトみたいにユーザー登録とか支払いとかすればいい。通常はP板に5ドル、送料が約20ドルってなんやねんみたいな金額なんだけど、8/27までは送料半額キャンペーン中で送料がお安くなっている。今回は11.95ドルで約1800円(一枚当たり180円)よ。日頃のちょっとした贅沢に替えてPCBの発注ってのも大人のたしなみってやつよ。

回路検討からPCB発注まで2日弱。今回は検討内容が少なかったから特に短かった(送料半額なので、最悪やっちまってもいいや的なゆるい気持ちってのもある)。、、、失敗してないといいけどな(¯―¯٥)

部品費を見積もったらPCB含めて1828円や。秋月の送料を入れると1878円。大量生産じゃないけど、普通のUNO R3と対決しても、まぁまぁコスト勝負できそうやな(ちゃんと動けば)。、、、てか10個全部作ったら18780円もかかるのか。たぶん1個しか作らんな。