找回密码
 立即注册
查看: 2595|回复: 2

STC8H8K64U与ESP32/Arduino的IIC通信问题

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:6
  • 最近打卡:2024-03-02 15:38:24

2

主题

22

回帖

86

积分

注册会员

积分
86
发表于 2024-3-1 11:41:59 | 显示全部楼层 |阅读模式
STC8H8K64U与ESP32/Arduino的新结合!!
最近研究STC8K8K64U与ESP32的主从IIC通信,以STC8H8K64U作为IIC从机接收ESP32(IIC主机)发送的消息。
目前遇到的问题是:STC8K8K64U接收到的消息与ESP32发送的消息不一致。
如下图所示:
截图202403011129382455.jpg

测试结果发现:STC8H8K64U接收到的消息是ESP32发送消息*2的内容,但当发送的数据是3位数后,STC接收到的数据是后2位数的结果。



ESP32采用ArduinoIDE编程,SCL->IO22, SDA->IO23,通过Serial0查看发送的消息,ESP32端代码如下:


/**
* 这是主机的程序
*/
#include <Wire.h>
int num = 1;           //用来发送给从机
int address = 0x5a;      //从机地址
void setup() {
  Serial.begin(115200);
  if(Wire.begin())    //主机加入I2C总线中
    Serial.println("i2c success");
  else
    Serial.println("i2c failed");
}
void loop() {
  //向从机发送数据
  char tmp[32];
  num=num+1;
  itoa(num, tmp, 10);  //将数字转换成字符串
  Wire.beginTransmission(address);
  Wire.write(num);    //传送数字字符串
  Serial.println(num);
  int ret = Wire.endTransmission();
  if(ret != 0)
  {
    Serial.printf("send failed: %d\r\n", ret);
    return;
  }


STC8K8K64U采用keil5编程,SCL->P3.2, SDA->P3.3,时钟 24MHz,通过USB转TTL模块接STC8K8K64U的3.0 3.1引脚查看接收到的消息,STC8K8K64U端代码如下:

#include "reg51.h"
#include "intrins.h"
#include "stdio.h"
#define MAIN_Fosc       24000000L   //定义主时钟
#define Baudrate        115200L
#define TM              (65536 -(MAIN_Fosc/Baudrate/4))
typedef     unsigned char   u8;
typedef     unsigned int    u16;
typedef     unsigned long   u32;
sfr INT_CLKO = 0x8F;
sfr AUXR = 0x8E;
sfr AUXR1 = 0xA2;
sfr P_SW1 = 0xA2;
sfr P_SW2 = 0xBA;
sfr TH2  = 0xD6;
sfr TL2  = 0xD7;
sfr IE2   = 0xAF;
sfr P1M1 = 0x91;    //PxM1.n,PxM0.n     =00--->Standard,    01--->push-pull
sfr P1M0 = 0x92;    //                  =10--->pure input,  11--->open drain
sfr P0M1 = 0x93;
sfr P0M0 = 0x94;
sfr P2M1 = 0x95;
sfr P2M0 = 0x96;
sfr P3M1 = 0xB1;
sfr P3M0 = 0xB2;
sfr P4M1 = 0xB3;
sfr P4M0 = 0xB4;
sfr P5M1 = 0xC9;
sfr P5M0 = 0xCA;
sfr P6M1 = 0xCB;
sfr P6M0 = 0xCC;
sfr P7M1 = 0xE1;
sfr P7M0 = 0xE2;
sfr P6   = 0xE8;
sfr P7   = 0xF8;
#define I2CCFG          (*(unsigned char volatile xdata *)0xfe80)
#define I2CMSCR         (*(unsigned char volatile xdata *)0xfe81)
#define I2CMSST         (*(unsigned char volatile xdata *)0xfe82)
#define I2CSLCR         (*(unsigned char volatile xdata *)0xfe83)
#define I2CSLST         (*(unsigned char volatile xdata *)0xfe84)
#define I2CSLADR        (*(unsigned char volatile xdata *)0xfe85)
#define I2CTXD          (*(unsigned char volatile xdata *)0xfe86)
#define I2CRXD          (*(unsigned char volatile xdata *)0xfe87)
#define I2CMSAUX        (*(unsigned char volatile xdata *)0xfe88)
#define Timer0_Reload   (65536UL -(MAIN_Fosc / 1000))       //Timer 0 中断频率, 1000次/秒
#define Baudrate1           115200L
#define UART1_BUF_LENGTH    64
/*************  本地变量声明    **************/
u8  TX1_Cnt;    //发送计数
u8  RX1_Cnt;    //接收计数
bit B_TX1_Busy; //发送忙标志
u8  xdata RX1_Buffer[UART1_BUF_LENGTH]; //接收缓冲
bit isda;                                       //设备地址标志
bit isma;                                       //存储地址标志
bit B_1ms;          //1ms标志
bit DisplayFlag;
u8 addr;
u8 Rx_Cnt;
u8 Tx_Cnt;
u8 RxBuffer[8];
u16 Test_cnt;   //测试用的秒计数变量
u8 code TxBuf0[8] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};
u8 code TxBuf1[8] = {0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18};
u8 code TxBuf2[8] = {0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28};
u8 code TxBuf3[8] = {0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38};
u8 code TxBuf4[8] = {0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48};
u8 code TxBuf5[8] = {0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58};
u8 code TxBuf6[8] = {0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68};
u8 code TxBuf7[8] = {0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78};
void WriteNbyte(u8 addr, u8 *p, u8 number);
void UART1_config(u8 brt);   // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
void UartPutc(unsigned char dat)
{
        B_TX1_Busy = 1;
        SBUF = dat;
        while(B_TX1_Busy);
}
char putchar(char c)
{
        UartPutc(c);
        return c;
}
void I2C_Isr() interrupt 24
{
    char store;
    store = P_SW2;
    P_SW2 |= 0x80;
    if (I2CSLST & 0x40)
    {
        I2CSLST &= ~0x40;                       //处理START事件
    }
    else if (I2CSLST & 0x20)
    {
        I2CSLST &= ~0x20;                       //处理RECV事件,SLACKO设置为0
        if (isda)
        {
            isda = 0;                           //处理RECV事件(RECV DEVICE ADDR)
        }
        else if (isma)
        {
            isma = 0;                           //处理RECV事件(RECV MEMORY ADDR)
            addr = I2CRXD;
            Tx_Cnt = 0;
            Rx_Cnt = 0;
            switch(addr)
            {
                case 0:
                    I2CTXD = TxBuf0[Tx_Cnt++];
                break;
                case 1:
                    I2CTXD = TxBuf1[Tx_Cnt++];
                break;
                case 2:
                    I2CTXD = TxBuf2[Tx_Cnt++];
                break;
                case 3:
                    I2CTXD = TxBuf3[Tx_Cnt++];
                break;
                case 4:
                    I2CTXD = TxBuf4[Tx_Cnt++];
                break;
                case 5:
                    I2CTXD = TxBuf5[Tx_Cnt++];
                break;
                case 6:
                    I2CTXD = TxBuf6[Tx_Cnt++];
                break;
                case 7:
                    I2CTXD = TxBuf7[Tx_Cnt++];
                break;
            }
        }
        else
        {
           // Rx_Cnt = 0;
            RxBuffer[Rx_Cnt++] = I2CRXD;            //处理RECV事件(RECV DATA)
        }
    }
    else if (I2CSLST & 0x10)
    {
        I2CSLST &= ~0x10;                       //处理SEND事件
        if (I2CSLST & 0x02)
        {
            I2CTXD = 0xff;
        }
        else
        {
            switch(addr)
            {
                case 0:
                    I2CTXD = TxBuf0[Tx_Cnt++];
                break;
                case 1:
                    I2CTXD = TxBuf1[Tx_Cnt++];
                break;
                case 2:
                    I2CTXD = TxBuf2[Tx_Cnt++];
                break;
                case 3:
                    I2CTXD = TxBuf3[Tx_Cnt++];
                break;
                case 4:
                    I2CTXD = TxBuf4[Tx_Cnt++];
                break;
                case 5:
                    I2CTXD = TxBuf5[Tx_Cnt++];
                break;
                case 6:
                    I2CTXD = TxBuf6[Tx_Cnt++];
                break;
                case 7:
                    I2CTXD = TxBuf7[Tx_Cnt++];
                break;
            }
        }
    }
    else if (I2CSLST & 0x08)
    {
        I2CSLST &= ~0x08;                       //处理STOP事件
        isda = 1;
        isma = 1;
        DisplayFlag = 1;
    }
    P_SW2 = store;
}
void main()
{
    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P3M1 = 0x0c;   P3M0 = 0x0c;   //SCL、SDA设置为开漏模式
    P_SW2 = 0x80;
    P_SW2 |= 0x30;   //I2C功能脚选择,00:P1.5,P1.4; 01:P2.5,P2.4; 11:P3.2,P3.3
    I2CCFG = 0x80;                              //使能I2C从机模式
    I2CSLADR = 0x2d;                            //设置从机设备地址为5A
    I2CSLST = 0x00;
    I2CSLCR = 0x78;                             //使能从机模式中断
    DisplayFlag = 0;
    UART1_config(1);    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
    EA = 1;
    isda = 1;                                   //用户变量初始化
    isma = 1;
    addr = 0;
    I2CTXD = RxBuffer[addr];
    while (1)
                {
                        if(DisplayFlag)
                        {
                                DisplayFlag = 0;
                                if(RxBuffer[0] < 10)
                                {
                                                printf("I2CRXD=%bd\r\n",I2CRXD);  //尝试打印接收到的消息
                                                printf("RxBuffer=%bd\r\n",RxBuffer[0]);  //尝试打印接收到的消息
                                                printf("Rx_Cnt=%bd\r\n",Rx_Cnt);  //尝试打印接收到的消息
                                }
                        }
                }
}
//========================================================================
// 函数: SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
//========================================================================
void SetTimer2Baudraye(u16 dat)  // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{
    AUXR &= ~(1<<4);    //Timer stop
    AUXR &= ~(1<<3);    //Timer2 set As Timer
    AUXR |=  (1<<2);    //Timer2 set as 1T mode
    TH2 = dat / 256;
    TL2 = dat % 256;
    IE2  &= ~(1<<2);    //禁止中断
    AUXR |=  (1<<4);    //Timer run enable
}
//========================================================================
// 函数: void UART1_config(u8 brt)
// 描述: UART1初始化函数。
// 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
// 返回: none.
//========================================================================
void UART1_config(u8 brt)    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{
    /*********** 波特率使用定时器2 *****************/
    if(brt == 2)
    {
        AUXR |= 0x01;       //S1 BRT Use Timer2;
        SetTimer2Baudraye(65536UL - (MAIN_Fosc / 4) / Baudrate1);
    }
    /*********** 波特率使用定时器1 *****************/
    else
    {
        TR1 = 0;
        AUXR &= ~0x01;      //S1 BRT Use Timer1;
        AUXR |=  (1<<6);    //Timer1 set as 1T mode
        TMOD &= ~(1<<6);    //Timer1 set As Timer
        TMOD &= ~0x30;      //Timer1_16bitAutoReload;
        TH1 = (u8)((65536UL - (MAIN_Fosc / 4) / Baudrate1) / 256);
        TL1 = (u8)((65536UL - (MAIN_Fosc / 4) / Baudrate1) % 256);
        ET1 = 0;    //禁止中断
        INT_CLKO &= ~0x02;  //不输出时钟
        TR1  = 1;
    }
    /*************************************************/
    SCON = (SCON & 0x3f) | 0x40;    //UART1模式, 0x00: 同步移位输出, 0x40: 8位数据,可变波特率, 0x80: 9位数据,固定波特率, 0xc0: 9位数据,可变波特率
//  PS  = 1;    //高优先级中断
    ES  = 1;    //允许中断
    REN = 1;    //允许接收
    P_SW1 &= 0x3f;
    P_SW1 |= 0x00;      //UART1 switch to, 0x00: P3.0 P3.1, 0x40: P3.6 P3.7, 0x80: P1.6 P1.7, 0xC0: P4.3 P4.4
//  PCON2 |=  (1<<4);   //内部短路RXD与TXD, 做中继, ENABLE,DISABLE
    B_TX1_Busy = 0;
    TX1_Cnt = 0;
    RX1_Cnt = 0;
}
//========================================================================bh88gt
// 函数: void UART1_int (void) interrupt UART1_VECTOR
// 描述: UART1中断函数。
// 参数: nine.
// 返回: none.
//========================================================================
void UART1_int (void) interrupt 4
{
    if(RI)
    {
        RI = 0;
        RX1_Buffer[RX1_Cnt] = SBUF;
        if(++RX1_Cnt >= UART1_BUF_LENGTH)   RX1_Cnt = 0;    //防溢出
    }
    if(TI)
    {
        TI = 0;
        B_TX1_Busy = 0;
    }
}


