2012年8月29日水曜日

Raspberry Pi で I2C その2 ストリナI2C LCD

 前回、GR-SAKURAでストロベリー・リナックスのI2C LCDが動かせたので、この前の Raspberry Piで秋月のI2C RTCを動かしたのとあわせて、当然、Raspberry Pi でこのI2C LCDは動くだろうと考えたのですが、甘かったです。単純に差し替えて動いてはくれませんでした。
 今回はRaspduinoアダプタは使わずに、Raspberry PiのGPIO端子から直接結線して試したのですが、文字が表示されず、というか、初期化のコマンドのところでI2Cへ出力する関数がエラーを返してきます。ロジアナで覗くと一発目のアドレス送信でNACKが返って(ACKにならない)いたり、ACKが返ってもタイミングがずれているように見えます。GR-SAKURAでは動いて、Raspberry PiではダメなんだからRaspberry Pi側の問題?切り分けのために、パソコンからI2C信号を送れるアダプタで試したところ同じ結果になりました。原因がわからないまま、悶々としつつ、このアダプタのマニュアルとかを読んでいたら、「プルアップ抵抗が内蔵されています。」の記述が。そういえば、Raspberry Piも(設定で切り替えられると思うが)デフォルトで内部プルアップだったはず。外部(ブレッドボード)上にもプルアップ抵抗(今回は1kΩ)をつけてあるのが逆効果なのか?でも、GR-SAKURAの時は普通に動いていたけど・・・。(GR-SAKURAはデフォルト内部プルアップだけど、さらに外付けしたほうが安定ってライブラリの説明に書いてあった。)
 まあ、ものは試しなので、プルアップ抵抗を外して試してみよう・・・。って、動いたよ。


 ふ~ん。そういうもんなの。Raspberry Pi 側の内蔵プルアップがあるのに外部に(しかも1kΩだと低すぎ?本来2.2kΩとか?)プルアップを余計につけてしまうと並列になった抵抗をLCD側がACK状態にドライブできない(あるいはタイミングが遅れる)ということでしょうか。
 と、いうわけで。以下、ソース。例によってdelay関数でwiringPiを使用。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include <wiringPi.h>


#define LCD_ADDRESS (0b0111110)
#define LCD_CONTRAST 100

#define LCD_RS_CMD (0x00)
#define LCD_RS_DATA (0x40)
#define LCD_CMD_CLEAR (0x01)
#define LCD_CMD_HOME (0x03)

// 関数プロトタイプ宣言。
void LCD_init(int fd);
void LCD_write(unsigned char rs, unsigned char data, int fd);
void LCD_clear(int fd);
void LCD_setCursor(unsigned char col, unsigned char row, int fd);
void LCD_putc(unsigned char c, int fd);
void LCD_puts(char *str, int fd);

int main(int argc, char **argv)
{
 int lcd;       // ファイルディスクリプタ。
 char *i2cFileName = "/dev/i2c-0"; // I2Cドライバファイル名。
 int lcdAddress = LCD_ADDRESS;  // I2C LCD のアドレス。
 
 printf("***** start i2c lcd test program *****\n");
 
 // I2CポートをRead/Write属性でオープン。
 if ((lcd = open(i2cFileName, O_RDWR)) < 0)
 {
  printf("Faild to open i2c port\n");
  exit(1);
 }
 
 // 通信先アドレスの設定。
 if (ioctl(lcd, I2C_SLAVE, lcdAddress) < 0)
 {
  printf("Unable to get bus access to talk to slave\n");
  exit(1);
 }
 
 delay(100);

 // LCD初期化。
 LCD_init(lcd);
 
 // メッセージ表示。
    LCD_setCursor(0, 0, lcd);
    LCD_puts("Hello!!", lcd);
    LCD_setCursor(5, 1, lcd);
    LCD_puts("I2C LCD!!", lcd);
 
 return 0;
}

void LCD_init(int fd)
{
 delay(40);
 // Function Set。8bit bus mode, 2-line mode,normal font,normal instruction mode。
    LCD_write(LCD_RS_CMD, 0b00111000, fd);
    // Function Set。extension instruction modeへ。
    LCD_write(LCD_RS_CMD, 0b00111001, fd);
    // Internal OSC frequency(extension instruction mode)設定。
    LCD_write(LCD_RS_CMD, 0b00010100, fd);
    // Contrast set(extension instruction mode)。コントラスト値下位4bit設定。
    LCD_write(LCD_RS_CMD, 0b01110000 | (LCD_CONTRAST & 0xF), fd);
    // Power/ICON/Contrast set(extension instruction mode)。
    // アイコン On,booster On,コントラスト値上位2bit設定。
    LCD_write(LCD_RS_CMD, 0b01011100 | ((LCD_CONTRAST >> 4) & 0x3), fd);
    // Follower control。internal follower on, 
    LCD_write(LCD_RS_CMD, 0b01101100, fd);
    // 時間待ち。
    delay(300);
    
    // Function Set。normal instruction mode。
    LCD_write(LCD_RS_CMD, 0b00111000, fd);
    // Display On/Off。Display Onに設定。
    LCD_write(LCD_RS_CMD, 0b00001100, fd);
    // Clear Display。
    LCD_write(LCD_RS_CMD, 0b00001100, fd);
    // 時間待ち。
    delay(2);
}

void LCD_write(unsigned char rs, unsigned char data, int fd)
{
    unsigned char buf[2];
 
    if (rs == LCD_RS_CMD || rs == LCD_RS_DATA)
    {
        // LCD_RS_CMD ならコマンドモード。LCD_RS_DATA ならデータモード。
        
  buf[0] = rs;
  buf[1] = data;
  if (write(fd, buf, 2) != 2)
  {
   printf("Error writeing to i2c slave1\n");
  }        
    }
    else
    {
        // rsの指定がLCD_RS_CMD,LCD_RS_DATA以外ならなにもしない。
    }
}

