他力本願C言語第三弾
I2C制御
CPU=PIC16F886
LCD=AQM1602AXA
IO=PCF8574AN 8bit
IO=MCP23017 16bit
IO=PCF8574AN 8bit の入力データを IO=MCP23017 16bit のGPA のLEDとLCDに表示
IO=MCP23017 16bit の入力データを LCDに表示
という単純な動作です、仕事として無意味ですが単なるプログラミングの勉強です
参考にしたホームページは下記の通り、御礼申し上げます
苦しんで覚えるC言語 さん
Wak-techさん
また、パクッたプログラムをコピペしたばかりで無く一貫性の無いボロボロの
プログラムにしてしまい申し訳御座いません、素人なのでご容赦下さい。<(__)>
ソース、そろそろファイル分割したほうが良いと考えていますがその方法もこれからです。
/* * File: main.c * Author: maru * pic16f886_pcf8574an_mcp23017_v000 * Created on 2019/01/09, 8:57 * 0 -> 1 -> 2...32768 -> -32768 -> -32767...-3 -> -2 -> -1 -> 0 の繰り返し */ //****************************************************************************** //コンフィギュレーション // CONFIG1 MPLAB XIDEの Production -> Set Configration bits で設定してもこのプログラムでの宣言が優先される //#pragma config FOSC = INTRC_NOCLKOUT //RA6 = I/O //デフォルトではRA6 = fosc/4 output // CONFIG1 #pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register) #pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT disabled) #pragma config MCLRE = ON // RE3/MCLR pin function select bit (RE3/MCLR pin function is MCLR)電源オン時、要リセット、しないとBootしない #pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled) #pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled) #pragma config BOREN = ON // Brown Out Reset Selection bits (BOR enabled) #pragma config IESO = ON // Internal External Switchover bit (Internal/External Switchover mode is disabled) #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled) #pragma config LVP = ON // Low Voltage Programming Enable bit (RB3/PGM pin has PGM function, low voltage programming enabled) // CONFIG2 #pragma config BOR4V = BOR40V // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V) #pragma config WRT = OFF // Flash Program Memory Self Write Enable bits (Write protection off) // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. //****************************************************************************** //コンパイラ #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 PCF8574AN_ADR 0x70 //Extentioned IO スレーブアドレス W #define PCF8574AN_ADR_R 0x73 //Extentioned IO スレーブアドレス W #define ToHome 0b00000010 #define shiftLeft 0b00011000 #define shiftRight 0b00011100 #define clear 0b00000001 //****************************************************************************** static unsigned char I2C_rcv(); static void I2C_nacksnd() ; static unsigned char I2C_ackchk(); char moji[] = " "; char moji2[] = "MCP23017=0x "; char moji3[] = "PCF8574AN=0x"; //この文字列を長くするとカウントアップがバグる、原因不明 char moji4[] = " "; //空白を書く、前の文字が残って邪魔をするから unsigned char k; // k = PCF8574ANから読み込んだデータ、char型 保存できる値は-127?127 //unsigned char型 保存できる値は 0-255です unsigned char k2; // k2 = MCP23017から読み込んだデータ、char型 保存できる値は-127?127 //unsigned char型 保存できる値は 0-255です unsigned char LED; //****************************************************************************** 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_Init() { SSPCON = 0x28; //RC3/SCK/SCL RC4/SDI/SDA として使用宣言 SMP = 1; CKE = 0; SSPADD = 0x21; //PICがスレーブの時の自分のスレーブアドレス、使わない } //------------------------------- 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; // } // ストップコンディション生成 ↑代替 maru void I2C_Master_Stop() { // SSP1CON2レジスタのPENビットを1に設定すると // ストップコンディションが生成される // 発行が完了するとSSP1IFが1になるのでwhile文で待つ SSPIF = 0; SSPCON2bits.PEN = 1; while (SSPIF == 0) {} SSPIF = 0; return; } //------------------------------- // void I2C_Master_Write(unsigned d) // { // I2C_Master_Wait(); // SSPBUF = d; // } //代替↑ void I2C_Master_Write(unsigned d) // SSP1BUFに送信したいデータをセットすると、そのデータが送信される // 発行が完了するとSSP1IFが1になるのでwhile文で待つ // ACK 待ちは無い、https://tool-lab.com/make/pic-practice-40/ { I2C_Master_Wait(); //これが無いと全く出鱈目に成る SSPIF = 0; SSPBUF = d; while (SSPIF == 0) {} SSPIF = 0; // return; } //------------------------------- // I2C ACK check static unsigned char I2C_ackchk() { unsigned char i2c_data; if (ACKSTAT) { i2c_data = 0xFF; } else { i2c_data = 0x00; } return(i2c_data); } //------------------------------- // I2C Recive //static unsigned char I2C_rcv() { // SSPIF = 0; // RCEN = 1; // while (RCEN) {} // return(SSPBUF); //} static unsigned char I2C_rcv() { SSPIF = 0; SSPCON2bits.RCEN = 1; while (SSPIF == 0) {} SSPIF = 0; return SSPBUF; } //------------------------------- // I2C NACK send //static void I2C_nacksnd() { // ACKDT = 1; // ACKEN = 1; // while (ACKEN) {} // return; //} static void I2C_nacksnd() { // ACKDTにNACKをセット(負論理なので1を設定) SSPCON2bits.ACKDT = 1; // NACK信号生成 SSPCON2bits.ACKEN = 1; while (SSPCON2bits.ACKEN) {} return; } //------------------------------- 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); } //****************************************************************************** //PCF8574AN Extentioned IO 部分 void PCF8574AN_W(){ //PCF8574AN Extentioned IO WRITE I2C_Master_Init(100000); I2C_Master_Start(); //スタート・コンディション I2C_Master_Write(PCF8574AN_ADR); //スレーブアドレス I2C_Master_Write(0xAA); //データ送信 I2C_Master_Stop(); //ストップ・コンディション __delay_ms(10); } void PCF8574AN_W2(char LED){ //PCF8574AN Extentioned IO WRITE I2C_Master_Init(100000); I2C_Master_Start(); //スタート・コンディション I2C_Master_Write(PCF8574AN_ADR); //スレーブアドレス I2C_Master_Write(LED); //データ送信 I2C_Master_Stop(); //ストップ・コンディション __delay_ms(10); } void PCF8574AN_R(){ //PCF8574AN Extentioned IO READ // I2C_Master_Init(100000); //これだと遅い I2C_Init(); //これだと速い I2C_Master_Start(); //スタート・コンディション I2C_Master_Write(PCF8574AN_ADR_R); //スレーブアドレス read I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする k = I2C_rcv(); //スレーブアドレス側からのデータを受信する // I2C_rcv(k); //この文法では読めない // PORTA = (k); //ここに入れるのもOK I2C_nacksnd(); //NACk送信 I2C_Master_Stop(); //ストップ・コンディション __delay_ms(10); } //****************************************************************************** // MCP23017 16bit IO 部分 int GPA_LED; int GPB_IN_DATA; void MCP23017_GPA_INIT(){ //MCP23017_GPAイニシャライズ I2C_Init(); //これだと速い I2C_Master_Start(); //スタート・コンディション I2C_Master_Write(0x40); //スレーブアドレス write I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_Write(0x00); //レジスタ・ナンバー I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_Write(0x00); //全入力設定 write I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_Stop(); //ストップ・コンディション } void MCP23017_GPB_INIT(){ //MCP23017_GPイニシャライズ I2C_Init(); //これだと速い I2C_Master_Start(); //スタート・コンディション I2C_Master_Write(0x40); //スレーブアドレス write I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_Write(0x01); //レジスタ・ナンバー I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_Write(0xFF); //全出力設定 write I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_Stop(); //ストップ・コンディション } void MCP23017_GPA_OUT(GPA_LED){ //MCP23017_GPAにデータ出力させる、Lチカ I2C_Init(); //これだと速い I2C_Master_Start(); //スタート・コンディション I2C_Master_Write(0x40); //スレーブアドレス write I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_Write(0x14); //レジスタ・ナンバー I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_Write(GPA_LED); //LED表示 write I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_Stop(); //ストップ・コンディション } void MCP23017_GPB_IN(){ //MCP23017_GPBのデータを読み込む、ディップスイッチ8bit I2C_Init(); //これだと速い I2C_Master_Start(); //スタート・コンディション I2C_Master_Write(0x40); //スレーブアドレス write I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_Write(0x13); //レジスタ・ナンバー I2C_Master_Wait(); I2C_ackchk(); //ACKチェック、無くても普通に動く、スレーブアドレス違ってもスルーする I2C_Master_RepeatedStart(); //SR = RepeatedStart I2C_Master_Write(0x41); //スレーブアドレス READ I2C_Master_Wait(); I2C_ackchk(); k2 = I2C_rcv(); //スレーブアドレス側からのデータを受信する = k2 I2C_nacksnd(); //NACk送信 I2C_Master_Stop(); //ストップ・コンディション __delay_ms(10); } //****************************************************************************** //****************************************************************************** int main(void) { PICinit(); //PICを初期化 __delay_ms(2); LCD_Init(); //LCD初期化 __delay_ms(2); MCP23017_GPA_INIT(); __delay_ms(2); //TRIGGER(); MCP23017_GPB_INIT(); __delay_ms(2); writeCommand(0x01); //画面をクリア __delay_ms(20); writeCommand(0x02); //ホームへカーソル移動 __delay_ms(2); // LCD側の処理待ち、LCDは全く読み込めないので見込み時間 int count = 0; //カウント初期値 int count2 = 0; //カウント初期値 __delay_ms(333); writeCommand(0x80); //1列目へ移動 LCD_str(moji3); // __delay_ms(100); writeCommand(0x80+0x40); //2列目へ移動 LCD_str(moji2); //毎回「counter」と書き込むのは無駄なのでループ外へ出した __delay_ms(100); //------------------------------- while(1) { //------------------------------- //データ受信 //TRIGGER(); //オシロスコープ用 PCF8574AN_R(); // k に受信データを持って来る //k = k & 0xFE; // RA0 はTRIGGER()で使用中だからマスクを掛ける PORTA = (k); //LED出力、PORTBだとフリーズする原因不明 //---------------- TRIGGER(); MCP23017_GPB_IN(); //MCP GPB IN data read = k2 //------------------------------- // writeCommand(ToHome); //画面左上へカーソルを移動 writeCommand(0x8B+0x40); //カーソル移動 // count++; //カウントアップ // PCF8574AN入力で得られたデータをLCDに出力表示する sprintf(moji,"%02X",k2); //LCD %=16進表示、02=2桁表示で左寄せしない、X=大文字表示(x=小文字)。MSBがゼロでも文字無しとしないでゼロ表示させる。ex) NG = 0x 1 Good = 0x01 %=文字変換 0=左寄せしないでゼロ表示、2=2桁表示 LCD_str(moji); //mojiを表示 //---------------- // writeCommand(ToHome); //画面左上へカーソルを移動 writeCommand(0x8C); //カーソル移動 // count++; //カウントアップ // PCF8574AN入力で得られたデータをLCDに出力表示する sprintf(moji,"%02X",k); //LCD 16進表示、2桁表示、左寄せしない、MSBがゼロでも文字無しとしないでゼロ表示させる。ex) NG = 0x 1 Good = 0x01 %=文字変換 0=左寄せしないでゼロ表示、2=2桁表示 LCD_str(moji); //mojiを表示 //------------------------------- //TRIGGER(); MCP23017_GPA_OUT(k); __delay_ms(2); //------------------------------- // count2++; // PCF8574AN_W2(count2); //インクリメントした数値をPCF8574AN Extentioned IO に I2C送信 //------------------------------- // PCF8574AN入力で得られたデータを別のPCF8574ANに出力、LED表示する k = ~k; //LEDが逆論理で発光するので逆論理に変更 PCF8574AN_W2(k); // //------------------------------- __delay_ms(10); //遅延 } return 0; //------------------------------- }