PIC16F1827 I2C slaveサイド ２バイト受信


24.11.16　訂正：slave割込みのフラグチェックを追加。

<iframe width="560" height="315" src="https://www.youtube.com/embed/93WeceDL1AM?si=eYMxR9aJinnbWYDr" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

**masterサイドで、コントローラの16bits押下データを取得。２バイトデータとして、slaveサイドにI2Cを通じて送信。送信側、受信側ともにおなじLEDを点灯させています。**

https://qiita.com/etoolsLab369/items/88e2a0c690d2798b2f97

**microchipのforumに、MSSP I2C Slaveの情報があります。**
題名：I2C Slave problem
https://forum.microchip.com/s/topic/a5C3l000000Mf1lEAC/t387764

もっとわかりやすいコード。
note:ESP32とPICをI2Cで繋いでみる（その2）PICにデータを転送
https://note.com/khe00716/n/nb5486d214d9b#wcQk4


:::note info
以下の設定で、スレーブとして動作しています。

・スレーブアドレス：0x32
・２バイトを受信。受信データから１６ビットデータを再構成。
・割り込み処理関数の中でスレーブ受信対応処理。
・LEDの点滅は、受信処理が終わってから、フラグで、mainループ内処理。

※MSSPモジュールI2Cとして機能させるため、SCL,SDAピンを入力に設定すること。
:::

# Masterサイドのコード
スレーブアドレス(0x32)に、ボタン押下状況をしめす２バイトデータを送信します。
```C:I2C_MSSP1.c

// PIC16F1827 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
//#pragma config FOSC = HS      // Oscillator Selection (HS Oscillator, High-speed crystal/resonator connected between OSC1 and OSC2 pins)
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP =ON      // Low-Voltage Programming Enable (Low-voltage programming enabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#include <stdlib.h>
#include <stdio.h>
#include "Interrupt.h"
#include "Peripheral.h"
#include "I2C_MSSP1.h"
#include "LCDdriver.h"
#include "famicon.h"

#define _XTAL_FREQ  16000000

void Oscillator_Init(void);
void Port_Init(void);

void main(void)
{    
    uint8_t cnt=0;
    uint8_t i,j,temp;
    uint16_t buttonState;
    uint8_t  direction;
    
    //Basic Hard Initialize
    Oscillator_Init();
    Port_Init(); 
    //Peripheral 
    Timer0_INIT();
    Timer2_INIT();
    USART_INIT();
    ADC_INIT();
    I2C1_Reg_Init();
    LCD_Init(LcdDeviceAdd);
    SSP1ADD=0x09;  //Fosc=16MHz Fclock=400kHz
    LCD_Printf(LcdDeviceAdd, "HostSide",8,0x80);
    
    famiconInit();
    //割り込み許可
    Interrupt_START();
    direction=false;
    while(1)
    {
        //USART interrupt processing
        if(usart.rxCompleted)
        {
            usart.rxCompleted=false;
            printf("%s\n",usart.rxBuf);
            usart.length=0;
            PIE1bits.RCIE=1;
        }
        
        //Timer0 interrupt processing
        if(tm0.up)
        {
            tm0.up=false;
            //LATBbits.LATB0=~LATBbits.LATB0;
            //printf("%d\n",cnt++);
            if(ADC.Completed)
            {
                ADC.Completed=false;
                //printf("%d\n", ADC.data[0]);
                PIE1bits.ADIE=1;
                PIE1bits.TMR2IE=1;
            }
            INTCONbits.TMR0IE=1;
        }
        
        buttonState = getButton();
        //buttonState = (uint16_t)strtol(usart.rxBuf,NULL,16);
        if((buttonState&0x0200)==0x0)//Left
        {
            printf("%02X\n",buttonState);            
            I2C1_b2Write(0x32,0xFD,0xF0);
            __delay_ms(50);
            LATBbits.LATB0=1;
        }
        
        if((buttonState&0x0100)==0x0)//Right
        {
            printf("%02X\n",buttonState);                
            I2C1_b2Write(0x32,0xFE,0xF0);
            __delay_ms(50);
            LATBbits.LATB3=1;
        }
        
        if((buttonState&0x8000)==0x0)
        {
            //printf("B ");
            I2C1_b2Write(0x32,0x7F,0xF0);
            LATBbits.LATB7=1;
            __delay_ms(50);
        }

        if((buttonState&0x4000)==0x0)
        {
            //printf("Y ");
            I2C1_b2Write(0x32,0xBF,0xF0);
            LATBbits.LATB6=1;
            __delay_ms(50);
        }
        
        if((buttonState&0x0080)==0x0)
        {
            //printf("A ");
            I2C1_b2Write(0x32,0xFF,0x70);
            LATBbits.LATB3=1;
            __delay_ms(50);
        }

        if((buttonState&0x0040)==0x0)
        {
            //printf("X ");
            I2C1_b2Write(0x32,0xFF,0xB0);
            LATBbits.LATB0=1;
            __delay_ms(50);
        }
        if(buttonState==0xFFF0)
        {
            I2C1_b2Write(0x32,0xFF,0xF0);
            __delay_ms(50);
            LATBbits.LATB0=0;
            LATBbits.LATB3=0; 
            LATBbits.LATB6=0;
            LATBbits.LATB7=0;  
        }
    }
    return;
}

void Oscillator_Init(void)
{
    OSCCONbits.SPLLEN=0;
    OSCCONbits.IRCF=0b1111;//16Mhz
    OSCCONbits.SCS=0b10;//InternalOscillator
}

void Port_Init(void)
{
    TRISA=0x00;
    ANSELA=0x00;
    TRISB=0x04;     //RX:RB2
    ANSELB=0x00;
    PORTA=0x00;
    PORTB=0x00;  
    //Altanative Pin Selective
    APFCON0bits.RXDTSEL=1;  //RX:RB2
    APFCON1bits.TXCKSEL=1;  //TX:RB5
    //I2C1
    TRISBbits.TRISB1=1;//SDA:RB1
    TRISBbits.TRISB4=1;//SCL:RB4
}

void delay_ms(uint16_t time)
{
    uint16_t t;
    for(t=0; t<time; t++)
        __delay_ms(1);
}
```

