找回密码
 立即注册
查看: 78|回复: 5

关于STC8G1K17A-DIP8驱动0.96寸OLED屏

[复制链接]
  • 打卡等级:偶尔看看III
  • 打卡总天数:34
  • 最近打卡:2025-12-12 21:51:19
已绑定手机

7

主题

19

回帖

93

积分

注册会员

积分
93
发表于 2025-11-19 23:01:27 | 显示全部楼层 |阅读模式
一直搞不到这段代码原理
    // 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,确实不适合。本来是想越简单越好。
下面是全部代码,有大佬的方便 看看可行吗?
  1. //STC8G1K17A NTC 热敏电阻采样加热台 OLED 显示
  2. //包含 ADC 采样、NTC 温度计算、按键调节、继电器控制、OLED 显示、掉电保存设置
  3. #include <STC8G.H>
  4. #include "OLED.h"  // 你的OLED驱动头文件
  5. #include <intrins.h>
  6. // 引脚定义
  7. sbit Relay  = P3^1;  // 继电器控制引脚(高电平吸合,低电平断开)
  8. sbit ADC_IN = P3^0;  // ADC输入引脚(NTC采样)
  9. sbit Key_Add = P5^4; // 增加目标温度按键
  10. sbit Key_Sub = P5^5; // 减小目标温度按键
  11. // 全局变量定义
  12. unsigned int ADC_Value = 0;        // ADC采样原始值
  13. unsigned char Current_Temp = 0;    // 当前温度(℃,整数)
  14. unsigned char Target_Temp = 50;    // 目标温度(默认50℃,范围30-150℃)
  15. unsigned char Save_Flag = 0;       // 掉电保存标志位
  16. // ADC配置参数
  17. #define ADC_CHAN 0x00  // ADC通道0(P3^0)
  18. #define V_REF 3.3      // 参考电压(V)
  19. #define R_DIV 10000    // 分压电阻阻值(10KΩ,与NTC串联)
  20. #define NTC_B 3950     // NTC B值(常见3950,需根据实际NTC参数修改)
  21. #define NTC_R25 10000  // NTC 25℃时的阻值(10KΩ,需匹配实际参数)
  22. // 温度范围限制
  23. #define TEMP_MIN 30    // 最低目标温度
  24. #define TEMP_MAX 150   // 最高目标温度
  25. // OLED显示参数(适配你的OLED.c驱动:6x8字体行0~7,列0~127)
  26. #define FONT_6X8 6     // 6x8字体(宽6像素,高8像素)
  27. #define OLED_LINE0 0   // 第0行(最上方)
  28. #define OLED_LINE1 1   // 第1行(下移8像素)
  29. #define OLED_LINE2 2   // 第2行(下移16像素)
  30. #define OLED_LINE3 3   // 第3行(下移24像素)
  31. #define OLED_COL0 0    // 第0列
  32. #define OLED_COL40 40  // 第40列(温度数值起始)
  33. #define OLED_COL60 60  // 第60列(温度单位起始)
  34. // 函数声明
  35. void Sys_Init(void);          // 系统初始化
  36. void ADC_Init(void);          // ADC初始化
  37. unsigned int ADC_Read(void);  // ADC采样
  38. void Temp_Calc(void);         // NTC温度计算(整数结果)
  39. float log_approx(float x);    // 自然对数近似函数
  40. void Key_Scan(void);          // 按键扫描(消抖)
  41. void Relay_Control(void);     // 继电器控制
  42. void OLED_Display(void);      // OLED显示(无重叠)
  43. void Delay_ms(unsigned int ms); // 软件延时函数(适配35MHz)
  44. void IAP_Write(unsigned int addr, unsigned char dat);  // 掉电保存
  45. unsigned char IAP_Read(unsigned int addr);             // 上电恢复
  46. void main() {
  47.     Sys_Init();          // 系统初始化
  48.     ADC_Init();          // ADC初始化
  49.     OLED_Init();         // OLED初始化
  50.     OLED_Clear();        // 清屏
  51.    
  52.     // 上电读取保存的目标温度
  53.     Target_Temp = IAP_Read(0x0800);
  54.     if(Target_Temp < TEMP_MIN || Target_Temp > TEMP_MAX) {
  55.         Target_Temp = 50;  // 异常值恢复默认
  56.     }
  57.     while(1) {
  58.         ADC_Value = ADC_Read();  // ADC采样
  59.         Temp_Calc();             // 计算当前温度(整数)
  60.         Key_Scan();              // 按键扫描
  61.         Relay_Control();         // 继电器控制
  62.         OLED_Display();          // OLED显示(无重叠)
  63.         Delay_ms(100);           // 100ms循环间隔
  64.     }
  65. }
  66. // 系统初始化(GPIO配置)
  67. void Sys_Init(void) {
  68.     // 配置P3^2(SCL)和P3^3(SDA)为准双向模式(OLED I2C引脚)
  69.     P3M0 &= ~0x0C; P3M1 &= ~0x0C;
  70.     // 配置P3.1(继电器)为推挽输出,P3.0(ADC)为高阻输入
  71.     P3M0 |= 0x02; P3M1 &= ~0x02;   // P3.1 推挽输出
  72.     P3M0 &= ~0x01; P3M1 |= 0x01;   // P3.0 高阻输入(ADC模式)
  73.     // 配置P5.4/P5.5(按键)为准双向模式(内置上拉)
  74.     P5M0 &= ~0x30; P5M1 &= ~0x30;
  75. }
  76. // ADC初始化(STC8G1K17A ADC配置)
  77. void ADC_Init(void) {
  78.     ADC_CONTR = 0x80;  // 开启ADC模块(ADON=1)
  79.     ADC_CONTR |= ADC_CHAN;  // 选择通道0(P3^0)
  80.     ADC_CONTR |= 0x08;  // 时钟分频Fosc/32(35MHz时约1.09MHz,符合ADC要求)
  81.     _nop_(); _nop_();   // 等待ADC模块稳定
  82. }
  83. // ADC采样(10位精度)
  84. unsigned int ADC_Read(void) {
  85.     unsigned int adc_val = 0;
  86.     ADC_CONTR |= 0x01;  // 启动ADC转换(ADSTART=1)
  87.     while(!(ADC_CONTR & 0x20));  // 等待转换完成(ADFLAG=1)
  88.     adc_val = ADC_RES;  // 读取高8位结果
  89.     adc_val = (adc_val << 2) | (ADC_RESL & 0x03);  // 拼接低2位,组成10位结果
  90.     ADC_CONTR &= ~0x20;  // 清除转换完成标志
  91.     return adc_val;
  92. }
  93. // 自然对数近似函数(误差<0.1%,适用于NTC阻值范围1K~100K)
  94. float log_approx(float x) {
  95.     float y, z;
  96.     z = (x - 1) / (x + 1);  // 变量替换,缩小输入范围
  97.     y = z * z;
  98.     // 泰勒级数展开近似:ln(x) = 2*(z + z^3/3 + z^5/5 + z^7/7)
  99.     return 2 * z * (1 + y/3 + y*y/5 + y*y*y/7);
  100. }
  101. // NTC温度计算(整数结果,移除小数点)
  102. void Temp_Calc(void) {
  103.     float V_adc = 0;     // ADC采样电压
  104.     float R_ntc = 0;     // NTC实时阻值
  105.     float temp = 0;      // 计算出的温度(开尔文)
  106.    
  107.     // 计算采样电压(10位ADC,最大值1023)
  108.     V_adc = (float)ADC_Value * V_REF / 1023.0;
  109.    
  110.     // 计算NTC阻值(分压公式:R_NTC = (V_ADC * R_DIV) / (V_REF - V_ADC))
  111.     if(V_REF - V_adc < 0.001) {  // 避免分母接近0导致计算异常
  112.         R_ntc = NTC_R25;
  113.     } else {
  114.         R_ntc = (V_adc * R_DIV) / (V_REF - V_adc);
  115.     }
  116.    
  117.     // B值法计算温度(转换为摄氏度并取整)
  118.     temp = 1 / (1/(25+273.15) + (1.0/NTC_B)*log_approx(R_ntc/NTC_R25));
  119.     Current_Temp = (unsigned char)(temp - 273.15);  // 开尔文转摄氏度,直接取整
  120.    
  121.     // 温度异常限制(避免显示无效值)
  122.     if(Current_Temp < 0 || Current_Temp > 200) {
  123.         Current_Temp = 0;
  124.     }
  125. }
  126. // 按键扫描(软件消抖,低电平有效,无长按加速)
  127. void Key_Scan(void) {
  128.     // 增加键(P5^4)
  129.     if(Key_Add == 0) {
  130.         Delay_ms(20);  // 消抖20ms
  131.         if(Key_Add == 0) {
  132.             Target_Temp++;
  133.             if(Target_Temp > TEMP_MAX) Target_Temp = TEMP_MAX;  // 上限150℃
  134.             Save_Flag = 1;  // 标记需要保存
  135.             while(!Key_Add);  // 等待按键释放
  136.         }
  137.     }
  138.    
  139.     // 减小键(P5^5)
  140.     if(Key_Sub == 0) {
  141.         Delay_ms(20);  // 消抖20ms
  142.         if(Key_Sub == 0) {
  143.             Target_Temp--;
  144.             if(Target_Temp < TEMP_MIN) Target_Temp = TEMP_MIN;  // 下限30℃
  145.             Save_Flag = 1;  // 标记需要保存
  146.             while(!Key_Sub);  // 等待按键释放
  147.         }
  148.     }
  149.    
  150.     // 掉电保存(按键释放后执行,避免频繁写Flash)
  151.     if(Save_Flag) {
  152.         IAP_Write(0x0800, Target_Temp);
  153.         Save_Flag = 0;
  154.     }
  155. }
  156. // 继电器控制(恒温逻辑)
  157. void Relay_Control(void) {
  158.     if(Current_Temp < Target_Temp && Current_Temp > 0) {
  159.         Relay = 1;  // 当前温度低于目标,启动加热
  160.     } else if(Current_Temp > Target_Temp) {
  161.         Relay = 0;  // 当前温度高于目标,停止加热
  162.     }
  163.     // 等于目标温度时保持当前状态(防频繁启停)
  164. }
  165. // OLED显示(6x8字体,分4行显示,无重叠)
  166. void OLED_Display(void) {
  167.     // 第0行:标题(第0行第20列,居中显示)
  168.     OLED_ShowString(FONT_6X8, OLED_LINE0, 20, "Heat Table");
  169.    
  170.     // 第1行:当前温度(第1行第0列)
  171.     OLED_ShowString(FONT_6X8, OLED_LINE1, OLED_COL0, "Cur: ");          // 标签
  172.     OLED_ShowNum(FONT_6X8, OLED_LINE1, OLED_COL40, Current_Temp, 3);    // 3位温度值
  173.     OLED_ShowChar(FONT_6X8, OLED_LINE1, OLED_COL60, 'C');              // 温度单位
  174.    
  175.     // 第2行:目标温度(第2行第0列)
  176.     OLED_ShowString(FONT_6X8, OLED_LINE2, OLED_COL0, "Set: ");          // 标签
  177.     OLED_ShowNum(FONT_6X8, OLED_LINE2, OLED_COL40, Target_Temp, 3);     // 3位目标温度
  178.     OLED_ShowChar(FONT_6X8, OLED_LINE2, OLED_COL60, 'C');              // 温度单位
  179.    
  180.     // 第3行:加热状态(第3行第0列)
  181.     if(Relay == 1) {
  182.         OLED_ShowString(FONT_6X8, OLED_LINE3, OLED_COL0, "Status: Heating");
  183.     } else {
  184.         OLED_ShowString(FONT_6X8, OLED_LINE3, OLED_COL0, "Status: Standby");
  185.     }
  186. }
  187. // 软件延时函数(适配35MHz晶振,近似ms级延时)
  188. void Delay_ms(unsigned int ms) {
  189.     unsigned int i, j;
  190.     for(i = ms; i > 0; i--) {
  191.         for(j = 350; j > 0; j--);  // 35MHz晶振,j=350时约1ms
  192.     }
  193. }
  194. // IAP写Flash(掉电保存目标温度到用户Flash区)
  195. void IAP_Write(unsigned int addr, unsigned char dat) {
  196.     IAP_CONTR = 0x80;  // 使能IAP功能(IAPEN=1)
  197.     IAP_CMD = 0x02;    // 选择Flash写命令
  198.     IAP_ADDRH = addr >> 8;  // 配置Flash地址高8位
  199.     IAP_ADDRL = addr & 0xFF;// 配置Flash地址低8位
  200.     IAP_DATA = dat;         // 配置要写入的数据
  201.    
  202.     EA = 0;  // 关中断,防止干扰Flash操作
  203.     IAP_TRIG = 0x5A;        // 触发Flash写操作(必须按0x5A+0xA5顺序)
  204.     IAP_TRIG = 0xA5;
  205.     _nop_();  // 等待操作完成
  206.     EA = 1;  // 开中断
  207.    
  208.     IAP_CONTR = 0x00;  // 关闭IAP功能,降低功耗
  209. }
  210. // IAP读Flash(上电恢复保存的目标温度)
  211. unsigned char IAP_Read(unsigned int addr) {
  212.     unsigned char dat;
  213.     IAP_CONTR = 0x80;  // 使能IAP功能
  214.     IAP_CMD = 0x01;    // 选择Flash读命令
  215.     IAP_ADDRH = addr >> 8;  // 配置Flash地址高8位
  216.     IAP_ADDRL = addr & 0xFF;// 配置Flash地址低8位
  217.    
  218.     EA = 0;
  219.     IAP_TRIG = 0x5A;    // 触发Flash读操作(0x5A+0xA5顺序)
  220.     IAP_TRIG = 0xA5;
  221.     _nop_();
  222.     dat = IAP_DATA;     // 读取Flash数据
  223.     EA = 1;
  224.    
  225.     IAP_CONTR = 0x00;   // 关闭IAP功能
  226.     return dat;
  227. }
