2012年7月30日月曜日

はじめてのPython @Raspberry Pi

 前回、Raspberry Pi が手に入ったのを機に、Pythonをかじってみようと思い、まずは形から、ということで、Raspberry Piにeclipseを入れたら重すぎて使えなかったわけですが、PythonにはもともとIDLEという統合開発環境が用意されていて、こちらを使えばよかったじゃん、という結果になりました。そこで、IDLE上でお約束のHello,Worldを試してみようということで、Web上のPythonの入門サイトなどを参考にさせてもらいながら、

import RPi.GPIO as GPIO
import time

print "Hello,Python! Hello,GPIO!!"

GPIO.setmode(GPIO.BCM)
GPIO.setup(22, GPIO.OUT)
GPIO.setup(23, GPIO.OUT)
GPIO.setup(24, GPIO.OUT)
GPIO.setup(25, GPIO.OUT)

GPIO.output(22, False)
GPIO.output(23, False)
GPIO.output(24, False)
GPIO.output(25, False)
    
while 1:
    GPIO.output(22, True)
    GPIO.output(25, False)
    time.sleep(0.5)
    GPIO.output(23, True)
    GPIO.output(22, False)
    time.sleep(0.5)
    GPIO.output(24, True)
    GPIO.output(23, False)
    time.sleep(0.5)
    GPIO.output(25, True)
    GPIO.output(24, False)
    time.sleep(0.5)

こんなコードを書いて。GPIOに関しては、こことかここを参照 しました。LEDを点灯させようと思ったのですが、Raspberry PiのIOはドライブ力が弱いようなので、トランジスタなどを外付するのが面倒で、とりあえず、ロジアナで端子の状態を見ながら、


 各端子のHigh/Lowが周期的に切り替わるのを確認。


 このIDLEでちゃんとステップ実行もできるし、変数のウォッチもできるのね。Eclipseいらなかった(っていうか、結果的に重くて使えなかったけど)じゃん、やっぱり。

2012年7月29日日曜日

Raspberry Pi に VNC Server をインストール

こちらに改めてセットアップに関して記述しています。

 先日届いたRaspberyy Piですが、HDMI入力端子のあるモニタがないので、とりあえずプラズマテレビにつないで、セットアップを行い、SSHでログインできるようにして、普段使っているPCからTeraTermで接続して使う状態にしていましたが、GUIの画面も見たいので、VNC Server を入れてみようと思いました。Google で検索すると、解説のVideoなんかもYoutubeで公開されているみたい。
 備忘録として、簡単に手順を記述すると

sudo apt-get install tightvncserver

 でTightVNCをインストール。rootはデフォルトでパスワードなしなので、sudoはパスワードなしでそのまま通るはず。

tightvncserver

で起動すると、VNC接続用のパスワード入力を聞いてくるので、入力、その後確認の入力すると、Viewだけのパスワードを設定するか聞いてくるので、自分はNで設定しませんでした。解像度の設定

vncserver :2 -geometry 1024x768 -dept 24

 VNCのポートが5900になっているとは限らないので(サービス1番目が5901、上記2なら5902になるんだと思う?たぶん)、.vnc/raspberrypi:2.log あたりを確認。PCから SSHとVNCで接続してみた。


 せっかくGUIで使えるようになったので、はじめてのPythonのためにeclipseをインストール

sudo apt-get install eclipse

して、PyDevプラグインをインストール。でも、eclipse重すぎ、ってRaspberry Piでeclipse動かすのが間違いか?統合開発環境がやっぱり使いたかったので、eclipse入れたんだけど


 よくみるとデスクトップに IDLEっていうのがあって、これでPythonの開発・デバッグができるみたい。こっちのほうが、軽くてよさそう。eclipseは使い慣れているけど、この環境ではあまりに重すぎる。起動に数分かかるし。


GR-SAKURA (がじぇるねボード)

 前回の RaXino に続いて、Arduino互換(Arduinoにピン配置を似せた)ボードの紹介です。RaXino同様にRXをCPUに搭載した GR-SAKURA ボードです。


 裏面はこんなかんじで、桜マーク入り。


 このボードの最大の特徴は、やっぱり、「ピンク色」なことでしょうか。CPUがRaXinoではRX62Nだったのが、こちらはRX63Nになっています。それと、USBホスト機能(用の端子)があります。それ以外は、ピン配置、Ethernet、MicroSD等はRaXinoと同じ。
 詳しい説明は、ここ とか ここ にあります。
 mbed風にブラウザ上のクラウドコンパイラでプログラムをコンパイルしてできたバイナリをPCに接続してストレージとして認識したGR-SAKURAにコピーするとプログラムが実行できるとのこと。(RaXinoもクラウドコンパイラを提供していて、こちらはE1とかFlashライタでの書込みになる。)
 本家、ルネサスがやっているので、RaXinoよりもコマース力はあるだろうし、日本のArduinoとして流行ってくれるといいな。RaXino共々、頑張って欲しいです。

2012年7月27日金曜日

RaXino (RXduino)

 今日は、Raspberry Pi に続いて、小物の紹介二連発です。


 特殊電子回路 さんの RaXino (RXduino) です。RaXino と RXduino と2つ名称があってよくわからないんですが、たぶん、物理的な基板がRaXinoで、それ(ら)用のArduinoの互換ライブラリ が RXduinoなのかなと思います。


 Arduino の裏面の Made in Italy の地図マークよろしく日本列島のマーク入りです。日本海の部分には Made in Japan の文字入りです。
 RXduino の名前のとおり、ルネサスのRXをMPUに搭載した、Arduino互換ボードです。色々と大変なルネサスですが、日本でほぼ唯一マイコンを作っている(ARMのCPUを製造している工場会社はいくつかあるけどね)メーカなので、頑張って欲しいですね。特殊電子回路さんもそんな思いでこのボードを企画したんだと思います。(極一部の特別な趣向の人たちの間で?)話題のピンク基板の元ではないか?と勝手に想像しています。CPUはRaXinoがRX62N、ピンクはRX63Nになっていますが。
 もともと、H8とかSHをよく使っていたので、それらが消える方向なのは寂しいですが、RXシリーズには頑張って欲しいです。もちろん、ATmegaとかPICとかもいいんですが、日本のメーカのマイコンを使いたいですしね。(なぜって、英語のマニュアルを読むのがいやだから・・・)

Raspberry Pi

 (一部)巷で話題の Raspberry Pi が届きました。



 早速、手持ちで余っていたSDカードに Raspberry.org から DebianベースのOSイメージをダウンロードしてきて、Win32DiskImager でSDカードにOSイメージを展開。このSDカードをRaspberry Piのスロットに挿して、電源はマイクロUSBで給電なので、スマホの充電用ACアダプタを準備、USBマウス・キーボードを接続して、モニタは・・・あれ、HDMI端子・・・、そんなモニタもってないよ。



仕方ないので、とりあえず、家庭用テレビのHDMI入力に接続、ACアダプタをマイクロUSBに挿してPower On(電源スイッチはありません)!


 無事、Linuxの起動画面が表示されました。画面左上には、ラズベリーのマーク。


 しばらくして、GUIの画面が起動。


 デスクトップの中央にもラズベリー。45インチモニタのラズベリーマークはなかなかの迫力?
 Raspberry Pi の元々のコンセプトが「子供たちにプログラミングを」なので、家庭用のテレビに繋いだり、学校の授業で大きな画面に映しだしたり、という感じで考えるとHDMI端子なのは妥当なのかもしれないですね。VGAとかDVIだとパソコンモニタがないといけないけど、35ドルのカードサイズPCを動かすためにパソコン用のモニタを数万円で買うのはおかしいし、もともとモニタ持っている人は普通にパソコン持っているわけだし。
 でも、このRaspberry Pi、かなりの人気のようで、入手するには予約待ちみたいで、今は(自分も含めて)「大きな」お友達の皆さんが熱くなっているみたい。いずれ子供たちのプログラミング環境として広がっていくといいな。そういえば、今はそこら中にコンピュータ(が入っているもの)があるけど、すべて、「プログラミングされた」状態。自分が子供の頃(いったいどんだけ昔だよ?)は、コンピュータって言うと、PC-8001とかPC-6001、MZ、X1とか中身空で自分でプログラミングするようなものだったし。PRINT "こんにちは"[RETURN](ENTERではない)とかやって、喜んでた。そのうち、近所のTK-80とかで遊んでるお兄さんの影響でニーモニック表片手にハンドアセンブルしだしたり。
 で、このRaspberry Pi(の今回試したOSイメージ)はプログラミング学習を意識しているので、Pythonとかが標準でインストールされて、デスクトップにアイコンまで用意してあって、これでプログラミング勉強してね!って感じ。今は干からびてしまった30年前の少年は、とりあえず、LAN経由でTeraTermとかでログインして、はじめてのPython、してみようかなと。居間のテレビ専有するわけに行かないしね。

2012年7月26日木曜日

