I2C S-5851A thermal sensor LCD PIC16F886

他力本願C言語シリーズ 温度計
CPU = PIC16F886
温度センサー = SII S-5851A  スレーブアドレス 0x90(W) / 0x91(R)
LCD = AQM1602XA  スレーブアドレス 0x7C READ不可

前回アセンブラで作った同一品をC言語化したものです
正直言うと一体何日間掛かったことか・・・何十回となく
書いては直し、壊しては書き直し、訳が判らなく成って
振り出しに戻り、また書いて失敗し失望しての繰り返しでした。

結果的に言えることは
・コピペばっかりやって来た事で一貫性が無く関数にバグが入ったこと
・ひらがなモードのスペースとか入るともうズタズタに成りました、視えないんだもん
・C言語のシミュレーションを別のコンパイラで行って初めて「ダメだこりゃ」だったこと
・LCDが曲者で長い文字列を書くと他の文字が文字化けすること、特にこの問題が
 異常に時間が掛かった主な理由です、従って原因不明の儘となっています
 四文字ならOKで12文字ぐらいだと文字化けする、ってなに?

ともあれ何とか形に成りました

ソースファイルです、参考に成らないと思いますが素人の極みと言うことで
ご容赦下さい。
/*
* File:   main.c
* Author: maru
* I2C_S5851A_thermal_sensor_c_v005
* Created on 2019/01/09, 8:57
*
*/
// CONFIG1 MPLAB XIDEの Production -> Set Configration bits で設定してもこのプログラムでの宣言が優先される
//#pragma config FOSC = INTRC_NOCLKOUT //RA6 = I/O          //デフォルトではRA6 = fosc/4 output
#pragma config FOSC = INTRC_CLKOUT //RA6 = fosc/4 output
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = ON
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = OFF
//#pragma config CLKOUTEN = OFF
//#pragma config IESO = OFF
#pragma config FCMEN = OFF
// CONFIG2
#pragma config WRT = OFF
//#pragma config VCAPEN = OFF
//#pragma config PLLEN = ON
//#pragma config STVREN = ON
#pragma config BOR4V = BOR40V
#pragma config LVP = ON
#include 
#include 
#include 
#include 
#define _XTAL_FREQ 8000000      //PIC16F886はINTOSC時最高8MHzまでしか出ない下のOSCCON = 0x70;と矛盾しては成らない
#define LCD_ADD 0x7C            //LCDスレーブアドレス W
#define S5851A_ADR 0x90         //温度センサースレーブアドレス W
#define ToHome 0b00000010
#define shiftLeft 0b00011000
#define shiftRight 0b00011100
#define clear 0b00000001
char moji[] = " ";
char moji2[] = ".";
char moji3[] = "C S5851A"; //この文字列を長くするとカウントアップがバグる、原因不明
char moji4[] = "v005";//この文字列を長くするとカウントアップがバグる、原因不明
unsigned char k3; // k3 = 温度センサーから読み込んだデータ、MSB  char型 保存できる値は-127?127  //unsigned char型 保存できる値は 0-255です
unsigned char k4; // k4 = 温度センサーから読み込んだデータ、LSB  但し上位4bitのみ、下位4bitはゼロ、char型 保存できる値は-127?127  //unsigned char型 保存できる値は 0-255です
void TRIGGER() {
PORTAbits.RA0 = 1; //for debug オシロスコープ・トリガ用 PORTA-RA0
__delay_us(10);
PORTAbits.RA0 = 0;
}
void I2C_Master_Init(const unsigned long c) {
SSPCON = 0b00101000; //RC3/SCK/SCL  RC4/SDI/SDA として使用宣言
SSPCON2 = 0;
SSPADD = (_XTAL_FREQ / (4 * c)) - 1;
SSPSTAT = 0b00000000; // 標準速度モードに設定する(100kHz)
}
void I2C_Master_Wait() {
while ((SSPSTAT & 0x04) || (SSPCON2 & 0x1F));
}
void I2C_Master_Start() {
I2C_Master_Wait();
SEN = 1;
}
void I2C_Master_RepeatedStart() {
I2C_Master_Wait();
RSEN = 1;
}
void I2C_Master_Stop() {
I2C_Master_Wait();
PEN = 1;
}
//-------------------------------
// I2C ACK check
static unsigned char I2C_ackchk() {
unsigned char i2c_data;
if (ACKSTAT) {
i2c_data = 0xFF;
} else {
i2c_data = 0x00;
}
return (i2c_data);
}
static unsigned char I2C_rcv() {
SSPIF = 0;
SSPCON2bits.RCEN = 1;
while (SSPIF == 0) {
}
SSPIF = 0;
return SSPBUF;
}
//-------------------------------
static void I2C_acksnd() {
// ACKDTにACKをセット
SSPCON2bits.ACKDT = 0;
// NACK信号生成
SSPCON2bits.ACKEN = 1;
while (SSPCON2bits.ACKEN) {
}
return;
}
//-------------------------------
static void I2C_nacksnd() {
// ACKDTにNACKをセット(負論理なので1を設定)
SSPCON2bits.ACKDT = 1;
// NACK信号生成
SSPCON2bits.ACKEN = 1;
while (SSPCON2bits.ACKEN) {
}
return;
}
void I2C_Master_Write(unsigned d) {
I2C_Master_Wait();
SSPBUF = d;
}
void writeData(char t_data) {
I2C_Master_Start(); //スタート・コンディション
I2C_Master_Write(LCD_ADD); //0x7C スレーブアドレス
I2C_Master_Write(0x40); //0x40  キャラクタを書くよって宣言
I2C_Master_Write(t_data); //キャラクタの送信
I2C_Master_Stop(); //ストップ・コンディション
__delay_ms(10);
}
void writeCommand(char t_command) {
I2C_Master_Start();
I2C_Master_Write(LCD_ADD);
I2C_Master_Write(0x00);
I2C_Master_Write(t_command);
I2C_Master_Stop();
__delay_ms(10);
}
void PICinit() {
OSCCON = 0x70; //0x70=8MHz PIC16F886はINTOSC時最高8MHzまでしか出ない上の#define _XTAL_FREQ 8000000;と矛盾しては成らない
ANSEL = 0b00000000;
ANSELH = 0b00000000;
TRISA = 0b00000000;
TRISB = 0b00000000;
TRISC = 0b00011000;
PORTA = 0b00000000; //2進数で書いた場合
PORTB = 0x00; //16進数で書いた場合
}
void LCD_Init() { //LCDの初期化、秋月のマニュアル通り
//TRIGGER();                //for debug オシロスコープ・トリガ用 PORTA-RA0
I2C_Master_Init(100000);
__delay_ms(400);
//TRIGGER();                //for debug オシロスコープ・トリガ用 PORTA-RA0
writeCommand(0x38); //このプロトコルは S 0x7C 0x00 0x38 A P と成っているから下のプロトコルも全部一個ずつ送信している
__delay_ms(20);
writeCommand(0x39);
__delay_ms(20);
writeCommand(0x14);
__delay_ms(20);
writeCommand(0x73);
__delay_ms(20);
writeCommand(0x52);
__delay_ms(20);
writeCommand(0x6C);
__delay_ms(250);
writeCommand(0x38);
__delay_ms(20);
writeCommand(0x01);
__delay_ms(20);
writeCommand(0x0C);
__delay_ms(20);
}
void LCD_str(char *c) { //LCDに配列の文字を表示
unsigned char i, wk;
for (i = 0;; i++) {
wk = c[i];
if (wk == 0x00) {
break;
}
writeData(wk);
}
}
//S5851A_ADR 温度センサー部分=========================================================================================
void S5851A_W(char t_data) {
I2C_Master_Init(100000);
I2C_Master_Start(); //スタート・コンディション
I2C_Master_Write(S5851A_ADR); //スレーブアドレス
I2C_Master_Write(0x01); //
I2C_Master_Write(t_data); //キャラクタの送信
I2C_Master_Stop(); //ストップ・コンディション
__delay_ms(10);
}
void s5851a_init() //温度センサー初期化
{
I2C_Master_Start(); //スタート・コンディション
I2C_Master_Write(0x90); //スレーブアドレス write
I2C_Master_Wait();
I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする
I2C_Master_Write(0x01); //コンフィギュレーション・レジスタ
I2C_Master_Wait();
I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする
I2C_Master_Write(0x00); //常温設定
I2C_Master_Wait();
I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする
I2C_Master_Stop(); //ストップ・コンディション
}
void s5851a_me1() //温度測定一回目のみ、二回目からはカレントリードと云う簡略化したプロトコルで良い
{
I2C_Master_Start(); //スタート・コンディション
I2C_Master_Write(0x90); //スレーブアドレス write
I2C_Master_Wait();
I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする
I2C_Master_Write(0x00); //ダミー
I2C_Master_Wait();
I2C_ackchk();
I2C_Master_RepeatedStart(); //リスタート
I2C_Master_Write(0x91); //スレーブアドレス Read
I2C_Master_Wait();
I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする
k3 = I2C_rcv(); //スレーブアドレス側からのデータを受信するMSB
I2C_acksnd(); //ACK送信
k4 = I2C_rcv(); //スレーブアドレス側からのデータを受信するLSB 但し上位4bitのみ下位4bitはゼロ
I2C_nacksnd(); //NACK送信
I2C_Master_Stop(); //ストップ・コンディション
}
void s5851a_me2() //温度測定一回目のみ、二回目からはカレントリードと云う簡略化したプロトコルで良い
{
I2C_Master_Start(); //スタート・コンディション
I2C_Master_Write(0x91); //スレーブアドレス write
I2C_Master_Wait();
I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする
k3 = I2C_rcv(); //スレーブアドレス側からのデータを受信するMSB
I2C_acksnd(); //ACK送信
k4 = I2C_rcv(); //スレーブアドレス側からのデータを受信するLSB 但し上位4bitのみ下位4bitはゼロ
I2C_nacksnd(); //NACK送信
I2C_Master_Stop(); //ストップ・コンディション
}
//=========================================================================================
int main(void) {
PICinit(); //PICを初期化
LCD_Init();
writeCommand(0x01); //画面をクリア
__delay_ms(10);
writeCommand(0x82); //一行目
__delay_ms(10);
LCD_str(moji2); //mojiを表示
__delay_ms(10); //遅延
writeCommand(0x87); //一行目
__delay_ms(10);
writeData(0xF2);//文字表示
__delay_ms(10); //遅延
writeCommand(0x88); //2行目
__delay_ms(10);
LCD_str(moji3); //mojiを表示
__delay_ms(10); //遅延
writeCommand(0x80 + 0x40); //2行目
__delay_ms(10);
LCD_str(moji4); //mojiを表示
__delay_ms(10); //遅延
//S5851A_ADR 温度センサー測定
s5851a_init();
__delay_ms(10); //遅延
s5851a_me1(); //一回目はダミー
__delay_ms(10); //遅延
while (1) {
//------------------------------------------------
//温度測定
TRIGGER();
s5851a_me2(); //2回目は本測定 この時点で k3 = MSB 8bit と k4 = LSB 但し上位4ビット
__delay_ms(10); //遅延
//------------------------------------------------
//温度下位8ビットの表示、詰まり0℃以下の部分のみで上位4ビットだけ有効
writeCommand(0x83); //一行目
__delay_ms(10); //遅延
k4 = k4 >> 4;
sprintf(moji, "%04d", k4 * 625); //mojiにcounter:countを代入
LCD_str(moji); //mojiを表示
__delay_ms(10); //遅延
//------------------------------------------------
//温度上位8ビットの表示、詰まり0℃以上の部分のみ
s5851a_me2(); //2回目は本測定 この時点で k3 = MSB 8bit と k4 = LSB 但し上位4ビット、ここで二度目の二度目をやっている理由はk3が化けるから原因不明
__delay_ms(10); //遅延
writeCommand(0x80); //一行目
__delay_ms(10); //遅延
sprintf(moji, "%02u", k3); //mojiにcounter:countを代入
LCD_str(moji); //mojiを表示
__delay_ms(10); //遅延
}
return 0;
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です