I2C从机中断模式.zip

57.03 KB, 下载次数: 149

回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2026-06-21 19:29:37

4

主题

6

回帖

78

积分

注册会员

积分
78
发表于 2026-6-21 19:29:37 | 显示全部楼层
我研究过你说的问题,我是用esp8266作为主控(因为管脚少),stc8h8k64u作为外设(管脚丰富),控制按键,单色spi显示屏,利用iic作为两者的通讯联系,stc单片机用中断的方式来工作,刚开始也困难,后来终于搞好了,貌似stc的例程有些问题,而且iic例程还有硬件和软件模拟两个版本,涉及到iic的应答信号等,手册也是比较含糊的,不过最后都解决了,可以给你参考下。
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2026-06-21 19:29:37

4

主题

6

回帖

78

积分

注册会员

积分
78
发表于 2026-6-21 19:39:39 | 显示全部楼层
esp8266端:
#ifndef ST75161Spi_h
#define ST75161Spi_h

#include "OLEDDisplay.h"
#include <SPI.h>

class SH1106Wire : public OLEDDisplay {
  private:
      uint8_t     _address;
      int         _sda;
      int         _scl;
      bool        _doI2cAutoInit = false;
      TwoWire*    _wire = NULL;
      long        _frequency;

  public:     
      uint16_t InitContrast = 0x0163;   //设置对比度电压为 0x0163=355 * 0.04+3.6 = 17.8V