# Slaveサイドのコード
MSSPの割り込みイベントで処理していきます。
受信終了、ストップコードが発行されたら、main内で、LED点灯処理を行っています。

```C:I2C_MSSP1_SLAVE_H

#ifndef I2C_MSSP1_SLAVE_H
#define	I2C_MSSP1_SLAVE_H

#ifdef	__cplusplus
extern "C" {
#endif

typedef struct
{
    uint8_t index;
    uint8_t rxData[10];
    uint8_t txData[10];
    uint8_t cnt1;
    uint8_t cnt2;
    bool    flag;
}_i2cStruct;

extern _i2cStruct i2cInfo;

void I2C1_Reg_Init_Slave(void);
void I2C1_Slave_Interrupt(void);


#ifdef	__cplusplus
}
#endif

#endif	/* I2C_MSSP1_SLAVE_H */
```

```C:I2C_MSSP1_SLAVE.c
#include "Peripheral.h"
#include "I2C_MSSP1_Slave.h"

_i2cStruct i2cInfo;

///I2C MSSPモジュール初期化　スレーブモード
void I2C1_Reg_Init_Slave(void)
{
    uint8_t val;
    SSP1STAT=0xC0;
    SSP1CON1=0x36;          //slave mode , 7bit address
    //SSP1CON1=0x3E;        //slave mode , 7bit address with interrupts
    SSP1CON2bits.SEN=1;     //0:Clock stretching is disable
    SSP1CON3bits.PCIE=0;
    SSP1CON3bits.SCIE=0;
    SSP1CON3bits.AHEN=0;
    SSP1CON3bits.DHEN=0;
    SSP1ADD=0x32;
    val=SSP1BUF;
    PIE1bits.SSP1IE=1;
   
}


///MSSPモジュール I2Cスレーブモード割込み処理
void I2C1_Slave_Intterrupt(void)
{
    uint8_t I2c1stat,i,buf;
    //---------MSSP1 ステータス読み出し---------
        PIR1bits.SSP1IF=0;
        I2c1stat=SSP1STAT & 0b00111101;
        
        /*1.--------I2c Writeモード　
                 スタートコンディション検出＆スレーブアドレス読み出し--------- */
        if(I2c1stat==0b00001001)
        {
            i2cInfo.index=0;
            for(i=0; i<=3; i++) i2cInfo.rxData[i]=0;
            i2cInfo.rxData[i2cInfo.index++]=SSP1BUF;    
            if(SSP1CON2bits.SEN==1)
                    SSP1CON1bits.CKP=1;
        }
        /*2.--------I2c Writeモード　受信データバイト読み出し---------*/
        if(I2c1stat==0b00101001)
        {
            i2cInfo.rxData[i2cInfo.index++]=SSP1BUF;
            if(i2cInfo.index==3)
            {
               i2cInfo.flag=true;//mainループで処理。
            }
            if(SSP1CON2bits.SEN==1)
                SSP1CON1bits.CKP=1;
        }
        /*3.--------I2c Readモード スタートコンディション検出&
                            データバイトバッファ出力 送信（アドレス部）--------- */
        if(I2c1stat==0b00001101)
        {
           
        }
        /*4.--------I2c Readモード　データバイトバッファ出力 送信（データ部）---------*/
        if(I2c1stat==0b00101100)
        {
           
        }
        /*5-1.--------I2c Writeモードストップビット検知---------*/
        if(I2c1stat==0b00110000)
        {
            
            if(SSP1CON2bits.SEN==1)
            {
                SSP1CON1bits.CKP=1;
            }
        }
        /*5-2.---------I2c Readモードストップビット検知---------*/
        if(I2c1stat==0b00110100)
        {
             if(SSP1CON2bits.SEN==1)
                SSP1CON1bits.CKP=1;               
        }
}
```
```C:interrupt.c
#include <pic16f1827.h>
#include "Peripheral.h"
#include "Interrupt.h"
#include "I2C_MSSP1_Slave.h"

void __interrupt()isr()
{
    uint8_t ch;
    
    //USART受信割り込み処理
    if(PIE1bits.RCIE==1 &&  PIR1bits.RCIF)
    {
        PIR1bits.RCIF=0;
        ch = getch();
        usart.rxBuf[usart.length++]=ch;
        if(usart.length>=rxLength)
        {
            usart.length=0;
        }
        if(ch==0x0A)
        {//CR,LF受信
            usart.rxCompleted=true;
            usart.rxBuf[usart.length-2]=0x00;
            PIE1bits.RCIE=0;
        }
    }
    
    //Timer0割り込み処理
    if(INTCONbits.TMR0IE==1 && INTCONbits.TMR0IF )
    {
        INTCONbits.TMR0IF=0;
        tm0.cnt++;
        if(tm0.cnt==20)
        {
            tm0.cnt=0;
            INTCONbits.TMR0IE=0;
            tm0.up=true;
        }
        TMR0=0x63;
    }
 
    //Timer2割り込み処理 A/D変換トリガー
    if(PIE1bits.TMR2IE == 1 && PIR1bits.TMR2IF)
    {
        PIR1bits.TMR2IF=0;
        ADCON0bits.ADGO=1;
       // while(ADCON0bits.ADGO);
    }
    
    //AD変換割り込み処理
    if(PIE1bits.ADIE==1 && PIR1bits.ADIF)
    {
        PIR1bits.ADIF = 0;
        ADC.data[0]=ADRESH;
        ADC.data[0]<<=8;
        ADC.data[0]|=ADRESL;
        PIE1bits.ADIE = 0;
        PIE1bits.TMR2IE=0;
        ADC.Completed = true;
    }

    if(PIR1bits.SSP1IF)//修正24.11.17
    I2C1_Slave_Interrupt();
}

```

