読者です 読者をやめる 読者になる 読者になる

不確定特異点

広く深く、ところどころ超深く

STM32F4 Discovery の開発環境 (4)

前回で一通りの開発環境は整いましたが、さらに一歩進んで UART を動かしたいと思います。UART があれば printf デバッグができるようになるので、観測性が一段と向上します。

UART でエコーバック

UART の通信設定は次のようにしました。

PC から STM32F4 に送ったデータを受信して、そのまま PC に返すという、簡単なエコーバックをやってみます。リセットハンドラで GPIOD と UART2 の設定を行った後、無限ループに入ります。実際のエコーバック処理は割り込み処理の中で行いました。

基板の接続はこんな感じ。USB シリアル変換基板にFT232RL USBシリアル変換モジュールというものを使ってます。そして STM32F4 も FT232RL も共に Mac OS X 上で動く仮想マシンLinux)の中から接続してます。リアルな HW を制御するのに、全部、仮想環境から制御している、というのが面白いですねw

f:id:tanakahx:20150823234829j:plain

UART エコーバックのコードです。リセットハンドラに直書きという手抜き。

void Reset_Handler()
{
    SystemInit();
    
    EnablePll();

    SystemCoreClockUpdate();

    MemoryInit();

    /* Enable clock for each peripherals */
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;

    /* PC12~PC15 pin is assigned for GPIO output */
    GPIOD->MODER = 0x55550000;
    
    GPIOD->MODER |= GPIO_MODER_MODER5_1 | GPIO_MODER_MODER6_1;
    GPIOD->AFR[0] = (0x7 << (5*4)) | (0x7 << (6*4));

    USART2->BRR = 0x16D; /* PCLK = 84MHz, Baud rate = 115200 bps, OVER8 = 0, hence BRR = 22.8125 */
    USART2->CR1 |= USART_CR1_UE |    /* USART enable */
                   USART_CR1_TE |    /* Transmitter enable */
                   USART_CR1_RE |    /* Receiver enable */
                   USART_CR1_RXNEIE; /* RXNE interrupt enable */

    NVIC_SetPriority(USART2_IRQn, 2);
    NVIC_EnableIRQ(USART2_IRQn);
    
    while (1)
        continue;
}

void USART2_IRQHandler()
{
    int d;

    if (USART2->SR & USART_SR_RXNE) {
        /* RXNE bit is cleared by reading the DR register */
        d = USART2->DR;
        
        /* Echo back the received data */
        USART2->DR = d;

        /* LED ON (debug) */
        GPIOD->ODR = 0x0000F000;
    }
}

端子共有

実は UART は UART1〜UART5 までありますが(こんなにあって何に使うんだろ?)、今回は UART2 を使います。UART2 は前回まで使っていたデバッグ用 LED と同じ PD 端子に繋がっているため、GPIO としての機能ではなく、UART としての機能に端子を切り替える必要があります。ちょっとややこしいですが、まず GPIO の MODER レジスタで別機能を果たすために入出力機能を殺します。そして、同じく GPIO の AFR レジスタを設定する事で、PD5 と PD6 端子に UART2 の TX/RX が通るようになります。実は、チップの各端子は 16 個の機能を共有する事ができるようになっており、AFR レジスタはそのセレクト信号となってます。上記の例では PD5 と PD6 の AFR レジスタに 7 を書いてますが、リファレンスマニュアル P273 にあるようにこれは USART 1...3 を意味します。

UART の設定

UART の BRR レジスタは UART のクロックと通信のボーレートと受信サンプリング回数により決まります。UART2 は APB2 42MHz に接続されており、ボーレートは 115200 bps、受信サンプリング回数(OVER8)は 16 回に定めた場合、データシートによると BRR = 22.8125 という設定になります。これを小数点以下 4 桁の固定小数点で表現したのが BRR になります。

続いて CR1 レジスタの UE/TE/RE ビットで UART そのものと、送受信機能を有効化します 。また、RXNEIE ビットは受信割り込みを有効にするものです。

割り込みハンドラ USART2_IRQHandler では SR レジスタの RXNE ビットを見て、受信したことを判断し、受信データを DR レジスタから読みます。DR レジスタを読むと自動的に RXNE ビットがクリアされるので、陽にクリアする必要がありません。最後に DR レジスタに書き戻す事で送信側(PC)にエコーバックします。

シリアル通信

Linux 上でシリアル通信する場合 cu コマンドが便利でした。次のように、ボーレートとデバイスファイルを指定するだけです。

$ cu -s 115200 -l /dev/ttyUSB0

終了する時は ~. を入力します。SSH で接続先の Linux マシンから cu を実行している場合、~. だと SSH の接続まで切れてしまうため、~~. と入力すると SSH 接続はそのままで、cu だけ終了します。

ちなみに、USB シリアルを使っている場合、cu コマンドを実行すると Line in use と怒られてしまうため、毎回 chmod +666 /dev/ttyUSB0 を実行していたのですが、USB を抜き差しするたびにパーミッションを設定するのは面倒なので、以下のようなファイルを追加しておきました。

/etc/udev/rules.d/48-udev-default.rules

# serial
KERNEL=="tty[A-Z]*[0-9]|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout", MODE="0666"

udevadm でルールファイルを再読み込みします。

$ sudo udevadm control --reload-rules

これで接続の度に、自動的にパーミッション 0666 なデバイスファイルを作ってくれるようになりました。

STM32F4 Discovery の開発環境の立ち上げに作ったコードは GitHub に置いておきました。"Basic support library for STM32F4 Discovery" と銘打ってますが、まだタイマーと UART のサンプルコードが置いてあるだけです。そのうち整理したい。

github.com