  public:
    /**
     * Create and initialize the Display using Wire library
     *
     * Beware for retro-compatibility default values are provided for all parameters see below.
     * Please note that if you don't wan't SD1306Wire to initialize and change frequency speed ot need to
     * ensure -1 value are specified for all 3 parameters. This can be usefull to control TwoWire with multiple
     * device on the same bus.
     *
     * @param address I2C Display address
     * @param sda I2C SDA pin number, default to -1 to skip Wire begin call
     * @param scl I2C SCL pin number, default to -1 (only SDA = -1 is considered to skip Wire begin call)
     * @param g display geometry dafault to generic GEOMETRY_128_64, see OLEDDISPLAY_GEOMETRY definition for other options
     * @param i2cBus on ESP32 with 2 I2C HW buses, I2C_ONE for 1st Bus, I2C_TWO fot 2nd bus, default I2C_ONE
     * @param frequency for Frequency by default Let's use ~700khz if ESP8266 is in 160Mhz mode, this will be limited to ~400khz if the ESP8266 in 80Mhz mode
     */
    SH1106Wire(uint8_t address, int sda = -1, int scl = -1, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64, HW_I2C i2cBus = I2C_ONE, long frequency = 700000) {
      setGeometry(g);

      this->_address = address;
      this->_sda = sda;
      this->_scl = scl;
#if !defined(ARDUINO_ARCH_ESP32)
      this->_wire = &Wire;
#else
      this->_wire = (i2cBus==I2C_ONE) ? &Wire : &Wire1;
#endif
      this->_frequency = frequency;
    }

