AtmelStudio と Arduino M0 PRO で、USB 通信してみよう

(更新

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() で浮動小数点の出力ができないようです。次回以降への課題としておきます。