复制代码


回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:586
  • 最近打卡:2025-12-18 09:02:22
已绑定手机

49

主题

2369

回帖

8422

积分

论坛元老

积分
8422
发表于 2025-11-20 09:23:07 | 显示全部楼层
STC8系列单片机除了P30,P31是准双向模式外,其它引脚默认都是高阻输入模式,如果要输出的话需要先配置IO口模式。
你的第一种方式,除了设置P30,P31外,还将P32~P37都设置为准双向模式。
第二种方式单独将P32,P33设置准双向模式,不改变其它引脚的IO口模式。
此外芯片可以开启内部上拉,不用外接上拉电阻。
至于OLED屏不显示的情况,用示波器测一下I2C引脚信号看看是否正常。
芯片内部的I2C模块是一样的,设置好都能使用。
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:34
  • 最近打卡:2025-12-12 21:51:19
已绑定手机

7

主题

19

回帖

93

积分

注册会员

积分
93
发表于 2025-11-21 01:29:02 | 显示全部楼层
乘风*** 发表于 2025-11-20 09:23
STC8系列单片机除了P30,P31是准双向模式外,其它引脚默认都是高阻输入模式,如果要输出的话需要先配置IO口 ...

不加上拉就真不显示
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:618
  • 最近打卡:2025-12-18 04:04:15

