cqjames 发表于 2026-5-27 22:17:50

求教:STC8H1K08 定时问题 | 提高系统时钟到 30MHz

各位大牛,小弟是个新手,遇到STC8H1K08 定时误差大的问题,请各位大牛解惑,到底是代码有问题还是买到假货了,谢谢:



1. 定时器0定时不准,定时器0初始化代码使用AiCube-ISP-v6.96W.生成的,5微秒间隔,结果用示波器检查波形为50微秒,代码如下:

#include "stc8h.h"
#include "intrins.h"

void TM0_Isr() interrupt 1
{
P10 = ~P10;
}

void Timer0_Init(void)   //5 us delay
{
      AUXR |= 0x80;
      TMOD &= 0xF0;
      TL0 = 0xC9;
      TH0 = 0xFF;
      TF0 = 0;
      TR0 = 1;
}

void main() {
      P_SW2 = 0xB0;
      P1M0 = 0x02;
      P1M1 = 0x00;
      P1SR = 0xfc;
      P1DR = 0x3c;
      Timer0_Init();
      ET0 = 1;
      EA = 1;
      while(1){
                _nop_();
      }
}


2.软件定时不准,设置50微秒延迟,软件延迟代码也是AiCube-ISP-v6.96W.生成的,结果输出波形750微秒左右,代码如下:

#include "stc8h.h"
#include "intrins.h"

void Delay50us()
{
      unsigned char data i;
      _nop_();
      _nop_();
      _nop_();
      i = 135;
      while (--i);
}

void main() {
      P_SW2 = 0xB0;
      P1M0 = 0x02;
      P1M1 = 0x00;
      P1SR = 0xfc;
      P1DR = 0x3c;
      while(1){
                Delay50us();
                P10 = ~P10;
      }
}



芯片型号为STC8H1K08,下面内容是下载软件的输出:




正在检测目标单片机 ...
单片机型号: STC8H1K08

当前芯片的硬件选项为:
. 当前系统ISP工作频率: 23.808MHz
. 上次下载设置的下次用户程序运行使用的内部IRC振荡器的频率: 11.054MHz
. 掉电唤醒定时器的频率: 35.450KHz
. 振荡器放大增益使能
. P3.2和P3.3与下次下载无关
. 上电复位时增加额外的复位延时
. 复位引脚用作普通I/O口
. 检测到低压时复位
. 低压检测门槛电压 : 2.00 V
. 上电复位时,硬件不启动内部看门狗
. 上电自动启动内部看门狗时的预分频数为 : 256
. 空闲状态时看门狗定时器停止计数
. 下次下载用户程序时,将用户EEPROM区一并擦除
. 下次下载用户程序时,没有相关的端口控制485
. 下次下载时不需要校验下载口令
. 内部参考电压: 1190 mV (参考范围: 1100~1300mV)

单片机型号: STC8H1K08

开始调节频率 ...                        
调节后的频率: 11.062MHz (0.022%)

正在重新握手 ... 成功                        
当前的波特率: 115200
正在擦除目标区域 ... 完成 !               
正在下载用户代码 ... 完成 !               
正在设置硬件选项 ... 完成 !               

更新后的硬件选项为:
. 当前系统ISP工作频率: 23.808MHz
. 上次下载设置的下次用户程序运行使用的内部IRC振荡器的频率: 11.062MHz
. 掉电唤醒定时器的频率: 35.450KHz
. 振荡器放大增益使能
. P3.2和P3.3与下次下载无关
. 上电复位时增加额外的复位延时
. 复位引脚用作普通I/O口
. 检测到低压时复位
. 低压检测门槛电压 : 2.00 V
. 上电复位时,硬件不启动内部看门狗
. 上电自动启动内部看门狗时的预分频数为 : 256
. 空闲状态时看门狗定时器停止计数
. 下次下载用户程序时,将用户EEPROM区一并擦除
. 下次下载用户程序时,没有相关的端口控制485
. 下次下载时不需要校验下载口令
. 内部参考电压: 1190 mV (参考范围: 1100~1300mV)

芯片出厂序列号 : F734C49A19B2E4

单片机型号: STC8H1K08

. 用户设定频率: 11.059MHz
. 调节后的频率: 11.062MHz ( 主时钟分频系数 = 2; )
. 频率调节误差: 0.022%

操作成功 !(2026-05-27 20:43:57)


神农鼎 发表于 2026-6-1 17:31:09


系统时钟改用高速时钟频率 30MHz, 中断改成最高优先级





void Timer0_Isr(void) interrupt 1
{
}

void Timer0_Init(void)                //5微秒@30.000MHz
{
        AUXR |= 0x80;                        //定时器时钟1T模式
        TMOD &= 0xF0;                        //设置定时器模式
        TL0 = 0x6A;                                //设置定时初始值
        TH0 = 0xFF;                                //设置定时初始值
        TF0 = 0;                                //清除TF0标志
        TR0 = 1;                                //定时器0开始计时
        ET0 = 1;                                //使能定时器0中断
}


神农鼎 发表于 2026-6-1 17:34:44