Smart Analog Stick でシリアル通信

 これとか、この辺りの続きで、Smart Analog Stick のシリアル通信で 送受信がなんとなく動くところまで持ってこれました。まだ、ボーレートの設定とか2文字以上連続して受信してバッファリングするところとかが動いてないかもしれませんが。


 "Hello,RL78!"を送信後、受信した文字をエコーバックする動作を確認しました。


 配線ぐちゃぐちゃ・・・。送信はTxD0を使ってTP39、40で接続していたんですが、それだと受信側がUSBコントローラ(の出力端子)に接続されている関係で使えなかったので、TxD2、RxD2のTP38とTP43を使うことにしました。E1エミュレータはTP46~49でエミュレータからの電源供給で駆動しています。Smart Analog Stick の TxD2(TP38)を秋月のシリアルUSB変換モジュールのRXDへ、RxD2(TP43)を変換のTXDへそれぞれ接続しています。
 開発環境はe2studioを使っています。RL78/G1Eは対応していないのでG1Aでプロジェクトを作成して、CubeSuite+が吐いた定数定義、下のソースで言うと"_0010_SAU_STOP_1"を真似して使っています。これの意味は、0x0010 でシリアル通信の設定レジスタにビットOR演算で設定することで設定レジスタの該当ビットを操作して設定しています。ヘッダファイルの中で定義して使っています。送受信周りのイベントハンドラはinterrupt_handlers.c内にあるイベントハンドラから自分のソース側の対応関数をコールするように記述を追加しておきます。単体では参考にならないかもしれませんが、自分の備忘録も兼ねてソースを掲載します。現時点で、ボーレート設定とか連続受信の辺りがまだ動いていない可能性大です。
そういえば、なんのためにSmart Analog Stickでシリアル通信しようと思ってたんだっけ?なにか本来の目的の補助ツール(デバッグ出力)としてシリアル通信を用意しようと思ってたんだけど・・・、本題が何だったかな?

/***********************************************************************/
/*                              */
/*      PROJECT NAME :  RL78UARTTest                                   */
/*      FILE         :  RL78UARTTest.cpp                               */
/*      DESCRIPTION  :  Main Program                                   */
/*      CPU SERIES   :  RL78 - G1A                                     */
/*      CPU TYPE     :  R5F10ELE                                       */
/*                              */
/*      This file is generated by e2studio.                        */
/*                              */
/***********************************************************************/ 
                                                                           
                       
                       
#include "iodefine.h"
#include "iodefine_ext.h"

#include "typedefine.h"
#include "rl78_serial.h"

#ifdef CPPAPP
//Initialize global constructors
extern "C" void __main()
{
  static int initialized;
  if (! initialized)
    {
      typedef void (*pfunc) ();
      extern pfunc __ctors[];
      extern pfunc __ctors_end[];
      pfunc *p;

      initialized = 1;
      for (p = __ctors_end; p > __ctors; )
 (*--p) ();

    }
}
#endif 

// マクロ定義。

// 定数define定義。
#define UART_TX_BUF_SIZE (64U)
#define UART_RX_BUF_SIZE (64U)

// グローバル変数宣言。
volatile uint8_t g_tx0_buffer[UART_TX_BUF_SIZE];
volatile uint16_t g_tx0_in_index;
volatile uint16_t g_tx0_out_index;
volatile uint8_t g_rx0_buffer[UART_RX_BUF_SIZE];
volatile uint16_t g_rx0_in_index;
volatile uint16_t g_rx0_out_index;
volatile uint8_t g_tx2_buffer[UART_TX_BUF_SIZE];
volatile uint16_t g_tx2_in_index;
volatile uint16_t g_tx2_out_index;
volatile uint8_t g_rx2_buffer[UART_RX_BUF_SIZE];
volatile uint16_t g_rx2_in_index;
volatile uint16_t g_rx2_out_index;


// 関数プロトタイプ宣言。
void setup(void);
void loop(void);
void UART0_begin(unsigned long baudrate);
void UART2_begin(unsigned long baudrate);
void UART0_write(uint8_t txData);
void UART2_write(uint8_t tcData);
uint16_t UART0_writes(uint8_t *str);
uint16_t UART2_writes(uint8_t *str);
uint8_t UART0_read(void);
uint8_t UART2_read(void);
uint16_t UART0_available(void);
uint16_t UART2_available(void);

// 割り込みイベントハンドラ。interrupt_handlers.c の
// 対応ハンドラ関数内でコールするように設定すること。
extern "C" void Handler_INT_ST0(void);
extern "C" void Handler_INT_ST2(void);
extern "C" void Handler_INT_SR0(void);
extern "C" void Handler_INT_SR2(void);

// HardwareSerial Class 定義。
class HardwareSerial
{

};

int main(void)
{
    // 初期設定。
 setup();

 while(1) {
  loop();
 }

  return 0;
}

void setup(void) {

 // 割り込み禁止。
 DI();

 // シリアルポートの初期化。
 //UART0_begin(9600);
 UART2_begin(9600);

 // 割り込み許可。
 EI();

 //UART0_write((uint8_t)'T');
 //UART2_write((uint8_t)'t');
 uint8_t msgHello[14] = {"Hello,RL78!\r\n"};
 UART2_writes(msgHello);
}

void loop(void)
{

 if (UART2_available() > 0) {
  UART2_write(UART2_read());
 }

}


void UART0_begin(unsigned long baudrate)
{
 // グローバル変数初期化。
 g_tx0_in_index = 0U;
 g_tx0_out_index = 0U;
 g_rx0_in_index = 0U;
 g_rx0_out_index = 0U;

 // ボーレート計算
 uint16_t sdr_value = ((uint16_t)(2000000 / baudrate))&0xfe;
 sdr_value = (sdr_value>>1) - 1;
 sdr_value = (sdr_value<<9)&0xff00;

 // --- SAU設定。SAU0-ch0,ch1をUART1のTx,Rxとして利用。
 // SAU1をイネーブル。
 SAU0EN = 1U;
 // シリアル・クロック選択レジスタSPS0を設定。
 SPS0.sps0 = _0004_SAU_CK00_FCLK_4 | _0040_SAU_CK01_FCLK_4;
 // シリアルチャネル停止レジスタST0に停止トリガを書き込んで、送受信を停止。
 ST0.st0 |= _0002_SAU_CH1_STOP_TRG_ON | _0001_SAU_CH0_STOP_TRG_ON;
 // シリアル送信転送完了割り込み INTST0 禁止。
 STMK0 = 1U;
 // シリアル送信転送完了割り込み INTST0 フラグクリア。
 STIF0 = 0U;
 // シリアル受信転送完了割り込み INTSR0 禁止。
 SRMK0 = 1U;
 // シリアル受信転送完了割り込み INTSR0 フラグクリア。
 SRIF0 = 0U;
 // シリアル受信エラー割り込み INTSRE0 禁止。
 SREMK0 = 1U;
 // シリアル受信エラー割り込み INTSRE0 フラグクリア。
 SREIF0 = 0;
 // シリアル送信割り込み優先順位を低(3)に設定。
 STPR00 = 1U;
 STPR10 = 1U;
 // シリアル受信割り込み優先順位を低(3)に設定。
 SRPR00 = 1U;
 SRPR10 = 1U;
 // シリアルエラー割り込み優先順位を低(3)に設定。
 SREPR00 = 1U;
 SREPR10 = 1U;
 // シリアルモードレジスタ SMR00 を設定。
 SMR00.smr00 =
   _0020_SAU_SMRMN_INITIALVALUE | // 初期値でBIT5=1U。
   _0000_SAU_CLOCK_SELECT_CK00 | // クロックはSPS0レジスタで設定したCK00。
   _0000_SAU_TRIGGER_SOFTWARE |  // スタートトリガはソフトトリガのみ有効。(UART送信)
   _0002_SAU_MODE_UART |   // UARTモード。
   _0000_SAU_TRANSFER_END;   // 転送完了割り込みモード。
 // シリアル通信動作設定レジスタ SCR00 を設定。
 SCR00.scr00 =
   _8000_SAU_TRANSMISSION |   // 送信のみを行う。
   _0000_SAU_INTSRE_MASK |   // エラー割り込みマスク(INSTRE)する。
   _0000_SAU_PARITY_NONE |   // パリティなし。
   _0080_SAU_LSB |     // LSBファーストで入出力。
   _0010_SAU_STOP_1 |     // ストップビット長=1bit。
   _0007_SAU_LENGTH_8;    // データ長=8bit。
 // シリアルデータレジスタ SDR00 (下位バイトが送受信バッファで、上位バイトが分周設定。)
 SDR00.sdr00 = sdr_value;
 // シリアルノイズフィルタ許可レジスタ NFEN0 を設定。
 NFEN0.nfen0 |= _01_SAU_RXD0_FILTER_ON;  // RXD0端子のノイズフィルタOn。
 // シリアルフラグクリアトリガレジスタ SIR01 を設定。
 SIR01.sir01 =
   _0004_SAU_SIRMN_FECTMN |  // SSR01のFEFビットを0クリア。
   _0002_SAU_SIRMN_PECTMN |  // SSR01のPEFビットを0クリア。
   _0001_SAU_SIRMN_OVCTMN;   // SSR01のOVFビットを0クリア。
 // シリアルモードレジスタ SMR01 を設定。
 SMR01.smr01 =
   _0020_SAU_SMRMN_INITIALVALUE | // 初期値でBIT5=1U。
   _0000_SAU_CLOCK_SELECT_CK00 | // クロックはSPS0レジスタで設定したCK00。
   _0100_SAU_TRIGGER_RXD |   // RxD端子の有効エッジ。(UART受信)
   _0000_SAU_EDGE_FALL |   // 立下りエッジをスタートビット、ビット反転なし。
   _0002_SAU_MODE_UART |   // UARTモード。
   _0000_SAU_TRANSFER_END;   // 転送完了割り込みモード。
 // シリアル通信動作設定レジスタ SCR01 を設定。
 SCR01.scr01 =
   _4000_SAU_RECEPTION |    // 受信のみを行う。
   _0400_SAU_INTSRE_ENABLE |  // エラー割り込み(INSTRE)発生を許可。
   _0000_SAU_PARITY_NONE |   // パリティなし。
   _0080_SAU_LSB |     // LSBファーストで入出力。
   _0010_SAU_STOP_1 |     // ストップビット長=1bit。
   _0007_SAU_LENGTH_8;    // データ長=8bit。
 // シリアルデータレジスタ SDR01 (下位バイトが送受信バッファで、上位バイトが分周設定。)
 SDR01.sdr01 = sdr_value;
 // シリアル出力レジスタ SO0 設定。
 SO0.so0 |= _0001_SAU_CH0_DATA_OUTPUT_1;  // CH0シリアル出力"1"。
 // シリアル出力レベルレジスタ SOL0 設定。
 SOL0.sol0 |= _0000_SAU_CHANNEL0_NORMAL;  // そのまま(レベル反転なし)で出力。
 // シリアル出力許可レジスタ SOE0 設定。
 SOE0.soe0 |= _0001_SAU_CH0_OUTPUT_ENABLE; // シリアル出力許可。

 // --- 端子設定。
 // P11 (UART0-RxD0) を入力端子に設定。
 PMC1.BIT.bit1 = 0U;
 PM1.BIT.bit1 = 1U;
 // P12 (UART0-TxD0) を出力端子に設定。
 PMC1.BIT.bit2 = 0U;
 P1.BIT.bit2 = 1U;
 PM1.BIT.bit2 = 0U;


 // シリアル送信転送完了割り込み INTST0 フラグクリア。
 STIF0 = 0U;
 // シリアル送信転送完了割り込み INTST0 許可。
 STMK0 = 0U;
 // シリアル受信転送完了割り込み INTSR0 フラグクリア。
 SRIF0 = 0U;
 // シリアル受信転送完了割り込み INTSR0 許可。
 SRMK0 = 0U;
 // シリアル受信エラー割り込み INTSRE0 フラグクリア。
 SREIF0 = 0;
 // シリアル受信エラー割り込み INTSRE0 許可。
 SREMK0 = 0U;
 // シリアル出力レジスタ SO0 設定。
 SO0.so0 |= _0001_SAU_CH0_DATA_OUTPUT_1;  // CH0シリアル出力"1"。
 // シリアル出力許可レジスタ SOE0 設定。
 SOE0.soe0 |= _0001_SAU_CH0_OUTPUT_ENABLE; // シリアル出力許可。
 // 送受信の開始。
 SS0.ss0 |= _0001_SAU_CH0_START_TRG_ON |_0002_SAU_CH1_START_TRG_ON;
}

