ZYNQに接続したRTCが動いた!
Cosmo-Zの新バージョンはRTCを搭載しています。
ZYNQからI2C経由でRTC(Microchip MCP79411)を操作することができました。その手順を示します。
まず、EDK(最近の人はVivadoですね)で、PSの設定を開き、I2Cの0番を有効にします。
そして、Export Hardware SDKを行い、fsblを再生成し、boot.binを再生成します。
新しいboot.binで起動したら何かキーを押してブートを止め、u-bootのコンソールに入ります。
u-bootにはi2cという便利なコマンドがあります。i2c probeと打つと、I2Cのバスに様々なアドレス(1,3,5,7,9,11,13…)を順番に出力して、どんなデバイスがつながっているかを調べてくれます。
上の画面はi2c probeを行ったときの表示で、57と6Fのデバイスが見つかっています。57がMCP79411内のEEPROMで、6FがMCP79411内のRTCとSRAMです。
i2cコマンドを通じたメモリの読み書きは、以下のコマンドで行えます。
メモリリード:i2c md デバイスアドレス サブアドレス 長さ
メモリライト:i2c mw デバイスアドレス サブアドレス 値
これでひととおりMCP79411の動作が確かめられたら、Linuxで使えるようにしたいのですが、そのためにはデバイスツリーというのを書かなければなりません。
デバイスツリーの正しい書き方はよくわかりませんが、
ps7-i2c@e0004000 {
compatible = "cdns,i2c-r1p10";
status = "okay";
clocks = <0x2 0x26>;
interrupt-parent = <0x3>;
interrupts = <0x0 0x19 0x4>;
reg = <0xe0004000 0x1000>;
#address-cells = <0x1>;
#size-cells = <0x0>;
clock-frequency = <0x9c40>;
という記述を追加しました。clocksなどの設定の意味はよくわかりませんが、これでうまくいきました。clock-frequencyは40kHzにしています。ZYNQではI2C_0のレジスタは0xe0004000にあるようで、これはデザインによらず固定です。
これでLinuxを起動すると、(dmesgの部分に)
cdns-i2c e0004000.ps7-i2c: 40 kHz mmio e0004000 irq 57
と表示されます。
そして、/dev/i2c-0というデバイスが出来ています。

i2c-0というのは、ZYNQのI2C_0に相当します。これは汎用のI2Cデバイスのマスタなので、ユーザがプログラムを組めば自由に操作できます。
このデバイスを使うには、まず
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
をincludeし、open("/dev/i2c-0",O_RDWR)でオープンします。
int i2c = open("/dev/i2c-0",O_RDWR);
if(i2c < 0) {
printf("I2C Open error");
return -1;
}
デバイスのスレーブアドレスを指定するには、
ioctl(i2c, I2C_SLAVE, 0x6f);
とします。そうしたら、read()やwrite()を使って読み書きします。
char val = 0x00;
char data[32];
write(i2c, &val, 1);
read(i2c, &data, 32);
とすれば、サブアドレス0から、32バイトのデータが読み出されます。
実際に作ったプログラムを載せます。
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
int main() {
int i2c = open("/dev/i2c-0",O_RDWR);
if(i2c >= 0) {
unsigned char dat[256];
unsigned char addr;
ioctl(i2c, I2C_SLAVE, 0x6f);
printf("RTC\n");
addr = 0x0;
write(i2c, &addr, 1);
read(i2c, &dat, 32);
for(i=0;i<32;i++) {
if((i & 15) == 0) printf("%04X ",i);
printf("%02x ",dat[i]);
if((i & 15) == 15) printf("\n");
}
printf("\n");
printf("SRAM\n");
addr = 0x20;
write(i2c, &addr, 1);
read(i2c, &dat, 64);
for(i=0;i<64;i++) {
if((i & 15) == 0) printf("%04X ",i+0x20);
printf("%02x ",dat[i]);
if((i & 15) == 15) printf("\n");
}
printf("\n");
printf("EEPROM\n");
addr = 0x00;
ioctl(i2c, I2C_SLAVE, 0x57);
write(i2c, &addr, 1);
read(i2c, &dat, 128);
for(i=0;i<128;i++) {
if((i & 15) == 0) printf("%04X ",i);
printf("%02x ",dat[i]);
if((i & 15) == 15) printf("\n");
}
printf("\n");
printf("Unique ID\n");
addr = 0xf0;
ioctl(i2c, I2C_SLAVE, 0x57);
write(i2c, &addr, 1);
read(i2c, &dat, 8);
for(i=0;i<8;i++) {
if((i & 15) == 0) printf("%04X ",i);
printf("%02x ",dat[i]);
if((i & 15) == 15) printf("\n");
}
printf("\n");
}
else {
printf("no i2c\n");
}
return 0;
}
これで実行すると、

これでMCP79411のすべてのレジスタが表示されています。
(最後のUnique IDの部分はMACアドレスに使われる部分なので、モザイクをかけています。)
ICの購入時点ではRTCは止まっています。RTCをスタートするには、アドレス0にbit7(STビット)に'1'を書き込みます。また、初期状態ではVBATの端子にバックアップ電源をつないでいても、電源OFFで消えてしまいます。VBATを使えるようにするには、アドレス3のbit3(VBATENビット)を'1'にします。
RTCの複数のバイトに書き込むには、
dat[0] = 0; // アドレス0から dat[1] = 0x80; // スタート 0秒 dat[2] = 0x18; // 18分 dat[3] = 0x15; // 15時 dat[4] = 0x08; // VBATEN 曜日は無視 dat[5] = 0x14; // 14日 dat[6] = 0x01; // 1月 dat[7] = 0x17; // 2017年 dat[8] = 0x80; write(i2c, &dat, 9); // 8バイト書き込み
のようにすればよいようです。
![]()
今回使っているのはMCP79411ですが、MCP79410とMCP79412でも同じようにできると思います。
本当は、デバイスドライバを作ってデバイスツリーに組み込むか、どこかからMPC7941x用のデバイスドライバを探してくれば、OS起動時に自動的に時刻をセットしたりもできるのだとは思いますが、ユーザモードアプリでいいんじゃないかという気になってきました。
| 固定リンク




コメント