    bool connect() {
#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP8266)
      _wire->begin();
#else
      // On ESP32 arduino, -1 means 'don't change pins', someone else has called begin for us.
      if(this->_sda != -1)
        _wire->begin(this->_sda, this->_scl);
#endif
      // Let's use ~700khz if ESP8266 is in 160Mhz mode
      // this will be limited to ~400khz if the ESP8266 in 80Mhz mode.
      if(this->_frequency != -1)
        _wire->setClock(this->_frequency);
      return true;
    }

    /*-----测试esp8266 与 stc8h8k64u的 IIC 功能-----*/
    uint8_t press_key(void)
    {
      uint8_t data;

      _wire->requestFrom(0x07, 1);
      while (_wire->available())
      {
        /* 读出stc8h的返回值 */
        data = _wire->read();
      }
      // Serial.print("data3 = 0x");
      // Serial.println(data, HEX);
      return data;
    }
    void WriteAbyte(uint8_t dev_addr, uint8_t mem_addr, uint8_t dat)      //write a byte to I2C
    {
      uint8_t ret;

      _wire->beginTransmission(dev_addr);
      _wire->write(mem_addr);
      _wire->write(dat);

      do
      {
        ret = _wire->endTransmission();       //该条语句包含了iic的起始、地址、应答、数据、停止信号等。
        // // Serial.println("void WriteAbyte();");
        // if(ret == 4)  Serial.println("return 4;  // line busy");
        // if(ret == 2)  Serial.println("return 2;  // received NACK on transmit of address");
        // if(ret == 3)  Serial.println("return 3;  // received NACK on transmit of data");
        // // if(ret == 0)  Serial.println("return 0;");  //通讯成功
        if(ret != 0)  ESP.restart();  //软起动esp8266,ESP.reset()、ESP.restart()
      } while(ret);
    }

    void WriteNbyte(uint8_t dev_addr, uint8_t mem_addr, uint8_t *p, uint8_t number)  /*   First Data Address,Byte lenth   */
    {
      uint8_t ret;

      _wire->beginTransmission(dev_addr);
      _wire->write(mem_addr);

      do
      {
        _wire->write(*p++);
      }
      while(--number);
      
      do
      {
        ret = _wire->endTransmission();       //该条语句包含了iic的起始、地址、应答、数据、停止信号等。
        // // Serial.println("void WriteNbyte();");
        // if(ret == 4)  Serial.println("return 4;  // line busy");
        // if(ret == 2)  Serial.println("return 2;  // received NACK on transmit of address");
        // if(ret == 3)  Serial.println("return 3;  // received NACK on transmit of data");
        // // if(ret == 0)  Serial.println("return 0;");  //通讯成功
        if(ret != 0)  ESP.restart();  //软起动esp8266,ESP.reset()、ESP.restart()
      } while(ret);
    }
    /*-----测试esp8266 与 stc8h8k64u的 IIC 功能-----*/