void UART2_begin(unsigned long baudrate)
{
 // グローバル変数初期化。
 g_tx2_in_index = 0U;
 g_tx2_out_index = 0U;
 g_rx2_in_index = 0U;
 g_rx2_out_index = 0U;

 // ボーレート計算
 uint16_t sdr_value = ((uint16_t)(2000000 / baudrate))&0xfe;
 sdr_value = (sdr_value>>1) - 1;
 sdr_value = (sdr_value<<9)&0xff00;

 // --- SAU設定。SAU1-ch0,ch1をUART1のTx,Rxとして利用。
 // SAU1をイネーブル。
 SAU1EN = 1U;
 // シリアル・クロック選択レジスタSPS1を設定。
 //SPS1.sps1 = _0002_SAU_CK00_FCLK_2 | _0090_SAU_CK01_FCLK_9;
 SPS1.sps1 = _0004_SAU_CK00_FCLK_4 | _0040_SAU_CK01_FCLK_4;
 // シリアルチャネル停止レジスタST1に停止トリガを書き込んで、送受信を停止。
 ST1.st1 |= _0002_SAU_CH1_STOP_TRG_ON | _0001_SAU_CH0_STOP_TRG_ON;
 // シリアル送信転送完了割り込み INTST2 禁止。
 STMK2 = 1U;
 // シリアル送信転送完了割り込み INTST2 フラグクリア。
 STIF2 = 0U;
 // シリアル受信転送完了割り込み INTSR2 禁止。
 SRMK2 = 1U;
 // シリアル受信転送完了割り込み INTSR2 フラグクリア。
 SRIF2 = 0U;
 // シリアル受信エラー割り込み INTSRE2 禁止。
 SREMK2 = 1U;
 // シリアル受信エラー割り込み INTSRE2 フラグクリア。
 SREIF2 = 0U;
 // シリアル送信割り込み優先順位を低(3)に設定。
 STPR12 = 1U;
 STPR02 = 1U;
 // シリアル受信割り込み優先順位を低(3)に設定。
 SRPR12 = 1U;
 SRPR02 = 1U;
 // シリアルエラー割り込み優先順位を低(3)に設定。
 SREPR12 = 1U;
 SREPR02 = 1U;
 // シリアルモードレジスタ SMR10 を設定。
 SMR10.smr10 =
   _0020_SAU_SMRMN_INITIALVALUE | // 初期値でBIT5=1U。
   _8000_SAU_CLOCK_SELECT_CK01 | // クロックはSPS1レジスタで設定したCK01。
   _0000_SAU_TRIGGER_SOFTWARE |  // スタートトリガはソフトトリガのみ有効。(UART送信)
   _0002_SAU_MODE_UART |   // UARTモード。
   _0000_SAU_TRANSFER_END;   // 転送完了割り込みモード。
 // シリアル通信動作設定レジスタ SCR10 を設定。
 SCR10.scr10 =
   _8000_SAU_TRANSMISSION |   // 送信のみを行う。
   _0000_SAU_INTSRE_MASK |   // エラー割り込みマスク(INSTRE)する。
   _0000_SAU_PARITY_NONE |   // パリティなし。
   _0080_SAU_LSB |     // LSBファーストで入出力。
   _0010_SAU_STOP_1 |     // ストップビット長=1bit。
   _0007_SAU_LENGTH_8;    // データ長=8bit。
 // シリアルデータレジスタ SDR10 (下位バイトが送受信バッファで、上位バイトが分周設定。)
 SDR10.sdr10 = sdr_value;
 // シリアルノイズフィルタ許可レジスタ NFEN0 を設定。
 NFEN0.nfen0 |= _01_SAU_RXD0_FILTER_ON;  // RXD0端子のノイズフィルタOn。
 // シリアルフラグクリアトリガレジスタ SIR11 を設定。
 SIR11.sir11 =
   _0004_SAU_SIRMN_FECTMN |  // SSR11のFEFビットを0クリア。
   _0002_SAU_SIRMN_PECTMN |  // SSR11のPEFビットを0クリア。
   _0001_SAU_SIRMN_OVCTMN;   // SSR11のOVFビットを0クリア。
 // シリアルモードレジスタ SMR11 を設定。
 SMR11.smr11 =
   _0020_SAU_SMRMN_INITIALVALUE | // 初期値でBIT5=1U。
   _0000_SAU_CLOCK_SELECT_CK00 | // クロックはSPS0レジスタで設定したCK00。
   _0100_SAU_TRIGGER_RXD |   // RxD端子の有効エッジ。(UART受信)
   _0000_SAU_EDGE_FALL |   // 立下りエッジをスタートビット、ビット反転なし。
   _0002_SAU_MODE_UART |   // UARTモード。
   _0000_SAU_TRANSFER_END;   // 転送完了割り込みモード。
 // シリアル通信動作設定レジスタ SCR11 を設定。
 SCR11.scr11 =
   _4000_SAU_RECEPTION |    // 受信のみを行う。
   _0400_SAU_INTSRE_ENABLE |  // エラー割り込み(INSTRE)発生を許可。
   _0000_SAU_PARITY_NONE |   // パリティなし。
   _0080_SAU_LSB |     // LSBファーストで入出力。
   _0010_SAU_STOP_1 |     // ストップビット長=1bit。
   _0007_SAU_LENGTH_8;    // データ長=8bit。
 // シリアルデータレジスタ SDR01 (下位バイトが送受信バッファで、上位バイトが分周設定。)
 SDR11.sdr11 = sdr_value;
 // シリアル出力レジスタ SO0 設定。
 SO1.so1 |= _0001_SAU_CH0_DATA_OUTPUT_1;  // CH0シリアル出力"1"。
 // シリアル出力レベルレジスタ SOL0 設定。
 SOL1.sol1 |= _0000_SAU_CHANNEL0_NORMAL;  // そのまま(レベル反転なし)で出力。
 // シリアル出力許可レジスタ SOE0 設定。
 SOE1.soe1 |= _0001_SAU_CH0_OUTPUT_ENABLE; // シリアル出力許可。

 // --- 端子設定。
 // P14 (UART1-RxD2) を入力端子に設定。
 PMC1.BIT.bit4 = 0U;
 PM1.BIT.bit4 = 1U;
 // P13 (UART1-TxD2) を出力端子に設定。
 PMC1.BIT.bit3 = 0U;
 P1.BIT.bit3 = 1U;
 PM1.BIT.bit3 = 0U;


 // シリアル送信転送完了割り込み INTST2 フラグクリア。
 STIF2 = 0U;
 // シリアル送信転送完了割り込み INTST2 許可。
 STMK2 = 0U;
 // シリアル受信転送完了割り込み INTSR2 フラグクリア。
 SRIF2 = 0U;
 // シリアル受信転送完了割り込み INTSR2 許可。
 SRMK2 = 0U;
 // シリアル受信エラー割り込み INTSRE2 フラグクリア。
 SREIF2 = 0;
 // シリアル受信エラー割り込み INTSRE2 許可。
 SREMK2 = 0U;
 // シリアル出力レジスタ SO0 設定。
 SO1.so1 |= _0001_SAU_CH0_DATA_OUTPUT_1;  // CH0シリアル出力"1"。
 // シリアル出力許可レジスタ SOE0 設定。
 SOE1.soe1 |= _0001_SAU_CH0_OUTPUT_ENABLE; // シリアル出力許可。
 // 送受信の開始。
 SS1.ss1 |= _0001_SAU_CH0_START_TRG_ON |_0002_SAU_CH1_START_TRG_ON;
}

