dd418890434 发表于 2025-11-19 23:01:27

关于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;
}

乘风飞扬 发表于 2025-11-20 09:23:07

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

dd418890434 发表于 2025-11-21 01:29:02

乘风飞扬 发表于 2025-11-20 09:23
STC8系列单片机除了P30,P31是准双向模式外,其它引脚默认都是高阻输入模式,如果要输出的话需要先配置IO口 ...

不加上拉就真不显示{:4_167:}

大明狐 发表于 2025-11-21 13:35:58

一开始能显示,说明屏幕和驱动正常。加上温控台之后屏幕不显示的问题大概出在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。





dd418890434 发表于 2025-11-21 22:44:05

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

第98行 启动 ...

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

dd418890434 发表于 2025-11-25 23:49:00

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

第98行 启动 ...

感谢大佬指点,现在显示正常了
页: [1]
查看完整版本: 关于STC8G1K17A-DIP8驱动0.96寸OLED屏