    void display(void) {
      initI2cIfNeccesary();
      #ifdef OLEDDISPLAY_DOUBLE_BUFFER
        uint8_t minBoundY = UINT8_MAX;
        uint8_t maxBoundY = 0;

        uint8_t minBoundX = UINT8_MAX;
        uint8_t maxBoundX = 0;

        uint8_t x, y;

        // Calculate the Y bounding box of changes
        // and copy buffer[pos] to buffer_back[pos];
        for (y = 0; y < (displayHeight / 8); y++) {
          for (x = 0; x < displayWidth; x++) {
           uint16_t pos = x + y * displayWidth;
           if (buffer[pos] != buffer_back[pos]) {
             minBoundY = _min(minBoundY, y);
             maxBoundY = _max(maxBoundY, y);
             minBoundX = _min(minBoundX, x);
             maxBoundX = _max(maxBoundX, x);
           }
           buffer_back[pos] = buffer[pos];
         }
         yield();
        }

        // If the minBoundY wasn't updated
        // we can savely assume that buffer_back[pos] == buffer[pos]
        // holdes true for all values of pos
        if (minBoundY == UINT8_MAX) return;

        // Calculate the colum offset
        uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F;
        uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 );

        // uint8_t k = 0;
        // for (y = minBoundY; y <= maxBoundY; y++) {
        //   sendCommand(0xB0 + y);
        //   sendCommand(minBoundXp2H);
        //   sendCommand(minBoundXp2L);
        //   for (x = minBoundX; x <= maxBoundX; x++) {
        //     if (k == 0) {
        //       _wire->beginTransmission(_address);
        //       _wire->write(0x40);
        //     }
        //     _wire->write(buffer[x + y * displayWidth]);
        //     k++;
        //     if (k == I2C_OLED_TRANSFER_BYTE)  {
        //       _wire->endTransmission();
        //       k = 0;
        //     }
        //   }
        //   if (k != 0)  {
        //     _wire->endTransmission();
        //     k = 0;
        //   }
        //   yield();
        // }

        // if (k != 0) {
        //   _wire->endTransmission();
        // }
        /*--------------------guochl--------------------*/
        //通过IIC外设传递esp8266的数据到8051芯片
        // set_address(minBoundX+16,minBoundY+6,maxBoundX+16,maxBoundY+6);        //放在屏幕正中间
        //设置显示区域
        uint8_t  disp[4];         
        disp[0] = minBoundX+16;
        disp[1] = minBoundY+6;
        disp[2] = maxBoundX+16;
        disp[3] = maxBoundY+6;
        WriteNbyte(0x01,0x01,disp, 4);

        //往显示区域传输数据
        for (y = minBoundY; y <= maxBoundY; y++) {
          for (x = minBoundX; x <= maxBoundX; x++) {
            WriteAbyte(0x02,0x05,buffer[x + y * displayWidth]);  //guochl不一定是128*8
        /*--------------------guochl--------------------*/   
          }
          yield();
        }        
      #else
        uint8_t * p = &buffer[0];
        for (uint8_t y=0; y<8; y++) {
          sendCommand(0xB0+y);
          sendCommand(0x02);
          sendCommand(0x10);
          for( uint8_t x=0; x<(128/I2C_OLED_TRANSFER_BYTE); x++) {
            _wire->beginTransmission(_address);
            _wire->write(0x40);
            for (uint8_t k = 0; k < I2C_OLED_TRANSFER_BYTE; k++) {
              _wire->write(*p++);
            }
            _wire->endTransmission();
          }
        }
      #endif
    }

    void setI2cAutoInit(bool doI2cAutoInit) {
      _doI2cAutoInit = doI2cAutoInit;
    }
    /*--------------------guochl--------------------*/
    void setContrast (uint8_t contrast) {
      WriteAbyte(0x03,0x06,contrast); //发送给stc8h的驱动液晶屏的对比度信息
    }
    void Vop_Fine_Tune_INC(void)  //微调增加对比度或是亮度
    {
      _wire->requestFrom(0x04, 1);  /* 发送请求1个字节的stc8h的到地址0x04信息 */
    }
    void Vop_Fine_Tune_DEC(void)  //微调减少对比度或是亮度  
    {
      _wire->requestFrom(0x05, 1);  /* 发送请求1个字节的stc8h的到地址0x05信息 */
    }
   
    bool init() {                 //注释掉用的就是父类的,否则需要打开。
      uint8_t ret,data = 0xFF;

      BufferOffset = getBufferOffset();

      if(!allocateBuffer()) {
        return false;
      }

      do  //等待stc8h的reset启动完毕
      {

        do{
            Serial.println("等待从机iic启动成功......");
            // delay(1000);//等待从机完成初始化,此处省略是用应答信号判断的。
            /* 发送请求1个字节的stc8h的到地址0x06信息 */
            ret = _wire->requestFrom(0x06, 1); //执行stc8h的sendInitCommands()命令;
            // while(ret != 1) //正常是1
            // {
            //   Serial.println("_wire->requestFrom(0x06, 1); ");
            //   Serial.print("ret:");Serial.println(ret);
            //   if(ret == 4)  Serial.println("return 4;  // line busy");
            //   if(ret == 2)  Serial.println("return 2;  // received NACK on transmit of address");

            //   //size_t read = (twi_readFrom(address, rxBuffer, size, sendStop) == 0) ? size : 0;
            //   if(ret == 0)  Serial.println("return 0;");  //返回的确实是0,如上语句变成了1
            // }

            while (_wire->available())
            {
              /* 读出stc8h的返回值,若不是0x06就重新发送请求信息 */
              data = _wire->read(); // receive a byte as character
            }
            // /* 显示stc8h送来的数据 */
            // Serial.print("data1 = 0x");
            // Serial.println(data, HEX);         
        } while(ret != 1);
        Serial.println("从机iic启动成功!!!!!!");

      }while(data == 0xFF);

      resetDisplay();
      return true;
    }
  /*--------------------guochl--------------------*/      
  private:
        int getBufferOffset(void) {
                return 0;
        }
    inline void sendCommand(uint8_t command) __attribute__((always_inline)){
      _wire->beginTransmission(_address);
      _wire->write(0x80);
      _wire->write(command);
      _wire->endTransmission();
    }

    void initI2cIfNeccesary() {
      if (_doI2cAutoInit) {
#ifdef ARDUINO_ARCH_AVR
        _wire->begin();
#else
        _wire->begin(this->_sda, this->_scl);
#endif
      }
    }
};

