- 打卡等级:初来乍到
- 打卡总天数:5
- 最近打卡:2026-06-21 19:29:37
注册会员
- 积分
- 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中断
} |
|