void UART0_write(uint8_t txData)
{
 if (SSR00.BIT.bit5 == 0) {
  // バッファが空(SSRレジスタのBFFビット=0)なら、直接送信。
  // INST0 割り込み禁止。
  STMK0 = 1U;
  // 送信データセット。
  TXD0.txd0 = txData;
  // INST0 割り込み許可。
  STMK0 = 0U;
 } else {
  // バッファに書き込み。
  g_tx0_buffer[g_tx0_in_index] = txData;
  g_tx0_in_index++;
  if (g_tx0_in_index >= UART_TX_BUF_SIZE) {
   g_tx0_in_index = 0;
  }
 }
}

void UART2_write(uint8_t txData)
{
 if (SSR10.BIT.bit5 == 0) {
  // バッファが空(SSRレジスタのBFFビット=0)なら、直接送信。
  // INST2 割り込み禁止。
  STMK2 = 1U;
  // 送信データセット。
  TXD2.txd2 = txData;
  // INST2 割り込み許可。
  STMK2 = 0U;
 } else {
  // バッファに書き込み。
  g_tx2_buffer[g_tx2_in_index] = txData;
  g_tx2_in_index++;
  if (g_tx2_in_index >= UART_TX_BUF_SIZE) {
   g_tx2_in_index = 0;
  }
 }
}

uint16_t UART0_writes(uint8_t *str)
{
 uint16_t i;
 for (i = 0; i < 1000; i++) {
  if (*(str + i) == '\0') break;
  UART0_write(*(str + i));
 }
 return i;
}

uint16_t UART2_writes(uint8_t *str)
{
 uint16_t i;
 for (i = 0; i < 1000; i++) {
  if (*(str + i) == '\0') break;
  UART2_write(*(str + i));
 }
 return i;
}
uint8_t UART0_read(void)
{
 uint8_t rxData;
 while(UART0_available() == 0);
 rxData = g_rx0_buffer[g_rx0_out_index];
 g_rx0_out_index++;
 if (g_rx0_out_index >= UART_RX_BUF_SIZE) {
  g_rx0_out_index = 0;
 }
 return rxData;
}

uint8_t UART2_read(void)
{
 uint8_t rxData;
 while(UART2_available() == 0);
 rxData = g_rx2_buffer[g_rx2_out_index];
 g_rx2_out_index++;
 if (g_rx2_out_index >= UART_RX_BUF_SIZE) {
  g_rx2_out_index = 0;
 }
 return rxData;
}

uint16_t UART0_available(void)
{
 uint16_t rcvCount = 0;

 if (g_rx0_in_index == g_rx0_out_index) {
  return 0;
 } else {
  if (g_rx0_in_index > g_rx0_out_index)
  {
   rcvCount = g_rx0_in_index - g_rx0_out_index;
  } else {
   rcvCount = UART_RX_BUF_SIZE - g_rx0_out_index;
   rcvCount += g_rx0_in_index;
  }

  return rcvCount;
 }
}

uint16_t UART2_available(void)
{
 uint16_t rcvCount = 0;

 if (g_rx2_in_index == g_rx2_out_index) {
  return 0;
 } else {
  if (g_rx2_in_index > g_rx2_out_index)
  {
   rcvCount = g_rx2_in_index - g_rx2_out_index;
  } else {
   rcvCount = UART_RX_BUF_SIZE - g_rx2_out_index;
   rcvCount += g_rx2_in_index;
  }

  return rcvCount;
 }
}

void Handler_INT_ST0(void)
{
 // 送信レジスタが空になった時に、バッファにデータがあれば、
 // 引き続き送信。
 if (g_tx0_in_index == g_tx0_out_index) {
  // バッファが空なら何もしない。
 } else {
  // バッファから送信。
  // INST0 割り込み禁止。
  STMK0 = 1U;
  // 送信データセット。
  TXD0.txd0 = g_tx0_buffer[g_tx0_out_index];
  g_tx0_out_index++;
  if (g_tx0_out_index >= UART_TX_BUF_SIZE) {
   g_tx0_out_index = 0;
  }
  // INST2 割り込み許可。
  STMK0 = 0U;
 }
}

void Handler_INT_ST2(void)
{
 // 送信レジスタが空になった時に、バッファにデータがあれば、
 // 引き続き送信。
 if (g_tx2_in_index == g_tx2_out_index) {
  // バッファが空なら何もしない。
 } else {
  // バッファから送信。
  // INST2 割り込み禁止。
  STMK2 = 1U;
  // 送信データセット。
  TXD2.txd2 = g_tx2_buffer[g_tx2_out_index];
  g_tx2_out_index++;
  if (g_tx2_out_index >= UART_TX_BUF_SIZE) {
   g_tx2_out_index = 0;
  }
  // INST2 割り込み許可。
  STMK2 = 0U;
 }
}

void Handler_INT_SR0(void)
{
 // 受信完了でバッファに書き込み。
 g_rx0_buffer[g_rx0_in_index] = RXD0.rxd0;
 g_rx0_in_index++;
 if (g_rx0_in_index >= UART_RX_BUF_SIZE) {
  g_rx0_in_index = 0;
 }
}

void Handler_INT_SR2(void)
{
 // 受信完了でバッファに書き込み。
 g_rx2_buffer[g_rx2_in_index] = RXD2.rxd2;
 g_rx2_in_index++;
 if (g_rx2_in_index >= UART_RX_BUF_SIZE) {
  g_rx2_in_index = 0;
 }
}

2012年7月23日月曜日

秋月のRTCモジュール

 今回は、秋月のRTCモジュール を試してみました。この秋月のRTCモジュールはセイコーのI2C接続リアルタイムクロックモジュールRTC-8564をDIPパッケージに実装したものです。今回、ArduinoでのI2C通信を使うのは初めてだったんですが、 RTC-8564のアプリケーションノートこちらのサイト を参考にさせてもらって、スムーズに動かす(とりあえず、動作を確認する)ことができました。
 レジスタの内容に関しては、アプリケーションノートなどが日本語なので助かりました。また、ArduinoでI2Cを使ってみるのが初めてだったので、前述のPIC AVR 工作室のサイトが大変参考になりました。実際、以下のテストに使ったスケッチも、当該サイトのものをほぼいただいてきたものです。感謝。

 まずは、接続。ブレッドボード(他の実験中の領域の隙間ですが・・・)にRTCモジュールを載せて、電源、GND、SDA、SDCをつなぐだけですが、SDA、SDCは1kとか2.2kの抵抗でプルアップしておきます。SDAはArduinoのAN4端子、同SDCはAN5端子に接続します。


 前述のサイトにあったソースを参考(というか、ほぼそのまま)にさせていただいたスケッチを書き込んで、動作確認。とりあえず、日時の初期値はスケッチ中で固定値(日付が変わる直前くらいの)を入れて、動きを見ました。


 時間の経過とともに、時間、日付、曜日などが更新されているところがわかります。流用元さんに感謝しつつ、以下、スケッチのソースです。

#include "Arduino.h"
#include "Wire.h"


#define RTC_ADDRESS (0xa2 >> 1)

int rtc[16];

void setup() {
 
 Serial.begin(9600);
 Serial.println("Initializing RTC unit...Please wait...");
 delay(1000);
 
 // Bus-MasterとしてI2Cを開始。
 Wire.begin();
 
 // RTCへの送信準備開始。
 Wire.beginTransmission(RTC_ADDRESS);
 // はじめにアドレス0x00を指定。次からはオートインクリメント。
 Wire.write(0x00);
 // コントロールレジスタ1。
 Wire.write(0x00);
 // コントロールレジスタ2。
 Wire.write(0x00);
 // 秒レジスタ。とりあえず、58秒。
 Wire.write(0x58);
 // 分レジスタ。とりあえず、59分。
 Wire.write(0x59);
 // 時レジスタ。とりあえず、23時。
 Wire.write(0x23);
 // 日レジスタ。とりあえず、23日。
 Wire.write(0x23);
 // 曜日レジスタ。とりあえず、月曜日。
 Wire.write(0x01);
 // 月レジスタ。とりあえず、7月。
 Wire.write(0x07);
 // 年レジスタ。取りあえず、12年。
 Wire.write(0x12);
 // アラーム設定。なし。
 Wire.write(0x00);
 Wire.write(0x00);
 Wire.write(0x00);
 // Clock出力設定。1Hz。
 Wire.write(0x83);
 // キューの送信。通信終了。
 Wire.endTransmission(); 
 
 delay(20);
 Serial.println("RTC Initialized.");
}