将 USB程序去除,系统时钟看程序说明




#include "stc8h.h"                        //STC8H的通用头文件,头文件不分大小写
#include "ai_usb.h"                        //USB库的头文件,头文件不分大小写
/*
如何获得上面这2个头文件,如已按照前面介绍的方法:
从www.STCAI.com 网站,【软件工具 | 工具软件】处,下载安装最新的【AIapp-ISP 软件】,
选择其中的选项卡 【Keil仿真设置 】,
点击【添加型号和头文件到Keil中,并添加仿真器驱动到Keil中】按钮,
则 stc8h 和 AI_usb.h 都会自动复制到Keil 中, 包含进来就可以直接使用,
大家可以打开看下其中的内容,AI_usb.h 和 stc32_stc8_usb.h是相似的,只需用其中的1个头文件,
具体见 Keil_v5\C251\INC\STC 目录,或 Keil_v5\C51\INC\STC 目录。
如将对应的 USB库文件添加到 项目中,后续用户程序中,USB如收到命令字符串 "@STCISP#"
USB库函数就会自动软复位到系统ISP程序区,实现 USB不停电下载,具体见相应部分的说明。
//char *USER_DEVICEDESC = NULL;
//char *USER_PRODUCTDESC = NULL;
//char *USER_STCISPCMD = "@STCISP#";
//使用20250319之前版本的库函数时需要在用户代码中增加上面3行定义。
//使用20250319及以后的版本都不再需要上面的定义
*/

bit         t0_flag = 0;                   //定义1个位变量,T0事件位变量标志,记录定时器0已产生中断
// 供主循环查询到该定时器已产生中断,在主循环中处理定时器0的中断任务,不堵塞其他中断
bit         t1_flag = 0;                   //定义1个位变量,T1事件位变量标志,记录定时器1已产生中断
// 供主循环查询到该定时器已产生中断,在主循环中处理定时器1的中断任务,不堵塞其他中断

void Timer0_Isr(void) interrupt TMR0_VECTOR                //定时器0中断服务程序
{
      P60 = ~P60;                              //P60灯闪烁,中断服务程序中尽量少执行长的任务,防止堵塞其他中断
      t0_flag = 1;                        //置1,记录定时器0已产生中断,供主循环判断要处理定时器0的中断任务
}
//定时器0中断服务程序,TMR0_VECTOR在stc8h头文件中已宏定义为1

void Timer0_Init(void)                //定时器0初始化,2秒@40.000MHz
{
      TM0PS = 0x65;                        //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
      AUXR &= 0x7F;                        //定时器时钟12T模式
      TMOD &= 0xF0;                        //设置定时器模式
      TL0 = 0xB1;                              //设置定时初始值
      TH0 = 0x00;                              //设置定时初始值
      TF0 = 0;                              //清除TF0标志
      TR0 = 1;                              //定时器0开始计时
      ET0 = 1;                              //使能定时器0中断
}

voidTimer1_Isr(void)interruptTMR1_VECTOR
{
      P67 = ~P67;                              //P67灯闪烁,中断服务程序中尽量少执行长的任务,防止堵塞其他中断
      t1_flag = 1;                        //置1,记录定时器1已产生中断,供主循环判断要处理定时器1的中断任务
}
//定时器1中断服务程序,TMR1_VECTOR在stc8h头文件中已宏定义为3

void Timer1_Init(void)                //定时器1初始化,500毫秒@40.000MHz
{
      TM1PS = 0x19;                        //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
      AUXR &= 0xBF;                        //定时器时钟12T模式
      TMOD &= 0x0F;                        //设置定时器模式
      TL1 = 0x99;                              //设置定时初始值
      TH1 = 0x05;                              //设置定时初始值
      TF1 = 0;                              //清除TF1标志
      TR1 = 1;                              //定时器1开始计时
      ET1 = 1;                              //使能定时器1中断
}

voidsys_init(void)                        //系统初始化
{
      P_SW2 |= 0x80;                              //允许访问扩展的特殊寄存器,XFR

                                                                //32位8051需要使用下面3句进行初始化
//      EAXFR = 1;                                        //允许访问扩展的特殊寄存器,XFR
//      WTST = 0;                                        //设置取程序代码等待时间,赋值为0表示不等待,程序以最快速度运行
//      CKCON = 0;                                        //设置访问片内的xdata速度,赋值为0表示用最快速度访问,不增加额外的等待时间

      P0M0 = 0x00; P0M1 = 0x00;         //设置 P0 口为准双向口模式
      P1M0 = 0x00; P1M1 = 0x00;         //设置 P1 口为准双向口模式
      P2M0 = 0x00; P2M1 = 0x00;         //设置 P2 口为准双向口模式
      P3M0 = 0x00; P3M1 = 0x00;         //设置 P3 口为准双向口模式
      P4M0 = 0x00; P4M1 = 0x00;         //设置 P4 口为准双向口模式
      P5M0 = 0x00; P5M1 = 0x00;         //设置 P5 口为准双向口模式
      P6M0 = 0x00; P6M1 = 0x00;         //设置 P6 口为准双向口模式
      P7M0 = 0x00; P7M1 = 0x00;         //设置 P7 口为准双向口模式
}

