18F27Q43(Client)とATOM Lite(Host)を使ってHost-Client通信
MCCの自動生成コードは参考にしましたが、作りは違っています。Hostから送信されるコマンド
・1バイト書き込みコマンド(3バイト送信) 18F27Q43のLEDがその値(0xAA or 0x55)で点滅。
・1バイト読み出しコマンド(2バイト送信+1バイト受信) AtomのLCDにClient内のインクリメント値を表示。
・10バイト読み出しコマンド(2バイト送信+10バイト受信) AtomのLCDにClietのAD5チャンネルの値を表示。
上記3コマンドは、実働しています。オシロの波形もきれいです。ただし....
私の書いたコードは、現時点で、I2C1CNTが機能していません。 I2C1CNTは、送受信のバイト総数を設定します。モジュールが送信または受信中にレジスタが更新されると、I2C1CNTレジスタの値を破損する可能性があります。前もってI2Cバスがアイドリング状態のときに書き込む必要があります。
Clientモードは、クロックストレッチがかかっていないとき(CSTR==0)、もしくは、Stopコンディションが受信された後(PCIF==1)のときに、I2C1CNTに書き込むことができます(P.633 36.3.12 Data Byte Count)。
考慮したうえでも、原因がわからず、究明中。
Client側コード (PIC18F27Q43) 暫定コード
#include "Q_I2C1_Client.h"
#include "Q_peripheral27Q43.h"
volatile i2c1_info_t i2c1Info;
volatile i2c1_client_state_t i2c1State=I2C1_IDLE;
void I2C1_ClientInit(void)
{
I2C1CON0bits.MODE=0b000;//Client 7bits address
//Clientアドレス---------------------------------------------
I2C1ADR0=0x30;
I2C1ADR1=0x30;
I2C1ADR2=0x30;
I2C1ADR3=0x30;
//--------------------
//アドレスバッファ
I2C1CON2bits.ABD=0;//0:copied to I2C1ADB0
I2C1CON1bits.CSD=0;//0:Client clock stretching proceeds normally.
I2C1CON2bits.BFRET=0b00; //8clock;
I2C1CNT=0xFF;
//Clock PadReg Configuration
RC3I2C = 0x51;
//Data PadReg Configuration
RC4I2C = 0x51;
//割り込み設定
I2C1PIEbits.SCIE=1; //Start interrupt
I2C1PIEbits.ADRIE=1; //Address interrupt
I2C1PIEbits.WRIE=1; //DataWrite interrupt
I2C1PIEbits.ACKTIE=1;//AckTime interrupt
I2C1PIEbits.RSCIE=1; //Restart interrupt
I2C1PIEbits.PCIE=1; //Stop interrupt
I2C1PIEbits.CNTIE=1; //counter interrupt
I2C1ERRbits.NACKIE=1;
PIE7bits.I2C1EIE=1;
//PIE7bits.I2C1RXIE=1;
//PIE7bits.I2C1TXIE=1;
PIE7bits.I2C1IE=1; //I2C1 interrupt
I2C1PIR=0x00;
I2C1CON0bits.EN=1;
}
#if 1
void I2C1_ClientISR(void)
{
static uint8_t Acnt=0;
//LATCbits.LATC0=~LATCbits.LATC0;
//#define out
#ifdef out
printf("PIR7:%02X ", PIR7);
printf("I2C1PIR:%02X ",I2C1PIR&0xDF);
printf("I2C1ERR:%02X ",I2C1ERR&0x77);
printf("I2C1STAT1:%02X ",I2C1STAT1&0xAD);
printf("I2C1CNT:%d ",I2C1CNT);
printf("TXBE:%d\r", I2C1STAT1bits.TXBE);
#endif
//割り込みフラグクリア
PIR7=0x00;
//StopInterrupt------------------
if(I2C1PIRbits.PCIF)
{
//printf("PIR7:%02X \r", PIR7);
I2C1PIRbits.PCIF=0;
//RXBF, TXBE, バッファクリア
I2C1STAT1bits.CLRBF=1;
I2C1ERR=0x00;
I2C1PIR=0x00;
I2C1CNT=0xFF;
i2c1State=I2C1_IDLE;
i2c1Info.complete=true;
PIE1bits.ADIE=1;
return;
}
//NackDetect interrupt------------
if(I2C1ERRbits.NACKIF)
{
I2C1ERRbits.NACK1IF=0;
return;
}
//ACKTIME interrupt----------------
if(I2C1PIRbits.ACKTIF)
{
I2C1PIRbits.ACKTIF=0;
if(i2c1State!= I2C1_DATA_TX)
{
//I2C1CON0bits.CSTR=0;
//return;
}else
{
//I2C1CON0bits.CSTR=0;
//while(I2C1CON1bits.ACKSTAT);
//__delay_us(30);//出力中ウェイト(400khz)
//__delay_us(100);//出力中ウェイト(100khz)
}
}
//I2C1CNT interrupt--------------------
if(I2C1PIRbits.CNTIF)
{
I2C1PIRbits.CNTIF=0;
}
//Restart interrupt------------------
if(I2C1PIRbits.RSCIF)
{
I2C1PIRbits.RSCIF=0;
//I2C1CNT=0xFF;
return;
}
//start condition interrupt----------
if(I2C1PIRbits.SCIF)
{
PIE1bits.ADIE=0;
I2C1PIRbits.SCIF=0;
I2C1PIRbits.CNTIF=0;
i2c1Info.rxlen=0;
i2c1Info.txlen=0;
i2c1State=I2C1_IDLE;
return;
}
if(!(I2C1STAT0bits.D))//アドレス部、データ部の選別
{//0:address部受信
if(I2C1PIRbits.ADRIF)//アドレス部受信
{//address一致
I2C1PIRbits.ADRIF=0;
if(!(I2C1STAT0bits.R))//read/writeの選別
{//0:write
//ACK値格納
I2C1CON1bits.ACKDT=I2CACK;
//ストレッチ終了、ライン解放、ACK返信。
//I2C1CON0bits.CSTR=0;
i2c1State = I2C1_DATA_RX;
}else
{//1:read
//ACK値格納
I2C1CON1bits.ACKDT=I2CACK;
//送信設定
//I2C1CNT=i2c1Info.cnt;
//I2C1TXB =i2c1Info.txBuf[i2c1Info.txlen++];
//printf("a:%x ",i2c1Info.txBuf[i2c1Info.txlen-1]);
//i2c1State = I2C1_DATA_TX;
//ストレッチ終了、ライン解放、ACK返信。
//I2C1CON0bits.CSTR=0;
}
}
}else
{//1:data部受信
if(!(I2C1STAT0bits.R))//read/writeの選別
{//write 書き込み client自身に読み込み
if(I2C1PIRbits.WRIF)
{//書き込みデータ受信
I2C1PIRbits.WRIF=0;
while(!I2C1STAT1bits.RXBF);//受信バッファから読み出し
i2c1Info.rxBuf[i2c1Info.rxlen++]=I2C1RXB;
//受信バイトの値で、次のシーケンスの準備
switch(i2c1Info.rxBuf[0])
{
case 0x11:
i2c1Info.cnt=0;
i2c1Info.txBuf[0]=Acnt++;
I2C1CNT=i2c1Info.cnt;
I2C1TXB =i2c1Info.txBuf[i2c1Info.txlen++];
break;
case 0x1E:
//if(i2c1Info.rxlen==2)
// i2c1Info.complete=true;
break;
case 0x13:
i2c1Info.cnt=1;
i2c1Info.txlen=0;
i2c1Info.txBuf[0]=(uint8_t)(adcData.val[0]>>8);
i2c1Info.txBuf[1]=(uint8_t)adcData.val[0];
//I2C1CNT=i2c1Info.cnt;
//I2C1TXB =i2c1Info.txBuf[i2c1Info.txlen++];
break;
case 0x14:
i2c1Info.cnt=1;
i2c1Info.txlen=0;
i2c1Info.txBuf[0]=(uint8_t)(adcData.val[1]>>8);
i2c1Info.txBuf[1]=(uint8_t)adcData.val[1];
//I2C1CNT=i2c1Info.cnt;
//I2C1TXB =i2c1Info.txBuf[i2c1Info.txlen++];
break;
case 0x20:
i2c1Info.cnt=1;
i2c1Info.txlen=0;
i2c1Info.txBuf[0]=(uint8_t)(adcData.val[0]>>8);
i2c1Info.txBuf[1]=(uint8_t)adcData.val[0];
i2c1Info.txBuf[2]=(uint8_t)(adcData.val[1]>>8);
i2c1Info.txBuf[3]=(uint8_t)adcData.val[1];
i2c1Info.txBuf[4]=(uint8_t)(adcData.val[2]>>8);
i2c1Info.txBuf[5]=(uint8_t)adcData.val[2];
i2c1Info.txBuf[6]=(uint8_t)(adcData.val[3]>>8);
i2c1Info.txBuf[7]=(uint8_t)adcData.val[3];
i2c1Info.txBuf[8]=(uint8_t)(adcData.val[4]>>8);
i2c1Info.txBuf[9]=(uint8_t)adcData.val[4];
I2C1CNT=i2c1Info.cnt;
I2C1TXB =i2c1Info.txBuf[i2c1Info.txlen++];
break;
}
//バッファーの大きさチェック
if(i2c1Info.txlen>I2CTXBUFSIZE)
{
i2c1Info.txlen=0;
}
//ACK値格納
I2C1CON1bits.ACKDT=I2CACK;
//ストレッチ終了、ライン開放、ACK返信
//I2C1CON0bits.CSTR=0;
}
}else
{//read 読み出しHostへ送信
//printf("b:%x\r",i2c1Info.txBuf[i2c1Info.txlen]);
while(!I2C1STAT1bits.TXBE);
I2C1TXB =i2c1Info.txBuf[i2c1Info.txlen++];
}
}
//ストレッチ終了、ライン解放、ACK返信。
I2C1CON0bits.CSTR=0;
}
#endif
Host側コード (M5 AtomLite) 決定稿
#include <M5Atom.h>
#include "myLovyanGFX.h"
//グローバル変数********************************************************
uint ledColorNum=0;//RGBLEDの色配列指定
int ledColor[4]={0x000000,0xFF0000,0x008000, 0x0000FF};//RGBLED Red(8),Green(8),Blue(8)
void setup()
{
M5.begin(true, false, true);
Wire.begin(26,32,400000U);
//SSD1306初期化 LovyanGFX--------------------------------------------
lcd.init();
canvas.createSprite(lcd.width(), lcd.height());
canvas.setTextWrap(false); //自動折返し:無し
//SSD1306初期描画-----------------------------------------------------
canvas.startWrite();/// 通信開始する。(ペリフェラルを占有する);
canvas.fillScreen(TFT_BLACK); //背景塗り潰し
canvas.setTextColor(TFT_WHITE); //文字色
canvas.setFont(&fonts::Font4);
canvas.setTextSize(0.2);
canvas.drawString("Client Num:", 5,0);
canvas.drawString("Client AD:", 5,40);
canvas.pushSprite(0,0);
canvas.endWrite();/// 通信を終了する。(ペリフェラルの占有を終了する);
//RGBLED点灯
M5.dis.drawpix(0,ledColor[2]);//blue点灯
}
void loop()
{
uint8_t ret,clr;
static uint8_t led=0x55;
uint16_t val16[5];
uint8_t ad[10];
uint8_t i;
byte val;
static uint8_t c=0;
if(led==0x55)
{
led=0xAA;
clr=2;
}else
{
led=0x55;
clr=1;
}
#if 1
//1byte write
Wire.beginTransmission(0x18);
Wire.write(0x1E);
Wire.write(led);
ret = Wire.endTransmission(true);
M5.dis.drawpix(0,ledColor[clr]);//blue点灯
#endif
#if 1
//1byte read
Wire.beginTransmission(0x18);
Wire.write(0x11);
Wire.endTransmission(false);
Wire.requestFrom(0x18, 1,1);
val = Wire.read();
//Serial.printf("Read 1byte val=%d\r",val);
//if(c==1)
// while(1);
#endif
#if 0
//2byte read
Wire.beginTransmission(0x18);
Wire.write(0x13);
Wire.endTransmission(false);
Wire.requestFrom(0x18, 2, 1);
while(Wire.available()>0)
{
ad[i++]=Wire.read();
}
val16[0]=ad[0];
val16[0]=(val16[0]<<8)|ad[1];
Serial.printf("Read 2byte CH0 %4d",val16[0]);
Serial.println("");
#endif
#if 0
i=0;
//2byte read
Wire.beginTransmission(0x18);
Wire.write(0x14);
Wire.endTransmission(false);
Wire.requestFrom(0x18, 2, 1);
while(Wire.available()>0)
{
ad[i++]=Wire.read();
}
val16[1]=ad[0];
val16[1]=(val16[1]<<8)|ad[1];
Serial.printf("Read 2byte CH1 %4d",val16[1]);
Serial.println("");
#endif
#if 1
i=0;
//10byte read
Wire.beginTransmission(0x18);
Wire.write(0x20);
Wire.endTransmission(false);
Wire.requestFrom(0x18, 10, 1);
while(Wire.available()>0)
{
ad[i++]=Wire.read();
}
for(int n=0; n<5; n++)
{
val16[n]=ad[2*n];
val16[n]=(val16[n]<<8)|ad[2*n+1];
}
Serial.printf("Read 6byte %4d",val16[0]);
Serial.println("");
#endif
delay(10);
//lcd表示
#if 1
canvas.startWrite();
canvas.setFont(&fonts::Font4);
canvas.setTextSize(0.6);
canvas.fillScreen(TFT_BLACK); //背景塗り潰し
canvas.drawString("Client Num:", 5,0);
canvas.setCursor(80,0);
canvas.printf("%3d",val);
canvas.drawString("Client AD:", 5,23);
canvas.setCursor(5,38);
canvas.printf("%04d %04d %04d\r\n",val16[0],val16[1],val16[2]);
canvas.printf("%04d %04d ",val16[3],val16[4]);
canvas.pushSprite(0,0);
canvas.endWrite();
#endif
delay(500);
c++;
#if 0
Wire.beginTransmission(0x18);
Wire.write(0x1E);
Wire.write(0x55);
ret=Wire.endTransmission(1);
M5.dis.drawpix(0,ledColor[2]);//Green点灯
delay(500);
#endif
}
Readモード時、Hostサイドへデータを送信するときのI2C1CNTが機能していません。初期値を1以上に設定しても、次の割り込みで0クリアされてしまいます。ClientサイドからACKを投げることは、Readモードではないので、なんとかなっていますが、きちんと送信長を設定してデクリメントされていくように改善したいです。