void LCD_clear(int fd)
{
    LCD_write(LCD_RS_CMD, LCD_CMD_CLEAR, fd);
    delay(2);
    LCD_write(LCD_RS_CMD, LCD_CMD_HOME, fd);
    delay(2);
}

void LCD_setCursor(unsigned char col, unsigned char row, int fd)
{
    unsigned char offset[] = {0x00, 0x40};
    
    if (row > 1)    row = 1;
    if (col > 16)    col = 16;
    
    LCD_write(LCD_RS_CMD, 0x80 | (col + offset[row]), fd);
}

void LCD_putc(unsigned char c, int fd)
{
    LCD_write(LCD_RS_DATA, c, fd);
}

void LCD_puts(char *str, int fd)
{
    int i;
    for (i = 0; i < 16; i++)
    {
        if (str[i] == 0x00)
        {
            break;
        }
        else
        {
            LCD_putc((unsigned int)str[i], fd);
        }
    }
}

 前日まで悩んでたのはなんだったんだ。ま、結果オーライということで。昨夜、飲んだら眠くなって早く寝過ぎて、今朝、2時前に目が覚めて、これに手を付けたんだけど、早起きは三文の得、ということで・・・。

2012年8月26日日曜日

GR-SAKURA I2C編3 ストリナI2C LCD

 今回は、GR-SAKURAでストロベリー・リナックスさんのI2C LCDを動かしてみました。というのも、以前に秋月のI2C LCDがArduinoでは簡単に動いたのに、SAKURAでは失敗したので、(原因追求するのも面倒なので)別のI2C LCD試してみようという、安易な発想からです。
 で結論。あっさり動いた。


 ソースは、前回のもののLCDのI2Cアドレスとか初期化の制御コマンドの辺りを変更しただけのもの。

#define RXDUINO

#ifdef RXDUINO
#include <rxduino.h>
#else
#include <Arduino.h>
#endif

#include <Wire.h>

#define LCD_ADDRESS    (0b0111110)
#define LCD_CONTRAST    100

#define LCD_RS_CMD    (0x00)
#define LCD_RS_DATA    (0x40)
#define LCD_CMD_CLEAR    (0x01)
#define LCD_CMD_HOME    (0x03)

// 関数プロトタイプ宣言。
void LCD_init();
void LCD_write(uint8_t rs, uint8_t data);
void LCD_clear();
void LCD_setCursor(byte col, byte row);
void LCD_putc(uint8_t c);
void LCD_puts(char *str);


void setup()
{
    // シリアルポートを9600bpsで開始。
    Serial.begin(9600);
    // 受信バッファにデータが入るまで待機。
    while (Serial.available() == 0);
    Serial.println("Initializing...");
    // 一旦、受信バッファにデータがあれば、
    if (Serial.available() > 0) {
        // 読み込んで、廃棄。
        char c = Serial.read();
    }
    
    // Bus-MasterとしてI2Cを開始。
    Wire.begin();

    delay(500);
    
    // LCDの初期化。
    LCD_init();
    
    // メッセージ表示。
    LCD_setCursor(0, 0);
    LCD_puts("Hello!!");
    LCD_setCursor(5, 1);
    LCD_puts("I2C LCD!!");

}

void loop()
{
    
}

// LCD初期化。
void LCD_init()
{
    delay(40);
    // Function Set。8bit bus mode, 2-line mode,normal font,normal instruction mode。
    LCD_write(LCD_RS_CMD, 0b00111000);
    // Function Set。extension instruction modeへ。
    LCD_write(LCD_RS_CMD, 0b00111001);
    // Internal OSC frequency(extension instruction mode)設定。
    LCD_write(LCD_RS_CMD, 0b00010100);
    // Contrast set(extension instruction mode)。コントラスト値下位4bit設定。
    LCD_write(LCD_RS_CMD, 0b01110000 | (LCD_CONTRAST & 0xF));
    // Power/ICON/Contrast set(extension instruction mode)。
    // アイコン On,booster On,コントラスト値上位2bit設定。
    LCD_write(LCD_RS_CMD, 0b01011100 | ((LCD_CONTRAST >> 4) & 0x3));
    // Follower control。internal follower on, 
    LCD_write(LCD_RS_CMD, 0b01101100);
    // 時間待ち。
    delay(300);
    
    // Function Set。normal instruction mode。
    LCD_write(LCD_RS_CMD, 0b00111000);
    // Display On/Off。Display Onに設定。
    LCD_write(LCD_RS_CMD, 0b00001100);
    // Clear Display。
    LCD_write(LCD_RS_CMD, 0b00001100);
    // 時間待ち。
    delay(2);
}

// LCDへデータ書き込み。
void LCD_write(uint8_t rs, uint8_t data)
{
    int rtnValue;
    
    if (rs == LCD_RS_CMD || rs == LCD_RS_DATA)
    {
        // LCD_RS_CMD ならコマンドモード。LCD_RS_DATA ならデータモード。
        
        // LCDへの送信準備開始。
        Wire.beginTransmission(LCD_ADDRESS);
        // モード(RS)送信。
        Wire.write(rs);
        // データ送信。
        Wire.write(data);
        // キューの送信。通信終了。
        rtnValue = Wire.endTransmission();
        Serial.print(rtnValue);
        
    }
    else
    {
        // rsの指定が0x00,0x80以外ならなにもしない。
    }
}

void LCD_clear()
{
    LCD_write(LCD_RS_CMD, LCD_CMD_CLEAR);
    delay(2);
    LCD_write(LCD_RS_CMD, LCD_CMD_HOME);
    delay(2);
}

