关于STC8G1K17A-DIP8驱动0.96寸OLED屏
一直搞不到这段代码原理// P3^2(SCL)和P3^3(SDA)
第一种:驱动必须加上这段代码才能显示,这段断码具体含义没搞清除。
P3M1=0x00;P3M0=0x02; //P31设置为推挽输出,P30设置为准双向口
P30=0;
P31=1;
第二种:或者用这段代码也能正常显示
// 配置P3^2(SCL)和P3^3(SDA)为准双向模式(OLED I2C引脚)外部电路上拉4.7K电阻
P3M0 &= ~0x0C; P3M1 &= ~0x0C;
当然第一种比第二种好,不用外加上拉电阻,但是要占用两个引脚。本身只有6个IO口。
所以只有选择第二种。
但是这个写好一个加热台温控程序,好像又显示不了了。估计这个8DIP的STC81K17,确实不适合。本来是想越简单越好。
下面是全部代码,有大佬的方便 看看可行吗?
//STC8G1K17A NTC 热敏电阻采样加热台 OLED 显示
//包含 ADC 采样、NTC 温度计算、按键调节、继电器控制、OLED 显示、掉电保存设置
#include <STC8G.H>
#include "OLED.h"// 你的OLED驱动头文件
#include <intrins.h>
// 引脚定义
sbit Relay= P3^1;// 继电器控制引脚(高电平吸合,低电平断开)
sbit ADC_IN = P3^0;// ADC输入引脚(NTC采样)
sbit Key_Add = P5^4; // 增加目标温度按键
sbit Key_Sub = P5^5; // 减小目标温度按键
// 全局变量定义
unsigned int ADC_Value = 0; // ADC采样原始值
unsigned char Current_Temp = 0; // 当前温度(℃,整数)
unsigned char Target_Temp = 50; // 目标温度(默认50℃,范围30-150℃)
unsigned char Save_Flag = 0; // 掉电保存标志位
// ADC配置参数
#define ADC_CHAN 0x00// ADC通道0(P3^0)
#define V_REF 3.3 // 参考电压(V)
#define R_DIV 10000 // 分压电阻阻值(10KΩ,与NTC串联)
#define NTC_B 3950 // NTC B值(常见3950,需根据实际NTC参数修改)
#define NTC_R25 10000// NTC 25℃时的阻值(10KΩ,需匹配实际参数)
// 温度范围限制
#define TEMP_MIN 30 // 最低目标温度
#define TEMP_MAX 150 // 最高目标温度
// OLED显示参数(适配你的OLED.c驱动:6x8字体行0~7,列0~127)
#define FONT_6X8 6 // 6x8字体(宽6像素,高8像素)
#define OLED_LINE0 0 // 第0行(最上方)
#define OLED_LINE1 1 // 第1行(下移8像素)
#define OLED_LINE2 2 // 第2行(下移16像素)
#define OLED_LINE3 3 // 第3行(下移24像素)
#define OLED_COL0 0 // 第0列
#define OLED_COL40 40// 第40列(温度数值起始)
#define OLED_COL60 60// 第60列(温度单位起始)
// 函数声明
void Sys_Init(void); // 系统初始化
void ADC_Init(void); // ADC初始化
unsigned int ADC_Read(void);// ADC采样
void Temp_Calc(void); // NTC温度计算(整数结果)
float log_approx(float x); // 自然对数近似函数
void Key_Scan(void); // 按键扫描(消抖)
void Relay_Control(void); // 继电器控制
void OLED_Display(void); // OLED显示(无重叠)
void Delay_ms(unsigned int ms); // 软件延时函数(适配35MHz)
void IAP_Write(unsigned int addr, unsigned char dat);// 掉电保存
unsigned char IAP_Read(unsigned int addr); // 上电恢复
void main() {
Sys_Init(); // 系统初始化
ADC_Init(); // ADC初始化
OLED_Init(); // OLED初始化
OLED_Clear(); // 清屏
// 上电读取保存的目标温度
Target_Temp = IAP_Read(0x0800);
if(Target_Temp < TEMP_MIN || Target_Temp > TEMP_MAX) {
Target_Temp = 50;// 异常值恢复默认
}
while(1) {
ADC_Value = ADC_Read();// ADC采样
Temp_Calc(); // 计算当前温度(整数)
Key_Scan(); // 按键扫描
Relay_Control(); // 继电器控制
OLED_Display(); // OLED显示(无重叠)
Delay_ms(100); // 100ms循环间隔
}
}
// 系统初始化(GPIO配置)
void Sys_Init(void) {
// 配置P3^2(SCL)和P3^3(SDA)为准双向模式(OLED I2C引脚)
P3M0 &= ~0x0C; P3M1 &= ~0x0C;
// 配置P3.1(继电器)为推挽输出,P3.0(ADC)为高阻输入
P3M0 |= 0x02; P3M1 &= ~0x02; // P3.1 推挽输出
P3M0 &= ~0x01; P3M1 |= 0x01; // P3.0 高阻输入(ADC模式)
// 配置P5.4/P5.5(按键)为准双向模式(内置上拉)
P5M0 &= ~0x30; P5M1 &= ~0x30;
}
// ADC初始化(STC8G1K17A ADC配置)
void ADC_Init(void) {
ADC_CONTR = 0x80;// 开启ADC模块(ADON=1)
ADC_CONTR |= ADC_CHAN;// 选择通道0(P3^0)
ADC_CONTR |= 0x08;// 时钟分频Fosc/32(35MHz时约1.09MHz,符合ADC要求)
_nop_(); _nop_(); // 等待ADC模块稳定
}
// ADC采样(10位精度)
unsigned int ADC_Read(void) {
unsigned int adc_val = 0;
ADC_CONTR |= 0x01;// 启动ADC转换(ADSTART=1)
while(!(ADC_CONTR & 0x20));// 等待转换完成(ADFLAG=1)
adc_val = ADC_RES;// 读取高8位结果
adc_val = (adc_val << 2) | (ADC_RESL & 0x03);// 拼接低2位,组成10位结果
ADC_CONTR &= ~0x20;// 清除转换完成标志
return adc_val;
}
// 自然对数近似函数(误差<0.1%,适用于NTC阻值范围1K~100K)
float log_approx(float x) {
float y, z;
z = (x - 1) / (x + 1);// 变量替换,缩小输入范围
y = z * z;
// 泰勒级数展开近似:ln(x) = 2*(z + z^3/3 + z^5/5 + z^7/7)
return 2 * z * (1 + y/3 + y*y/5 + y*y*y/7);
}
// NTC温度计算(整数结果,移除小数点)
void Temp_Calc(void) {
float V_adc = 0; // ADC采样电压
float R_ntc = 0; // NTC实时阻值
float temp = 0; // 计算出的温度(开尔文)
// 计算采样电压(10位ADC,最大值1023)
V_adc = (float)ADC_Value * V_REF / 1023.0;
// 计算NTC阻值(分压公式:R_NTC = (V_ADC * R_DIV) / (V_REF - V_ADC))
if(V_REF - V_adc < 0.001) {// 避免分母接近0导致计算异常
R_ntc = NTC_R25;
} else {
R_ntc = (V_adc * R_DIV) / (V_REF - V_adc);
}
// B值法计算温度(转换为摄氏度并取整)
temp = 1 / (1/(25+273.15) + (1.0/NTC_B)*log_approx(R_ntc/NTC_R25));
Current_Temp = (unsigned char)(temp - 273.15);// 开尔文转摄氏度,直接取整
// 温度异常限制(避免显示无效值)
if(Current_Temp < 0 || Current_Temp > 200) {
Current_Temp = 0;
}
}
// 按键扫描(软件消抖,低电平有效,无长按加速)
void Key_Scan(void) {
// 增加键(P5^4)
if(Key_Add == 0) {
Delay_ms(20);// 消抖20ms
if(Key_Add == 0) {
Target_Temp++;
if(Target_Temp > TEMP_MAX) Target_Temp = TEMP_MAX;// 上限150℃
Save_Flag = 1;// 标记需要保存
while(!Key_Add);// 等待按键释放
}
}
// 减小键(P5^5)
if(Key_Sub == 0) {
Delay_ms(20);// 消抖20ms
if(Key_Sub == 0) {
Target_Temp--;
if(Target_Temp < TEMP_MIN) Target_Temp = TEMP_MIN;// 下限30℃
Save_Flag = 1;// 标记需要保存
while(!Key_Sub);// 等待按键释放
}
}
// 掉电保存(按键释放后执行,避免频繁写Flash)
if(Save_Flag) {
IAP_Write(0x0800, Target_Temp);
Save_Flag = 0;
}
}
// 继电器控制(恒温逻辑)
void Relay_Control(void) {
if(Current_Temp < Target_Temp && Current_Temp > 0) {
Relay = 1;// 当前温度低于目标,启动加热
} else if(Current_Temp > Target_Temp) {
Relay = 0;// 当前温度高于目标,停止加热
}
// 等于目标温度时保持当前状态(防频繁启停)
}
// OLED显示(6x8字体,分4行显示,无重叠)
void OLED_Display(void) {
// 第0行:标题(第0行第20列,居中显示)
OLED_ShowString(FONT_6X8, OLED_LINE0, 20, "Heat Table");
// 第1行:当前温度(第1行第0列)
OLED_ShowString(FONT_6X8, OLED_LINE1, OLED_COL0, "Cur: "); // 标签
OLED_ShowNum(FONT_6X8, OLED_LINE1, OLED_COL40, Current_Temp, 3); // 3位温度值
OLED_ShowChar(FONT_6X8, OLED_LINE1, OLED_COL60, 'C'); // 温度单位
// 第2行:目标温度(第2行第0列)
OLED_ShowString(FONT_6X8, OLED_LINE2, OLED_COL0, "Set: "); // 标签
OLED_ShowNum(FONT_6X8, OLED_LINE2, OLED_COL40, Target_Temp, 3); // 3位目标温度
OLED_ShowChar(FONT_6X8, OLED_LINE2, OLED_COL60, 'C'); // 温度单位
// 第3行:加热状态(第3行第0列)
if(Relay == 1) {
OLED_ShowString(FONT_6X8, OLED_LINE3, OLED_COL0, "Status: Heating");
} else {
OLED_ShowString(FONT_6X8, OLED_LINE3, OLED_COL0, "Status: Standby");
}
}
// 软件延时函数(适配35MHz晶振,近似ms级延时)
void Delay_ms(unsigned int ms) {
unsigned int i, j;
for(i = ms; i > 0; i--) {
for(j = 350; j > 0; j--);// 35MHz晶振,j=350时约1ms
}
}
// IAP写Flash(掉电保存目标温度到用户Flash区)
void IAP_Write(unsigned int addr, unsigned char dat) {
IAP_CONTR = 0x80;// 使能IAP功能(IAPEN=1)
IAP_CMD = 0x02; // 选择Flash写命令
IAP_ADDRH = addr >> 8;// 配置Flash地址高8位
IAP_ADDRL = addr & 0xFF;// 配置Flash地址低8位
IAP_DATA = dat; // 配置要写入的数据
EA = 0;// 关中断,防止干扰Flash操作
IAP_TRIG = 0x5A; // 触发Flash写操作(必须按0x5A+0xA5顺序)
IAP_TRIG = 0xA5;
_nop_();// 等待操作完成
EA = 1;// 开中断
IAP_CONTR = 0x00;// 关闭IAP功能,降低功耗
}
// IAP读Flash(上电恢复保存的目标温度)
unsigned char IAP_Read(unsigned int addr) {
unsigned char dat;
IAP_CONTR = 0x80;// 使能IAP功能
IAP_CMD = 0x01; // 选择Flash读命令
IAP_ADDRH = addr >> 8;// 配置Flash地址高8位
IAP_ADDRL = addr & 0xFF;// 配置Flash地址低8位
EA = 0;
IAP_TRIG = 0x5A; // 触发Flash读操作(0x5A+0xA5顺序)
IAP_TRIG = 0xA5;
_nop_();
dat = IAP_DATA; // 读取Flash数据
EA = 1;
IAP_CONTR = 0x00; // 关闭IAP功能
return dat;
}
STC8系列单片机除了P30,P31是准双向模式外,其它引脚默认都是高阻输入模式,如果要输出的话需要先配置IO口模式。
你的第一种方式,除了设置P30,P31外,还将P32~P37都设置为准双向模式。
第二种方式单独将P32,P33设置准双向模式,不改变其它引脚的IO口模式。
此外芯片可以开启内部上拉,不用外接上拉电阻。
至于OLED屏不显示的情况,用示波器测一下I2C引脚信号看看是否正常。
芯片内部的I2C模块是一样的,设置好都能使用。
乘风飞扬 发表于 2025-11-20 09:23
STC8系列单片机除了P30,P31是准双向模式外,其它引脚默认都是高阻输入模式,如果要输出的话需要先配置IO口 ...
不加上拉就真不显示{:4_167:} 一开始能显示,说明屏幕和驱动正常。加上温控台之后屏幕不显示的问题大概出在ADC采样的地方。
第98行 启动ADC转换,应该是 ADC_CONTR |= 0x40;// 启动ADC转换(ADSTART=1)
程序里写成了 0x01,从而ADC没有启动,所以程序卡在了第99行,一直在等待转换完成,从而没法执行后面的那些函数。
如果是ADC_CONTR |= 0x01; 是把ADC通道设置成了 ADC1,从而把初始化里 ADC0 给冲掉了,引脚也变成了P3^1,跟继电器引脚冲突了,测量温度和控制继电器应该都混乱了。
另外 ADC初始化函数 也有个问题
第91行 ADC_CONTR |= 0x08;// 时钟分频Fosc/32(35MHz时约1.09MHz,符合ADC要求)
设置ADC工作时钟频率的寄存器应该是 ADCCFG 的低四位。
根据STC8G数据手册的表格,时钟分频 Fosc/32 应该是 ADCCFG |= 0x0F; (SPEED = 1111)
0x08是 SYSclk/2/9
如果是ADC_CONTR |= 0x08; 是把ADC通道又设置成了ADC8,八脚的STC8G1K17A的ADC通道只有 0~5 和 15,没有通道8。
大明狐 发表于 2025-11-21 13:35
一开始能显示,说明屏幕和驱动正常。加上温控台之后屏幕不显示的问题大概出在ADC采样的地方。
第98行 启动 ...
感谢回复,看他们都是用STC32 STCH 系列。我就想用这么8IDP的插件做一个恒温加热台,让别人DIY起来也简单,不是所有有人都能焊贴片CPU。纯粹是想玩玩 大明狐 发表于 2025-11-21 13:35
一开始能显示,说明屏幕和驱动正常。加上温控台之后屏幕不显示的问题大概出在ADC采样的地方。
第98行 启动 ...
感谢大佬指点,现在显示正常了
页:
[1]