35

主题

440

回帖

4057

积分

荣誉版主

积分
4057
发表于 2025-11-21 13:35:58 | 显示全部楼层
一开始能显示,说明屏幕和驱动正常。加上温控台之后屏幕不显示的问题大概出在ADC采样的地方。

第98行 启动ADC转换,应该是 ADC_CONTR |= 0x40;  // 启动ADC转换(ADSTART=1)
程序里写成了 0x01,从而ADC没有启动,所以程序卡在了第99行,一直在等待转换完成,从而没法执行后面的那些函数
截图202511211317229810.jpg
如果是ADC_CONTR |= 0x01; 是把ADC通道设置成了 ADC1,从而把初始化里 ADC0 给冲掉了,引脚也变成了P3^1,跟继电器引脚冲突了,测量温度和控制继电器应该都混乱了。

另外 ADC初始化函数 也有个问题
第91行 ADC_CONTR |= 0x08;  // 时钟分频Fosc/32(35MHz时约1.09MHz,符合ADC要求)
设置ADC工作时钟频率的寄存器应该是 ADCCFG 的低四位。
截图202511211348169094.jpg
根据STC8G数据手册的表格,时钟分频 Fosc/32 应该是
ADCCFG |= 0x0F; (SPEED[3:0] = 1111)
0x08是 SYSclk/2/9
截图202511211316278904.jpg
如果是ADC_CONTR |= 0x08; 是把ADC通道又设置成了ADC8,八脚的STC8G1K17A的ADC通道只有 0~5 和 15,没有通道8。





能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:34
  • 最近打卡:2025-12-12 21:51:19
已绑定手机

7

主题

19

回帖

93

积分

注册会员

积分
93
发表于 2025-11-21 22:44:05 | 显示全部楼层
大*** 发表于 2025-11-21 13:35
一开始能显示,说明屏幕和驱动正常。加上温控台之后屏幕不显示的问题大概出在ADC采样的地方。

第98行 启动 ...

感谢回复,看他们都是用STC32 STCH 系列。我就想用这么8IDP的插件做一个恒温加热台,让别人DIY起来也简单,不是所有有人都能焊贴片CPU。纯粹是想玩玩
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:34
  • 最近打卡:2025-12-12 21:51:19
已绑定手机

7

主题

19

回帖

93

积分

注册会员

积分
93
发表于 2025-11-25 23:49:00 | 显示全部楼层
大*** 发表于 2025-11-21 13:35
一开始能显示,说明屏幕和驱动正常。加上温控台之后屏幕不显示的问题大概出在ADC采样的地方。

第98行 启动 ...

感谢大佬指点,现在显示正常了
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2025-12-18 19:02 , Processed in 0.111768 second(s), 75 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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