PIC18F27Q43 I2C Host-Client通信 2＜Client側＞

# 18F27Q43(Client)とATOM Lite(Host)を使ってHost-Client通信
<iframe width="560" height="315" src="https://www.youtube.com/embed/JN07ZiiXNQ8?si=9s0MyxfMdRbCd_4g" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
MCCの自動生成コードは参考にしましたが、作りは違っています。

:::note info
***Hostから送信されるコマンド***
・**１バイト書き込みコマンド(３バイト送信）** 18F27Q43のLEDがその値(0xAA or 0x55)で点滅。
・**１バイト読み出しコマンド(２バイト送信＋1バイト受信）** AtomのLCDにClient内のｲﾝｸﾘﾒﾝﾄ値を表示。
・**10バイト読み出しコマンド(２バイト送信＋10バイト受信)**  AtomのLCDにClietのAD５チャンネルの値を表示。

上記３コマンドは、実働しています。オシロの波形もきれいです。ただし....
:::

:::note warn
私の書いたコードは、現時点で、**I2C1CNTが機能していません。** I2C1CNTは、送受信のバイト総数を設定します。**モジュールが送信または受信中にレジスタが更新されると、I2C1CNTレジスタの値を破損する可能性があります。前もってI2Cバスがアイドリング状態のときに書き込む必要があります。
Clientモードは、クロックストレッチがかかっていないとき(CSTR==0)、もしくは、Stopコンディションが受信された後(PCIF==1)のときに、I2C1CNTに書き込むことができます**(P.633 36.3.12 Data Byte Count)。
考慮したうえでも、原因がわからず、究明中。
:::

:::note warn
# Client側コード (PIC18F27Q43) 暫定コード
```C:I2C_Client.c
#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


```
:::

:::note
# Host側コード (M5 AtomLite) 決定稿
```C++:I2C_Host.cpp
#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が機能していません。初期値を１以上に設定しても、次の割り込みで0クリアされてしまいます。ClientサイドからACKを投げることは、Readモードではないので、なんとかなっていますが、きちんと送信長を設定してデクリメントされていくように改善したいです。