void LCD_setCursor(byte col, byte row)
{
    byte offset[] = {0x00, 0x40};
    
    if (row > 1)    row = 1;
    if (col > 16)    col = 16;
    
    LCD_write(LCD_RS_CMD, 0x80 | (col + offset[row]));
}

void LCD_putc(uint8_t c)
{
    LCD_write(LCD_RS_DATA, c);
}

void LCD_puts(char *str)
{
    int i;
    for (i = 0; i < 16; i++)
    {
        if (str[i] == 0x00)
        {
            break;
        }
        else
        {
            LCD_putc((unsigned int)str[i]);
        }
    }
}

 いつものように RXDUINO のdefineをコメントアウトしてArduinoと兼用できるような書き方にしていますが、今回はArudino側での動作確認はしていませんので、あしからず。
 しかし、じゃあ、なんで、秋月のI2C LCDは動かないのさ、ということになるんですが。まあ、しらべる元気もないんだけど・・・。あした、そのうち、いつか・・・調べようかな・・・。明日っていつの明日よっ?
 もうひとつ気になることが・・・。上のソースのI2Cに送信してendTranmission()する戻り値をコンソールに出力しているんだけど、全部"2"(アドレス送信時にNACKを受信)となっているんだけど、いいのかな?成功(ACKを受信というかLCD側が応答)せずに文字だけ表示している?


2012年8月23日木曜日

Raspberry Pi で I2C その1 秋月RTCモジュール

 Raspberry Pi 用GPIOアダプタ(Raspduino)ArduinoGR-SAKURA のように秋月のI2C接続RTCが使えるかどうかを試してみました。


 下準備として ここ の記述を参考に以下を行いました。ただし、自分の環境は色々なものをインストールしているので、何がどう影響(良くも悪くも)しているかが正直わからないんですが・・・
 まず、/etc/modules に
     i2c-dev
の一行を追加して、/etc/modprobe.d/raspi-blacklist.conf の
     blacklist i2c-bcm2708
の先頭に#をつけてコメントアウト。一旦、再起動後に
     sudo apt-get install i2c-tools
で i2c-tools をインストールしました。
動作確認に使ったソースはこんな感じ。ただし、delay関数を使うために wiringPiのライブラリを使用しています。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include <wiringPi.h>



int main(int argc, char **argv)
{
 int rtc;       // ファイルディスクリプタ。
 char *i2cFileName = "/dev/i2c-0"; // I2Cドライバファイル名。
 int rtcAddress = 0xa2 >> 1;   // I2C LCD のアドレス。右1bitシフト。
 unsigned char buf[16];    // バッファ。
 
 printf("***** start i2c test program *****\n");
 printf("Initializing RTC unit... Please wait\n");
 delay(1000);
 
 // I2CポートをRead/Write属性でオープン。
 if ((rtc = open(i2cFileName, O_RDWR)) < 0)
 {
  printf("Faild to open i2c port\n");
  exit(1);
 }
 
 // 通信先アドレスの設定。
 if (ioctl(rtc, I2C_SLAVE, rtcAddress) < 0)
 {
  printf("Unable to get bus access to talk to slave\n");
  exit(1);
 }
 
 // RTC初期化。
 delay(15);
 // はじめにアドレス0x00を指定。次からはオートインクリメント。
 buf[0] = 0x00;
 // コントロールレジスタ1。
 buf[1] = 0x00;
 // コントロールレジスタ2。
 buf[2] = 0x00;
 // 秒レジスタ。とりあえず、58秒。
 buf[3] = 0x58;
 // 分レジスタ。とりあえず、59分。
 buf[4] = 0x59;
 // 時レジスタ。とりあえず、23時。
 buf[5] = 0x23;
 // 日レジスタ。とりあえず、23日。
 buf[6] = 0x23;
 // 曜日レジスタ。とりあえず、木曜日。
 buf[7] = 0x04;
 // 月レジスタ。とりあえず、8月。
 buf[8] = 0x08;
 // 年レジスタ。取りあえず、12年。
 buf[9] = 0x12;
 // アラーム設定。なし。
 buf[11] = 0x00;
 buf[12] = 0x00;
 buf[13] = 0x00;
 // Clock出力設定。1Hz。
 buf[14] = 0x83;
 if (write(rtc, buf, 15) != 15)
 {
  printf("Error writeing to i2c slave\n");
  exit(1);
 }
 delay(20);
 printf("RTC Initialized.\n");
 
 int i;
 for (i = 0; i < 20; i++)
 {
  // データ要求ダミーコマンド送信。
  buf[0] = 0;
  if ((write(rtc, buf, 1)) != 1) {
   printf("Error writing to i2c slave\n");
   exit(1);
  }
  // データ読み込み。
  if (read(rtc, buf, 16) != 16)
  {
   printf("Uneble to read\n");
   exit(1);
  }
  
  // 西暦年。以下数値はBCD。
  printf("20");
  printf("%x", buf[8]);
  printf("/");
  // 月。下位9bit。
  printf("%x", buf[7] & 0x1f);
  printf("/");
  // 日。下位10bit。
  printf("%x", buf[5] & 0x3f);
  printf(" ");
  // 曜日。
  switch (buf[6] & 0x07) {
  case 0:
   printf("Sun");
   break;
  case 1:
   printf("Mon");
   break;
  case 2:
   printf("Tue");
   break;
  case 3:
   printf("Wed");
   break;
  case 4:
   printf("Thr");
   break;
  case 5:
   printf("Fri");
   break;
  case 6:
   printf("Sat");
   break;
  default:
   printf("   ");
   break;
  }
  printf("   ");
  // 時。
  printf("%x", buf[4] & 0x3f);
  printf(":");
  // 分。
  printf("%x", buf[3] & 0x7f);
  printf(":");
  // 秒。
  printf("%x", buf[2] & 0x7f);
  printf("\n");
  
  delay(500);
 }
 
 return 0;
}



  どうやら、RTCから情報を取得できているようです。

