目的
前回LCDで文字表示ができたのでI2C接続でセンサーから温度と湿度のデジタルデータを取得してその値を表示させてみました。
使用機器
- 前回のLCD表示で用いたPIC 16F13145とLCD(ACM1602K-NLW-BBW)一式
- SHT40I-AD1F 温湿度センサ データシート:HT_DS_Datasheet_SHT4xI-Digital_V4.2.pdf
回路図
前回のLCD表示に用いた回路に新たにI2C接続のSHT40Iセンサを配線しました。LCDはパラレル通信で温湿度センサはI2Cで通信しています。図1に回路図を示します。

デバイス設定
MCCを使用して図2の様に設定しました。


前回のLCDのピン設定RC0~RC3とRS,Eの割り当てを行ったのと新たにAddComponentからI2C_Hostドライバーを追加しています。さらにMSSP1のSCL1とSDA1をPortBの6と4に割り当てています。またデフォルトでチェックが入っているかもしれませんがInterruptDrivenを有効にしておきます。設定が終わったらGenerateを押して生成します。
ソースコード
あまりきれいではないですが参考までにソースコードを載せます。以前のLCDのコードにI2C通信のコードを追記したものになります。またSHT40Iのデータシートに記載のあったサンプルプログラムを参考にデータ処理部分は流用しています。
#include "mcc_generated_files/system/system.h"
#include "stdio.h"
void lcd4_send(uint8_t code, uint8_t mode)
{
LATC = (code & 0xF0) >> 4; // RC0-RC3に出力
if (mode == 0)
RS_SetHigh(); // 表示データ時
else
RS_SetLow(); // コマンドデータ時
E_SetHigh();
E_SetLow();
}
void lcd4_char(uint8_t chr){
lcd4_send(chr, 0); // 上位4ビット出力
lcd4_send(chr<<4, 0); // 下位4ビット出力
__delay_us(50);
}
void lcd4_ctl(uint8_t ctl){
lcd4_send(ctl, 1); // 上位4ビット出力
lcd4_send(ctl<<4, 1); // 下位4ビット出力
__delay_ms(2);
}
void lcd4_clear(){
lcd4_ctl(0x01);
__delay_ms(15);
}
void lcd4_init(){
__delay_ms(150);
lcd4_send(0x30, 1); // 8bit mode set
__delay_ms(5);
lcd4_send(0x30, 1); // 8bit mode set
__delay_ms(1);
lcd4_send(0x30, 1); // 8bit mode set
__delay_ms(1);
lcd4_send(0x20, 1); // 4bit mode set
__delay_ms(1);
lcd4_ctl(0x2E); // DL=0 4bit mode
lcd4_ctl(0x08); // display off C=D=B=0
lcd4_ctl(0x0D); // display on C=D=1 B=0
lcd4_ctl(0x06); // entry I/D=1 S=0
lcd4_ctl(0x02); // cursor home
}
void lcd4_str(uint8_t *str){
while(*str != 0){
lcd4_char(*str++);
}
}
int main(void)
{
SYSTEM_Initialize();
lcd4_init();
INTCONbits.GIE = 1; //INTCONbits.GIEと
INTCONbits.PEIE = 1;//INTCONbits.PEIEを記載しないと動きませんでした
char temp[32],hum[32]; //文字列格納用
uint8_t rx[6]; //受信データ格納用
uint8_t cmd = 0xFD; //High Precisionコマンド
uint16_t t_ticks, rh_ticks; //受信データ2バイト 結合用
float t_degC, rh_pRH;
while(1)
{
I2C1_Write(0x44, &cmd, 1); //コマンド送信
while(I2C1_IsBusy());
__delay_ms(10);
I2C1_Read(0x44, rx, 6); //データ受信
while(I2C1_IsBusy());
t_ticks = rx[0] * 256 + rx[1]; //データ処理部分 SHT40Iのデータシート参照
//checksum_t = rx[2]; //データシートに記載のチェックサム用の変数だが動作確認が目的なので使用していない
rh_ticks = rx[3] * 256 + rx[4]; //1バイトずつ受信しているのでそれを2バイトのデータ列に変換
//checksum_rh = rx[5]; //データシートに記載のチェックサム用の変数だが動作確認が目的なので使用していない
t_degC = -45.0f + 175.0f * ((float)t_ticks/65535.0f); //小数に変換(温度)
rh_pRH = -6.0f + 125.0f * ((float)rh_ticks/65535.0f); //小数に変換(湿度)
if (rh_pRH > 100) //相対湿度が100 %を超えた場合
rh_pRH = 100;
if (rh_pRH < 0) //相対湿度が0 %を下回った場合
rh_pRH = 0;
sprintf(temp, "Temp: %f", t_degC); //文字列変換
sprintf(hum, "Hum: %f", rh_pRH); //文字列変換
lcd4_ctl(0x80);
lcd4_str(temp);
lcd4_ctl(0xC0);
lcd4_str(hum);
__delay_ms(1000);
lcd4_clear();
}
}
動作確認

図4の様に実際に室温と湿度が数値として取得できていることが確認できます。
余談
I2Cの通信がうまくいかず3日近く悩みました。原因は、割り込みの許可をプログラムのほうで行っていなかっただけです。MCCでI2Cのドライバーを追加してInterrupt.cが生成されたので大丈夫だと思っていたのですが駄目でした。(Interruptって書いてあったら割り込みも自動でやってくれるもんだと思ってました甘く見てました)
あまり生成AIを使わずに自分の力でできたらと思って取り組んでいたのですが、一向に解決できないので彼の力も借りました。便利な時代になりましたね。
古いバージョンだと、自分で信号の制御を記述していたようですが新しいバージョンではそこが抽象化されて今回で言えばI2C1_Write関数だけで書き込みができるようになっているようです。
またI2Cの書き方でスレーブアドレスとレジスタアドレスそしてコマンドを送る形式の記述をよく見ていて、この温湿度センサではスレーブアドレスとコマンドしかデータシートに記述がなく...「レジスタアドレスって何?どう記述すればいいんだろうか...」と思っていたのですが、動作確認を通して同じI2Cでも物によってデータ形式が異なることを学びました。またパラレル通信では信号線にLEDを並列に入れても普通に動作していたのですがI2Cの場合は並列にLEDを入れるときちんと動作しなくなりました。動作確認のために入れていたのですがかえって悪化させていました。ここまで読んでいただきありがとうございました。
