苏紫方璇 发表于 2024-9-5 20:43:04

求问,如何获取内部不精准的32k时钟准确值

首先我知道这个低功耗的32k时钟十分不准,但还是想不加晶振的情况下,尽可能的利用这个做一些东西。现在想获取他在某一时刻的准确值。

我的思路是使用rtc的秒中断,设置一个100us一次的定时器,中断时记录计数值。准确频率值应该是(32768/计数值)x10000。
但是我发现,计算值,和用主时钟输出,示波器/另一个单片机pwm测量值不相同,比如计算值是24983Hz,示波器或pwm测量是25370Hz。
我想问下,这两者之间哪个较为准确,如果计算值不准确,能否看看是哪里的问题。万分感谢

附上主要代码
#include        "config.h"
#include        "STC8G_H_GPIO.h"
#include        "STC8G_H_UART.h"
#include        "STC8G_H_Delay.h"
#include        "STC8G_H_NVIC.h"
#include        "STC8G_H_Switch.h"

bit f_1s;
u16 timeCnt, lastCnt, fwt;
float tmp;
u8 st;

void        GPIO_config(void)
{
    GPIO_InitTypeDef        GPIO_InitStructure;                //结构定义

    P0M0 = 0x00;
    P0M1 = 0x00;
    P1M0 = 0x00;
    P1M1 = 0x00;
    P2M0 = 0x00;
    P2M1 = 0x00;
    P3M0 = 0x00;
    P3M1 = 0x00;
    P4M0 = 0x00;
    P4M1 = 0x00;
    P5M0 = 0x00;
    P5M1 = 0x00;

    GPIO_InitStructure.Pin= GPIO_Pin_0 | GPIO_Pin_1;                //指定要初始化的IO, GPIO_Pin_0 ~ GPIO_Pin_7
    GPIO_InitStructure.Mode = GPIO_PullUp;        //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
    GPIO_Inilize(GPIO_P3, &GPIO_InitStructure);        //初始化
}

