2013年3月8日金曜日

Raspberry Pi で UART通信

 今回は、Raspberry Pi でUART通信を試して見ることにしました。I2C は過去に何度か試しているんですが、そういえば、UARTを使っていませんでした。UARTが使えれば、別のマイコンとインタフェースしたり、何かと便利だと思われます。
 Raspberry Pi の IOは3.3Vなので、5V TTL - USB変換アダプタに抵抗分圧してつなごうかと思いましたが、 ランニングエレクトロニクスさんのSBDBTがあったので、これを使って、一気にUARTの無線(Bluetooth)化です。



 しかし、当初、単純に配線するだけでは上手く動いてくれませんでした。そこで、Google先生に助けを求め、先人の知恵を借りる事にしました。こちらのClick Labさんのサイトに解決方法が掲載されていました。Click LabさんはInterfaceにRaspberry Piのラジコンカーの記事を書かれた方のようです。丸々転載で申し訳ないですが、

/boot/cmdline.txt の

 dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait

記述を

 dwc_otg.lpm_enable=0 rpitestmode=1 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait

のように変更し、

/etc/inittab の

 TO:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

の先頭に#を付けてコメントアウトします。

 #TO:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

設定変更後リブートすることで、UARTが利用可能になりました。デフォルトではUARTはシリアルコンソールに割り当てられているそうです。

 これらの設定をすることで、UARTを(Bluetooth経由で)利用できるようになりました。


 配線はこんな感じ。Raspberry Pi の拡張端子のピン配置はこちらを参照して下さい。


 動作確認用のプログラムは、せっかくなので、先日用意したクロス開発デバッグ環境です。


 Eclipse 上で、ブレークポイントを設定して、Bluetooth接続でコンソールに接続、"Hello~"のメッセージをコンソール側に表示後、コンソールに文字を入力すると、UART受信関数が文字を取得してブレークがかかります。変数にマウスを当てると、変数の値も表示されます。Raspberry Pi上でGeanyを使えばいいんですが、何気にクロス開発環境でeclipseも便利かも。

以下、テストに使ったソース。


#define _POSIX_SOURCE 1

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/signal.h>
#include <unistd.h>
#include <fcntl.h>

// グローバル変数宣言。
int fd;
struct termios old_term_io;  // 現在の設定の退避用。

// 関数プロトタイプ宣言。
int UART_open_port(char *serial_dev);
void UART_close_port(void);
int UART_put_char(unsigned char c);
int UART_put_string(char *s);
int UART_get_char(void);

int main() {

 char uart_rBuffer[1024];
 char uart_sBuffer[256];
 char *uart_dev = "/dev/ttyAMA0";
 int c;

 // シリアル通信用バッファの初期化。NULL終端文字で埋める。
 memset(uart_rBuffer, '\0', 1024);
 memset(uart_sBuffer, '\0', 256);

 // テスト送信データの設定。
 sprintf(uart_sBuffer, "Hello,UART!!\r\n");

 // UARTの初期化。
 UART_open_port(uart_dev);

 // UART送信。
 UART_put_string(uart_sBuffer);

 // メインループ。
 while(1)
 {
  c = 0;

  /// 1バイト受信。データが来るまでブロックされる。
  c = UART_get_char();

  if (c != 0)
   UART_put_char((unsigned char)c);
 }

 return 0;
}


int UART_open_port(char *serial_dev)
{
 struct termios new_term_io;

 // デバイスをオープン。
 fd = open(serial_dev, O_RDWR | O_NOCTTY);
 if (fd < 0)
 {
  /// デバイスオープンに失敗。
  perror(serial_dev);
  exit(-1);
 }

 // 既存の設定を取得・保存。
 tcgetattr(fd, &old_term_io);

 // new_term_ioの制御文字の初期化。
 new_term_io.c_iflag = 0;
 new_term_io.c_oflag = 0;
 new_term_io.c_cflag = 0;
 new_term_io.c_lflag = 0;
 new_term_io.c_line = 0;
 //bzero(new_term_io.c_cc, sizeof(new_term_io.c_cc));
 memset(new_term_io.c_cc, '\0', sizeof(new_term_io.c_cc));

 // ポートの設定。
 //  B9600 = 9600bps
 //  CS8 = 8bit, no parity, 1 stopbit
 //  CLOCAL = ローカル接続(モデム制御なし)
 //  CREAD = 受信文字有効。
 new_term_io.c_cflag = B9600 | CS8 | CLOCAL | CREAD;

 // パリティデータエラーは無視に設定。
 new_term_io.c_iflag = IGNPAR;

 // Rawモード出力。
 new_term_io.c_oflag = 0;
 // 入力モード設定。non-canonical, no echo,
 new_term_io.c_lflag = 0;
 // inter-character timer
 new_term_io.c_cc[VTIME] = 0;
 // 1文字来るまで読み込みをブロック。
 new_term_io.c_cc[VMIN] = 1;

 // モデムラインのクリア。
 tcflush(fd, TCIFLUSH);

 // 新しい設定を適用。
 tcsetattr(fd, TCSANOW, &new_term_io);

 return(0);
}

void UART_close_port(void)
{
 tcsetattr(fd, TCSANOW, &old_term_io);
 close(fd);
}
int UART_put_char(unsigned char c)
{
 if (write(fd, &c, 1) != 1)
  return(-1);

 return(0);
}

int UART_put_string(char *s)
{
 if (write(fd, s, strlen(s)) != 1)
  return(-1);

 return(0);
}
int UART_get_char(void)
{
 unsigned char c;
 int res;

 res = read(fd, (char *)&c, 1);

 return(c);
}