24.11.16 訂正:slave割込みのフラグチェックを追加。
masterサイドで、コントローラの16bits押下データを取得。2バイトデータとして、slaveサイドにI2Cを通じて送信。送信側、受信側ともにおなじLEDを点灯させています。
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
以下の設定で、スレーブとして動作しています。
・スレーブアドレス:0x32
・2バイトを受信。受信データから16ビットデータを再構成。
・割り込み処理関数の中でスレーブ受信対応処理。
・LEDの点滅は、受信処理が終わってから、フラグで、mainループ内処理。
※MSSPモジュールI2Cとして機能させるため、SCL,SDAピンを入力に設定すること。
Masterサイドのコード
スレーブアドレス(0x32)に、ボタン押下状況をしめす2バイトデータを送信します。
// 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点灯処理を行っています。
#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 */
#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;
}
}
#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();
}
// 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
}