#endif  //SH1106Wire.h


stc8h8k64u端:
/*************  本程序功能说明  **************
guochl:20251123
本程序基于STC8H8K64U为主控芯片的IIC通讯,从esp8266接收IIC数据,转而控制TG160160液晶屏显示
其大致流程为:ESP8266_IIC -> STC8H_IIC => STC8H_IIC -> STC8H_SPI => TG160160 显示
ESP8266模拟I2C做主机模式:SCL->D1(GPIO5), SDA->D2(GPIO4)
STC8H内部集成I2C串行总线控制器做从机模式,SCL->P1.5, SDA->P1.4
通过内部走线连接 D2(GPIO4)->P1.4, D1(GPIO5)->P1.5,实现I2C自发自收功能
两者的IIC通讯没有做数据同步工作,靠自身时序完成数据传输功能
下载时, 选择时钟 24MHZ (用户可自行修改频率)

需要关闭:REMOVEUNUSED选项,否则编译报错
20251123:增加的调节对比度的中断处理程序,十分不容易,基本算是最终版本了!

Wire.h and Wire.cpp
C:\Users\GCL\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\Wire
core_esp8266_si2c.cpp
C:\Users\GCL\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266
******************************************/

// #include "stc8h.h"                  //包含此头文件后,不需要再包含"reg51.h"头文件
// #include "intrins.h"
#include "STC8Hxxx.h"

/*可以选择第三高的的频率40Mhz,频率再高无效*/
/*20251116:下载时, 选择时钟 24MHZ,程序很稳定*/
#define MAIN_Fosc   24000000L       //定义主时钟

// typedef unsigned char   uint8_t;
// typedef unsigned int    uint16_t;
// typedef unsigned long   uint32_t;

#include "TG160160.h"

typedef struct
{
        uint8_t        isma;                                //MEMORY ADDRESS 接收判断标志
        uint8_t        isda;                                //DEVICE ADDRESS 接收判断标志
        uint8_t        addr;                                //ADDRESS 缓存
} I2C_IsrTypeDef;
/*************  本地变量声明    **************/
// sbit buzzer = P1^6;              //有源蜂鸣器
uint8_t key_values;
uint8_t pdata buffer[10];
I2C_IsrTypeDef I2CIsr;

