前面介绍过STC32G12K128的时钟系统使用的几种时钟源。接下来,我使用不同的时钟源测试时钟的精度,思路是,使用不同的时钟源,利用定时器计数,在GPIO口输出脉冲信号,然后测量脉冲信号的周期,看看差异以及与预想的是否一致来做比较。对于使用外部晶振的情况,因为受控于外部晶振的精度,所以不作为本次测试的内容。
下面是测试的三种分支。
测试代码:
/************* 功能说明 **************
使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。
edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。
切换时钟源,分别是默认IRC主频,主频24分频,PLL 96M 16分频,内部32K IRC。
下载时, 选择时钟 24MHz (用户可自行修改频率).
******************************************/
#include "STC32G.h"
#include "stdio.h"
#include "intrins.h"
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
#define MAIN_Fosc 24000000UL
#define Timer4_Reload (MAIN_Fosc / 5000) //Timer 4 中断频率, 5000次/秒
/****************************** 用户定义宏 ***********************************/
/*****************************************************************************/
/************* 本地常量声明 **************/
#define CKMS 0x80
#define HSIOCK 0x40
#define MCK2SEL_MSK 0x0c
#define MCK2SEL_SEL1 0x00
#define MCK2SEL_PLL 0x04
#define MCK2SEL_PLLD2 0x08
#define MCK2SEL_IRC48 0x0c
#define MCKSEL_MSK 0x03
#define MCKSEL_HIRC 0x00
#define MCKSEL_XOSC 0x01
#define MCKSEL_X32K 0x02
#define MCKSEL_IRC32K 0x03
#define ENCKM 0x80
#define PCKI_MSK 0x60
#define PCKI_D1 0x00
#define PCKI_D2 0x20
#define PCKI_D4 0x40
#define PCKI_D8 0x60
void Timer4_init(void);
/************* 本地变量声明 **************/
u8 mode = 0;
/************* 本地函数声明 **************/
void delay_ms(u8 ms);
void MCLK_Sel(void);
/********************* 主函数 *************************/
void main(void) {
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
P6M1 = 0xff; P6M0 = 0xff; //设置为漏极开路(实验箱加了上拉电阻到3.3V)
P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
EA = 1; //打开总中断
Timer4_init();
//MCLK_Sel();
while(1) {
if (P20 == 0) {
delay_ms(20);
if (P20 == 0) {
P65=0;
while(P20 == 0);
P65=1;
mode = (mode+1)%4;
MCLK_Sel();
if (mode==0) {
P60=1;
P61=1;
} else if (mode==1) {
P60=0;
P61=1;
} else if (mode==2) {
P60=1;
P61=0;
} else if (mode==3) {
P60=0;
P61=0;
}
}
}
}
}
//========================================================================
// 函数: void MCLK_Sel(void)
// 描述: 系统时钟设置函数。
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-7-29
// 备注:
//========================================================================
void MCLK_Sel(void) {
if(mode == 0) {
HIRCCR = 0x80; //启动内部 IRC
while (!(HIRCCR & 1)); //等待时钟稳定
CLKDIV = 0;
CLKSEL = 0x00; //选择内部 IRC ( 默认 )
} else if(mode == 1) {
HIRCCR = 0x80; //启动内部 IRC
while (!(HIRCCR & 1)); //等待时钟稳定
CLKDIV = 24; //MCLK/24
CLKSEL = 0x00; //选择内部 IRC ( 默认 )
} else if(mode == 2) {
CLKSEL &= ~CKMS; //选择PLL的96M作为PLL的输出时钟
USBCLK |= PCKI_D2; //输入时钟2分频(选择PLL输入时钟分频,保证输入时钟为12M)
//启动PLL
USBCLK |= ENCKM; //使能PLL倍频
delay_ms(1); //等待PLL锁频
CLKDIV = 16; //主时钟选择高速频率前,必须先设置分频系数,否则程序会当掉
CLKSEL &= ~MCKSEL_MSK;
CLKSEL &= ~MCK2SEL_MSK;
CLKSEL |= MCKSEL_HIRC; //选择内部高速IRC作为主时钟
CLKSEL |= MCK2SEL_PLLD2; //选择PLL输出时钟2分频后的时钟作为主时钟
} else if(mode == 3) {
IRC32KCR = 0x80; //启动内部 32K IRC
while (!(IRC32KCR & 1)); //等待时钟稳定
CLKDIV = 0x00; //时钟不分频
CLKSEL = 0x03; //选择内部 32K
}
}
//========================================================================
// 函数: void Timer4_init(void)
// 描述: timer4初始化函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2015-1-12
//========================================================================
void Timer4_init(void) {
T4R = 0; //停止计数
#if (Timer4_Reload < 64) // 如果用户设置值不合适, 则不启动定时器
#error "Timer4设置的中断过快!"
#elif ((Timer4_Reload/12) < 65536UL) // 如果用户设置值不合适, 则不启动定时器
ET4 = 1; //允许中断
T4_CT = 0; //定时
T4CLKO = 0; //不输出时钟
#if (Timer4_Reload < 65536UL)
T4x12 = 1; //1T mode
T4H = (u8)((65536UL - Timer4_Reload) / 256);
T4L = (u8)((65536UL - Timer4_Reload) % 256);
#else
T4x12 = 0; //12T mode
T4H = (u8)((65536UL - Timer4_Reload/12) / 256);
T4L = (u8)((65536UL - Timer4_Reload/12) % 256);
#endif
T4R = 1; //开始运行
#else
#error "Timer4设置的中断过慢!"
#endif
}
//========================================================================
// 函数: void timer4_int (void) interrupt TIMER4_VECTOR
// 描述: timer4中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2015-1-12
//========================================================================
void timer4_int(void) interrupt 20 {
P67 = ~P67;
P27 = P67;
}代码中使用了STC原厂提供的改变时钟源的代码。
1、使用内部高精度 IRC,不分频
测出的定时周期为198.456us,理论值应该是200us,误差为0.772%
2、使用内部高精度 IRC,24分频
测出的定时周期为4.783ms,理论值应该是4.8ms,误差约0.354%。
3、使用PLL时钟,16分频

测出的定时周期为1.585ms,理论值应该是1.67ms,误差为5.090%。
4、使用内部 32KHz 的 IRC,不分频

测出的定时周期为135.139ms,理论值应该是152.58ms,误差约为11.431%
从以上的测试结果上看,使用内部有RC构成的32.768KHz的时钟源,误差是最大的。其次是内部PLL输出,也挺大,达到5%。总体来说内部高精度IRC时钟还是比较靠谱的,误差很小,不到1%。
通过这次测试,对于单片机的时钟源的选择上的相关知识有了实际认识。涉及数据计算的部分,理解可能还有不到位的地方,期待大佬指正。
测试工程包:STC32时钟源.zip
我要赚赏金