voidmain(void)
{
      sys_init();                                        //调用系统初始化函数
      usb_init();                                        //调用USB初始化函数,不需要立即判断电脑已正确识别到USB从设备
      /*         USB型单片机从设备,如需要主动向电脑发送数据,
                在执行USB_SendData( )函数和printf_usb( )函数时,
                这两个函数已增加了判断电脑是否已正确识别到USB从设备的程序。
                如果电脑要主动发送数据给USB从设备,电脑自己会主动判断与USB从设备是否已正确连接。
      */
      EA = 1;                                                //使能中断

      Timer0_Init();                              //调用定时器0初始化函数
      Timer1_Init();                              //调用定时器1初始化函数

      P40 = 0;                                        //给LED灯供电

      while(1)                                          //主循环中查询需要处理的各种事件,如T0/T1中断事件,并打印输出状态
      {
                if(t0_flag)                              //主循环中查询,定时器0是否已产生中断,是否有需要处理的定时器0事件
                {
                        t0_flag = 0;                //清0,T0事件位变量标志
                        printf_usb("Timer0!\r\n");
                }
                if(t1_flag)                              //主循环中查询,定时器1是否已产生中断,是否有需要处理的定时器1事件
                {
                        t1_flag = 0;                //清0,T1事件位变量标志
                        printf_usb("Timer1!\r\n");
                }
      }
}


晓飛飛 发表于 2026-5-27 23:47:39

你玩的是P10,但是把P11配置成推挽输出了,代码改一下试试
    P1M0 = 0x01; P1M1 = 0x00;

wulin 发表于 2026-5-28 07:20:40

#include "stc8h.h"
#include "intrins.h"

void Timer0_Isr(void) interrupt 1
{
      P10 = ~P10;
}

void Timer0_Init(void)                //5微秒@11.0592MHz
{
      AUXR |= 0x80;                        //定时器时钟1T模式
      TMOD &= 0xF0;                        //设置定时器模式
      TL0 = 0xC9;                              //设置定时初始值
      TH0 = 0xFF;                              //设置定时初始值
      TF0 = 0;                              //清除TF0标志
      TR0 = 1;                              //定时器0开始计时
      ET0 = 1;                              //使能定时器0中断
}


void main()
{
      P0M0 = 0x00;P0M1 = 0x00;
      P1M0 = 0x00;P1M1 = 0x00;
      P2M0 = 0x00;P2M1 = 0x00;
      P3M0 = 0x00;P3M1 = 0x00;
      P4M0 = 0x00;P4M1 = 0x00;
      P5M0 = 0x00;P5M1 = 0x00;
      P6M0 = 0x00;P6M1 = 0x00;
      P7M0 = 0x00;P7M1 = 0x00;
      Timer0_Init();
      EA = 1;
      while(1);
}



狂热主宰 发表于 2026-5-28 09:19:39

晓飛飛 发表于 2026-5-27 23:47
你玩的是P10,但是把P11配置成推挽输出了,代码改一下试试
    P1M0 = 0x01; P1M1 = 0x00;...

习惯M1在前


32G12K128 发表于 2026-5-28 10:02:31

推荐用软件延时计算器:

C_wolf 发表于 2026-5-28 10:28:43

是不是时钟选错了

cqjames 发表于 2026-5-28 18:54:29

晓飛飛 发表于 2026-5-27 23:47
你玩的是P10,但是把P11配置成推挽输出了,代码改一下试试
    P1M0 = 0x01; P1M1 = 0x00;...

P11有其他作用,和定时无关,所以没调整。

cqjames 发表于 2026-5-28 18:55:40

wulin 发表于 2026-5-28 07:20
#include "stc8h.h"
#include "intrins.h"



我确实选错型号了,但是选对型号,循环次数更少,时间应该更短,所以原因不是这个。

cqjames 发表于 2026-5-28 18:58:02

C_wolf 发表于 2026-5-28 10:28
是不是时钟选错了

下载编程日志显示了,时钟实际设置值为11.062M,比代码中要求参数还更高,定时应该还短一些啊。

芯片出厂序列号 : F734C49A19B2E4

单片机型号: STC8H1K08

. 用户设定频率: 11.059MHz
. 调节后的频率: 11.062MHz ( 主时钟分频系数 = 2; )
. 频率调节误差: 0.022%

操作成功 !(2026-05-27 20:43:57)

wulin 发表于 2026-5-28 20:21:21

cqjames 发表于 2026-5-28 18:58
下载编程日志显示了,时钟实际设置值为11.062M,比代码中要求参数还更高,定时应该还短一些啊。

芯片出 ...

测试不熟悉的MCU最好不要用不易观察的方法,你测试定时不准有可能代码问题,也有可能是方法问题,还可能是工具问题。你定个1秒,用个LED显示,非常直观。单片机损坏的可能性极小。绝大多数是人为问题。
页: [1] 2 3
查看完整版本: 求教:STC8H1K08 定时问题 | 提高系统时钟到 30MHz