void loop() {
 
 // I2C通信開始。
 Wire.beginTransmission(RTC_ADDRESS);
 // レジスタアドレス0x00を指定。
 Wire.write(0x00);
 // キューの送信。
 Wire.endTransmission();
 
 // データ要求。
 Wire.requestFrom(RTC_ADDRESS, 16);
 // データ受信。
 for (int i = 0; i < 16; i++) {
  while (Wire.available() == 0);
  rtc[i] = Wire.read();
 }
 
 // (西暦)年。以下、数値類はBCD。
 Serial.print("20");
 Serial.print(rtc[8], HEX);
 Serial.print("/");
 // 月。下位9bit。
 Serial.print(rtc[7] & 0x1f, HEX);
 Serial.print("/");
 // 日。下位10bit。
 Serial.print(rtc[5] & 0x3f, HEX);
 Serial.print(" ");
 // 曜日。
 switch (rtc[6] & 0x07) {
  case 0:
   Serial.print("Sun");
   break;
  case 1:
   Serial.print("Mon");
   break;
  case 2:
   Serial.print("Tue");
   break;
  case 3:
   Serial.print("Wed");
   break;
  case 4:
   Serial.print("Thr");
   break;
  case 5:
   Serial.print("Fri");
   break;
  case 6:
   Serial.print("Sat");
   break;
  default:
   Serial.print("   ");
   break;
 }
 Serial.print("   ");
 // 時。
 Serial.print(rtc[4] & 0x3f, HEX);
 Serial.print(":");
 // 分。
 Serial.print(rtc[3] & 0x7f, HEX);
 Serial.print(":");
 // 秒。
 Serial.print(rtc[2] & 0x7f, HEX);
 Serial.println();
 
 delay(200);
 
}

2012年7月22日日曜日

Smart Analog Stick でシリアル送信 その2

 昨日のSmart Analog StickでのUARTに手持ちの在庫で秋月のUSBシリアル変換があったのでつないでみました。


 だんだん配線がごちゃごちゃしてきました。Smart Analog Stickが通常の2.54ピッチで、ピンヘッダなどが使えれば、ユニバーサル基板をスタックできたりしたと思うのですが。仕方ないので、また、ジャンパ用の線で引っ張りだして、ブレッドボード上で配線しました。パソコンの仮想COMポートとして認識したポートをTeraTermで受信して、


 Smart Analog StickからPCへ送信できていることを確認しました。

2012年7月21日土曜日

Smart Analog Stick でシリアル送信

 Smart Analog Stick をいじり始めて、E1エミュレータ + CubeSuite+ の組み合わせで変数のリアルタイムモニタ(おそらく実際には一瞬止めて値を拾って、また動かすっていうのを自動でやってくれるんだと思う)が可能なので、それで動かしながらのAD値を参照等ができるというのがわかったんですが、やっぱり、シリアル通信で色々と値とかデバッグ情報とか出力したいわけで・・・。なので、今回はSmart Analog Stick というかRL78マイコンのUART送信(受信よりも送信のが簡単なので、まず、送信から)を試すことにしました。H8とかSHのSCIでのシリアル通信は過去に何度も、まあ、数度目以降はコピペで何も考えなくなってしまうんですが・・・、使っているので、同じルネサスマイコンだから簡単だろうと思っていたら、半日ハマってしまいました。
 H8/SHのSCIとは(シリアル通信、UARTっていう部分は何も変わらないわけですが)、RL78のSAU(シリアル・アレイ・ユニット)はだいぶ構成が違っていて、ハードウェアマニュアルと格闘する事になりました。ここ最近Arduinoの便利さに浸っていたので、各レジスタの説明をマニュアルで調べてビットをセットしていくのはしんどかったです。まあ、好きでやっているんですが。で、途中までやっていて、(今回はe2studioでプログラムを作っていましたが)CubeSuite+のピンコンフィグのコード生成機能使えばいいじゃん・・・っていうことに気がついて、RL78/G1A用のプロジェクトを作成(Smart Analog はRL78/G1Eですが、CubeSuite+が対応していないみたい?自動コード生成が使えません)して、SAUのUART0を使うようにGUIで設定してコードを生成してコンパイルして実行したら、あっさり動いちゃった。当然といえば当然ですが。
 さっきまでの苦労はなんだったの・・・。せっかくなので、CubeSuite+が吐いたシリアル用のヘッダファイルの一部をコピペ(レジスタのビット設定に便利な定数が定義されている)してe2studio側で作成途中のプロジェクトで一部流用させてもらって、引き続きハードウェアマニュアルを見ながら独自iodefine.hをちまちま作って、なんとかUART送信させるところまで持って来ました。


 ソースを載せようと思ったけど、CubeSuite+の生成したものをコピペした部分があるから、どうなんだろう。
 ちなみに、本当はSmart Analog Stick のUSBにシリアル出力したかったんですが、USBコントローラに MAX3420E というSPIで接続するタイプのものが載っていて、どう扱っていいのかわからなかったんで、諦めました。MAX3420E に接続されているピン39と40をジャンパー線で引っ張りだして、ロジアナで動作を確認しました。 MAX3420E じゃなくて、FT232 とかだったらUARTでそのままUSBでパソコンに繋げられるんじゃないかと思うんですが。


 追って受信動作も実現できたら、秋月のUSBシリアル変換 をつないで、PCでシリアル経由でモニタできるようにしようと思っています。(元々ついてるUSBはなんなんだって話はあるけど。ルネサスが使い方資料とかサンプル出してくれないと、正直使い方わからん。MAX3420E の資料は充実しているんだけど、RL78/G1E側のSPIがよくわからないです。Smart Analog部を制御するSPIとは別みたい?SAUの三線通信?)

2012年7月19日木曜日

秋月のジャイロセンサモジュール

 この間の秋月の加速度センサモジュールに続いて、今回は2軸ジャイロセンサモジュールを試してみました。


 実体配線図はこんな感じ。


 ArduinoのスケッチとPC側のグラフ表示は前回のものをそのまま流用して、センサ(Akiduino+ブレッドボード)を動かしてみました。加速度センサの時は3軸に水平に往復するような感じで振りましたが、今回はXY軸に対して回転させるように振ります。


 各軸ごとに回転させるように往復で振ったところ、たしかにそれぞれの軸に反応が出ました。次に一方の軸に対して、一方向におよそ90度だけ回転させて停止するような動きをしました。


 あれ?ちょっとおかしいですね。角速度を検出しているはずなので、予想ではもっと丸い山が一方向にできて、一方向に回転を始めてやがて止まる、という「山」を想像していたんですが、随分尖った(ここは振る速度によるかもしれませんが)山とさらに青丸で囲った部分はマイナス側に「跳ね返り」があります。これだと、角速度というよりは角加速度(はじめに加速して、後半減速、つまりマイナス加速する)のグラフのように見えます。
 そこで、Google先生に相談してみると、やはりネット上で同じセンサを調べた先人の方が多数いらっしゃって、幾つかの情報を見たところでは、要約すると「秋月の加速度センサモジュールには(メーカーデータシート指定の)ドリフト防止ハイパス・フィルタ(のためのコンデンサ)が入っているんだけど、結果的にそのせいで、微分された結果が出力される。」ということでした。なるほど、角速度の微分(つまり角加速度)なら、上のグラフは納得です。で、対策というか使い方としては、「角加速度なんだから二重に積分する。」か「コンデンサを外して角速度を取り出す。」になるようです。前者の方法ですと、二重に積分するのでオフセットやノイズ、誤差の積み上げが問題になるようです。後者の方法でも二重にやるよりはいいけど積分の誤差積み上げは問題になるんですが、それ以前にコンデンサを外すと温度ドリフトがかなり大きいのと、センサ静止時の出力にばらつき(偏り)があって、モジュールによっては使えないことがあるらしいです。
 いずれの方法にしても、オフセットやドリフトをどう誤魔化して積分(加算)しても使えるデータを作れるかがポイントのようで、もう少し、調査・検討が必要ということがわかって、本日は終了です。

2012年7月15日日曜日