```C:main.c

// PIC16F1827 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
//#pragma config FOSC = HS      // Oscillator Selection (HS Oscillator, High-speed crystal/resonator connected between OSC1 and OSC2 pins)
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP =ON      // Low-Voltage Programming Enable (Low-voltage programming enabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#include <stdlib.h>
#include <stdio.h>
#include "Interrupt.h"
#include "Peripheral.h"
#include "I2C_MSSP1_Slave.h"
#include "famicon.h"


#define _XTAL_FREQ  16000000

void Oscillator_Init(void);
void Port_Init(void);

void main(void)
{    
    uint8_t cnt=0;
    uint8_t i,j,temp;
    uint16_t buttonState;
    uint8_t  direction;
    
    //Basic Hard Initialize
    Oscillator_Init();
    Port_Init(); 
    //Peripheral 
    //Timer0_INIT();
    Timer2_INIT();
    USART_INIT();
    //ADC_INIT();
    I2C1_Reg_Init_Slave();
    
    //famiconInit();
    //割り込み許可
    Interrupt_START();
    direction=false;
    while(1)
    {
        //USART interrupt processing
        if(usart.rxCompleted)
        {
            usart.rxCompleted=false;
            printf("%s\n",usart.rxBuf);
            usart.length=0;
            PIE1bits.RCIE=1;
        }
        
        if(i2cInfo.flag)
        {
            i2cInfo.flag=false;
            //buttonState = getButton();
            //buttonState = (uint16_t)strtol(usart.rxBuf,NULL,16);
            buttonState = i2cInfo.rxData[1];
            buttonState <<=8;
            buttonState |= i2cInfo.rxData[2];
            //printf("rcvData: %X %X\n",rcvdata[1],rcvdata[2]);
            //printf("RES:%02X\n",buttonState);
             if((buttonState&0x0200)==0x0)//Left
            {               
                LATBbits.LATB0=1;
            }

            if((buttonState&0x0100)==0x0)//Right
            {               
                LATBbits.LATB3=1;
            }

            if((buttonState&0x8000)==0x0)
            {    
                //printf("B ");
                LATBbits.LATB7=1;               
            }

            if((buttonState&0x4000)==0x0)
            {      
                //printf("Y ");
                LATBbits.LATB6=1;                
            }

            if((buttonState&0x0080)==0x0)
            {            
                //printf("A ");
                LATBbits.LATB3=1;
            }

            if((buttonState&0x0040)==0x0)
            {
                //printf("X ");
                LATBbits.LATB0=1;
            }
            
            if(buttonState==0xFFF0)
            {
                LATBbits.LATB0=0;
                LATBbits.LATB3=0; 
                LATBbits.LATB6=0;
                LATBbits.LATB7=0;  
            }
        }
    }
    return;
}

void Oscillator_Init(void)
{
    OSCCONbits.SPLLEN=0;
    OSCCONbits.IRCF=0b1111;//16Mhz
    OSCCONbits.SCS=0b10;//InternalOscillator
}

void Port_Init(void)
{
    TRISA=0x00;
    ANSELA=0x00;
    TRISB=0x04;     //RX:RB2
    ANSELB=0x00;
    PORTA=0x00;
    PORTB=0x00;  
    //Altanative Pin Selective
    APFCON0bits.RXDTSEL=1;  //RX:RB2
    APFCON1bits.TXCKSEL=1;  //TX:RB5
    //I2Cinitialize
    TRISBbits.TRISB1=1;     //RB1
    TRISBbits.TRISB4=1;     //RB4
}


```