C言語(SDCC)を使って、STC8G1K08AでキャラクタLCDを使用して時計と温湿度表示の制御をしたいと思います。

キャラクタLCDは、前回の「KKHMF DC 5V 1602 LCD ディスプレイモジュール 16×2キャラクタ LCDブルーブラックライト」と「EasyWordMall 1602 LCD ブラック IIC/I2C/TWI/SPI シリアル インタフェース ボード モジュール」(ともにamazonです)を使用します。

リアルタイムクロックモジュール(RTC)は、DS3231とEEPROM(AT24C32)のモジュールになっているHiletgoのRTCモジュールを使用します。(アマゾンなど)
このモジュールはバッテリー用のソケットもついていますが、LIR2032(2次電池、充電可能)専用になっています。
今回は1次電池のCR2032を使用するので、充電用の抵抗(200Ω)を外しました。

温湿度センサーはAM2322を使用しました。(秋月電子など)

回路図は下記のとおりです。

まずは、RTCに時刻を設定することから始めます。

以前作成したZ80用のSDCCを参考にします。

I2Cを使用するので、I2Cアドレスを指定して、書き込みあるいは読み込みバイト数を指定し、バッファーに読み書きする関数を作成します。

void i2c_write(uint8_t i2c_addr_lcd,uint8_t number,uint8_t *i2c_data)
void i2c_read(uint8_t i2c_addr_lcd,uint8_t number,uint8_t *i2c_data)

// I2C 関連関数 
//

//I2C  データwrite関数

void i2c_write(uint8_t i2c_addr_lcd,uint8_t number,uint8_t *i2c_data){

    I2CMSCR = 0x01;                     //I2C スタート
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;   
    
    I2CTXD = ((i2c_addr_lcd << 1) | 0x00);  //i2c_addr + write 送信
    I2CMSCR = 0x02;
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;

    I2CMSCR = 0x03;                     //ACK 受信
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       
    uint8_t i;
    for(i=0;i <= (number -1);i++){
        
        I2CTXD = *(i2c_data+i);        //I2C_data送信
        I2CMSCR = 0x02;
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;       
        
        I2CMSCR = 0x03;                 //ACK 受信
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;       
    }
    I2CMSCR = 0x06;                     //I2C ストップ
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       
}
//I2C データread関数

void i2c_read(uint8_t i2c_addr_lcd,uint8_t number,uint8_t *i2c_data){

    I2CMSCR = 0x01;                     //I2C スタート
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;   
    
    I2CTXD = ((i2c_addr_lcd << 1) | 0x01);  //i2c_addr + read 送信
    I2CMSCR = 0x02;
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;

    I2CMSCR = 0x03;                     //ACK 受信
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       

    for(uint8_t i=0;i <= (number -1);i++){

        I2CMSCR = 0x04;                 //I2C data受信
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;
         *(i2c_data+i)= I2CRXD;       

        if (i == (number -1)){
            I2CMSST = 0x01;
            I2CMSCR = 0x05;                 //NACK 送信
            while(!(I2CMSST & 0x40));
            I2CMSST &= ~0x40;  
        }else{
            I2CMSST = 0x00;
            I2CMSCR = 0x05;                 //ACK 送信
            while(!(I2CMSST & 0x40));
            I2CMSST &= ~0x40;       
        }
    }    
    I2CMSCR = 0x06;                     //I2C ストップ
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       
}

RTCモジュール上のI2Cアドレスは
DS3231 : 0x68
AT24C32 : 0x57

RTCの設定レジスタは、BCD(二進化十進数、Binary-coded decimal)となっていますので、設定をコード変換しなくてはなりません。
方法としては、10で割って、商を2桁目、余りを1桁目にして |(論理和)を取り、必要なビットになりように&(論理積)を施しました。
例えば、月では、
out_buff[6] = (((months/10) << 4) | months%10) & 0x1f;

例として 2025年5月3日13時59分00秒の場合

   int16_t years,months,days,hours,minutes,seconds;
  
   years = 25;
    months = 5;
    days =  3;
    hours = 13;
    minutes = 59;
    seconds = 00;
    
    out_buff[0] = 0x00;                                         //address
    out_buff[1] = (((seconds/10) << 4) | seconds%10) & 0x7f;
    out_buff[2] = (((minutes/10) << 4) | minutes%10) & 0x7f;
    out_buff[3] = (((hours/10) << 4) | hours%10) & 0x3f;        //24h
    out_buff[4] = 0x00;
    out_buff[5] = (((days/10) << 4) | days%10) & 0x3f;
    out_buff[6] = (((months/10) << 4) | months%10) & 0x1f;
    out_buff[7] = (((years/10) << 4) | years%10);

