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作成したものと同じ表示になります。)