SA-Designerのチュートリアルを試す

 前回、Smart Analog用の構成ソースを生成してくれる SA-Designer というのがリリースされたらしいということを書きましたが、ダウンロードして試してみました。
 回路構成画面はいつもの感じで、メニューから生成を選ぶと3つのヘッダと2つのCソースファイルが生成されるので、それらをプロジェクトに取り込んで、利用します。SPIでSmart Analog部に初期化やパラメータを送信する関数と回路構成画面で構成した相当の送信用パラメータが書かれたファイルが生成されているようです。 これらを CubeSuite+ のプロジェクトに取り込んで、利用します。
 今回は、SA-Designerのヘルプにあったチュートリアルに従ってお試しをしました。Smart Analogの温度センサをADに接続して値を読むというシンプルなものです。
 チュートリアルに従って、構成ファイルを生成して、CubeSuite+のプロジェクトを作成して、チュートリアル内のソースをコピーして、幾つかのプロジェクトのパラメータを変更してビルドして、出来上がりです。
 ただ、このチュートリアルはSmart Analog Stick用ではなく別の評価ボード用のものなので、温度出力端子とADの接続のピン情報は適宜読み替えが必要です。Smart Analog Stickはすでに(チュートリアルとは別の)ADに温度センサ出力が接続されていますが、今回はソースをできるだけそのまま使いたかった(RL78のAD周りのレジスタ設定をまだ勉強してないです)ので、チュートリアルのADに合わせて配線を追加しました。


 チュートリアルでは評価ボードのLEDを制御していますが、Analog Stickにはモニタ用のLEDはないので、その部分のはPIN IOはコメントアウトして、実行。


 g_tmp がADの値で、g_tmp_ref はリセット直後の値。この値は温度が上がると小さくなり、温度が下がると大きくなります。


 CubeSuite+の全体画面。g_tmp は指で暖めたところ下がって来ました。


 逆にエアダスタで冷却してみたら数値が大きくなりました。こうして、一応チュートリアルの動作確認はできたのですが、今回、ほぼ初めて CubeSuite+ を使ってみてわからない点がありました。
 サポートされている?チップだとコンフィグレーションツールが左側のプロジェクトツリーに表示されて、GUIでピンとか周辺機能のパラメータを設定するとコンフィグソースを作ってくれるみたいで、HEWでいうiodefine.hが周辺機能毎に別ファイルで生成されるようです。しかし、今回のSmart Analogのプロジェクトではそういうiodefine.h的なファイルは作成されていませんでした。けれども、サンプルソース内では、P7.3 = 1U のような記述ができていて、一体どこで定義されているんだろう?っていうか、定義が見れなかったら使えないような気がするんだけど。それと、コード補完機能は付いているんだろうか?これがあれば、定義ファイルを見なくても、それっぽいきっかけの先頭部分を入力していくことで、構造体のメンバとかも表示されるのでOKなんだけど。Visual StudioとかEclipseだとコードを入力していけば勝手に候補が表示されて絞られていくけど、CubeSuite+はそれはなかったです。なにかキーを押すとjか、オプションの設定なのかな?今時のIDEで補完機能がないはずはないですから。

2012年7月13日金曜日

秋月の加速度センサモジュール

 今回は秋月の3軸加速度センサモジュールをArduino (Akiduino)につないで試してみました。接続といっても、ブレットボードに載せて、配線するだけです。


 回路図を書く程でもなかったので、配線イメージ図っていう感じで


 はじめはArduinoのスケッチで、起動時数回取得したAD値をアベレージして、オフセット値として、その後、毎回数回繰り返し取得したAD値の平均からオフセットを引いいて、数値をシリアルで画面に出して・・・ってやってみたんですが、数値がパラパラ変わるのは分かるんですが、早すぎて、何が起こってるんだかよくわからない・・・。
 そこで、オフセットとか平均はとりあえずおいておいて、取得したAD値をシリアルに垂れ流して、パソコン側でグラフ表示して、何が起こってるか見てみようと思いました。
で、簡略化したスケッチが

#include "Arduino.h"

// 端子の割当。
const int PIN_ACC_X = 0;
const int PIN_ACC_Y = 1;
const int PIN_ACC_Z = 2;

// 変数宣言。
int adX = 0, adY = 0, adZ = 0;
byte sendData[8];

void setup() {
 // シリアルポートの初期化。
 Serial.begin(9600);
}

void loop() {
 
 // AD値取得。
 adX = analogRead(PIN_ACC_X);
 adY = analogRead(PIN_ACC_Y);
 adZ = analogRead(PIN_ACC_Z);
 
 // 送信データ構成。
 sendData[0] = 0x02;
 sendData[1] = (byte)((adX & 0xff00) >> 8);
 sendData[2] = (byte)(adX & 0x00ff);
 sendData[3] = (byte)((adY & 0xff00) >> 8);
 sendData[4] = (byte)(adY & 0x00ff);
 sendData[5] = (byte)((adZ & 0xff00) >> 8);
 sendData[6] = (byte)(adZ & 0x00ff);
 sendData[7] = 0x03;
 
 // 送信。
 for (int i = 0; i < 8; i++) {
  Serial.write(sendData[i]);
 }
 
 delay(200);
}

 これで、シリアルに吐き出されたデータをPCに側で受信して、グラフ表示するプログラムを作って、表示させたら、こんな感じになりました。


 赤・青・緑がそれぞれの軸のAD値で、加速度センサのブレッドボードを加速度センサの3軸方向にそれぞれ平行に振ってみたものです。3つの黄色の枠でそれぞれの方向に反応しているのがわかります。赤だけオフセットが大きいですが、赤はZ軸というか重力方向の軸です。重力加速度の影響かな?すみません、加速度センサのことをあまり勉強してないのでわかりません。でも、ためしにブレッドボード(Z軸の方向)をひっくり返したらオフセットが下側に行ったので、おそらく重力によるものでしょう。
 ちなみにグラフ表示プログラムは Visual Studio 2010 のグラフ表示コントロールを使っています。
ソースはこんな感じ。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Management;
using System.Windows.Forms.DataVisualization.Charting;
using System.IO.Ports;


namespace SerialGraph
{
    class acceleration
    {
        private int valueX = 0;
        private int valueY = 0;
        private int valueZ = 0;

        public acceleration(int x, int y, int z)
        {
            this.valueX = x;
            this.valueY = y;
            this.valueZ = z;
        }

        public int getValue(int no)
        {
            if (no == 0)
            {
                return this.valueX;
            }
            else if (no == 1)
            {
                return this.valueY;
            }
            else
            {
                return this.valueZ;
            }
        }
    }

    public partial class frmMain : Form
    {
        // キーと値をセットで保存。「COM1」が「標準シリアルポート1」など。
        Dictionary<string, string> ComPortTable = new Dictionary<string, string>();

        // 取得データの履歴を保存。
        const int MAX_DATA_COUNT = 40;
        Queue<acceleration> dataHistory = new Queue<acceleration>();

        // グラフ構成用。
        Series[] series = new Series[3];


        public frmMain()
        {
            InitializeComponent();

            // シリアルポートの実装状況を取得。
            ManagementClass mcW32SerialPort = new ManagementClass("Win32_SerialPort");
            foreach (ManagementObject aSerialPort in mcW32SerialPort.GetInstances())
            {
                ComPortTable.Add(
                    (string)aSerialPort.GetPropertyValue("Caption"),
                    (string)aSerialPort.GetPropertyValue("DeviceID"));
            }
            string[] ports = SerialPort.GetPortNames();
            foreach (string port in ports)
            {
                if (!ComPortTable.ContainsValue(port)) {
                    ComPortTable.Add(port, port);
                }
            }
        }

        private void drawChart(Chart chart)
        {
            try
            {
                chart.Series.Clear();
                for (int i = 0; i < 3; i++)
                {
                    series[i].Points.Clear();
                    foreach(acceleration acc in dataHistory)
                    {
                        series[i].Points.Add(new DataPoint(0, acc.getValue(i)));
                    }
                }
                for (int i = 0; i < 3; i++ )
                {
                    chart.Series.Add(series[i]);
                }

            }
            catch (Exception ex)
            {
                string errMsg = "@showChart(): ";
                errMsg += ex.Message;
                Console.WriteLine(errMsg);
                MessageBox.Show(errMsg);
            }
            
        }


        private void frmMain_Load(object sender, EventArgs e)
        {
            int i;

            // 画面の構成。

            // シリアルポートのコンボボックス構成。
            cmbPortNo.Items.Clear();
            foreach (string aPortCaption in ComPortTable.Keys)
            {
                cmbPortNo.Items.Add(aPortCaption);
            }
            if (ComPortTable.Count > 0)
            {
                cmbPortNo.SelectedIndex = 0;
            }
            else
            {
                MessageBox.Show("シリアルポートが存在しません。");
            }

            // シリアルポート「接続」「切断」ボタン。
            btnConnect.Enabled = true;
            btnDisconnect.Enabled = false;

            // グラフ表示領域の構成。
            chart.ChartAreas[0].AxisX.MinorGrid.Enabled = true;
            chart.ChartAreas[0].AxisY.Minimum = 0;
            chart.ChartAreas[0].AxisY.Maximum = 1023;

            // グラフ要素の初期化。
            for (i = 0; i < 3; i++)
            {
                series[i] = new Series();
                series[i].ChartType = SeriesChartType.FastLine;
                series[i].IsVisibleInLegend = false;
            }
            
            // グラフ線色設定。
            series[0].Color = Color.Red;
            series[1].Color = Color.Green;
            series[2].Color = Color.Blue;

            // チャート表示用データのクリア。
            while (dataHistory.Count <= MAX_DATA_COUNT)
            {
                dataHistory.Enqueue(new acceleration(0, 0, 0));
            }

            // タイマーを1秒(1000ms)周期に初期化。
            timer.Interval = 1000;
            timer.Enabled = true;
        }