配列をポインターに変換して、i2cアドレス、長さ、ポインタアドレスを指定してi2c_writeで書き込みします。

    uint8_t addr=0x68;
    uint8_t len=8;
    uint8_t out_buff[8];

    uint16_t *buff = out_buff;

    i2c_write(i2c_addr_rtc,len,buff);

続いてRTCからの読み込みは、最初にRTCの読み込みアドレスを書き込みしてから、順次読み込みしていきます。

buffの先頭は0x00(out_buff[0]=0x00)なので、1バイトだけ書き込みします。
続いて、i2c_readすると、buffは読み込みデータで上書きされます。

        uint8_t data_set = 0x00;                        //rtc read
        i2c_write(i2c_addr_rtc,1,&data_set);
        i2c_read(i2c_addr_rtc,len,buff);  

読み込みされたデータは、BCDコードなので、コード変換します。

        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_set = 0x80;
        lcd8bitwrite(i2c_addr_lcd,data_set);            //x80 (1line top address)
        uint8_t i;
        for (i=0; i<16 ;i++)
            data_lcd8bitwrite(i2c_addr_lcd,mesg1[i]);

全体のプログラムは下記のとおりです。(ファイルネーム STC8G1K08A_I2C_rtc_set.c)

// STC8G1K08A I2C RTC setting C言語版
//
// STC8G1K08A   MPU
// Flash ROM 8Kbyte
// RAM       1Kbyte
// 
// SDCC 4.0.0
//
// file name STC8G1K08A_I2C_rtc_set.c
// $ sdcc -mmcs51 --model-small --xram-size 0x400 --xram-loc 0x0000 --code-size 0x2000 STC8G1K08A_I2C_rtc_set.c
// $ packihx STC8G1K08A_I2C_rtc_set.ihx > STC8G1K08A_I2C_rtc_set.hex
// $ stcgal -P stc8g -t 22118.4 -p /dev/ttyUSB0 STC8G1K08A_I2C_rtc_set.hex
//

#include <8052.h>
#include <stdio.h>
#include <stdint.h>

//
// XFR(拡張RAM領域特殊機能レジスタ) 設定
//

#define CLKDIV (*(unsigned char volatile __xdata *)0xfe01)

#define I2CCFG (*(unsigned char volatile __xdata *)0xfe80)
#define I2CMSCR (*(unsigned char volatile __xdata *)0xfe81)
#define I2CMSST (*(unsigned char volatile __xdata *)0xfe82)
#define I2CSLCR (*(unsigned char volatile __xdata *)0xfe83)
#define I2CSLST (*(unsigned char volatile __xdata *)0xfe84)
#define I2CSLADR (*(unsigned char volatile __xdata *)0xfe85)
#define I2CTXD (*(unsigned char volatile __xdata *)0xfe86)
#define I2CRXD (*(unsigned char volatile __xdata *)0xfe87)

//BYTE 指定可能なレジスター

__sfr __at  0x8e AUXR  ;
__sfr __at  0xba P_SW2  ;

__sfr __at  0xb2 P3M0  ;    
__sfr __at  0xb1 P3M1  ;    
__sfr __at  0xca P5M0  ;    
__sfr __at  0xc9 P5M1  ;

__sbit __at 0xb3 SDA;
__sbit __at 0xb2 SCL;

//
// uart 関連関数
//
    
// uart 初期化
void uart_init(void) {
    
    P_SW2 = 0x80;
    CLKDIV = 0x02;          //システムクロック分周値を 1/2にセット
    P_SW2 = 0x00;
    
    AUXR = 0xc0;            //SYSCLK/1 timer0 or timer1 clock souce 
    SCON  = 0x50;           //UARTはモード1に設定、受信イネーブル
                            //SCON=01010000B
    TMOD |= 0x20;           //タイマー1を8ビット自動リロードモードに設定
                            //TMOD=00100000B
    TH1   = 0xf7;           //タイマーカウンターセット
    TL1   = 0xf7;
    TR1   = 1;              // タイマー1スタート

}

// 1文字シリアル出力
int putchar(int data) {
    TI = 0;                 //SCONのTI(1bit目)をケリア
    SBUF = data;            //dataからSBUFバッファーに格納
    while (!TI);            //SCONのTI(1bit目)が"1"なるまでループ
    return data;
}

// 1文字シリアル入力
int getchar(void) {
    while (!RI);            //SCONのRI(0bit目)が"1"なるまでループ
    RI = 0;                  //SCONのRI(0bit目)をクリア
    return SBUF;            // 受信データ返却
}   

