Next trial is, enabling USB CDC to connect Arduino M0 PRO to PC. Again, without Arduino IDE!
無事に Arduino M0 PRO を AtmelStudio 7(以下 AS7)で開発できるようになりましたので、今度は USB CDC(Communications Device Class)を使ってパソコンと通信できるようにしてみましょう。ここまでできれば、Arduino IDE でのプログラミングに近い環境が得られます。
前回のブログで、AS7 と ASF(Atmel Software Framework)の基本を学んだので、今日はその理解を前提として進めます。まず最初に、ASF の USB Device (service) cdc を使って一文字ずつの出力を試し、その後、USB Device (service) cdc_stdio を使って、printf() 関数が使えるようにします。
まずは一文字ずつの出力
必要な ASF サービス
今回必要となるのは、以下の ASF service です。まず最初は一文字ずつの出力を試します。
- USB Device (service) cdc
ASF Wizard で USB Device (service) を選ぶとプルダウンメニューが現れるので、cdc を選びます。
ソースコードの変更
Atmel のここのサイトを見ると詳しい説明があります。凝り過ぎで本質の分からないサンプルコードを示すよりは、私はこのような説明の仕方が好きなのですが、さすがにサンプルコードがないとハードルが高い気もします。
そこで、上記サイトの説明を踏襲しつつ、ソースコード(プロジェクト生成時の空の main.c)への変更点を赤字を示していきます。
#include <asf.h> static volatile bool my_flag_autorize_cdc_transfert = false; bool my_callback_cdc_enable(void) { my_flag_autorize_cdc_transfert = true; return true; } void my_callback_cdc_disable(void) { my_flag_autorize_cdc_transfert = false; } int main (void) { system_init(); irq_initialize_vectors(); cpu_irq_enable(); // sleepmgr_init(); // Optional udc_start(); /* Insert application code here, after the board has been initialized. */ for (;;) if (my_flag_autorize_cdc_transfert) udi_cdc_putc('A'); }
これ以外に、2つのファイル修正が必要です。まずは、config/conf_clocks.h からです。赤字の部分が変更箇所です。
/* System clock bus configuration */
# define CONF_CLOCK_CPU_CLOCK_FAILURE_DETECT false
# define CONF_CLOCK_FLASH_WAIT_STATES 2
# define CONF_CLOCK_CPU_DIVIDER SYSTEM_MAIN_CLOCK_DIV_1
# define CONF_CLOCK_APBA_DIVIDER SYSTEM_MAIN_CLOCK_DIV_1
# define CONF_CLOCK_APBB_DIVIDER SYSTEM_MAIN_CLOCK_DIV_1
# define CONF_CLOCK_APBC_DIVIDER SYSTEM_MAIN_CLOCK_DIV_1
さらに
/* SYSTEM_CLOCK_SOURCE_DFLL configuration - Digital Frequency Locked Loop */ # define CONF_CLOCK_DFLL_ENABLE true # define CONF_CLOCK_DFLL_LOOP_MODE SYSTEM_CLOCK_DFLL_LOOP_MODE_USB_RECOVERY # define CONF_CLOCK_DFLL_ON_DEMAND true
この辺りの設定については、ハードウェアのマニュアル(データシート)を勉強しないと辛いのですが、まずは「動けば正解」 🙂 で進めたいと思います。
さらに変更が必要です。
/* Configure GCLK generator 0 (Main Clock) */ # define CONF_CLOCK_GCLK_0_ENABLE true # define CONF_CLOCK_GCLK_0_RUN_IN_STANDBY true # define CONF_CLOCK_GCLK_0_CLOCK_SOURCE SYSTEM_CLOCK_SOURCE_DFLL # define CONF_CLOCK_GCLK_0_PRESCALER 1 # define CONF_CLOCK_GCLK_0_OUTPUT_ENABLE false
続いて、config/conf_usb.h を修正します。conf_usb.h を修正しないと動きませんよ、ということでコンパイル時に警告が出るようになっていますが、以下で修正するので、まずは警告が出ないようにコメントアウトします。
#include "compiler.h"
// #warning You must refill the following definitions with a correct values
もともとある UDI_CDC_ENABLE_EXT と UDI_CDC_DISABLE_EXT のマクロ定義をコメントアウトし、一方で、コメントアウトされている(上記赤字の)行を有効にします。(コメントアウトを外します。)
//! Interface callback definition // #define UDI_CDC_ENABLE_EXT(port) true // #define UDI_CDC_DISABLE_EXT(port) #define UDI_CDC_RX_NOTIFY(port) #define UDI_CDC_TX_EMPTY_NOTIFY(port) #define UDI_CDC_SET_CODING_EXT(port,cfg) #define UDI_CDC_SET_DTR_EXT(port,set) #define UDI_CDC_SET_RTS_EXT(port,set) #define UDI_CDC_ENABLE_EXT(port) my_callback_cdc_enable() extern bool my_callback_cdc_enable(void); #define UDI_CDC_DISABLE_EXT(port) my_callback_cdc_disable() extern void my_callback_cdc_disable(void);
これで修正は終わりです。
動かしてみる
最後に、プロジェクトをビルドして動かしてみましょう。この辺は前回の手順と同様ですが、ステップ実行だと分かりづらいので、デバッグをスタートしたら、まずは Debug → Continue でプログラムを連続動作させてみます。そうしたら、USB ケーブルをもう一本用意し、Arduino M0 PRO の NATIVE USB ポートをパソコンに接続します。Windows であれば COM ポートが認識されますし、Mac OS だと /dev/tty.usbmodem(なんとか)のようなデバイスが認識されますので、Tera Term や screen コマンド等で接続しましょう。(Arduino GUI のシリアルモニタでも良いかも知れません。) なお、USB CDC クラスではボーレート(通信速度)の指定はあまり意味がないようなので、ボーレートの設定はいくつでも大丈夫です。
接続すると、ターミナル画面の上に「A」という文字が連続して表示されれば正しく動いています。
printf() を使えるようにする
このままでは printf() 等ができずに不便ですので、ASF の CDC Standard I/O という機能を使ってみましょう。
必要な ASF サービス
今回必要となるのは、以下の ASF service です。
- USB Device (service) cdc_stdio
ASF Wizard で USB Device (service) を選ぶとプルダウンメニューが現れるので、cdc_stdio を選びます。
ソースコードの変更
main.c を以下のように修正します。上記の「まずは一文字ずつの出力」における修正は、既に入っているものとします。
int main (void) { system_init(); irq_initialize_vectors(); cpu_irq_enable(); // sleepmgr_init(); // Optional udc_start(); stdio_usb_init(); stdio_usb_enable(); /* Insert application code here, after the board has been initialized. */ for (;;) if (my_flag_autorize_cdc_transfert) { // udi_cdc_putc('A'); printf("Hello, world\r\n"); } }
なお、stdio_usb_init() の中で udc_start() を呼んでいるようですが、副作用はないようなので udc_start() の呼び出しは残してあります。これをビルドして実行すると、先ほどの AAAA… の代わりに、Hello, world が表示されるようになります。
なお、これは備忘録ですが、上記のままでは printf() で浮動小数点の出力ができないようです。次回以降への課題としておきます。