void I2C_ISR_Handler() interrupt I2C_VECTOR //I2C_VECTOR = 24
{
        // TODO: 在此处添加用户代码
        if (I2CSLST & 0x40)
        {
                I2CSLST &= ~0x40;                                                //处理START事件
        }
        else if (I2CSLST & 0x20)
        {
                I2CSLST &= ~0x20;                             //处理RECV事件,SLACKO设置为0

                if (I2CIsr.isda)
                {       
                        I2CIsr.isda = 0;                    //处理RECV事件(RECV DEVICE ADDR)
            buffer[0] = I2CRXD;                 //guochl:接收到的硬件iic地址
        }
                else if (I2CIsr.isma)
                {
                        I2CIsr.isma = 0;                    //处理RECV事件(RECV MEMORY ADDR)
                        I2CIsr.addr = I2CRXD;
                }
                else
                {
                        buffer[I2CIsr.addr++] = I2CRXD;     //处理RECV事件(RECV DATA)
                }
        }
        else if (I2CSLST & 0x10)
        {
                I2CSLST &= ~0x10;                       //处理SEND事件
        // 添加用户程序
        }
        else if (I2CSLST & 0x08)
        {
                I2CSLST &= ~0x08;                       //处理STOP事件
                I2CIsr.isda = 1;
                I2CIsr.isma = 1;
   
        switch(buffer[0] >> 1)
        {
            case (0x01):    //设置液晶屏幕的显示区域  
                set_address(buffer[1],buffer[2],buffer[3],buffer[4]);
                break;
            case (0x02):    //在液晶屏幕的显示区域传输数据  
                sendData(buffer[5]);     
                break;
            case (0x03):    //设置液晶屏的对比度信息  
                setContrast(buffer[6]);  
                break;
            case (0x04):    //设置液晶屏的对比度递增
                Vop_Fine_Tune_INC();   
                break;
            case (0x05):    //设置液晶屏的对比度递减   
                Vop_Fine_Tune_DEC();   
                break;
            case (0x06):    //初始化液晶屏   
                sendInitCommands();                     
                I2CTXD = 0x06;      //返回给esp8266,表示初始化成功
                break;  

            /*press_key*/   
            case (0x07):            //stc8h -> esp8266 传递按键值        
                I2CTXD = key_values;
                key_values = 0xFF;  //清除按键值,等待下一次的按键  
                break;
                                                                                          
            default:                          
                break;  
        }         
        }
}
//========================================================================
// 函数: void        P1INT_ISR(void) interrupt 38
// 描述: P1(P1.0~P1.7)中断处理函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void P1INT_ISR(void) interrupt 38                //P1.0~P1.7 IO中断处理代码
{
        uint8_t        i;
        i = P1INTF;                //读取中断标志
        P1INTF &= ~i;        //清除中断标志

        if((i & INTF_0) != 0)        //P1.0中断
    {
        //P1.0口中断
        key_values = 0x7F;  //RIGHT;K04
    }

    if((i & INTF_7) != 0)
    {
        //P1.7口中断
        key_values = 0xFE;  //B;K06
    }

    /*复合按键K01+K04 = UP+RIGHT = 上+右*/
    if((i & INTF_1) != 0)
    {
        //P1.1口中断
        key_values = 0xEF;  //UP;K01

        /*复合按键K01+K04 = UP+RIGHT = 上+右*/
        if((i & INTF_0) != 0)
        {
            key_values = 0x6F;  //K01+K04 = UP+RIGHT = 上+右
        }

        /*复合按键K01+K02 = UP+LEFT = 上+左*/
        if((P3INTF & INTF_7) != 0)  //P3口的IO中断优先级高于P1口
        {
            key_values = 0xAF;  //K01+K02 = UP+LEFT = 上+左
        }
        
        // /*复合按键K01+K04 = UP+START = 上+开始*/
        // if((i & INTF_7) != 0)
        // {
        //     key_values = 0xEE;  //K01+K06 = UP+START = 上+开始
        // }                  
    }
    if((i & INTF_3) != 0)
    {
        //P1.3口中断
        key_values = 0xDF;  //DOWN;K03
    }
}

//========================================================================
// 函数: void        P3INT_ISR(void) interrupt 40
// 描述: P3(P3.0~P3.7)中断处理函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void P3INT_ISR(void) interrupt 40                //P3.0~P3.7 IO中断处理代码
{
        uint8_t        i;
        i = P3INTF;                //读取中断标志
        P3INTF &= ~i;        //清除中断标志

        // if((i & INTF_0) != 0)        //P3.0中断
        // {
        //         P00 = ~P00;                        //取反P0.0指示
        // }
        // if((i & INTF_1) != 0)        //P3.1中断
        // {
        //         P01 = ~P01;                        //取反P0.1指示
        // }
        // if((i & INTF_2) != 0)        //P3.2中断
        // {
        //         P02 = ~P02;                        //取反P0.2指示
        // }
        // if((i & INTF_3) != 0)        //P3.3中断
        // {
        //         P03 = ~P03;                        //取反P0.3指示
        // }
        if((i & INTF_7) != 0)        //P3.7中断
        {
        //P3.7口中断
        key_values = 0xBF;  //LEFT;K02
        }

    // /*复合按键K01+K02 = UP+LEFT = 上+左
    //   20251123:经过检验,不成功,原因是优先级的问题;
    //   P1口的IO中断优先级低于P3口,导致以下程序先执行。
    // */
        // if((P1INTF & INTF_1) != 0 && (i & INTF_7) != 0)
        // {
    //     key_values = 0xAF;  //K01+K02 = UP+LEFT = 上+左
        // }   
}