//
// I2C 関連関数 
//

//I2C  データwrite関数

void i2c_write(uint8_t i2c_addr,uint8_t number,uint8_t *i2c_data){

    I2CMSCR = 0x01;                     //I2C スタート
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;   
    
    I2CTXD = ((i2c_addr << 1) | 0x00);  //I2C_ADDR + write 送信
    I2CMSCR = 0x02;
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;

    I2CMSCR = 0x03;                     //ACK 受信
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       
    uint8_t i;
    for(i=0;i <= (number -1);i++){
        
        I2CTXD = *(i2c_data+i);        //I2C_data送信
        I2CMSCR = 0x02;
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;       
        
        I2CMSCR = 0x03;                 //ACK 受信
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;       
    }
    I2CMSCR = 0x06;                     //I2C ストップ
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       
}
//I2C データread関数

void i2c_read(uint8_t i2c_addr,uint8_t number,uint8_t *i2c_data){

    I2CMSCR = 0x01;                     //I2C スタート
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;   
    
    I2CTXD = ((i2c_addr << 1) | 0x01);  //I2C_ADDR + read 送信
    I2CMSCR = 0x02;
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;

    I2CMSCR = 0x03;                     //ACK 受信
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       

    for(uint8_t i=0;i <= (number -1);i++){
        printf_tiny("i=%d \n\r",i);
        I2CMSCR = 0x04;                 //I2C data受信
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;
         *(i2c_data+i)= I2CRXD;       

        if (i == (number -1)){
            I2CMSST = 0x01;
            I2CMSCR = 0x05;                 //NACK 送信
            while(!(I2CMSST & 0x40));
            I2CMSST &= ~0x40;  
        }else{
            I2CMSST = 0x00;
            I2CMSCR = 0x05;                 //ACK 送信
            while(!(I2CMSST & 0x40));
            I2CMSST &= ~0x40;       
        }
    }    
    I2CMSCR = 0x06;                     //I2C ストップ
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       
}

int16_t bcd_to_dec(uint8_t bcd){
    int16_t dec10=(bcd >>4) & 0x0f;
    return (dec10 * 10 +(bcd  & 0x0f));
} 

void wait_1ms(void)
{
    __asm

    delay1ms:
        mov r0,#0xa0
        mov r1,#0x10
    delay:
        djnz    r0,delay
        djnz    r1,delay
        ret
    __endasm;
}
void wait_time(uint16_t wait_data)  //時間待ち関数
{
    uint16_t    i;          //forループによる時間待ち
    for(i=0;i < wait_data ;i++){
        wait_1ms();
    }
}   

//
// LCD操作関数
//

// 4bitコマンド出力関数

void lcd4bitwrite(uint8_t i2c_addr_lcd,uint8_t command){
    command <<= 4;
    command |= (uint8_t)0b00001000;
    command &= (uint8_t)0b11111000;
    i2c_write(i2c_addr_lcd,1,&command);
    command |= (uint8_t)0b00001100;
    command &= (uint8_t)0b11111100;
    i2c_write(i2c_addr_lcd,1,&command);
    command |= (uint8_t)0b00001000;
    command &= (uint8_t)0b11111000;
    i2c_write(i2c_addr_lcd,1,&command);  
}

// 8bitコマンド出力関数

void lcd8bitwrite(uint8_t i2c_addr_lcd,uint8_t command){

    uint8_t command2;
    
    command2 = command;
    command >>= 4;
    lcd4bitwrite(i2c_addr_lcd,command);
    lcd4bitwrite(i2c_addr_lcd,command2);
}

//4bitデータ出力関数

void data_lcd4bitwrite(uint8_t i2c_addr_lcd,uint8_t command){

    command <<= 4;
    command |= (uint8_t)0b00001001;
    command &= (uint8_t)0b11111001;
    i2c_write(i2c_addr_lcd,1,&command);
    command |= (uint8_t)0b00001101;
    command &= (uint8_t)0b11111101;
    i2c_write(i2c_addr_lcd,1,&command);
    command |= (uint8_t)0b00001001;
    command &= (uint8_t)0b11111001;
    i2c_write(i2c_addr_lcd,1,&command);   
}

// 8bitデータ出力関数

void data_lcd8bitwrite(uint8_t i2c_addr_lcd,uint8_t command){

    uint8_t command2;
    
    command2 = command;
    command >>= 4;
    data_lcd4bitwrite(i2c_addr_lcd,command);
    data_lcd4bitwrite(i2c_addr_lcd,command2);
    
    
}
        //LCD 初期化