2012年8月14日火曜日

Arduino と GR-SAKURA I2C編2 秋月I2C LCD


 前回の秋月I2C RTCに続いて、秋月のI2C LCDモジュールを試してみました。結論から言うと、今のところ動かせてません(残念・・・)。
 といっても、Arduinoでは何事も無くというか、あっさり動いてしまいました。


 スケッチはこんなの。
#define RXDUINO

#ifdef RXDUINO
#include <rxduino.h>
#else
#include <Arduino.h>
#endif

#include <Wire.h>

#define LCD_CMD    (0x00)
#define LCD_DATA    (0x80)
#define LCD_ADDRESS (0x50)
#define LCD_CMD_CLEAR    (0x01)
#define LCD_CMD_HOME    (0x03)

// 関数プロトタイプ宣言。
void LCD_init();
void LCD_write(uint8_t rs, uint8_t data);
void LCD_clear();
void LCD_setCursor(byte col, byte row);
void LCD_putc(uint8_t c);
void LCD_puts(char *str);


void setup()
{
    // シリアルポートを9600bpsで開始。
    Serial.begin(9600);
    // 受信バッファにデータが入るまで待機。
    while (Serial.available() == 0);
    Serial.println("Initializing...");
    // 一旦、受信バッファにデータがあれば、
    if (Serial.available() > 0) {
        // 読み込んで、廃棄。
        char c = Serial.read();
    }
    
    // Bus-MasterとしてI2Cを開始。
    Wire.begin();
    
    
    // LCDの初期化。
    LCD_init();
    
    // LCDクリア。
    LCD_clear();
    
    // メッセージ表示。
    LCD_setCursor(0, 0);
    LCD_puts("Hello!!");
    LCD_setCursor(5, 1);
    LCD_puts("I2C LCD!!");

}

void loop()
{
    
}

// LCD初期化。
void LCD_init()
{
    delay(15);
    LCD_write(LCD_CMD, 0x01);
    delay(10);
    LCD_write(LCD_CMD, 0x38);
    delay(5);
    LCD_write(LCD_CMD, 0x0f);
    delay(5);
    LCD_write(LCD_CMD, 0x06);
    delay(5);
}

// LCDへデータ書き込み。
void LCD_write(uint8_t rs, uint8_t data)
{
    int rtnValue;
    
    if (rs == 0x00 || rs == 0x80)
    {
        // 0x00 ならコマンドモード。0x80 ならデータモード。
        
        // LCDへの送信準備開始。
        Wire.beginTransmission(LCD_ADDRESS);
        // モード(RS)送信。
        Wire.write(rs);
        // データ送信。
        Wire.write(data);
        // キューの送信。通信終了。
        rtnValue = Wire.endTransmission();
        Serial.print(rtnValue);
    }
    else
    {
        // rsの指定が0x00,0x80以外ならなにもしない。
    }
}

void LCD_clear()
{
    LCD_write(LCD_CMD, LCD_CMD_CLEAR);
    delay(2);
    LCD_write(LCD_CMD, LCD_CMD_HOME);
    delay(2);
}

void LCD_setCursor(byte col, byte row)
{
    byte offset[] = {0x00, 0x40};
    
    if (row > 1)    row = 1;
    if (col > 16)    col = 16;
    
    LCD_write(LCD_CMD, 0x80 | (col + offset[row]));
}

void LCD_putc(uint8_t c)
{
    LCD_write(LCD_DATA, c);
}

void LCD_puts(char *str)
{
    int i;
    for (i = 0; i < 16; i++)
    {
        if (str[i] == 0x00)
        {
            break;
        }
        else
        {
            LCD_putc((unsigned int)str[i]);
        }
    }
}

 ArduinoではWire.endTransmissino()関数はすべて正常終了して0を返してくるのに、GR-SAKURAでは一つ目は成功して0を返すものの、2つ目以降が2(アドレス送信でNACK)を返してきます。試しにロジアナで見てみると


 2つ目以降はNACK(ACK応答なし)となっています。なんで?コマンドを変えたりして試すと、やっぱり、一つ目はうまくいくけど2つ目以降が失敗します。試しに強引に、一つ目送信後、LCD をブレッドボードから(通電状態のままだけど)引っこ抜いて挿しなおして(リセットして?)やると2つ目も受信します。まあ、その場合は3つ目が受信できないんですが。GR-SAKURA側なのかI2C LCD側なのか切り分けができていないのですが、Arduinoで出来てGR-SAKURAでできないとなるとGR-SAKURAを疑いたくなりますが、LCDをリセット(引っこ抜いて挿し直し)すると次を受信するということはLCD側に問題があるようにも見えます。電源電圧はSAKURAは3.3Vですが、元々このI2C LCDは3.3V(~5V)動作なので問題ないはず。
 さて、困った。原因を調べるのに少し時間がかかりそうなので、一旦保留かな?何か他のものを先に試そうかな。

2012年8月13日月曜日

Arduino と GR-SAKURA I2C編1 秋月RTCモジュール

 以前に Arduinoで試した秋月のI2C接続RTCモジュールをGR-SAKURAで試してみました。ソース(スケッチ)もほぼそのままで動きました。



 ソースはこんな感じ。

#define RXDUINO

#ifdef RXDUINO
#include <rxduino.h>
#else
#include <Arduino.h>
#endif

#include <Wire.h>

#define RTC_ADDRESS (0xa2 >> 1)

int rtc[16];

