Arduino M0 Proのシリアル通信
はじめに
前回はAtmelStudioでArduino M0 Pro上のLEDを点滅させました。
今回は、シリアル入出力とprintfが使えることが確認できたので、その方法を書いておこうと思います。
この記事の実行環境は下記のとおりです。
ホストOS:Windows7 x64
開発ソフト:AtmelStudioバージョン 7.0.634
ターゲット:Arduino M0 Proファームウェアバージョン 2.10
シリアル通信
ASFの公式ドキュメントQuick Start Guide for SERCOM USART - Basic を調べると、SERCOM USARTというシリアル通信のドライバが見つかりました。
サンプルコードも書いてあります!
しかし、サンプルコードをそのまま貼り付けてもEDBG_CDC_~の定義名全てが見当たらず、コンパイルができません。
調べると、公式のフォーラムAtmel Communityの記事SAMD21 Serial Port Setup and Hello World | AVR Freaksにヒントがありました。
記事抜粋
config_usart.mux_setting = USART_RX_1_TX_0_XCK_1; config_usart.pinmux_pad0 = PINMUX_PA16D_SERCOM3_PAD0; config_usart.pinmux_pad1 = PINMUX_PA17D_SERCOM3_PAD1; config_usart.pinmux_pad2 = PINMUX_UNUSED; config_usart.pinmux_pad3 = PINMUX_UNUSED;
pinmux_pad0~3には、TXまたはRXの接続を書くようです。使わないものには未使用の定義名PINMUX_UNUSEDが書かれています。
また、mux_settingはpinmux_pad0~3がTXなのかRXなのかを書くようです。公式ドキュメントSERCOM USART MUX Settingsに設定の詳細が書かれてありました。
pinmux_pad0~3の接続先を探るべく、Arduino M0 Proの回路図を読んでみます。
回路図によると、EDBGの配線は下記のような接続になっています。
TX:PB22_SERCOM5_PAD2
RX:PB23_SERCOM5_PAD3
また、Arduinoではお馴染み、Pin0とPin1もシリアル入出力が割り当てられています。
自前のアプリケーションでシリアル通信を使うのはPin0とPin1を、デバッグ用途にEDBG側を使えば良さそうですね。
TX:PINMUX_PA11D_SERCOM2_PAD3;
RX:PINMUX_PA10D_SERCOM2_PAD2;
コード補完で上記に近い定数名が出ます。
これで、シリアルターミナルを別に立ち上げ、プログラムを実行してみるとシリアルターミナルに文字が出てきました!!
Pin0とPin1を使ったシリアル入出力のサンプル
#include <asf.h> #include <compiler.h> #include <board.h> #include <conf_board.h> #include <port.h> //TX is PA11D_SERCOM2_PAD3 //RX is PA10D_SERCOM2_PAD2 static struct usart_module usart_instance; static void configure_console(void) { struct usart_config usart_conf; usart_get_config_defaults(&usart_conf); usart_conf.mux_setting = USART_RX_3_TX_2_XCK_3;//http://asf.atmel.com/docs/latest/samd21/html/asfdoc_sam0_sercom_usart_mux_settings.html usart_conf.pinmux_pad0 = PINMUX_UNUSED; usart_conf.pinmux_pad1 = PINMUX_UNUSED; usart_conf.pinmux_pad2 = PINMUX_PA11D_SERCOM2_PAD3; usart_conf.pinmux_pad3 = PINMUX_PA10D_SERCOM2_PAD2; usart_conf.baudrate = 9600; while (usart_init(&usart_instance, SERCOM2, &usart_conf) != STATUS_OK) { } usart_enable(&usart_instance); } int main (void) { system_init(); configure_console(); /* Insert application code here, after the board has been initialized. */ struct port_config pin_conf; port_get_config_defaults(&pin_conf); pin_conf.direction = PORT_PIN_DIR_OUTPUT; port_pin_set_config(PIN_PA17, &pin_conf); //http://asf.atmel.com/docs/3.16.0/sam0.drivers.tc.unit_test.samd21_xplained_pro/html/asfdoc_sam0_sercom_usart_basic_use_case.html uint8_t string[] = "Hello\r\n"; usart_write_buffer_wait(&usart_instance, string, sizeof(string)); uint16_t temp; while (1) { if (usart_read_wait(&usart_instance, &temp) == STATUS_OK) { while (usart_write_wait(&usart_instance, temp) != STATUS_OK) { } } } }
EDBGを使ったシリアル入出力のサンプル
#include <asf.h> #include <compiler.h> #include <board.h> #include <conf_board.h> #include <port.h> //TX is PB22_SERCOM5_PAD2 //RX is PB23_SERCOM5_PAD3 static struct usart_module usart_instance; static void configure_console(void) { struct usart_config usart_conf; usart_get_config_defaults(&usart_conf); usart_conf.mux_setting = USART_RX_3_TX_2_XCK_3;//http://asf.atmel.com/docs/latest/samd21/html/asfdoc_sam0_sercom_usart_mux_settings.html usart_conf.pinmux_pad0 = PINMUX_UNUSED; usart_conf.pinmux_pad1 = PINMUX_UNUSED; usart_conf.pinmux_pad2 = PINMUX_PB22D_SERCOM5_PAD2;//PINMUX_PA11D_SERCOM2_PAD3; usart_conf.pinmux_pad3 = PINMUX_PB23D_SERCOM5_PAD3;//PINMUX_PA10D_SERCOM2_PAD2; usart_conf.baudrate = 9600; while (usart_init(&usart_instance, SERCOM5/*SERCOM2*/, &usart_conf) != STATUS_OK) { } usart_enable(&usart_instance); } int main (void) { system_init(); configure_console(); /* Insert application code here, after the board has been initialized. */ struct port_config pin_conf; port_get_config_defaults(&pin_conf); pin_conf.direction = PORT_PIN_DIR_OUTPUT; port_pin_set_config(PIN_PA17, &pin_conf); //http://asf.atmel.com/docs/3.16.0/sam0.drivers.tc.unit_test.samd21_xplained_pro/html/asfdoc_sam0_sercom_usart_basic_use_case.html uint8_t string[] = "Hello\r\n"; usart_write_buffer_wait(&usart_instance, string, sizeof(string)); uint16_t temp; while (1) { if (usart_read_wait(&usart_instance, &temp) == STATUS_OK) { while (usart_write_wait(&usart_instance, temp) != STATUS_OK) { } } } }
上記ふたつのサンプルを実行すれば、シリアルターミナル側から文字を送ると、Arduino M0 Proで受信内容を返送します。
さらに、公式ドキュメントQuick Start Guide for SERCOM USART - Callback にはコールバックを使ったシリアル通信のサンプルコードもありました。
シリアル受信・送信にコールバック関数を使うサンプル
#include <asf.h> #include <compiler.h> #include <board.h> #include <conf_board.h> #include <port.h> #define MAX_RX_BUFFER_LENGTH 5 volatile uint8_t rx_buffer[MAX_RX_BUFFER_LENGTH]; //TX is PB22_SERCOM5_PAD2 //RX is PB23_SERCOM5_PAD3 static struct usart_module usart_instance; static void configure_console(void) { struct usart_config usart_conf; usart_get_config_defaults(&usart_conf); usart_conf.mux_setting = USART_RX_3_TX_2_XCK_3;//http://asf.atmel.com/docs/latest/samd21/html/asfdoc_sam0_sercom_usart_mux_settings.html usart_conf.pinmux_pad0 = PINMUX_UNUSED; usart_conf.pinmux_pad1 = PINMUX_UNUSED; usart_conf.pinmux_pad2 = PINMUX_PB22D_SERCOM5_PAD2;//PINMUX_PA11D_SERCOM2_PAD3; usart_conf.pinmux_pad3 = PINMUX_PB23D_SERCOM5_PAD3;//PINMUX_PA10D_SERCOM2_PAD2; usart_conf.baudrate = 9600; while (usart_init(&usart_instance, SERCOM5/*SERCOM2*/, &usart_conf) != STATUS_OK) { } usart_enable(&usart_instance); } void usart_read_callback(struct usart_module *const usart_module) { usart_write_buffer_job(&usart_instance, (uint8_t *)rx_buffer, MAX_RX_BUFFER_LENGTH); } void usart_write_callback(struct usart_module *const usart_module) { port_pin_toggle_output_level(PIN_PA17); } void configure_usart_callbacks(void) { usart_register_callback(&usart_instance, usart_write_callback, USART_CALLBACK_BUFFER_TRANSMITTED); usart_register_callback(&usart_instance, usart_read_callback, USART_CALLBACK_BUFFER_RECEIVED); usart_enable_callback(&usart_instance, USART_CALLBACK_BUFFER_TRANSMITTED); usart_enable_callback(&usart_instance, USART_CALLBACK_BUFFER_RECEIVED); } int main (void) { system_init(); /* Insert application code here, after the board has been initialized. */ struct port_config pin_conf; port_get_config_defaults(&pin_conf); pin_conf.direction = PORT_PIN_DIR_OUTPUT; port_pin_set_config(PIN_PA17, &pin_conf); configure_console(); configure_usart_callbacks(); system_interrupt_enable_global(); uint8_t string[] = "Hello World!\r\n"; usart_write_buffer_wait(&usart_instance, string, sizeof(string)); while (true) { usart_read_buffer_job(&usart_instance, (uint8_t *)rx_buffer, MAX_RX_BUFFER_LENGTH); } }
printfを使う方法
ログを出すときなど、printfで出力を整形したいということは多いはず。
printfの出力先をUARTにつなぐことができるらしいとは知っていますが、自分ではやったことがありません。
Areduino M0 Proのprintf出力について調べてみると、ASFを使えば簡単に接合できそうなのでやってみました。
atmel sensor using printf - Stack Overflowの手順に従って、ASF WizardでStandard serial I/OとUSARTをプロジェクトに追加します。
configure_consoleを以下のように書き換えます。
printfを使ってEDBG側にシリアル出力を行うサンプル
#include <asf.h> #include <compiler.h> #include <board.h> #include <conf_board.h> #include <port.h> //TX is PB22_SERCOM5_PAD2 //RX is PB23_SERCOM5_PAD3 static struct usart_module usart_instance; static void configure_console(void) { struct usart_config usart_conf; usart_get_config_defaults(&usart_conf); usart_conf.mux_setting = USART_RX_3_TX_2_XCK_3;//http://asf.atmel.com/docs/latest/samd21/html/asfdoc_sam0_sercom_usart_mux_settings.html usart_conf.pinmux_pad0 = PINMUX_UNUSED; usart_conf.pinmux_pad1 = PINMUX_UNUSED; usart_conf.pinmux_pad2 = PINMUX_PB22D_SERCOM5_PAD2;//PINMUX_PA11D_SERCOM2_PAD3; usart_conf.pinmux_pad3 = PINMUX_PB23D_SERCOM5_PAD3;//PINMUX_PA10D_SERCOM2_PAD2; usart_conf.baudrate = 9600; stdio_serial_init(&usart_instance, SERCOM5, &usart_conf); usart_enable(&usart_instance); } int main (void) { system_init(); configure_console(); /* Insert application code here, after the board has been initialized. */ struct port_config pin_conf; port_get_config_defaults(&pin_conf); pin_conf.direction = PORT_PIN_DIR_OUTPUT; port_pin_set_config(PIN_PA17, &pin_conf); int cnt = 0; printf("Hello\r\n"); while(1){ printf("%d\r\n", cnt); port_pin_set_output_level(PIN_PA17, true); pseudo_usleep(1000); port_pin_set_output_level(PIN_PA17, false); pseudo_usleep(1000); cnt++; } }
1000msec毎にLEDの点滅を繰り返しつつ、点滅の回数をシリアルで出力します。
さいごに
ASFを使えば、案外簡単にシリアル入出力とprintf利用ができでとても驚きました。
それは、公式のASFドキュメントが親切丁寧であり、フォーラムに情報が多数寄せられるためだと思っています。ありがたい!
今後はUSB Host/Device機能を使って、キーボードやボリュームコントローラを作ってみたいです。難しいかなぁ。