void lcd_init(uint8_t i2c_addr_lcd){

    uint8_t data_set;

    data_set=0x03;
    lcd4bitwrite(i2c_addr_lcd,data_set);
    wait_time(5);
    lcd4bitwrite(i2c_addr_lcd,data_set);
    lcd4bitwrite(i2c_addr_lcd,data_set);
    data_set=0x02;
    lcd4bitwrite(i2c_addr_lcd,data_set);            //4bit mode
    data_set=0x28;
    lcd8bitwrite(i2c_addr_lcd,data_set);            //function set 4bit bus,2 line ,1 line=8
    data_set=0x0c;
    lcd8bitwrite(i2c_addr_lcd,data_set);            //disp on,under cursor off,block
    data_set=0x01;
    lcd8bitwrite(i2c_addr_lcd,data_set);            //disp clr
    wait_time(2);
    data_set=0x06;
    lcd8bitwrite(i2c_addr_lcd,data_set);            //disp address incrmant on,disp shift off
    data_set=0x02;
    lcd8bitwrite(i2c_addr_lcd,data_set);            //cursor home set
    wait_time(2);
}    

       
//
// main関数
//

void main(void)                                 //main関数
{
    P3M0 = 0x00;                                //P3ポートを準双方向モードに
    P3M1 = 0x00;
    P5M0 = 0x00;                                //P5ポートを準双方向モードに
    P5M1 = 0x00;    

    uart_init();
    
    printf_tiny("STC8G1K08A I2C RTC Setting !\r\n");

    P_SW2 = 0x80;                               //XFRアクセス許可、I2C(P3.2(SCL),P3.3(SDA)スイッチ切り替え
    I2CCFG = 0xe0;                              //I2Cマスターモード
    I2CMSST = 0x00;

    
    const uint8_t i2c_addr_lcd = 0x27;          //LCD I2Cアドレス
    const uint8_t i2c_addr_rtc = 0x68;          //DS3231 RTC I2Cアドレス
    
    
    uint8_t addr=0x68;
    uint8_t len=8;
    uint8_t out_buff[8];
    
    uint8_t mesg1[16];                          //LCD メッセージ用buffer
    uint8_t mesg2[]=" **STC8G1K08A** "; 

    

    int16_t years,months,days,hours,minutes,seconds;
  
    uint16_t *buff = out_buff;
    
    lcd_init(i2c_addr_lcd);
        
    years = 25;
    months = 5;
    days =  22;
    hours = 17;
    minutes = 25;
    seconds = 00;
    
    out_buff[0] = 0x00;                                         //address
    out_buff[1] = (((seconds/10) << 4) | seconds%10) & 0x7f;
    out_buff[2] = (((minutes/10) << 4) | minutes%10) & 0x7f;
    out_buff[3] = (((hours/10) << 4) | hours%10) & 0x3f;        //24h
    out_buff[4] = 0x00;
    out_buff[5] = (((days/10) << 4) | days%10) & 0x3f;
    out_buff[6] = (((months/10) << 4) | months%10) & 0x1f;
    out_buff[7] = (((years/10) << 4) | years%10);

        
    i2c_write(i2c_addr_rtc,len,buff);
    wait_time(5);

    while(1){ 
    
        uint8_t data_set = 0x00;                        //rtc read
        i2c_write(i2c_addr_rtc,1,&data_set);
        i2c_read(i2c_addr_rtc,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(i2c_addr_lcd,data_set);            //x80 (1line top address)
        uint8_t i;
        for (i=0; i<16 ;i++)
            data_lcd8bitwrite(i2c_addr_lcd,mesg1[i]);
    
        data_set = 0xc0;                               //x80 + 0x40(2 line top address)
        lcd8bitwrite(i2c_addr_lcd,data_set);            
        for (i=0;mesg2[i] != '\0';i++){
            data_lcd8bitwrite(i2c_addr_lcd,mesg2[i]);
        }; 
        wait_time(1000);
    }   
}

設定年月・時刻を上記ファイルに設定します。(ここでは、2025年5月22日17時25分00秒)

    years = 25;
    months = 5;
    days =  22;
    hours = 17;
    minutes = 25;
    seconds = 00;

コンパイルします。

$ sdcc -mmcs51 --model-small --xram-size 0x400 --xram-loc 0x0000 --code-size 0x2000 STC8G1K08A_I2C_rtc_set.c

HEXコードに変換します。

$ packihx STC8G1K08A_I2C_rtc_set.ihx > STC8G1K08A_I2C_rtc_set.hex
packihx: read 71 lines, wrote 130: OK.

STC8G1K08Aに書き込みします。

$ stcgal -P stc8g -t 22118.4 -p /dev/ttyUSB0 STC8G1K08A_I2C_rtc_set.hex
Waiting for MCU, please cycle power: done
Target model:
  Name: STC8G1K08A-8PIN
  Magic: F794
  Code flash: 8.0 KB
  EEPROM flash: 4.0 KB
Target frequency: 22.138 MHz
Target BSL version: 7.3.13U
Target wakeup frequency: 36.800 KHz
Target ref. voltage: 1185 mV
Target mfg. date: 2024-07-01
Target options:
  reset_pin_enabled=False
  clock_gain=high
  watchdog_por_enabled=False
  watchdog_stop_idle=True
  watchdog_prescale=64
  low_voltage_reset=False
  low_voltage_threshold=2
  eeprom_erase_enabled=True
  bsl_pindetect_enabled=False
  por_reset_delay=long
  rstout_por_state=high
  uart1_remap=False
  uart2_passthrough=True
  uart2_pin_mode=push-pull
  epwm_open_drain=True
  program_eeprom_split=8192
Loading flash: 1954 bytes (Intel HEX)
Target frequency: Protocol error: incorrect frame start
Disconnected!

書き込み後、約1分たったら、時刻がインクリメントするはずです。

ここまでで、RTCの時刻設定ができましたので、次は時計の表示と温湿度の表示を行います。
時刻については、すでに時刻表示はできていますので(LCD一行目)温湿度表示を行います。

AM2322の前機種でAM2321でZ80でSDCCで記述しています
こちらの記事を参考に作成していきます。

AM2322にダミー信号を送ってAM2322を起こすのですが、AM2322では新たに関数を作成しました。
起こす方法ですが、 I2Cアドレス+Wを送ったのち(多分NACKが返ってくるはず)1ms待ってからstop信号を送ります。

//
// I2C AM2322 起動ルーチン
//

void wakeup_am2322(uint8_t i2c_addr_lcd){
    I2CMSCR = 0x01;                     //I2C スタート
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;   
    
    I2CTXD = ((i2c_addr_lcd << 1) | 0x00);  //i2c_addr + write 送信
    I2CMSCR = 0x02;
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;

    I2CMSCR = 0x03;                     //ACK 受信
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       

    wait_time(1);
    
    I2CMSCR = 0x06;                     //I2C ストップ
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;  
}   

4バイトの読み込み信号を送ります。
読み込まれた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値に合成します。

温度の符号を取り出し、+、-を表示します。

温度と湿度はそれぞれ、10倍された値となっています。
なので、少数点はそのまま、表示させます。

表示方法は、100で割って商を2桁目(10の位)
100で割って、余りを10で割って商を1桁目(1の位)
100で割って、余りを10で割って余りを小数点1桁目にして、0x30と論理和を取ってasciiコードに変換しています。

        wakeup_am2322(i2c_addr_tmp);                    //AM2322 Wake up
     
        
        out_buff[0] = 0x03;                             //4 bytes temperature and humidity
        out_buff[1] = 0x00;
        out_buff[2] = 0x04;
        i2c_write(i2c_addr_tmp,3,buff);
        wait_time(2);
        i2c_read(i2c_addr_tmp,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_tiny("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  

LCD2行目のを指定して、data_lcd8bitwriteでLCDに表示させています。

        if(tenmetu == 0x01){                            // Blinking
            mesg2[15] = 0x20;
        }else{
            mesg2[15] = 0xff;
        }
        tenmetu = tenmetu ^ 0x01;
    
    
    
        data_set = 0xc0;
        lcd8bitwrite(i2c_addr_lcd,data_set);                 //x80 + 0x40(2 line top address)
        for(i=0; i<16;i++){
            data_lcd8bitwrite(i2c_addr_lcd,mesg2[i]);

AM2322の温湿度の読み込みは2秒周期で表示させています。

全体のプログラムは下記のとおりです、(ファイル名は、STC8G1K08A_I2C_lcd_rtc_tmp.c)

// STC8G1K08A I2C LCD + RTC(DS3231) + 温度/湿度(AM2322)  C言語版
//
// STC8G1K08A   MPU
// Flash ROM 8Kbyte
// RAM       1Kbyte
// 
// SDCC 4.0.0
//
// file name STC8G1K08A_I2C_lcd_rtc_tmp.c
// $ sdcc -mmcs51 --model-small --xram-size 0x400 --xram-loc 0x0000 --code-size 0x2000 STC8G1K08A_I2C_lcd_rtc_tmp.c
// $ packihx STC8G1K08A_I2C_lcd_rtc_tmp.ihx > STC8G1K08A_I2C_lcd_rtc_tmp.hex
// $ stcgal -P stc8g -t 22118.4 -p /dev/ttyUSB0 STC8G1K08A_I2C_lcd_rtc_tmp.hex
//

#include <8052.h>
#include <stdio.h>
#include <stdint.h>

//
// XFR(拡張RAM領域特殊機能レジスタ) 設定
//

#define CLKDIV (*(unsigned char volatile __xdata *)0xfe01)

#define I2CCFG (*(unsigned char volatile __xdata *)0xfe80)
#define I2CMSCR (*(unsigned char volatile __xdata *)0xfe81)
#define I2CMSST (*(unsigned char volatile __xdata *)0xfe82)
#define I2CSLCR (*(unsigned char volatile __xdata *)0xfe83)
#define I2CSLST (*(unsigned char volatile __xdata *)0xfe84)
#define I2CSLADR (*(unsigned char volatile __xdata *)0xfe85)
#define I2CTXD (*(unsigned char volatile __xdata *)0xfe86)
#define I2CRXD (*(unsigned char volatile __xdata *)0xfe87)

//BYTE 指定可能なレジスター

__sfr __at  0x8e AUXR  ;
__sfr __at  0xba P_SW2  ;

__sfr __at  0xb2 P3M0  ;    
__sfr __at  0xb1 P3M1  ;    
__sfr __at  0xca P5M0  ;    
__sfr __at  0xc9 P5M1  ;

__sbit __at 0xb3 SDA;
__sbit __at 0xb2 SCL;

//
// uart 関連関数
//
    
// uart 初期化
void uart_init(void) {
    
    P_SW2 = 0x80;
    CLKDIV = 0x02;          //システムクロック分周値を 1/2にセット
    P_SW2 = 0x00;
    
    AUXR = 0xc0;            //SYSCLK/1 timer0 or timer1 clock souce 
    SCON  = 0x50;           //UARTはモード1に設定、受信イネーブル
                            //SCON=01010000B
    TMOD |= 0x20;           //タイマー1を8ビット自動リロードモードに設定
                            //TMOD=00100000B
    TH1   = 0xf7;           //タイマーカウンターセット
    TL1   = 0xf7;
    TR1   = 1;              // タイマー1スタート

}

// 1文字シリアル出力
int putchar(int data) {
    TI = 0;                 //SCONのTI(1bit目)をケリア
    SBUF = data;            //dataからSBUFバッファーに格納
    while (!TI);            //SCONのTI(1bit目)が"1"なるまでループ
    return data;
}

// 1文字シリアル入力
int getchar(void) {
    while (!RI);            //SCONのRI(0bit目)が"1"なるまでループ
    RI = 0;                  //SCONのRI(0bit目)をクリア
    return SBUF;            // 受信データ返却
}   
// I2C 関連関数 
//

//I2C  データwrite関数

void i2c_write(uint8_t i2c_addr_lcd,uint8_t number,uint8_t *i2c_data){

    I2CMSCR = 0x01;                     //I2C スタート
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;   
    
    I2CTXD = ((i2c_addr_lcd << 1) | 0x00);  //i2c_addr + write 送信
    I2CMSCR = 0x02;
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;

    I2CMSCR = 0x03;                     //ACK 受信
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       
    uint8_t i;
    for(i=0;i <= (number -1);i++){
        
        I2CTXD = *(i2c_data+i);        //I2C_data送信
        I2CMSCR = 0x02;
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;       
        
        I2CMSCR = 0x03;                 //ACK 受信
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;       
    }
    I2CMSCR = 0x06;                     //I2C ストップ
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       
}
//I2C データread関数

void i2c_read(uint8_t i2c_addr_lcd,uint8_t number,uint8_t *i2c_data){

    I2CMSCR = 0x01;                     //I2C スタート
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;   
    
    I2CTXD = ((i2c_addr_lcd << 1) | 0x01);  //i2c_addr + read 送信
    I2CMSCR = 0x02;
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;

    I2CMSCR = 0x03;                     //ACK 受信
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       

    for(uint8_t i=0;i <= (number -1);i++){

        I2CMSCR = 0x04;                 //I2C data受信
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;
         *(i2c_data+i)= I2CRXD;       

        if (i == (number -1)){
            I2CMSST = 0x01;
            I2CMSCR = 0x05;                 //NACK 送信
            while(!(I2CMSST & 0x40));
            I2CMSST &= ~0x40;  
        }else{
            I2CMSST = 0x00;
            I2CMSCR = 0x05;                 //ACK 送信
            while(!(I2CMSST & 0x40));
            I2CMSST &= ~0x40;       
        }
    }    
    I2CMSCR = 0x06;                     //I2C ストップ
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       
}
//
// 時間待ち関数
//
void wait_1ms(void)
{
    __asm

    delay1ms:
        mov r0,#0xa0
        mov r1,#0x10
    delay:
        djnz    r0,delay
        djnz    r1,delay
        ret
    __endasm;
}
void wait_time(uint16_t wait_data)          //時間待ち関数(1ms*wait_data)
{
    uint16_t    i;                          //forループによる時間待ち
    for(i=0;i < wait_data ;i++){
        wait_1ms();
    }
}   
//
// I2C AM2322 起動ルーチン
//

void wakeup_am2322(uint8_t i2c_addr_lcd){
    I2CMSCR = 0x01;                     //I2C スタート
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;   
    
    I2CTXD = ((i2c_addr_lcd << 1) | 0x00);  //i2c_addr + write 送信
    I2CMSCR = 0x02;
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;

    I2CMSCR = 0x03;                     //ACK 受信
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;       

    wait_time(1);
    
    I2CMSCR = 0x06;                     //I2C ストップ
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;  
}   

//
// LCD操作関数
//

// 4bitコマンド出力関数

void lcd4bitwrite(uint8_t i2c_addr_lcd,uint8_t command){
    command <<= 4;
    command |= (uint8_t)0b00001000;
    command &= (uint8_t)0b11111000;
    i2c_write(i2c_addr_lcd,1,&command);
    command |= (uint8_t)0b00001100;
    command &= (uint8_t)0b11111100;
    i2c_write(i2c_addr_lcd,1,&command);
    command |= (uint8_t)0b00001000;
    command &= (uint8_t)0b11111000;
    i2c_write(i2c_addr_lcd,1,&command);  
}

// 8bitコマンド出力関数

void lcd8bitwrite(uint8_t i2c_addr_lcd,uint8_t command){

    uint8_t command2;
    
    command2 = command;
    command >>= 4;
    lcd4bitwrite(i2c_addr_lcd,command);
    lcd4bitwrite(i2c_addr_lcd,command2);
}

//4bitデータ出力関数

void data_lcd4bitwrite(uint8_t i2c_addr_lcd,uint8_t command){

    command <<= 4;
    command |= (uint8_t)0b00001001;
    command &= (uint8_t)0b11111001;
    i2c_write(i2c_addr_lcd,1,&command);
    command |= (uint8_t)0b00001101;
    command &= (uint8_t)0b11111101;
    i2c_write(i2c_addr_lcd,1,&command);
    command |= (uint8_t)0b00001001;
    command &= (uint8_t)0b11111001;
    i2c_write(i2c_addr_lcd,1,&command);   
}

// 8bitデータ出力関数

void data_lcd8bitwrite(uint8_t i2c_addr_lcd,uint8_t command){

    uint8_t command2;
    
    command2 = command;
    command >>= 4;
    data_lcd4bitwrite(i2c_addr_lcd,command);
    data_lcd4bitwrite(i2c_addr_lcd,command2);
    
    
}
        //LCD 初期化
void lcd_init(uint8_t i2c_addr_lcd){

    uint8_t data_set;

    data_set=0x03;
    lcd4bitwrite(i2c_addr_lcd,data_set);
    wait_time(5);
    lcd4bitwrite(i2c_addr_lcd,data_set);
    lcd4bitwrite(i2c_addr_lcd,data_set);
    data_set=0x02;
    lcd4bitwrite(i2c_addr_lcd,data_set);            //4bit mode
    data_set=0x28;
    lcd8bitwrite(i2c_addr_lcd,data_set);            //function set 4bit bus,2 line ,1 line=8
    data_set=0x0c;
    lcd8bitwrite(i2c_addr_lcd,data_set);            //disp on,under cursor off,block
    data_set=0x01;
    lcd8bitwrite(i2c_addr_lcd,data_set);            //disp clr
    wait_time(2);
    data_set=0x06;
    lcd8bitwrite(i2c_addr_lcd,data_set);            //disp address incrmant on,disp shift off
    data_set=0x02;
    lcd8bitwrite(i2c_addr_lcd,data_set);            //cursor home set
    wait_time(2);
}    

//
// main関数
//

void main(void)                                 //main関数
{
    P3M0 = 0x00;                                //P3ポートを準双方向モードに
    P3M1 = 0x00;
    P5M0 = 0x00;                                //P5ポートを準双方向モードに
    P5M1 = 0x00;    

     uart_init();
    
    P_SW2 = 0x80;                               //XFRアクセス許可、I2C(P3.2(SCL),P3.3(SDA)スイッチ切り替え
    I2CCFG = 0xe0;                              //I2Cマスターモード
    I2CMSST = 0x00;

    const uint8_t i2c_addr_lcd = 0x27;          //LCD I2Cアドレス
    const uint8_t i2c_addr_rtc = 0x68;          //DS3231 RTC I2Cアドレス
    const uint8_t i2c_addr_tmp = 0x5c;          //AM2322 温湿度センサー I2Cアドレス
    
    uint8_t len=8;
    uint8_t out_buff[8];
    uint16_t *buff = out_buff;
    
    uint8_t mesg1[16];                          //LCD メッセージ用buffer
    uint8_t mesg2[16];
    
 
     uint8_t tenmetu=0x01;                      //LCD 点滅用初期値
    
     uint16_t temp,hum;                         //温湿度値

    lcd_init(i2c_addr_lcd);                     //LCD 初期化
    
 
  while(1){                                     //LCD 書き込みループ(時刻+温湿度)
    
  //RTC LCDメーセージ書き込み
    
        uint8_t data_set = 0x00;                        //rtc read
        i2c_write(i2c_addr_rtc,1,&data_set);
        i2c_read(i2c_addr_rtc,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(i2c_addr_lcd,data_set);             //x80 (1line top address)
        uint8_t i;
        for (i=0; i<16 ;i++)
            data_lcd8bitwrite(i2c_addr_lcd,mesg1[i]);

 //AM2322 LCDメーセージ書き込み     
 
        wakeup_am2322(i2c_addr_tmp);                    //AM2322 Wake up
     
        
        out_buff[0] = 0x03;                             //4 bytes temperature and humidity
        out_buff[1] = 0x00;
        out_buff[2] = 0x04;
        i2c_write(i2c_addr_tmp,3,buff);
        wait_time(2);
        i2c_read(i2c_addr_tmp,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_tiny("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(i2c_addr_lcd,data_set);                 //x80 + 0x40(2 line top address)
        for(i=0; i<16;i++){
            data_lcd8bitwrite(i2c_addr_lcd,mesg2[i]);
       }
   wait_time(2000);                                         //2秒周期でLCD書き込み
  }         
            
    
}

コンパイルします。

$ sdcc -mmcs51 --model-small --xram-size 0x400 --xram-loc 0x0000 --code-size 0x2000 STC8G1K08A_I2C_lcd_rtc_tmp.c

HEXコードに変換します。

$ packihx STC8G1K08A_I2C_lcd_rtc_tmp.ihx > STC8G1K08A_I2C_lcd_rtc_tmp.hex
packihx: read 72 lines, wrote 130: OK.

書き込みします。

$ stcgal -P stc8g -t 22118 -p /dev/ttyUSB0 STC8G1K08A_I2C_lcd_rtc_tmp.hex
Waiting for MCU, please cycle power: done
Target model:
  Name: STC8G1K08A-8PIN
  Magic: F794
  Code flash: 8.0 KB
  EEPROM flash: 4.0 KB
Target frequency: 22.116 MHz
Target BSL version: 7.3.13U
Target wakeup frequency: 36.800 KHz
Target ref. voltage: 1185 mV
Target mfg. date: 2024-07-01
Target options:
  reset_pin_enabled=False
  clock_gain=high
  watchdog_por_enabled=False
  watchdog_stop_idle=True
  watchdog_prescale=64
  low_voltage_reset=False
  low_voltage_threshold=2
  eeprom_erase_enabled=True
  bsl_pindetect_enabled=False
  por_reset_delay=long
  rstout_por_state=high
  uart1_remap=False
  uart2_passthrough=True
  uart2_pin_mode=push-pull
  epwm_open_drain=True
  program_eeprom_split=8192
Loading flash: 1976 bytes (Intel HEX)
Target frequency: Target 22.118 MHz
Adjusted frequency: 22.116 MHz(-0.009%)
Switching to 115200 baud: done
Erasing flash: done
Writing flash: 2112 Bytes [00:00, 5882.00 Bytes/s]
Finishing write: done
Setting options: done
Target UID: F794C4161F25D0
Disconnected!

書き込みが終了すれば下記のような表示が出るはずです。(過去に作成したZ80でSDCC作成したものと同じ表示になります。)

おすすめの記事