void setup() {
    
    // シリアルポートを9600bpsで開始。
    Serial.begin(9600);
    // 受信バッファにデータが入るまで待機。
    while (Serial.available() == 0);        
    Serial.println("Initializing...");
    // 一旦、受信バッファにデータがあれば、
    if (Serial.available() > 0) {
        // 読み込んで、廃棄。
        char c = Serial.read();
    }
    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);
    // 日レジスタ。とりあえず、13日。
    Wire.write(0x13);
    // 曜日レジスタ。とりあえず、月曜日。
    Wire.write(0x01);
    // 月レジスタ。とりあえず、8月。
    Wire.write(0x08);
    // 年レジスタ。取りあえず、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);
    
}

このI2C RTCモジュールは3.3Vでも5Vでも動く(基本は3.3V用かな?)ので電源電圧も気にしない(3.3Vから取らなきゃだめだけど)でいいので、Arduino、GR-SAKURA(RaXino)でもどちらでも使えていいですね。I2C接続タイプのものはそういうのが多いのかな?というか、だんだんと低電圧化しているんだろうけど、自分はRaXino使うまでは5Vマイコンばっかりだったから、慣れないです。そのうち、5Vを3.3V系に突っ込んで、なにか壊したりしそうな予感。

2012年8月12日日曜日

Raspberry Pi + LCD Shield

 前回、Raspberry Pi で Arduino用のMotor Shieldが動かせたので、今回は調子に乗ってLCD Shieldを試して見ることにしました。と、いっても、WiringPi に元々LCDを使うための関数が作りこまれているので、今回使用したPrototying Lab LCD Shieldにピン配置を合わせて、コーディングしただけです。ただし、pythonのラップはなかったのでCで書いています。


 クロス開発しなくていいのがRaspberry Pi のいいところですが、コーディングをする際などにもたつきを感じたりすることがあります。そこで、ネット上でSambaの設定を調べて、Windowsパソコンとファイル共有できる状態にして、あらかたのコードを普段使っているWindowsの環境で作っておき、ビルド、実行、デバッグ、修正をRaspberry Pi上でやるような感じにしています。


 今回のコードはこんなかんじです。Gordons ProjectsWiringPiを使わしてもらっています。


#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>

#include <wiringPi.h>
#include <lcd.h>

// Raspduino のピン定義。
#define PIN_RXD 16 
#define PIN_TXD 15
#define PIN_D02 0
#define PIN_D03 1
#define PIN_D04 2
#define PIN_D05 3
#define PIN_D06 4
#define PIN_D07 5
#define PIN_D08 6
#define PIN_D09 7
#define PIN_D10 10
#define PIN_D11 12
#define PIN_D12 13
#define PIN_D13 14
#define PIN_SDA 8
#define PIN_SCL 9

#define PIN_LOW 0
#define PIN_HIGH 1

// LCD Shield のピン定義。
const int PIN_LCD_RS = PIN_D12;
const int PIN_LCD_E = PIN_D11;
const int PIN_LCD_D4 = PIN_D05;
const int PIN_LCD_D5 = PIN_D04;
const int PIN_LCD_D6 = PIN_D03;
const int PIN_LCD_D7 = PIN_D02;


// LCD関連定数の定義。
const int LCD_ROW_NUM = 2;
const int LCD_CLM_NUM = 16;
const int LCD_MODE_4BIT = 4;


int main(int argc, char **argv)
{
 int lcd;
 
 // WiringPi 初期化。
 if (wiringPiSetup() == -1) exit(1);
 
 // LCD初期化。
 lcd = lcdInit(LCD_ROW_NUM, // LCD行数。
    LCD_CLM_NUM,  // LCDカラム数。
    LCD_MODE_4BIT,  // 4bitモード。
    PIN_LCD_RS,  // RS
    PIN_LCD_E,  // E
    PIN_LCD_D4,  // 以下データピン
    PIN_LCD_D5,
    PIN_LCD_D6,
    PIN_LCD_D7,
    0, 0, 0, 0);
 if (lcd == -1)
 {
  printf("LCD Initialize Error\n");
  return 1;
 }
 
 sleep(1);
 
 lcdPosition(lcd, 0, 0);
 lcdPuts(lcd, "Hello,Raspberry!");
 
 lcdPosition(lcd, 0, 1);
 lcdPuts(lcd, "Raspduino!!");
   
 return 0;
}


 Raspduino (Raspberry Pi GPIOコネクタ ←→ Arduino ピン配置変換)アダプタが思ったよりも使えそうなので、回路図(というのは恥ずかしすぎるものですが)的にはこんな感じです。


 要はRaspberry Pi のGPIOを ロジックレベル変換モジュール(自分は秋月の双方向ロジックレベル変換モジュールを使いました)とI2Cレベル変換モジュール(自分は秋月のI2Cレベル変換モジュールを使いました)でRaspberry Pi側3.3VとArduino側コネクタを切り離し(変換)しています。Arduino側はジャンパの切り替えで外部電源とRaspberry Piの5V(USBコネクタからの給電)を切り替えられるようにしています。ピンマッピング等の検討はこんな感じ

2012年8月10日金曜日

Raspberry Pi その後

 本来、「世界中の子供達にコンピュータを」ということで作られたRaspberry Piですが、今はもっぱら、何十年か前に子供だった大きなお友だちの皆さんが、熱くなっているわけです。そんなお友達の一人である自分の Raspberry Pi でのもっぱらの遊び方は GPIOでなんか出来ないか?というところなんですが、その為の準備として、raspbian環境に色々インストールしてみました。


 DebianとかLinuxとか全然わからないので(Debianって天然水かなんかかと思った)、その道に詳しい人達のサイトなんかを参考にしながら、適当に色々やってみました。

 ブラウザは 普段Chrome を使っているので、Chromium を入れました。

     sudo apt-get install chromium

 日本語表示のために フォントをインストール。

     sudo apt-get install ttf-kochi-gothic xfonts-intl-japanese
     sudo apt-get install ttf-takao-mincho
     sudo apt-get install ttf-takao

