一直搞不到这段代码原理
// 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;
- }
复制代码
|