C言語(SDCC)を使用して、TMPZ84C015でのI2Cデバイスを使用してRTC(DS3231)、温度・湿度のLCD表示プログラムを作成したいと思います。
作成したプログラムをLCDに表示させると下記のようになります。
早速、必要なモジュールを確認していきたいと思います。
今回使用している、LCD、RTC、温湿度センサーは次のようなものです。
LCD:「KKHMF DC 5V 1602 LCD ディスプレイモジュール 16×2キャラクタ LCDブルーブラックライト」と
「EasyWordMall 1602 LCD ブラック IIC/I2C/TWI/SPI シリアル インタフェース ボード モジュール」
(ともにamazonです)
I2C シリアル インタフェース ボード モジュール I2Cアドレス設定はオープンの状態です。
プログラムは「Z80(SDCC)に萌えたい。TMPZ84C015/I2C書き込み」を参考にします。
RTC:リアルタイムクロックは、HiletgoのRTCモジュール(DS3231とEEPROM(AT24C32)) (amazon)
プログラムは、「Z80(SDCC)に萌えたい。TMPZ84C015/I2C読み込み」を参考にします。
また、RTCへの時刻設定は、「 Z80(SDCC)に萌えたい。TMPZ84C015/I2C読み込み」 のプログラムの
中で設定を行っています。
温湿度センサー:
AM2321を使用します。秋月電子で販売していましたが、廃品のようです。後継のAM2322が同じように
使えるかは確認していません^^;
通信方法は「Arduinoに湿度センサー(AM2321)を繋いでみる。」を参考にしました。
I2C用の制御ICは、PCA9564PWです。
ではプログラムの方を確認してみます。
各モジュールのI2Cアドレス
const uint8_t i2c_lcd_addr = 0x27;
uint8_t tmp_addr=0x5c;
uint8_t rtc_addr=0x68;
LCDの表示バッファーとして1行目、2行目の配列を確保します。
uint8_t mesg1[16];
uint8_t mesg2[16];
I2C読み書き用に配列と、ポインタを確保します。
uint8_t len=8;
uint8_t out_buff[8];
uint16_t *buff = out_buff;
I2CイニシャライズとLCDの初期化を行った後、while文で無限ループを作成します。
無限ループ内では、RTCの読み込みとLCD 一行目の表示、温湿度センサー読み込みとLCD2行目の表示を行います。
LCD2行目最終桁(16桁目)をスペースと塗りつぶしの交互ブランキングを行ったのち、 500mSの時間つぶし を行います
RTCの読み込みでは、アドレス0x00から0x06までのレジスターを読み込みます。
各レジスタの上位ビットは必要ビットをマスクしてから4bit右シフトし、0x30と論理和を取ることで、asciiコードの数値に変換しています。
下位ビットも同様に必要ビットをマスクしてから、 0x30と論理和を取ることで、asciiコードの数値に変換しています。
2022/4/10:分の値で39分までの表示になっていたので修正しました。
mesg1[14] =((out_buff[1] & 0x3f) >>4) | 0x30; //minutes の0x3fを0x7fに変更しました。
data_set = 0x00; //rtc read
i2c_write(rtc_addr,1,&data_set);
i2c_read(rtc_addr,len,buff);
mesg1[0] = 0x32; // 2
mesg1[1] = 0x30; // 0
mesg1[2] =(out_buff[6] >>4) | 0x30; //years
mesg1[3] =(out_buff[6] & 0x0f) | 0x30;
mesg1[4] = 0x2f; //
mesg1[5] =((out_buff[5] & 0x1f) >>4) | 0x30; //months
mesg1[6] =(out_buff[5] & 0x0f) | 0x30;
mesg1[7] = 0x2f; //
mesg1[8] =((out_buff[4] & 0x3f) >>4) | 0x30; //days
mesg1[9] =(out_buff[4] & 0x0f) | 0x30;
mesg1[10] = 0x20; //
mesg1[11] =((out_buff[2] & 0x3f) >>4) | 0x30; //hours
mesg1[12] =(out_buff[2] & 0x0f) | 0x30;
mesg1[13] = 0x3a; //
mesg1[14] =((out_buff[1] & 0x7f) >>4) | 0x30; //minutes
mesg1[15] =(out_buff[1] & 0x0f) | 0x30;
LCDへの表示は、1ラインのアドレスをしてしてから、data_lcd8bitwriteで書き込みします。
data_set = 0x80;
lcd8bitwrite(data_set); //x80 (1line top address)
for (i=0; i<16 ;i++){
data_lcd8bitwrite(mesg1[i]);
// printf("%c\n",mesg1[i]);
}
温湿度センサー(AM2321)は一度ダミーの信号を送り、 AM2321 を起こします。
続いて、4バイト読み込み用のread信号を送ります。
読み込まれたI2C信号は次のように構成なります。
0x03:レジスターを読み込みます
0x04:4つのレジスター内容を4バイトで返します。
0x??:湿度の高位バイトを返します
0x??:湿度の低位バイトを返します
0x??:温度の高位バイトを返します
0x??:温度の低位バイトを返します
0xCRC1: CRCを2バイトに分けて返します
0xCRC2:
CRCは今回は計算は行わず、無視しています。
湿度は、out_buff[2]、[3]に、温度はout_buff[4]、[5]にデータが入ります。
湿度は、上位桁を8ビット左シフトして、下位8ビットと論理和を取り、16ビットのint値に合成します。
温度は、上位8ビット目が符号を表すので、0x7fでマスクしてから、 上位桁を8ビット左シフトして、下位8ビットと論理和を取り、16ビットのint値に合成します。
data_addr = 0x00; //AM2321 Wake up
i2c_write(tmp_addr,1,&data_addr);
sleep(1);
out_buff[0] = 0x03; //4 bytes temperature and humidity
out_buff[1] = 0x00;
out_buff[2] = 0x04;
i2c_write(tmp_addr,3,buff);
sleep(2);
i2c_read(tmp_addr,len,buff); // get temperature and humidity
hum=((uint8_t)out_buff[2] << 8) | (uint8_t)out_buff[3]; //humidity
temp=((((uint8_t)out_buff[4]) & 0x7f) << 8) | (uint8_t)out_buff[5]; //temperature
// printf("temp=%1x,%1x,%1x\n",temp/100,(temp%100)/10,(temp%100) %10);
温度の符号を取り出し、+、-を表示します。
if ((out_buff[4] & 0x80) == 0x80){ //temperature Upper 7-bit polarity
mesg2[0]= 0x2d; // -
}else{
mesg2[0]= 0x2b; // +
}
温度と湿度はそれぞれ、10倍された値となっています。
なので、少数点はそのまま、表示させます。
表示方法は、100で割って商を2桁目(10の位)
100で割って、余りを10で割って商を1桁目(1の位)
100で割って、余りを10で割って余りを小数点1桁目にして、0x30と論理和を取ってasciiコードに変換しています。
LCD2行目のを指定して、data_lcd8bitwriteでLCDに表示させています。
mesg2[1] = ((temp/100) | 0x30) & 0x3f; //temperature Tenth digit
mesg2[2] = (((temp%100) /10) | 0x30 ) & 0x3f; //temperature 1st digit
mesg2[3] = 0x2e; // .
mesg2[4] = (((temp%100) %10) | 0x30 ) & 0x3f; //temperature 1st decimal place
mesg2[5] = 0xdf; // do
mesg2[6] = 0x43; // C
mesg2[7] = 0x20; // space
mesg2[8] = ((hum/100) | 0x30 ) & 0x3f; //humidity Tenth digit
mesg2[9] = (((hum%100) /10) | 0x30 ) & 0x3f; //humidity 1st digit
mesg2[10] = 0x2e; // .
mesg2[11] = (((hum%100) %10) | 0x30 ) & 0x3f; //humidity 1st decimal place
mesg2[12] = 0x25; // %
mesg2[13] = 0x52; // R
mesg2[14] = 0x48; // H
if(tenmetu == 0x01){ // Blinking
mesg2[15] = 0x20;
}else{
mesg2[15] = 0xff;
}
tenmetu = tenmetu ^ 0x01;
data_set = 0xc0;
lcd8bitwrite(data_set); //x80 + 0x40(2 line top address)
for (i=0; i<16;i++){
data_lcd8bitwrite(mesg2[i]);
// printf("%2x\n",mesg2[i]);
}
全体的なプログラムは下記のとおりです。
// I2C(PCA9564) rtc(DS3231) temp,hum(AM2321) LCD(i2c) Display
#include <stdio.h>
#include <stdint.h>
extern void i2c_init(void);
extern uint8_t i2c_read(uint8_t,uint8_t,uint16_t *);
extern uint8_t i2c_write(uint8_t,uint8_t,uint16_t *);
extern void sleep(uint16_t);
void i2c_lcd_init(void);
void lcd4bitwrite(uint8_t command);
void lcd8bitwrite(uint8_t command);
void data_lcd4bitwrite(uint8_t command);
void data_lcd8bitwrite(uint8_t command);
const uint8_t i2c_lcd_addr = 0x27;
int main(){
uint8_t tmp_addr=0x5c;
uint8_t rtc_addr=0x68;
uint8_t len=8;
uint8_t out_buff[8];
uint16_t *buff = out_buff;
uint8_t data_set=0x00;
uint8_t mesg1[16];
uint8_t mesg2[16];
uint8_t tenmetu=0x01;
uint8_t data_addr = 0x00;
uint16_t temp,hum;
int8_t i;
i2c_init();
i2c_lcd_init();
while(1){ // main loop
data_set = 0x00; //rtc read
i2c_write(rtc_addr,1,&data_set);
i2c_read(rtc_addr,len,buff);
mesg1[0] = 0x32; // 2
mesg1[1] = 0x30; // 0
mesg1[2] =(out_buff[6] >>4) | 0x30; //years
mesg1[3] =(out_buff[6] & 0x0f) | 0x30;
mesg1[4] = 0x2f; //
mesg1[5] =((out_buff[5] & 0x1f) >>4) | 0x30; //months
mesg1[6] =(out_buff[5] & 0x0f) | 0x30;
mesg1[7] = 0x2f; //
mesg1[8] =((out_buff[4] & 0x3f) >>4) | 0x30; //days
mesg1[9] =(out_buff[4] & 0x0f) | 0x30;
mesg1[10] = 0x20; //
mesg1[11] =((out_buff[2] & 0x3f) >>4) | 0x30; //hours
mesg1[12] =(out_buff[2] & 0x0f) | 0x30;
mesg1[13] = 0x3a; //
mesg1[14] =((out_buff[1] & 0x7f) >>4) | 0x30; //minutes
mesg1[15] =(out_buff[1] & 0x0f) | 0x30;
data_set = 0x80;
lcd8bitwrite(data_set); //x80 (1line top address)
for (i=0; i<16 ;i++){
data_lcd8bitwrite(mesg1[i]);
// printf("%c\n",mesg1[i]);
}
data_addr = 0x00; //AM2321 Wake up
i2c_write(tmp_addr,1,&data_addr);
sleep(1);
out_buff[0] = 0x03; //4 bytes temperature and humidity
out_buff[1] = 0x00;
out_buff[2] = 0x04;
i2c_write(tmp_addr,3,buff);
sleep(2);
i2c_read(tmp_addr,len,buff); // get temperature and humidity
hum=((uint8_t)out_buff[2] << 8) | (uint8_t)out_buff[3]; //humidity
temp=((((uint8_t)out_buff[4]) & 0x7f) << 8) | (uint8_t)out_buff[5]; //temperature
// printf("temp=%1x,%1x,%1x\n",temp/100,(temp%100)/10,(temp%100) %10);
if ((out_buff[4] & 0x80) == 0x80){ //temperature Upper 7-bit polarity
mesg2[0]= 0x2d; // -
}else{
mesg2[0]= 0x2b; // +
}
mesg2[1] = ((temp/100) | 0x30) & 0x3f; //temperature Tenth digit
mesg2[2] = (((temp%100) /10) | 0x30 ) & 0x3f; //temperature 1st digit
mesg2[3] = 0x2e; // .
mesg2[4] = (((temp%100) %10) | 0x30 ) & 0x3f; //temperature 1st decimal place
mesg2[5] = 0xdf; // do
mesg2[6] = 0x43; // C
mesg2[7] = 0x20; // space
mesg2[8] = ((hum/100) | 0x30 ) & 0x3f; //humidity Tenth digit
mesg2[9] = (((hum%100) /10) | 0x30 ) & 0x3f; //humidity 1st digit
mesg2[10] = 0x2e; // .
mesg2[11] = (((hum%100) %10) | 0x30 ) & 0x3f; //humidity 1st decimal place
mesg2[12] = 0x25; // %
mesg2[13] = 0x52; // R
mesg2[14] = 0x48; // H
if(tenmetu == 0x01){ // Blinking
mesg2[15] = 0x20;
}else{
mesg2[15] = 0xff;
}
tenmetu = tenmetu ^ 0x01;
data_set = 0xc0;
lcd8bitwrite(data_set); //x80 + 0x40(2 line top address)
for (i=0; i<16;i++){
data_lcd8bitwrite(mesg2[i]);
// printf("%2x\n",mesg2[i]);
}
sleep(500); //500mS wait
}
return(0);
}
void i2c_lcd_init(void){
//Display initialization
uint8_t data_set;
data_set=0x03;
lcd4bitwrite(data_set);
sleep(5);
lcd4bitwrite(data_set);
lcd4bitwrite(data_set);
data_set=0x02;
lcd4bitwrite(data_set); //4bit mode
data_set=0x28;
lcd8bitwrite(data_set); //function set 4bit bus,2 line ,1 line=8
data_set=0x0c;
lcd8bitwrite(data_set); //disp on,under cursor off,block
data_set=0x01;
lcd8bitwrite(data_set); //disp clr
sleep(2);
data_set=0x06;
lcd8bitwrite(data_set); //disp address incrmant on,disp shift off
data_set=0x02;
lcd8bitwrite(data_set); //cursor home set
sleep(2);
}
void lcd4bitwrite(uint8_t command){
command <<= 4;
command |= (uint8_t)0b00001000;
command &= (uint8_t)0b11111000;
i2c_write(i2c_lcd_addr,1,&command);
command |= (uint8_t)0b00001100;
command &= (uint8_t)0b11111100;
i2c_write(i2c_lcd_addr,1,&command);
command |= (uint8_t)0b00001000;
command &= (uint8_t)0b11111000;
i2c_write(i2c_lcd_addr,1,&command);
}
void lcd8bitwrite(uint8_t command){
uint8_t command2;
command2 = command;
command >>= 4;
lcd4bitwrite(command);
lcd4bitwrite(command2);
}
void data_lcd4bitwrite(uint8_t command){
command <<= 4;
command |= (uint8_t)0b00001001;
command &= (uint8_t)0b11111001;
i2c_write(i2c_lcd_addr,1,&command);
command |= (uint8_t)0b00001101;
command &= (uint8_t)0b11111101;
i2c_write(i2c_lcd_addr,1,&command);
command |= (uint8_t)0b00001001;
command &= (uint8_t)0b11111001;
i2c_write(i2c_lcd_addr,1,&command);
}
void data_lcd8bitwrite(uint8_t command){
uint8_t command2;
command2 = command;
command >>= 4;
data_lcd4bitwrite(command);
data_lcd4bitwrite(command2);
}
上記プログラムをi2c_lcd_rtc_tmp_main.cとして保存します。
コンパイルします。
$ sdcc -mz80 --out-fmt-ihx --code-loc 0xa000 --no-std-crt0 -o i2c_lcd_rtc_tmp_main.ihx mycrt0.rel i2c_lcd_rtc_tmp_main.c i2c_init.rel i2c_read.rel i2c_write.rel putchar.rel sleep.rel
i2c_lcd_rtc_tmp_main.c:124: warning 126: unreachable code
Conflicting flags in area _CODE
Conflicting flags in area _DATA
Conflicting flags in area _CODE
Conflicting flags in area _DATA
Conflicting flags in area _CODE
Conflicting flags in area _DATA
Conflicting flags in area _CODE
Conflicting flags in area _DATA
Conflicting flags in area _CODE
Conflicting flags in area _DATA
Conflicting flags in area _CODE
Conflicting flags in area _DATA
一部ワーニングが出ますが無視してコンパイルしました。
モニターのlコマンドで読み込み、実行します。
>l 20A00000DDE5DD210000DD3921C8FF39F921220039DD75F8DD74F9DD7EF8DD77FEDD7EF947
20A02000DD77FFDD36E900DD36FD01DD36C800CDE3A5CD3AA4DD36E90021210039E5116810
20A0400001D5CD80A6F1F1DD6EFEDD66FFE5116808D5CD01A6F1F1211100394D443632696C
中略
20A74000ED6AED523001193F1710F5CB10505FEBC9210300395E2B6ECD1FA7EBC9F1E1D142
08A76000D5E5F5CD22A7EBC9F8
00000001FF
OK
>
>c a000 y:
無限ループになっているので、停止は、リセットする必要があります。