日本語入力用のIMEをインストール。

     apt-get install ibus-anthy

Preferences → IBusPreferences → Input Method → Select an input method → Japanese → Anthy をAddすると、画面下のタスクバーの右下にキーボードマークが出てIMEが使えるように。[Ctrl] + [Space] キーでOn/Off。

必要ならlocal設定を変更。自分はインストール時に設定してあった。

     sudo raspi-config

で初期起動時のような設定画面がコンソールに表示されます。

 C/C++ でのプログラミング用に Geany というエディタを入れてみました。サイトを見ながらインストールしたんですが、はじめはGDBのプラグインが上手くインストールできず、なんか色々やっているうちに、使えるようになったんですが、自分の中で手順が整理できてないです。よくわからない状態でとりあえず、インストールできたって感じで説明できないです。すみません。

 あと、xterm。Greany をインストールしているときに、必要だった?かな?

     sudo apt-get install xterm

スペックがスペックなので、Eclipseとかは動かせませんが、クロス開発しなくていいのが、Raspberry Pi のいいところですね。
 






2012年8月9日木曜日

Raspduino (Raspberry Pi 用GPIOアダプタ) 動作確認1

 前回のRaspberry Pi 用GPIOアダプタの配線がだいぶ進んだので、動作確認をしてみました。一応、Raspduinoとでも呼んでおきます。そういえば、PCWatchの武蔵野電波のプロトタイパーズにRaspberry Piの記事 がありました。武蔵野電波は日本語訳リファレンスがあるArduinoのWikiとか、船田氏の訳書とかも有名ですね。この記事ではハードウェアペリフェラルの26pinにファイルとしてアクセスしていますが、自分は使い始めたばかりのpythonで、ここにあるRPi.GPIO とか ここのWiringPi とかで行ってみたいと思います。

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(22, GPIO.OUT)

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

まずは、RPi.GPIO の方でディジタル出力を確認しました。LEDと抵抗を出力ピンとグランドの間に入れて、上記のソースでLEDの点滅を確認しました。 次に、WiringPi の方でPWM出力を試してみました。
import wiringpi
import time

io = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_PINS)
io.pinMode(1, io.PWM_OUTPUT)

while 1:
    count = 0;
    while count < 100:
        io.pwmWrite(1, count)
        count += 1
        time.sleep(0.1)
    while count > 0:
        io.pwmWrite(1, count)
        count -= 1
        time.sleep(0.1)



 抵抗は通さずに、PWM出力ピン→LED→GNDに接続してしまうことにしました。



 タイマーでじわじわ明るくなったり、暗くなったりするのを確認。調子に乗って、Motor Shieldを装着。


 こちらは動きませんでした・・・。PWM出力AchのLEDはそれっぽく強弱を繰り返して点灯するのですが、モータは回りません。PWMの信号周波数とかの問題だろうか?週末にオシロを引っ張りだしてきて、出力信号を見てみることにしよう。
 動きました。PWM出力以外のdirectionピンやbrakeピンを初期化していなかったのと、アナログ出力の設定値が0~254だと勝手に思っていたんですが、実際には0~1023でした。Arduinoでは254なので、そう思い込んでいて、LEDくらいは光るけどモータを動かすには足りない値を設定していたようです。breakピンの状態に寄ってはブレーキがかかっていたかもしれません。



2012年8月8日水曜日

Raspberry Pi 用GPIOアダプタの検討

 Raspbery Pi にはボード上に26pinのピンヘッダが出ています。これをつかって何かできないかと思っているんですが・・・、とりあえず、変換アダプタを作ってみようと思っています。


 まだ、作りかけですが、イメージとしては上の写真のような感じ。これを26pinヘッダに挿すと、


 こんな感じで端子の並びが変換されて、


 こんな感じでスタックできるような・・・、を検討中。上手くいくといいけど。
(※写真は想像図です。動きません・・・・。)

2012年8月7日火曜日

ArduinoとGR-SAKURA (micro)SD-CARD編1

 シリアル、analogRead, analogWrite, Ethernet と来て、次は(micro)SD-CARDです。Arduino の Ethernet Shield はmicroSDカードスロットもついています。GR-SAKURA や RaXino では本体裏側に標準でmicroSDカードスロットがついています。



 動かしたソース(スケッチ)は次のようなものです。

#define RXDUINO

#ifdef RXDUINO
#include <rxduino.h>
#include <sdmmc.h>
#else
#include <Arduino.h>
#include <SD.h>
#endif

#ifdef RXDUINO
SDMMC SD;
#endif

void setup()
{
    // シリアルポートを9600bpsで開始。
    Serial.begin(9600);
    // 受信バッファにデータが入るまで待機。
    while (Serial.available() == 0);        
    Serial.println("Initializing...");
    // 一旦、受信バッファにデータがあれば、
    if (Serial.available() > 0) {
        // 読み込んで、廃棄。
        char c = Serial.read();
    }
    
    // SDカード(利用機能)の初期化。
    Serial.println("Initializing SD card function...");
#ifdef RXDUINO
    SD.begin();
#else
    // Arudion + Ethernet SD Shield では、SPIの
    // SDカードのCEは4pinになるが、SPIを使う場合は、
    // SS(10pin)を常にOUTPUTモードにしておく必要がある。
    pinMode(10, OUTPUT);
    SD.begin(4);
#endif
    Serial.println("SD initializatoin done.");
    
    File file = SD.open("test.txt", FILE_WRITE);
    
    file.println("Hello, GR-SAKURA!!");
    
    file.close();
    
    Serial.println("finished.");
}

