lzyor 发表于 2024-4-1 20:29:38

【整个小活】基于STC8H内部RC时钟温飘的温度计,0外设测量温度

众所周知,STC8H系列内部32K时钟温飘特别大,频率温飘基本相当于一个NTC电阻的阻值。




把系统时钟调低,比如11.0592MHz,这样内部16位计时器就能充分的测量一个1/32的RTC中断,即可从计时器值估测内部RTC频率;
再根据温飘映射到文档数值,就知道此时单片机的温度了。

void init_rtc() {
    // 选择内部 32K
    IRC32KCR = 0x80; // 启动内部 32K 振荡器
    while (!(IRC32KCR & 0x01))
      ;         // 等待时钟稳定
    RTCCFG |= 0x02; // 选择内部 32K 作为 RTC 时钟源

    // 设置 RTC 时间
    INIYEAR = 00;
    INIMONTH = 1;
    INIDAY = 15;
    INIHOUR = 15;
    INIMIN = 30;
    INISEC = 0;
    INISSEC = 0;
    RTCCFG |= 0x01; // 触发 RTC 寄存器初始化

    RTCIF = 0;   // 清中断标志
    RTCIEN = 0x01; // 使能 RTC 1/32秒中断
    RTCCR = 0x01;// RTC 使能
}

// @11.0592MHz
void init_tm0(void) {
    AUXR &= 0x7F; // 定时器时钟12T模式
    TMOD &= 0xF0; // 设置定时器模式
    TMOD |= 0x01; // 设置定时器模式
}

uint8_t data temp_tl = 0;
uint8_t data temp_th = 0;

void isr_rtc() interrupt(RTC_VECTOR) using(1) {
    RTCIF = 0; // 清中断标志
    TR0 = 0;
    temp_tl = TL0;
    temp_th = TH0;
    TL0 = 0xff;
    TH0 = 0xff;
    TF0 = 0;
    TR0 = 1;
}

void main() {
    init_sys();
    init_uart();
    init_tm0();
    init_rtc();

    while (1) {
      uint16_t c = 0xffff - (temp_th << 8 | temp_tl);
      int16_t t = (650 * ((int32_t)33563 - c)) / 7900;
      printf("t: %d\r\n", t);
      delay_ms(1000);
    }
}

量程0 ~ 65度,上面的简单线性映射是我用冰块和热水校准的。
其中映射关系因为每个芯片的体制不一样,必须校准。
为了避免计算浮点数,温度t的数值单位是10摄氏度,既24.3C => 243。
假定电压和主时钟是稳定的。

测量精度在室温还说的过去{:4_187:}

// 室温输出
t: 242
// 手按上去加热
t: 271
// 冰块贴到芯片上
t: 18


各位愚人节快乐

lzyor 发表于 2024-4-1 20:31:39

测试用的芯片是stc8h8k64u,实测。

yjawei 发表于 2024-4-1 22:13:35

未曾设想的逆向思维

kksk 发表于 2024-4-1 22:46:09

很有趣的想法{:4_174:}

社区闲人 发表于 2024-4-2 05:50:10

楼主的想象力很丰富。

小飞侠 发表于 2024-4-2 07:44:45

奇思妙想

lezjin 发表于 2024-4-2 07:49:13

还能这么搞啊

小涵子爸爸 发表于 2024-4-2 08:05:43

佩服楼主的思维{:4_174:}

wangxiangtan 发表于 2024-4-2 08:14:32

可以,用MATLAB拟合一下可以比较准确的获取当前温度

_奶咖君_ 发表于 2024-4-2 08:51:19

是不是还可以将ADC参考电压和MCU电源接起来,根据ADC15通道 测量单片机的供电电压再根据电压图修正一下。。{:4_187:}
页: [1] 2
查看完整版本: 【整个小活】基于STC8H内部RC时钟温飘的温度计,0外设测量温度