        // 接続ボタンのハンドラ。
        private void btnConnect_Click(object sender, EventArgs e)
        {
            try {
                string comPort = ComPortTable[cmbPortNo.SelectedItem.ToString()];
                if (comPort != null)
                {
                    // COMポート接続。
                    serialPort.PortName = comPort;
                    if (serialPort.IsOpen) {
                        MessageBox.Show("既に" + comPort +"はオープンされています。");
                    } else {
                        serialPort.BaudRate = 9600;
                        serialPort.Parity = System.IO.Ports.Parity.None;
                        serialPort.StopBits = System.IO.Ports.StopBits.One;
                        serialPort.DataBits = 8;
                        serialPort.Open();
                        btnConnect.Enabled = false;
                        btnDisconnect.Enabled = true;
                    }
                }
            } catch (Exception ex) {
                if (serialPort.IsOpen) serialPort.Close();
                string errMsg = "@btnConnect_Click(): ";
                errMsg += ex.Message;
                Console.WriteLine(errMsg);
                MessageBox.Show(errMsg);
                btnConnect.Enabled = true;
                btnDisconnect.Enabled = false;
            }
        }

        // 切断ボタンのハンドラ。
        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            try
            {
                if (serialPort.IsOpen)
                {
                    serialPort.Close();
                    btnConnect.Enabled = true;
                    btnDisconnect.Enabled = false;
                }
            } catch (Exception ex) {
                string errMsg = ex.Message;
                Console.WriteLine(errMsg);
                MessageBox.Show(errMsg);
            }
        }

        // シリアルポート受信ハンドラ。
        private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            byte[] rcvData = new byte[8];
            ushort adX, adY, adZ;
            //int readCount;

            try
            {
                //serialPort.Read(rcvData, 0, 1);
                rcvData[0] = (byte)serialPort.ReadByte();
                if (rcvData[0] == 0x02)
                {
                    //readCount = serialPort.Read(rcvData, 1, 7);
                    // ↑ 7byte取得に設定しても5~6byteのみ取得する。受信が間に合っていない?
                    // ↓ 1byteずつ同期受信に変更。
                    for (int i = 1; i < 8; i++)
                    {
                        rcvData[i] = (byte)serialPort.ReadByte();
                    }
                    if (rcvData[7] == 0x03)
                    {
                        // コマンド解析。
                        adX = (ushort)((((ushort)rcvData[1]) << 8) + (ushort)rcvData[2]);
                        adY = (ushort)((((ushort)rcvData[3]) << 8) + (ushort)rcvData[4]);
                        adZ = (ushort)((((ushort)rcvData[5]) << 8) + (ushort)rcvData[6]);

                        // キューに登録。
                        dataHistory.Enqueue(new acceleration((int)adX, (int)adY, (int)adZ));
                        
                    }
                }
            }
            catch (Exception ex)
            {
                string errMsg = ex.Message;
                Console.WriteLine(errMsg);
                MessageBox.Show(errMsg);
            }
        }

        // タイマのハンドラ。
        private void timer_Tick(object sender, EventArgs e)
        {
            // キューの最大値を超えたら、古いものから削除。
            while (dataHistory.Count > MAX_DATA_COUNT)
            {
                dataHistory.Dequeue();
            }

            // グラフの再描画。
            drawChart(chart);
        }

    }
}

相変わらず、使い道とか考えてないですが、まあ、こういうことをやること自体が楽しいっていうことで。

2012年7月8日日曜日

Smart Analog Stick に E1エミュレータをつないでみた

 前回のJTAGICE3に続き、ICEシリーズ?で、RENESAS E1をSmart Analog Stick につないでみました。Smart Analog Stick は Smart Analog MCU の評価キット的なものですが、USBメモリ的な外観にこだわりたかったのか、無理やりサイズを小さくして、ユニバーサルエリアのピッチが2mmとなっていて、評価・実験のやりにくい構造です。


 ブレッドボードで使うようなピン付きのワイヤでSmart Analog Stick の中央下辺りにあるTP46~49を引っ張りだしてきて、E1の14pinコネクタに変換するアダプタを作成して接続しました。Smart Analog Stick側は
   P46 - GND
   P47 - Vdd
   P48 - RESET
   P49 - TOOL
Smart Analog Stick の回路図の一部


 E1エミュレータ側のRLマイコンとの接続資料にあった図


 これらを元に変換アダプタを作成して、CubeSuite+ の評価版で接続したところ、


 とりあえず、つながったかな?今日は、ここまで。
 このSmart Analog、面白そうだと思って入手したんですが、まだ、遊び方を見つけられていません。ルネサスさんからの情報(アナログ回路をマイコン側から構成するためのサンプルコード)とかが少なかったりするのと、自分としては何かアナログセンサを接続して、フロントエンドのアンプを構成して、と思ったところ、意外と接続する(アンプが必要な)センサが手元にありません。マイコンとの接続性がいい5Vとか3.3Vに合わせて出力されるようなセンサ(モジュール)ばかりしか持っておらず、せっかくのアンプ部を活用するものがないんですね。
 ところで、「マイコン側からアナログ部を構成するサンプルがない」といいましたが、最近ルネサスさんから SA-Designer というツールが提供されはじめたみたいです。これを使うと、アナログ部を構成するマイコン側のソースを自動生成してくれるみたいです。まだ、試していませんが、(対象になりそうなセンサ類を見つけて)そのうち試してみようと思います。

Motor Shield と PSDセンサで差動二輪

 以前に試した Motor Shield と この前PSD距離センサを接続するために作った センサコネクタシールドArduino Uno にスタックして、差動二輪のお試しをしてみました。


 とりあえず、動かしてみたかったので、配線とかUSBケーブルとかそのままねじりっこで束ねて、かなり酷い外観になっていますが、まあ、そのあたりは追々改善するということで・・・


 で、とりあえず、走らせてみる。


 適当すぎて、役には立たないだろうけど、一応、Arduinoのソース(スケッチ)を載せてみます。



#include <Arduino.h>

/**
* Motor & PSD センサのテストスケッチ。
*/

#define DBG_MODE 1

// PSDセンサのアナログ入力ピン設定。
#define PIN_PSD_L 4
#define PIN_PSD_R 5

// Motor Shield Rev.3 のピン設定。
#define PIN_CH1_PWM  3
#define PIN_CH1_DIR  12
#define PIN_CH1_BRK  9
#define PIN_CH2_PWM  11
#define PIN_CH2_DIR  13
#define PIN_CH2_BRK  8

// 各モータの配置・配線で正逆転を適宜変更。
#define CH1_CW  HIGH
#define CH1_CCW  LOW
#define CH2_CW  LOW
#define CH2_CCW  HIGH

#define DIR_CW  0
#define DIR_CCW  1

#define MOTOR_LEFT 1
#define MOTOR_RIGHT 2

// PSDセンサの近接側測定不可領域閾値。
const int PSD_DEADZONE = 80;

// 通信終端文字コード定義。
static char CHAR_CR = 0x0d;
static char CHAR_LF = 0x0a;

// グローバル変数定義。
char rcvChar;
char rcvBuf[64];
int rcvCnt;
boolean isRcvCmd;
int spd1, spd2;
int rngL, rngR;
char *tmpc1, *tmpc2;
int tmpi1, tmpi2;
String dbg_msg = "";
int nearL, nearR;
int verynearL, verynearR;

// PSDセンサの電圧をcmに変換。
int analogToCentimeter(int analogValue) {
 return (int)((6787 / (analogValue -3)) -4 );
}



//  回転方向(出力ピン)の設定。
//    ch  -  設定チャネル 1 or 2
//    dir  -  回転方向 DIR_CWで正転、DIR_CCWで逆転
//  戻り値  なし。
void setDirection(int ch, int dir) {
 if(ch == 1) {
  if(dir == DIR_CW) {
   digitalWrite(PIN_CH1_DIR, CH1_CW);
  } else if (dir == DIR_CCW) {
   digitalWrite(PIN_CH1_DIR, CH1_CCW);
  }
 } else if (ch == 2) {
  if (dir == DIR_CW) {
   digitalWrite(PIN_CH2_DIR, CH2_CW);
  } else if (dir == DIR_CCW) {
   digitalWrite(PIN_CH2_DIR, CH2_CCW);
  }
 }
}

//  各チャネルのモータ出力(スピード)設定。
//    ch  -  設定チャネル 1 or 2
//    spd  -  設定出力。0~255
//  戻り値  なし。
boolean setMotorSpeed(int ch, int spd) {
 if ((ch != 1) && (ch != 2))  return false;
 if (spd > 255)  spd = 255;
 if (spd < -255)  spd = -255;
 
 if (spd < 0) {
  spd = -1 * spd;
  setDirection(ch, DIR_CCW);
 }
 else {
  setDirection(ch, DIR_CW);
 }
 
 if (ch == 1) {
  analogWrite(PIN_CH1_PWM, spd);
 }
 else if (ch == 2) {
  analogWrite(PIN_CH2_PWM, spd);
 }
 
 return true;
}

//  モータ出力 ch1,2設定。
//    ch1spd  -  ch1の出力。0~255
//    ch2spd  -  ch2の出力。0~255
//  戻り値  なし。
void setMotorSpeed2(int ch1spd, int ch2spd) {
 setMotorSpeed(1, ch1spd);
 setMotorSpeed(2, ch2spd);
}