void loop()
{
}

Arduino + (Ethernet &)SD Shield でも GR-SAKURA でも microSD-CARDのルート直下に"Test.txt"ファイルが作成され、ファイルの中身が"Hello, GR-SAKURA!!"となっていることが確認できました。

2012年8月5日日曜日

ArduinoとGR-SAKURA Ethernet編1

 シリアルanalogRead, analogWrite の次は、Ethernet について試してみようと思います。Ethernetといっても色々あるので、今回はArduinoとGR-SAKURAを超簡易版Web Serverにするスケッチを試します。Arduinoのサンプルスケッチの中にあるAD値をブラウザで表示できるWeb ServerのサンプルのAD部分を省略した以下のソース(スケッチ)をArduino と GR-SAKURA で動かしてみました。

#define RXDUINO

#ifdef RXDUINO
#include <rxduino.h>
#else
#include <Arduino.h>
#include <SPI.h>
#endif

#include <Ethernet.h>

#ifdef RXDUINO
TEthernet Ethernet;
#endif

// MACアドレスは適宜変更が必要。
byte mac[] = {
    0x90, 0xA2, 0xDA, 0x0D, 0x02, 0x8C};

EthernetServer server(80);

void setup()
{
    // シリアルポートを9600bpsで開始。
    Serial.begin(9600);
    // 受信バッファにデータが入るまで待機。
    while (Serial.available() == 0);        
    Serial.println("Initializing...");
    // 一旦、受信バッファにデータがあれば、
    if (Serial.available() > 0) {
        // 読み込んで、廃棄。
        char c = Serial.read();
    }
    
    // DHCPでネットワークを開始する。
    if (Ethernet.begin(mac) == 0) {
        // DHCPのアドレス取得に失敗。
        Serial.println("Failed get address on DHCP");
        while(1);
    }
    Serial.println("Get IP address from DHCP is success.");
    // サーバーの開始して、取得アドレスを報告。
    server.begin();
    Serial.print("Please access by browser at http://");
    Serial.println(Ethernet.localIP());
}

void loop()
{
    // クライアントの接続リスン。
    EthernetClient client = server.available();
    // サーバに接続されたクライアントがあれば、
    if (client) {
        Serial.println("new client");
        
        boolean currentLineIsBlank = true;
        // クライアントが接続されていれば、
        while (client.connected()) {
#ifdef RXDUINO
            // (Rxduinoでの)Ethernet受信処理実行。
            Ethernet.processPackets();
#endif
            // クライアントから受け取ったデータがあれば、
            if (client.available()) {
                // データを受信してシリアルへ。
                char c = client.read();
                Serial.write(c);
                // EOL(\n)を受け取ったら、
                if (c == '\n' && currentLineIsBlank) {
                    // http レスポンスを送信する。
                    client.println("HTTP/1.1 200 OK");
                    client.println("Content-Type: text/html");
                    client.println("Connection: close");
                    client.println();
                    client.println("<!DOCUTYPE HTML>");
                    client.println("<html>");
                    client.println("Hello!");
                    client.println("</html>");
                    break;
                }
                
                // EOL(\n)を受け取ったら、
                if (c == '\n') {
                    currentLineIsBlank = true;
                }
                // 
                else if (c != '\r') {
                    currentLineIsBlank = false;
                }
            }
        }
        
        delay(1);
        
        client.stop();
        Serial.println("client disconnected");
    }       
}



 Arudiono では、Ethernetを使うためにはEthernet Shieldが必要になりますが、GR-SAKURAやRaxinoは標準でEthernetポートを持っています。


 Arduino + Ethernet Shield では当然ながら動きます。


 GR-SAKURA(Rxduino)ではEthernet受信処理を行うのに"Ethernet.prosessPackets();"という記述が必要なようで、これをいれないとうまく動きませんでした。
 とりあえず、Web Serverの動作は確認できたかな?

2012年8月4日土曜日

ArduinoとGR-SAKURA analogRead、analogWrite編

 シリアルに続いて、ArduinoとGR-SAKURAのanalogRead, analogWrite についてです。
以下のソース(スケッチ)をArduino と GR-SAKURA で動かしてみました。

#define RXDUINO

#ifdef RXDUINO
#include <rxduino.h>
#else
#include <Arduino.h>
#endif

#define INTERVAL 500
#define PIN_ANALOG_IN0    0
#define PIN_ANALOG_OUT    9


void setup()
{
    // IO初期化。
    pinMode(PIN_ANALOG_OUT, OUTPUT);
#ifdef RXDUINO
    analogReference(EXTERNAL);
#endif
    
    // シリアルポートを9600bpsで開始。
    Serial.begin(9600);
    // 受信バッファにデータが入るまで待機。
    while (Serial.available() == 0);        
    Serial.println("Hello!");
    // 一旦、受信バッファにデータがあれば、
    if (Serial.available() > 0) {
        // 読み込んで、廃棄。
        char c = Serial.read();
    }
}

void loop()
{
    unsigned int intValue = analogRead(PIN_ANALOG_IN0);
    Serial.println(intValue);
    analogWrite(PIN_ANALOG_OUT, (int)(intValue / 4));
    delay(INTERVAL);
}

ブレッドボード上に可変抵抗をおいて、電源電圧からボリューム調整で分圧した電圧をAN0に入れて、analogRead(AN0)で読み込みます。読み込んだ10bit値を4でわって8bit値に縮めて、analogWrite(PIN9)でPWM出力して抵抗を直列に入れたLEDを光らせています。



 一応、同じような動きをしてくれていますね。ここでも、注意が必要なのは、電源電圧で、Arduinoは5V、GR-SAKURAは3.3Vで、PWM等で供給できる電圧も当然低くなるわけで。
 また、AD入力も3.3V以上の入力を加えても測定できないばかりか、壊してしまう可能性もあるので、要注意です。

 また、GR-SAKURAでは電源3.3Vをブレッドボードに供給して、当然、最大3.3Vがアナログ入力に帰ってくるわけですが、
   analogReference(EXTERNAL);