/*************        本地函数声明        **************/
void GPIO_INT_Config(uint8_t gpio, uint8_t mode, uint8_t pin);        //普通IO中断配置函数, 中断模式(只取其一): INT_FALL, INT_RISE, INT_LOW, INT_HIGH. 分别是 上升沿 下降沿 低电平  高电平 中断.

void main()
{
    P_SW2 |= 0x80;              //扩展寄存器(XFR)访问使能

    P0M0 = 0x00; P0M1 = 0x00;
    P1M0 = 0x00; P1M1 = 0x00;
    P2M0 = 0x00; P2M1 = 0x00;
    P3M0 = 0x00; P3M1 = 0x00;
    P4M0 = 0x00; P4M1 = 0x00;
    P5M0 = 0x00; P5M1 = 0x00;
    P6M0 = 0x00; P6M1 = 0x00;
    P7M0 = 0x00; P7M1 = 0x00;

    // RSTCFG=0x50;             //开启RST键进入ISP模式

    P1PU |= 0x30;               //P1.4、P1.5设置上拉电阻
    // P1M0 |= 0x40; P1M1 &= ~0x40;    //P1.6是蜂鸣器,设置成推挽口

    P_SW2 |= 0x00;              //I2C功能脚选择,0x00:P1.5,P1.4; 0x10:P2.5,P2.4;
                                //              0x20:P7.7,P7.6; 0x30:P3.2,P3.3
    I2CCFG = 0x80;              //使能I2C从机模式
    // I2CSLADR = 0x5a;         //设置从机设备地址为5A,此程序中不能被打开
    I2CSLADR = 0x01;            //忽略从机设备地址
    I2CSLST = 0x00;             //从机状态寄存器清零
    // I2CSLCR = 0x00;          //禁止从机模式中断开
    I2CSLCR = 0x78;             //使能从机模式中断全部开

    //普通IO中断配置函数, 中断模式只取其一:INT_FALL/上升沿、INT_RISE/下降沿、INT_LOW/低电平、INT_HIGH/高电平;
    GPIO_INT_Config(GPIO_P1, INT_LOW, Pin0 | Pin1 | Pin3 | Pin7);   //INT_LOW/低电平方式,兼容游戏
    GPIO_INT_Config(GPIO_P3, INT_LOW, Pin7);                        //INT_LOW/低电平方式,兼容游戏
    // GPIO_INT_Config(GPIO_P1, INT_RISE, Pin0 | Pin1 | Pin3 | Pin7);      //INT_LOW/低电平方式,不兼容游戏
    // GPIO_INT_Config(GPIO_P3, INT_RISE, Pin7);                           //INT_LOW/低电平方式,不兼容游戏

    /*测试用*/
        // GPIO_INT_Config(GPIO_P3, INT_LOW, Pin0 | Pin1 | Pin2 | Pin3);       

    STC8H_reset();
    EA = 1;                     //打开总中断,过早打开会影响主从机的iic通讯
    while (1);
}

//========================================================================
// 函数: void        GPIO_INT_Config(uint8_t gpio, uint8_t pin, uint8_t mode)
// 描述: IO中断初始化函数.
// 参数: gpio: 要初始化的IO组, 参数取值(只取其一): GPIO_P0, GPIO_P1, GPIO_P2, GPIO_P3, GPIO_P4, GPIO_P5, GPIO_P6, GPIO_P7, GPIO_P8, GPIO_P9.
//       mode: 中断模式,       参数取值(只取其一): INT_FALL, INT_RISE, INT_LOW, INT_HIGH. 分别是 上升沿 下降沿 低电平  高电平 中断.
//       pin:  要初始化的引脚, 参数取值(多个值时用或操作): Pin0, Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7. PinAll.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void GPIO_INT_Config(uint8_t gpio, uint8_t mode, uint8_t pin)
{
        uint8_t        xdata        *pt;
        uint8_t        j;

        if(gpio > GPIO_P7)        return;        //IO口错误
        if(mode >= 4)                return;        //模式错误
        P_SW2 |= 0x80;                        //访问XFR

        pt  = 0xfd10 + gpio;        //PxINTF端口中断标志寄存器
        *pt = *pt & ~pin;                //清除中断标志

        pt  = 0xfd30 + gpio;                        //PxIM1端口中断模式配置寄存器1
        if(mode & 0x02)        j = 0xff & pin;
        else                        j = 0;
        *pt = (*pt & ~pin) | j;                        //设置模式

        pt  = 0xfd20 + gpio;                        //PxIM0端口中断模式配置寄存器0
        if(mode & 0x01)        j = 0xff & pin;
        else                        j = 0;
        *pt = (*pt & ~pin) | j;                        //设置模式

        pt  = 0xfd00 + gpio;        //PxINTE端口中断使能寄存器
        *pt |= pin;                                //允许IO中断
}
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-7-5 11:55 , Processed in 0.073079 second(s), 56 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表