/***************串口初始化函数 *****************/
void        UART_config(void)
{
    COMx_InitDefine                COMx_InitStructure;                                        //结构定义

    COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;        //模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTx
    COMx_InitStructure.UART_BRT_Use   = BRT_Timer1;                        //选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)
    COMx_InitStructure.UART_BaudRate= 115200ul;                        //波特率, 一般 110 ~ 115200
    COMx_InitStructure.UART_RxEnable= ENABLE;                                //接收允许,   ENABLE或DISABLE
    COMx_InitStructure.BaudRateDouble = DISABLE;                        //波特率加倍, ENABLE或DISABLE
    UART_Configuration(UART1, &COMx_InitStructure);                //初始化串口1 UART1,UART2,UART3,UART4
    NVIC_UART1_Init(ENABLE, Priority_1);                //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3

    UART1_SW(UART1_SW_P30_P31);                //UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}


/**********************************************/

void Timer0_Init(void)                //100微秒@24.000MHz
{
    AUXR |= 0x80;                        //定时器时钟1T模式
    TMOD &= 0xF0;                        //设置定时器模式
    TL0 = 0xA0;                                //设置定时初始值
    TH0 = 0xF6;                                //设置定时初始值
    TF0 = 0;                                //清除TF0标志
    ET0 = 1;
    TR0 = 1;                                //定时器0开始计时
}

void Timer0Proc() interrupt 1
{
    timeCnt++;
}

//rtc
void RTC_Isr() interrupt 13//RTC_VECTOR
{
    if(RTCIF & 0x08) //秒中断
    {
      RTCIF &= ~0x08;
      lastCnt = timeCnt;
      timeCnt = 0;
      f_1s = 1;
    }
}

void RTC_Init()
{
    //使用内部时钟
    IRC32KCR = 0x80;
    while(!(IRC32KCR & 0x01));
    RTCCFG |= 0x02;
    INIYEAR = 23;                                                                                                    //Y:23
    INIMONTH = 02;                                                                                                   //M:02
    INIDAY = 21;                                                                                                   //D:19
    INIHOUR = 23;                                                                                                    //H:23
    INIMIN = 00;                                                                                                   //M:45
    INISEC = 00;                                                                                                   //S:55
    INISSEC = 0;                                                                                              //S/128:0
    RTCCFG |= 0x01;

    RTCIF = 0;
    RTCIEN = 0x08; //秒中断
    timeCnt = 0;
    RTCCR = 1; //启动

}

void main(void)
{
    u8        i;

    EAXSFR();                /* 扩展寄存器访问使能 */
    GPIO_config();
    UART_config();
    EA = 1;
    Timer0_Init();
    RTC_Init();
       
    fwt = CHIPID9 << 8 | CHIPID10;
    st = 0;
    MCLKOCR = 0x01;                           //主时钟输出到P5.4口
    while (1)
    {
      if(st == 10)
      {
            st++;
            //关闭rtc
            RTCIEN = 0;
            timeCnt = 0;
            RTCCR = 0;
                        //切换主时钟
            CLKDIV = 0x00;                              //时钟不分频
            CLKSEL = 0x03;                              //选择内部32K
      }

      if(f_1s)
      {
            f_1s = 0;
            tmp = 32768.0 / lastCnt * 10000.0;
            printf("b=%huHzCnt=%hu calc=%fHz\r\n", fwt, lastCnt, tmp);
            st++;
      }
    }
}

输出示例 b=30070Hz Cnt=13116 calc=24983Hz

DebugLab 发表于 2024-9-5 20:55:39

内部高频时钟比内部32K时钟精度高

DebugLab 发表于 2024-9-5 20:57:50

内部32K时钟精度只适合掉电唤醒和段式LCD刷屏,有温飘和压飘,不要做别的用

苏紫方璇 发表于 2024-9-5 21:23:55

DebugLab 发表于 2024-9-5 20:57
内部32K时钟精度只适合掉电唤醒和段式LCD刷屏,有温飘和压飘,不要做别的用 ...

你说的我知道,通过上面代码试验我也看出来了,飘的挺多,费劲去校准不如加外部晶振。正常需要精准定时是不会去用的。我现在想的是否是代码问题导致获取到的频率不同,或者有什么不了解的机制

cnos 发表于 2024-9-5 21:27:42

你这个属于逻辑问题啊,没有参照,怎么会知道自己是多少呢?
你手上只有一把尺子,你能有办法判断这把尺子准不准吗?没有其他已知量的情况下。

soma 发表于 2024-9-5 22:13:23

最简单的方法还是加外部晶振啊

Yim_Hom 发表于 2024-9-5 23:50:08

既然pwm测量值=示波器测量值,那肯定相信这个准确。
至于理论计算值,怎么也要把能想到的误差都考虑进去然后更正,这样应该可以无限接近测量值,且在一定的范围内。
至于何时准确的话,怎么也必须得把测试条件(温度、湿度、供电等)调整到最相近的理论条件吧。

llyymm 发表于 2024-9-6 08:22:18

这种钻研的精神值得学习,如果各方面都试过确实不行,至少测试过程的经验是真正的收获

苏紫方璇 发表于 2024-9-6 10:18:01

Yim_Hom 发表于 2024-9-5 23:50
既然pwm测量值=示波器测量值,那肯定相信这个准确。
至于理论计算值,怎么也要把能想到的误差都考虑进去然 ...

对,测量值应该是准确的,现在就是想不出来计算的值误差在哪里,用的stc8h8k64u,换了一个单片机也是差200-500,所以就想把代码发出来给大佬们看看有无没考虑到的地方

苏紫方璇 发表于 2024-9-6 10:30:11

cnos 发表于 2024-9-5 21:27
你这个属于逻辑问题啊,没有参照,怎么会知道自己是多少呢?
你手上只有一把尺子,你能有办法判断这把尺子 ...
按老师您说的,我认为我并不是只有一把尺子,是3把,两把长尺(示波器和另一个单片机)可以直接测量出准确值,一把短尺(单片机自身高速时钟),通过计算得到数据。现在尺子是准的,测量/计算方法可能有问题
页: [1] 2
查看完整版本: 求问,如何获取内部不精准的32k时钟准确值