STC8H8K64U与ESP32/Arduino的IIC通信问题
STC8H8K64U与ESP32/Arduino的新结合!!最近研究STC8K8K64U与ESP32的主从IIC通信,以STC8H8K64U作为IIC从机接收ESP32(IIC主机)发送的消息。
目前遇到的问题是:STC8K8K64U接收到的消息与ESP32发送的消息不一致。
如下图所示:
测试结果发现: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;
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
/*************本地变量声明 **************/
u8TX1_Cnt; //发送计数
u8RX1_Cnt; //接收计数
bit B_TX1_Busy; //发送忙标志
u8xdata RX1_Buffer; //接收缓冲
bit isda; //设备地址标志
bit isma; //存储地址标志
bit B_1ms; //1ms标志
bit DisplayFlag;
u8 addr;
u8 Rx_Cnt;
u8 Tx_Cnt;
u8 RxBuffer;
u16 Test_cnt; //测试用的秒计数变量
u8 code TxBuf0 = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};
u8 code TxBuf1 = {0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18};
u8 code TxBuf2 = {0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28};
u8 code TxBuf3 = {0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38};
u8 code TxBuf4 = {0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48};
u8 code TxBuf5 = {0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58};
u8 code TxBuf6 = {0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68};
u8 code TxBuf7 = {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;
break;
case 1:
I2CTXD = TxBuf1;
break;
case 2:
I2CTXD = TxBuf2;
break;
case 3:
I2CTXD = TxBuf3;
break;
case 4:
I2CTXD = TxBuf4;
break;
case 5:
I2CTXD = TxBuf5;
break;
case 6:
I2CTXD = TxBuf6;
break;
case 7:
I2CTXD = TxBuf7;
break;
}
}
else
{
// Rx_Cnt = 0;
RxBuffer = I2CRXD; //处理RECV事件(RECV DATA)
}
}
else if (I2CSLST & 0x10)
{
I2CSLST &= ~0x10; //处理SEND事件
if (I2CSLST & 0x02)
{
I2CTXD = 0xff;
}
else
{
switch(addr)
{
case 0:
I2CTXD = TxBuf0;
break;
case 1:
I2CTXD = TxBuf1;
break;
case 2:
I2CTXD = TxBuf2;
break;
case 3:
I2CTXD = TxBuf3;
break;
case 4:
I2CTXD = TxBuf4;
break;
case 5:
I2CTXD = TxBuf5;
break;
case 6:
I2CTXD = TxBuf6;
break;
case 7:
I2CTXD = TxBuf7;
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;
while (1)
{
if(DisplayFlag)
{
DisplayFlag = 0;
if(RxBuffer < 10)
{
printf("I2CRXD=%bd\r\n",I2CRXD);//尝试打印接收到的消息
printf("RxBuffer=%bd\r\n",RxBuffer);//尝试打印接收到的消息
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 = SBUF;
if(++RX1_Cnt >= UART1_BUF_LENGTH) RX1_Cnt = 0; //防溢出
}
if(TI)
{
TI = 0;
B_TX1_Busy = 0;
}
}
我研究过你说的问题,我是用esp8266作为主控(因为管脚少),stc8h8k64u作为外设(管脚丰富),控制按键,单色spi显示屏,利用iic作为两者的通讯联系,stc单片机用中断的方式来工作,刚开始也困难,后来终于搞好了,貌似stc的例程有些问题,而且iic例程还有硬件和软件模拟两个版本,涉及到iic的应答信号等,手册也是比较含糊的,不过最后都解决了,可以给你参考下。 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 to buffer_back;
for (y = 0; y < (displayHeight / 8); y++) {
for (x = 0; x < displayWidth; x++) {
uint16_t pos = x + y * displayWidth;
if (buffer != buffer_back) {
minBoundY = _min(minBoundY, y);
maxBoundY = _max(maxBoundY, y);
minBoundX = _min(minBoundX, x);
maxBoundX = _max(maxBoundX, x);
}
buffer_back = buffer;
}
yield();
}
// If the minBoundY wasn't updated
// we can savely assume that buffer_back == buffer
// 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);
// 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_tdisp;
disp = minBoundX+16;
disp = minBoundY+6;
disp = maxBoundX+16;
disp = maxBoundY+6;
WriteNbyte(0x01,0x01,disp, 4);
//往显示区域传输数据
for (y = minBoundY; y <= maxBoundY; y++) {
for (x = minBoundX; x <= maxBoundX; x++) {
WriteAbyte(0x02,0x05,buffer);//guochl不一定是128*8
/*--------------------guochl--------------------*/
}
yield();
}
#else
uint8_t * p = &buffer;
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;
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 = I2CRXD; //guochl:接收到的硬件iic地址
}
else if (I2CIsr.isma)
{
I2CIsr.isma = 0; //处理RECV事件(RECV MEMORY ADDR)
I2CIsr.addr = I2CRXD;
}
else
{
buffer = 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 >> 1)
{
case (0x01): //设置液晶屏幕的显示区域
set_address(buffer,buffer,buffer,buffer);
break;
case (0x02): //在液晶屏幕的显示区域传输数据
sendData(buffer);
break;
case (0x03): //设置液晶屏的对比度信息
setContrast(buffer);
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中断
}
页:
[1]