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:

無限ループになっているので、停止は、リセットする必要があります。

おすすめの記事