//  モータ設定の初期化。
//  引数  なし。
//  戻り値  なし。
void initMotor() {
 pinMode(PIN_CH1_PWM, OUTPUT);
 pinMode(PIN_CH1_DIR, OUTPUT);
 pinMode(PIN_CH1_BRK, OUTPUT);
 pinMode(PIN_CH2_PWM, OUTPUT);
 pinMode(PIN_CH2_DIR, OUTPUT);
 pinMode(PIN_CH2_BRK, OUTPUT);
 
 setMotorSpeed2(0, 0);
 
 digitalWrite(PIN_CH1_BRK, LOW);
 digitalWrite(PIN_CH2_BRK, LOW);
}

void breakMotor() {
 digitalWrite(PIN_CH1_BRK, HIGH);
 digitalWrite(PIN_CH2_BRK, HIGH);
 spd1 = 0;
 spd2 = 0;
 setMotorSpeed2(spd1, spd2);
 delay(200);
 digitalWrite(PIN_CH1_BRK, LOW);
 digitalWrite(PIN_CH2_BRK, LOW);
}

// 初期設定。
void setup() {
 // シリアル通信初期化。
 Serial.begin(115200);
 if (DBG_MODE) {
  Serial.println("Hello, Motor and PSD Test program...");
 }
 
 // モーター初期化。
 initMotor();
 
 // 変数初期化。
 spd1 = 0;
 spd2 = 0;
 rngL = 0;
 rngR = 0;
 nearL = 0;
 verynearL = 0;
 nearR = 0;
 verynearR = 0;
}

// メインループ。
void loop() {
 
 if (DBG_MODE) {
  dbg_msg = "";
 }
 
 // PSDセンサ読み取り処理。
 int value = analogRead(PIN_PSD_L);
 // 読み取り値が閾値を超えていれば、距離に変換。
 if (value > PSD_DEADZONE) {
  rngL = analogToCentimeter(value);
 } else {
  // しきい値に達しなければ、とりあえず0。
  rngL = 0;
 }
 // もう一つのPSDも同様。
 value = analogRead(PIN_PSD_R);
 if (value > PSD_DEADZONE) {
  rngR = analogToCentimeter(value);
 } else {
  rngR = 0;
 }
 
 // 速度制限。
 if ((30 >= rngL) && (rngL > 15)) {
  /// 距離30~15で
  nearL++;
  verynearL = 0;
 } else if ((15 >= rngL) && (rngL > 0)) {
  /// 距離15以下。ただし0(測定不能かも)は除く
  verynearL++;
 } else {
  /// それ以外はとりあえず制限なし。
  nearL = 0;
  verynearL = 0;
 }
 if ((30 >= rngR) && (rngR > 15)) {
  /// 距離30~15で
  nearR++;
  verynearR = 0;
 } else if ((15 >= rngL) && (rngL > 0)) {
  /// 距離15以下。ただし0(測定不能かも)は除く
  verynearR++;
 } else {
  /// それ以外はとりあえず制限なし。
  nearR = 0;
  verynearR = 0;
 }
 
 // 車輪スピード設定。
 setMotorSpeed2(spd1, spd2);
 if (verynearL > 2) {
  /// 左側停止。
  setMotorSpeed(MOTOR_LEFT, 0);
 } 
 if (verynearR > 2) {
  /// 右側停止。
  setMotorSpeed(MOTOR_RIGHT, 0);
 }
 if (nearL > 0) {
  /// 左側が近接なら、右へ旋回。
  setMotorSpeed(MOTOR_RIGHT, (int)(spd2/2));
  nearL= 0;
 } 
 if (nearR > 0) {
  /// 右側が近接なら、左へ旋回。
  setMotorSpeed(MOTOR_LEFT, (int)(spd1/2));
  nearR = 0;
 }
 /*
 if (DBG_MODE) {
  Serial.print(rngL, DEC);
  Serial.print(", ");
  Serial.println(rngR, DEC);
 }
 */
 
 // シリアル受信処理。受信バッファにデータがあれば、
 if (Serial.available() > 0) {
  /// 1文字読み込み。
  rcvChar = Serial.read();
  /// 受信文字が改行文字の場合は、
  if ((rcvChar == CHAR_CR) || (rcvChar == CHAR_LF)) {
   //// 既に1文字以上受信していれば、
   if (rcvCnt > 0){
    ///// 文字列終端を設定して、コマンド受信フラグ立てる。
    rcvBuf[rcvCnt] = '\0';
    isRcvCmd = true;
    rcvCnt = 0;
   }
  }
  /// 受信文字が改行文字以外の場合は、
  else {
   //// 配列に順次取り込み。
   rcvBuf[rcvCnt] = rcvChar;
   rcvCnt++;
  }
 }    // シリアル受信処理、ここまで。
 
 // コマンド解析処理。
 if (isRcvCmd == true) {
  /// 受信フラグクリア。
  isRcvCmd = false;
  /// 1文字目で処理振り分け。
  switch (rcvBuf[0]) {
   case 'D':
   case 'd':
   //// モータスピード指定。
   tmpc1 = strtok(rcvBuf, ",");
   tmpc1 = strtok(NULL, ",");
   tmpc2 = strtok(NULL, ",");
   if ((tmpc1 == NULL) || (tmpc2 == NULL))  break;
   tmpi1 = atoi(tmpc1);
   tmpi2 = atoi(tmpc2);
   if ((tmpi1 < -255) || (tmpi1 > 255)) break;
   if ((tmpi2 < -255) || (tmpi2 > 255)) break;
   spd1 = tmpi1;
   spd2 = tmpi2;
   if (DBG_MODE) {
    Serial.print("D command : ");
    Serial.print(spd1, DEC);
    Serial.print(", ");
    Serial.println(spd2, DEC);
   }
   break;
   case 'B':
   case 'b':
   //// ブレーキ。
   breakMotor();
   if (DBG_MODE) {
    Serial.println("Break");
   }
   break;
   case '?':
   //// デバッグ情報取得。
   if (DBG_MODE) {
    dbg_msg += "spd1 = ";
    dbg_msg += spd1;
    dbg_msg += ", ";
    dbg_msg += "spd2 = ";
    dbg_msg += spd2;
    dbg_msg += ", ";
    dbg_msg += "PSD Left = " ;
    dbg_msg += rngL;
    dbg_msg += ", ";
    dbg_msg += "PSD Right = ";
    dbg_msg += rngR;
    dbg_msg += ", ";
    Serial.println(dbg_msg);
   }
   break;
   default:
   //// 該当なし。
   break;
  }
 }    // コマンド解析処理、ここまで。
 
}

2012年7月2日月曜日

JTAGICE3

 今日の小物は「JTAGICE3」です。AVRマイコンにISP書き込みしたり、DebugWireでデバッグしたりできます。


 AVR Dragon でいいじゃんという話もありますが、はい、そのとおりです。ただ単に好きなんです。ICEとかエミュレータが。一応、自分の中ではJTAGICE3がメインでDragonは(Fuses書き込みでダメにしたチップの復活のための)パラレル書き込み用ということで。
 実は(つかいもしないのに)ルネサスのRXやRL用のE1エミュレータとかSH、H8用のBITRANのJTAGデバッガとか持っていたりします。なんていうか、デバッガをつなぐとマイコンと心が通じ合ったような気分になれて、こんにちはマイコンって感じ(このマイコンはパソコンの意味のMyコンピュータだけど)です。
 今回は、JTAGICE3とAtmel Studio 6でArduinoのスケッチぽいものを動かせるか試してみようと思います。
 まず、Arduinoは秋月の互換ボード、Akiduinoとでも勝手に呼ばしてもらいましょう、で。MPUはATmega368Pを載せています。
 Atmel StudioでATmega368P用のプロジェクトを作成して、プロジェクト設定でincludeパスにArduino-IDEの hardware\arduino\cores\arduino を指定して、さらに同フォルダの *.cと*.cpp ファイルをプロジェクトに追加します。また、 hardware\arduino\variants\standard をincludeパスに追加して pins_arduino.h がここから読まれるようにします。別のArduino Megaボードなどはそれに合わせたincludeパスを指定することになると思います。
 この状態で、プロジェクトの *.cpp ファイルに Arduino.h をincludeしてスケッチ相当の処理を書きます。main()関数などはmain.cに書かれています。
 ひとまず、コンパイルをかけると「F_CPUが定義されていない」的なエラー。Arduino-IDE内のソースやファイルを適当に眺めていると、hardware\arduino\boads.txt というテキストファイルにボード構成情報があり、その中に"f_cpu=16000000L"という記述があり、ソース内では #ifdef F_CPU のような記述があったので、プロジェクトのリンカオプションで -D"F_CPU=16000000L"になるように設定してみたところ、ビルドが通って hex ファイルが作成されました。早速ISP書き込みして実行すると、










おっ、動いた。シリアルポートにメッセージを出力して、LEDが点滅を始めました。やったね。

気をよくして、ステップ実行の確認。setupに入ったところでブレークして、ステップ実行でメッセージ表示とLED点灯までをコマ送りで確認。Loop()に入ってからの点滅はタイマが絡むせいか、ブレーク状態と噛み合わない感じ。

今後、もう少し調べてみるつもりです。Arduinoの各関数やライブラリを使えるかとか、ブレーク時の変数ウォッチとか、条件ブレークなんかもできるのかな?