中華STM32基板のBlackpillを味見しています.
前回は、ADCを2ch運用しましたが、サンプル周波数は500Hzと遅かったです.
今回は、サンプル周波数を20kHzまで上げます.
無理矢理で40kHzまで行けるかな?って感触ですが、20kで止めておきました.律速は割り込みroutineでbuffer copyとかする部分ですので高速化の余地はありそうですが、さりとて100kHzまで上げられる気はしません.
なお、CPU STM32F401のADC性能では1MHz近いサンプルが可能のようです.
ーーーー
やったことのリスト
・サンプリング周波数は20kHz
・連続する1000ptsをメモリにストア
・同1000ptsをCDC(USB COM)でPCへ送信する
・サンプリング周期はTIM3の出力する50uSecで決める (つまりhardware自走)
・ADC入力はIN1とIN9の2本 (2ch scan)
・ADCは1トリガで2サンプル (2ch scan)
・DMAを使う
・DMAはIN1サンプル→DMA→IN9サンプル→DMAという動作をする
・すなわち、TIM3→ADC(IN1)→DMA→ADC(IN9)→DMAがhardwareで自走する
・ADC完了をIRQで知る
・同IRQで1000pts x 2ch分のbufferに積む
・同IRQでbuffer fullになったらUSB送信を起動
・USB転送終了をIRQで知る
・USB busyの最中はADC IRQでbufferに積まない (buffer衝突防止)
・ADCは12bitなので、1サンプルあたりASCIIの3bytesをbufferに積む
・ゆえに1USB送信あたり1000x2x3=6000bytes
・USB FULL SPEED(12Mbps)で6000bytes送信するのにover head込みで50mSecぐらいかかっている(実測)
ーーーー
前回も触れましたが、いろんな疑問点について
Q:STM32CubeIDEとArduinoIDEのどちらを使う?
A:STM32CubeIDE
ArduinoIDEだとUSB関連のexampleが見当たらず使いませんでした.
STMCubeIDEのCDC exampleを雛形にして改造しました.
Q:TIM3+ADC+DMAを起動するやりかた
A:HAL_Delay(3000);
HAL_TIM_Base_Start_IT(&htim3);
HAL_ADC_Start_DMA(&hadc1, &buffer[0], 2);
main()の最初の方の各種Initの後で1回だけこれをやると、あとはhardwareが自走してくれます.詳細は
前回を参照.
HAL_Delay(3000)は必須です.USB接続の直後は不安定なので3秒待ちます.
Q:ADC終了割り込みはどれを使えばいいんだ?
A:HAL_ADC_ConvCpltCallback()
Q:UFB終了割り込みはどれを使えばいいんだ?
A:CDC_TransmitCplt_FS()
USB COMのUSBの転送終了を知るにはこれが良いようです.
USB転送終了までbufferへのADC dataの上書きを禁止するためにこの割り込みを使います.
Q:DMAのdata widthは8/16/32bitのどれにしようか?
A:32bitにした
Q:USB転送終了calbackでやること
A:busy flugを下げる
USB転送が終わると自動的にこの関数がcallされます.
CDC_TransmitCplt_FS(){
uint8_t result = USBD_OK;
UNUSED(Buf);
UNUSED(Len);
UNUSED(epnum);
usb_busy = 0; ←これを追加
return result;
}
Q:HAL_ADC_ConvCpltCallback()で何をやる?
A:改造の中心部がここ、main()の上の辺りにでも追加、weak宣言されてる
#define PTS 1000 連続サンプル数1000
#define CH 2 ADC 2ch scan
#define BUFSIZE (3*PTS*CH) USB bufferサイズ6000bytes
int usb_busy = 0; USB busyフラグ
static uint32_t buffer[4]; ADC dataを都度格納する場所
static uint8_t usbbuf[BUFSIZE+100]; USB buffer確保
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
static int N = 0;
if(usb_busy==0){
char c[15];
sprintf(c,"%03X",buffer[0]); IN1 ASCII変換
usbbuf[N++] = c[0]; strcat()は遅いので使うのやめた
usbbuf[N++] = c[1];
usbbuf[N++] = c[2];
sprintf(c,"%03X",buffer[1]); IN9 ASCII変換
usbbuf[N++] = c[0];
usbbuf[N++] = c[1];
usbbuf[N++] = c[2];
}
if(N>=BUFSIZE){ 6000bytes積んだらUSB起動
usbbuf[BUFSIZE] = '\n';
N=0;
usb_busy=1; USB転送はmain()で起動する
}
}
追記:sprintf()も遅いので自作のitoa()に変えたら50kHzサンプルで動きました.
Q:main()でUSB転送を起動するには?
A:while()でCDC_Transmit_FS()をcallする
while (1) {
if(usb_busy==1) CDC_Transmit_FS(&usbbuf[0],BUFSIZE+1);
}
ーーーー
STM32CubeMXの設定について.
ADCの設定
DMAの設定
TIM3の設定
clk=84MHzなので、84M÷84÷50=20kHz
BlackPillを味見するのはここまでにしとうございます.
簡易オシロを作ろうかな.ESP32のほうがいろいろと高速かもしれない.
かしこ