編集履歴一覧に戻る
pastaのアイコン画像

pasta が 2022年09月25日19時02分51秒 に編集

初版

タイトルの変更

+

spresenseを利用した超音波距離計

タグの変更

+

spresense

+

SPRESENSE

+

ADC

メイン画像の変更

メイン画像が設定されました

記事種類の変更

+

製作品

ライセンスの変更

+

(GPL-3.0+) GNU General Public License, version 3

本文の変更

+

spresenseを利用して同相フェーズドアレイ超音波距離計を作りました。 超音波を物体に向けて送信して、反射波が返ってくるまでの時間を計測するToF(Time of Flight)方式です。 (FMCW方式を試そうとしたところ、送信機と受信機のクロストークが酷かったため諦めました。) 距離はUSBシリアルから出力された波形から判別します。 ![キャプションを入力できます](https://camo.elchika.com/e45baabdf8a700001868b33611d2d12abfa6a271/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f34653563626135302d343464342d343237342d383164622d3330356539383661326564332f37336434643232632d613132372d346431362d393039352d333432623936376638343530/) # 構成 ![構成](https://camo.elchika.com/4e99f1c4ef1c4523dfce743f71435f97f96215d9/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f34653563626135302d343464342d343237342d383164622d3330356539383661326564332f34303563656661642d313633352d346330392d623330332d613935316263393032653538/) # 使用した部品など(一部抜けがあります) - SPRESENSE - Arduino UNO互換ボード - LM324 - 超音波スピーカー・マイク https://akizukidenshi.com/catalog/g/gI-00120/ # 送信機 Arduino UNOを用いて4つの送信機を40kHz 10Vp-pで駆動しています。 各送信機に別々のGPIOピンが割り当てられているので、位相はプログラマブルに制御することが可能です。 # 受信機 ## 受信側アンプの回路図 1段目で100倍,2段目で10倍の増幅をしています。 入力インピーダンスは10 kΩです。 ## 送信機のコード ```C #include <Arduino.h> void setup() { // put your setup code here, to run once: DDRD = 0b11111111; DDRC = 0b00000001; for (;;) { PORTC=0b00000001; for (int i = 0; i < 100; i++) { PORTD = 0b10101010; // PORTD = 0b00100000; _delay_us(12); PORTD = 0b01010101; // PORTD = 0b00010000; _delay_us(12); } PORTC=0b00000000; PORTD = 0; _delay_ms(100); } } ``` ## SPRESENSEのソースコード トリガーが信号が入ると、ADCを開始して、FIFOバッファーの値を一定ビット数分だけglobal配列にコピーして、内容をprintfします。 超音波スピーカー以外にもADCの測定テストに用いることができます。 ```C #include <sdk/config.h> #include <nuttx/arch.h> ​ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/boardctl.h> ​ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <debug.h> #include <arch/chip/pin.h> #include <arch/board/board.h> ​ #ifdef CONFIG_CXD56_ADC #include <arch/chip/scu.h> #include <arch/chip/adc.h> #endif ​ /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ ​ #ifndef CONFIG_EXAMPLES_ADC_MONITOR_DEVPATH #define CONFIG_EXAMPLES_ADC_MONITOR_DEVPATH "/dev/hpadc0" #endif ​ #ifndef CONFIG_EXAMPLES_ADC_MONITOR_BUFSIZE #define CONFIG_EXAMPLES_ADC_MONITOR_BUFSIZE 10000 #endif bool signal_triggered = false; /**************************************************************************** * Name: myapp_main ****************************************************************************/ static int gpio_handler(int irq, FAR void *context, FAR void *arg) { board_gpio_int(PIN_UART2_RXD, false); // printf("interrupt occured"); fflush(stdout); signal_triggered = true; board_gpio_write(PIN_UART2_TXD, 1); ​ return OK; } ​ int max(int a, int b) { return a > b ? a : b; } int min(int a, int b) { return a < b ? a : b; } ​ char wave_buffer[CONFIG_EXAMPLES_ADC_MONITOR_BUFSIZE]; int main(int argc, FAR char *argv[]) { board_gpio_config(PIN_UART2_TXD, 0, false, true, PIN_FLOAT); board_gpio_config(PIN_UART2_RXD, 0, true, false, PIN_PULLDOWN); board_gpio_intconfig(PIN_UART2_RXD, INT_RISING_EDGE, 0, gpio_handler); board_gpio_int(PIN_UART2_RXD, true); // printf("interrupt initialized"); fflush(stdout); // board_gpio_int(PIN_UART2_RXD, true) int ret; int errval = 0; int fd; char *buftop = &wave_buffer; ​ fd = open(CONFIG_EXAMPLES_ADC_MONITOR_DEVPATH, O_RDONLY); if (fd < 0) { printf("open %s failed: %d\n", CONFIG_EXAMPLES_ADC_MONITOR_DEVPATH, errno); errval = 4; goto errout; } ​ /* SCU FIFO overwrite */ ​ ret = ioctl(fd, SCUIOC_SETFIFOMODE, 1); if (ret < 0) { errval = errno; printf("ioctl(SETFIFOMODE) failed: %d\n", errval); goto errout_with_dev; } ​ while (!signal_triggered) { __asm__("nop"); } /* Start A/D conversion */ ret = ioctl(fd, ANIOC_CXD56_START, 0); if (ret < 0) { errval = errno; printf("ioctl(START) failed: %d\n", errval); goto errout_with_dev; } ​ int total_bytes = 0; for (;;) { ssize_t nbytes; nbytes = read(fd, buftop + total_bytes, min(CONFIG_EXAMPLES_ADC_MONITOR_BUFSIZE - total_bytes, CONFIG_CXD56_HPADC0_FSIZE)); if (nbytes < 0) { errval = errno; printf("read failed:%d\n", errval); goto errout_with_dev; } total_bytes += nbytes; if (total_bytes >= CONFIG_EXAMPLES_ADC_MONITOR_BUFSIZE) { break; } } /* Stop A/D conversion */ ​ ret = ioctl(fd, ANIOC_CXD56_STOP, 0); if (ret < 0) { int errcode = errno; printf("ioctl(STOP) failed: %d\n", errcode); } ​ close(fd); char *start = buftop; char *end = buftop + CONFIG_EXAMPLES_ADC_MONITOR_BUFSIZE; while (1) { int16_t data = (int16_t)(*(uint16_t *)(start)); start += sizeof(uint16_t); if (start >= end) { break; } printf("%d ", data); } printf("\n"); ​ board_gpio_write(PIN_UART2_TXD, 0); return OK; ​ /* Error exits */ ​ errout_with_dev: close(fd); ​ errout: printf("ADC monitor example terminating!\n"); if (buftop) { free(buftop); buftop = NULL; } fflush(stdout); return errval; } ``` SDK Configは"ADC"をベースに以下の部分を変更しました。 ![SDK Config](https://camo.elchika.com/fa6b98c2ad6d6ad8b64cd85bc47551bd0bcac560/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f34653563626135302d343464342d343237342d383164622d3330356539383661326564332f66306264356237382d373663662d346130342d393261312d396534616263623032343135/) ## 観測波形 サンプリング周波数は256 kHzです。 2m程度離れたガラス窓に向けて計測した波形です。 ![キャプションを入力できます](https://camo.elchika.com/63646717e72d8473ce21a229b72fc932d774de93/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f34653563626135302d343464342d343237342d383164622d3330356539383661326564332f61663463356634312d346133322d343637352d393938392d326236613261316230306462/) # 今後の課題 - フェーズドアレイの位相を制御して2次元スキャンを行う - 高電圧で超音波モジュールを駆動する - カメラの画像を用いて点群データにテクスチャマッピングを行う - microSDに深度画像を保存する - HPADC1に別の受信機をつないでダイバーシティ受信を実装する # 感想 ## GPIOについて - GPIO割り込みの速度は超音波の計測には十分だった - GPIO出力に関して、位相制御を行うには遅延が大きすぎた - ASMPワーカーから呼び出せるのか呼び出せないのかドキュメントに明記されていなかった ## ADCについて - read関数を呼ぶ間隔が大きすぎるとFIFOバッファーがオーバーフローしてデータが一部失われてしまう