を入れておかないと、3.3Vを5Vを1023としたときの値、3.3V→675として返して来てしまいます。3.3Vを1023として取得したい場合は、上記の記述が必要でした。

2012年8月3日金曜日

ArduinoとGR-SAKURA シリアル通信編

せっかくなので、ArduinoとGR-SAKURAボードの両方で遊んでみようと思います。まずは、手頃というか、何はなくともはじめに必要になるシリアル通信を試します。

/*GR-SAKURA Sketch Template Version: V1.00*/
#include <rxduino.h>

char c;

void setup()
{
    // シリアルポートを9600bpsで開始。
    Serial.begin(9600);
    // 受信バッファにデータが入るまで待機。
    while (Serial.available() == 0);        
    Serial.println("Hello, GR-SAKURA!");
    // 一旦、受信バッファにデータがあれば、
    if (Serial.available() > 0) {
        // 読み込んで、廃棄。
        c = Serial.read();
    }
}

void loop()
{
    // 受信バッファにデータがあれば、
    if (Serial.available() > 0) {
        // 読み込んで、
        c = Serial.read();
        // エコーバック。
        Serial.write(c);
        // 特定の文字の時だけ、メッセージ。
        switch (c) {
            case 'A':
                Serial.println();
                Serial.println("Hello!");
                break;
            case 'B':
                Serial.println();
                Serial.println("SAKURA");
                break;
            case 'C':
                Serial.println();
                for (int i = 0; i < 0x10; i++) {
                    Serial.print(i, HEX);
                }
                Serial.println();
                break;
            default:
                break;
        }
    }
}

 上記のようなソース(スケッチ)をGR-SAKURA用にWebコンパイラ上で作成、ビルドして、Webコンパイラからソースをコピペして、Arduino-IDE上に貼り付けて、ほぼそのまま(includeを変えるくらい)ビルドして、それぞれ、GR-SAKURAとArduino Unoに書き込んで実行しました。


 GR-SAKURAでは上のようにTeraTerm等での確認になりますが、下のArduinoと同じように動作しています。(ターミナルの改行の動作の設定が違っていたので、改行の状態が違っていますが。)


 だから、何?といわれると、別にどうということはないんですが。
 ちなみに通常のArduino(Unoも含めて)はシリアルポートは一つで、PCと接続・給電をかねたUSB端子に割当たっていますが、Arduino MegaだとSerialの他にSerial1,Serial2,Serial3のハードウェアシリアルが使えます。また、SoftwareSerial機能を使うと通常のGPIOピンにソフトウェア的(CPUの負荷を使って)にシリアル通信の機能を持たせることも可能です。
 一方のGR-SAKURAの方では、SerialはMega同様Serial,Serial1,Serial2,Serial3の4本を利用できるようです。ソフトウェアで任意のピンをシリアル通信させるようなライブラリは現状(2012年8月現在)はないようです。

2012年8月1日水曜日

GR-SAKURA + Motor Shield

 がじぇるね GR-SAKURA ボードはArduinoとピン互換ということで、それなら、Arduinoのシールドはどの程度流用できるのか?というのは気になるところです。大きな違いは電源の5Vと3.3Vかと思います。GR-SAKURAボードやRaXinoボードではデフォルトでは5V Power端子には出力が出ていません。5V出力を供給するにはボード上のジャンパパターン「J2」をはんだづけしてショートする必要があります。


 GR-SAKURAでは表側のコネクタ脇に、RaXinoでは裏側の同じような位置にあります。このJ2はデフォルトオープンなのではんだづけしてショートします。


 そうすると、5Vピンに電源が供給されます。ただし、USBから給電している場合、実際に5Vピンに出力される電圧は4Vくらいになります。これは、USBの電源との間に保護ダイオードが入っているからで、ACアダプタから給電する場合はACアダプタの電源がそのまま出力されます。気をつけないといけないのは、GR-SAKURAやRaXinoではACアダプタを利用した場合はこの端子にACアダプタの出力がそのまま出てきて、ACアダプタは5Vを使う前提ということでしょうか。Arduinoの場合はUSB給電の場合はUSBの5Vがそのまま出てきて、ACアダプタ(普通9Vくらいを使う?)の場合は三端子を通して5Vが給電されます。なので、今回はGR-SAKURAにUSB給電なので、5Vが実際には4Vくらいになっています。
 Motor Shield の方はドライバICのL298のカタログスペックで行くと、電源(回路側)が5V以上、ロジックのHIGHレベルのスレッショルドが3V以上です。PWM端子は3.3Vで行けそうですが、電源が4Vで動くかどうか、試してみました。(とりあえず、モータを回せるかを確認しますが、Motor Shieldは電流検出してAnalog入力端子に電圧を戻す機能もあり、ここに3.3V以上かかると壊れる可能性ありです。今回は、PWM出力を50%くらいにして、Analog入力端子にあまり大きな電圧がかからないようにしたつもり?で試しました。)


 GR-SAKURA に Motor Shield をスタックして、


 ついでに、センサ接続用に作ったコネクタシールドも電源を5Vと3.3Vをジャンパで切り替えられるようにしてから、スタックしてみました。


 以前にArduinoで試した台車に載せて


 試してみたところ、とりあえずモータは回りました。Arduinoのスケッチを流用して、(単純すぎて、CPUがもったいないですが)同じ動きをさせられるか、試していこうと思います。