数码之家

 找回密码
 立即注册
搜索
查看: 3016|回复: 17

[C51] 根据定时器计算出来的PWM频率不对,问题出在那里?

[复制链接]
发表于 2023-12-26 20:41:47 | 显示全部楼层 |阅读模式
看到坛友用51单片机生成PWM的帖子,发现计算结果怎么算也对不上,请各位大神看看问题出在哪里:

首先是定时器的设置,经过坛友的指导终于搞明白了,这里的10在12Mhz晶振的情况下代表每10微秒中断一次:
  1.     TH0=(65536-10)/256;      //10us初值高8位
  2.     TL0=(65536-10)%256;      //10us初值低8位
复制代码

接下来是中断函数,每中断100次就重新开始计数,而每次中断是10us,所以这里应该是1000微秒也就是1毫秒重新开始输出一次PWM,也就是PWM的频率应该是1000HZ:
  1. void time0 ()interrupt 1    //定时器中断函数
  2. {   
  3.     TR0=0;                 //
  4.     TH0=(65536-10)/256;
  5.     TL0=(65536-10)%256;
  6.     TR0=1;                 //
  7.     time++;    //
  8.      if(time>=100)      //如果定时满了100次
  9.         time=0;        //定时清零
  10.     if(time<=duty)    //如果小于10,相当于只有10%时间是高电平,
  11.         PWM=1;          //输出高电平
  12.     else
  13.          PWM=0;          //其它时间输出低电平
  14. }     
复制代码
但是坛友用示波器测出来的频率是263HZ,并不是计算出来的1000HZ,是哪里出了问题?






本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
发表于 2023-12-26 22:34:15 | 显示全部楼层
这几步执行都要消耗时间
TH0=(65536-10)/256;
    TL0=(65536-10)%256;
    TR0=1;
另外,单片机在检测到中断申请时,需要将当前正在运行的工作寄存器中的值保存下来,以便中断结束后恢复。也就是所谓的进栈出栈。这些都要消耗时间。
时间长了,记不清了。
回复 支持 反对

使用道具 举报

发表于 2023-12-26 22:46:31 | 显示全部楼层
过来学习,敬等高手上场。
回复 支持 反对

使用道具 举报

发表于 2023-12-26 23:35:25 | 显示全部楼层
你确认10us里51能跑完那一串命令? 用汇编还差不多.
回复 支持 反对

使用道具 举报

发表于 2023-12-27 00:33:48 | 显示全部楼层
fishok 发表于 2023-12-26 23:35
你确认10us里51能跑完那一串命令? 用汇编还差不多.

如果是12MHz的STC89C52,10μS确实跑不完中断中的程序。
  1. CSEG        AT        0000BH
  2.         LJMP        time0        <font color="#ff0000">5T</font>

  3. ; void time0 ()interrupt 1    //定时器中断函数

  4.         RSEG  ?PR?time0?IO_MODE
  5.         USING        0
  6. time0:
  7.         PUSH         ACC        <font color="#ff0000">5T</font>
  8.         PUSH         PSW        <font color="#ff0000">5T</font>
  9.                         ; SOURCE LINE # 30
  10. ; {   
  11. ;     TR0=0;                 //
  12.                         ; SOURCE LINE # 32
  13.         CLR          TR0        <font color="#ff0000">3T</font>
  14. ;     TH0=(65536-10)/256;
  15.                         ; SOURCE LINE # 33
  16.         MOV          TH0,#0FFH        <font color="#ff0000">3T</font>
  17. ;     TL0=(65536-10)%256;
  18.                         ; SOURCE LINE # 34
  19.         MOV          TL0,#0F6H        <font color="#ff0000">3T</font>
  20. ;     TR0=1;                 //
  21.                         ; SOURCE LINE # 35
  22.         SETB         TR0        <font color="#ff0000">3T</font>
  23. ;     time++;    //
  24.                         ; SOURCE LINE # 36
  25.         INC          time        <font color="#ff0000">3T</font>
  26. ;      if(time>=100)      //如果定时满了100次
  27.                         ; SOURCE LINE # 37
  28.         MOV          A,time        <font color="#ff0000">2T</font>
  29.         CLR          C        <font color="#ff0000">1T</font>
  30.         SUBB         A,#064H        <font color="#ff0000">2T</font>
  31.         JC           ?C0001        <font color="#ff0000">2T/4T</font>
  32. ;         time=0;        //定时清零
  33.                         ; SOURCE LINE # 38
  34.         MOV          time,#00H        <font color="#ff0000">3T</font>
  35. ?C0001:
  36. ;     if(time<=duty)    //如果小于10,相当于只有10%时间是高电平,
  37.                         ; SOURCE LINE # 39
  38.         MOV          A,time        <font color="#ff0000">2T</font>
  39.         SETB         C        <font color="#ff0000">1T</font>
  40.         SUBB         A,duty        <font color="#ff0000">2T</font>
  41.         JNC          ?C0002        <font color="#ff0000">2T/4T</font>
  42. ;         PWM=1;          //输出高电平
  43.                         ; SOURCE LINE # 40
  44.         SETB         PWM        <font color="#ff0000">3T</font>
  45.         SJMP         ?C0004        <font color="#ff0000">4T</font>
  46. ?C0002:
  47. ;     else
  48. ;          PWM=0;          //其它时间输出低电平
  49.                         ; SOURCE LINE # 42
  50.         CLR          PWM        <font color="#ff0000">3T</font>
  51. ; }     
  52.                         ; SOURCE LINE # 43
  53. ?C0004:
  54.         POP          PSW        <font color="#ff0000">4T</font>
  55.         POP          ACC        <font color="#ff0000">4T</font>
  56.         RETI                 <font color="#ff0000">8T</font>
  57. ; END OF time0
复制代码
即使忽略中断响应过程,从中断跳转开始算,中断中用时约69T,如果是12MHz的STC89C52,即使工作在6分频模式,也要69*(1/(12MHz/6))=34.5μS。看程序设置初值(65536-10),可能是工作在12分频模式,用时69*(1/(12MHz/12))=69μS。即使按官方资料最高频率42MHz、6分频模式,也需要69*(1/(42MHz/6))=9.86μS。

回复 支持 反对

使用道具 举报

发表于 2023-12-27 10:47:24 | 显示全部楼层
我也有此疑问,今天又做了一次试验
我把初值改成100 定时次数改为10 总的中断时间还是1mS 频率794Hz

TH0=(65536-100)/256;      //
TL0=(65536-100)%256;      //


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复 支持 反对

使用道具 举报

发表于 2023-12-27 11:21:06 | 显示全部楼层
hellozwt 发表于 2023-12-27 10:47
我也有此疑问,今天又做了一次试验
我把初值改成100 定时次数改为10 总的中断时间还是1mS 频率794Hz

其实答案楼上几位已经给出了
把中断处理代码改成最简单的方波试试

如果无法优化代码 就简化任务



回复 支持 反对

使用道具 举报

发表于 2023-12-27 11:29:31 | 显示全部楼层


时钟周期



[color=rgba(0, 0, 0, 0.9)]时钟周期也叫振荡周期或晶振周期,即晶振的单位时间发出的脉冲数,一般有外部的振晶产生,比如12MHZ=12×10的6次方,即每秒发出12000000个脉冲信号,那么发出一个脉冲的时间就是时钟周期,也就是1/12微秒。通常也叫做系统时钟周期。是计算机中最基本的、最小的时间单位。
[color=rgba(0, 0, 0, 0.9)]在传统的8051单片机中把一个时钟周期定义为一个节拍(用P表示),二个节拍定义为一个状态周期(用S表示)。



机器周期



[color=rgba(0, 0, 0, 0.9)]单片机在执行指令的过程中,其实需要完成很多个操作,比如,取指令、存储器读、存储器写等,这每一项工作称为一个基本操作。在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶段,每一阶段完成一项工作。完成一个基本操作所需要的时间称为机器周期。一般情况下,一个机器周期由若干个S周期(状态周期)组成。传统8051系列单片机的一个机器周期由6个S周期(状态周期)组成。节拍和状态周期前面已经介绍过了。传统8051单片机的机器周期由6个状态周期组成,也就是说一个机器周期=6个状态周期=12个时钟周期。
[color=rgba(0, 0, 0, 0.9)]在传统的51单片机中,一般情况下,1个机器周期=12个时钟周期。在一个单片机最小系统中,如果外接的是12MHZ的晶振,那么机器周期=1微秒(前几天问小编为什么是1us的小伙伴到此处来领取答案)。
[color=rgba(0, 0, 0, 0.9)]单片机工作时,是一条一条地从ROM中取指令,然后一步一步地执行。单片机访问一次存储器的时间,称之为一个机器周期,这是一个时间基准。
[color=rgba(0, 0, 0, 0.9)]机器周期不仅对于指令执行有着重要的意义,而且机器周期也是单片机定时器和计数器的时间基准。例如一个单片机选择了12MHZ晶振,那么当定时器的数值加1时,实际经过的时间就是1us,这就是单片机的定时原理。



指令周期



[color=rgba(0, 0, 0, 0.9)]指令周期是执行一条指令所需要的时间,一般由若干个机器周期组成。指令不同,所需的机器周期数也不同。对于一些简单的的单字节指令,在取指令周期中,指令取出到指令寄存器后,立即译码执行,不再需要其它的机器周期。对于一些比较复杂的指令,例如转移指令、乘法指令,则需要两个或者两个以上的机器周期。
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]




系统时钟


[color=rgba(0, 0, 0, 0.9)]系统时钟:系统时钟就是CPU指令运行的频率,这个才是CPU真正的频率。
[color=rgba(0, 0, 0, 0.9)]一般来说,单片机只有一个时钟源.用了外部晶振,就不用内部RC,用了内部RC,就不用外部晶振。振荡器振荡,产生周期波.单片机在这样的周期波的作用一下有规律的一拍一拍的工作,波的频率越高,单片工作得就越快,波的频率越低,单片机工作得就越慢。
[color=rgba(0, 0, 0, 0.9)]单片机内部所有工作,都是基于由晶振产生的同一个触发信号源,由这个信号来同步协调工作步骤,我们把这个信号称为系统时钟,系统时钟一般由晶振产生,但在单片机内部系统时钟不一定等于晶振频率,有可能小于晶振频率,也有可能大于晶振频率,具体要看单片机的实际设计及其原理。比如传统的51单片机,其实际的系统时钟就只有晶振频率的1/12,;比如大家觉得比较高级的STM32单片机,内部具有时钟倍频电路,可以通过程序设置系统时钟是实际晶振的多少倍,常见的STM32开发板上实际的晶振都是8Mhz的,可以通过倍频,实际的系统时钟都是运行在72Mhz。
[color=rgba(0, 0, 0, 0.9)]系统时钟是整个单片机工作节奏的基准,它每振荡一次,单片机就被触发执行一次操作。
[color=rgba(0, 0, 0, 0.9)]


12T模式和1T模式


[color=rgba(0, 0, 0, 0.9)]前面我们说的绝大部分都是传统51单片机的例子,现在传统51单片机已经可以收藏了,51单片机中小伙伴们最常见应该是手推车(STC)。STC51的单片机基本已经把51内核压榨到了极致,本节标题里说的额12T和1T也就是STC提出来的。STC把绝大部分的汇编指令所需要的的时钟周期压缩到了1个时钟周期,而传统的51单片机是最快的指令都需要12个时钟周期,所以STC官方就把他们的单片机叫做是1T的单片机。说到1T和12T,还有一个常用到的就是在用STC单片机的定时器时,和定时器相关的寄存器中专门有设置定时器是1T模式还是12T模式,如果是1T模式,而我们计算初值时又计算为了12T模式,那这样出来的定时器会比实际的速度快12倍,在使用时需要注意
[color=rgba(0, 0, 0, 0.9)]


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-12-27 13:49:14 | 显示全部楼层
hellozwt 发表于 2023-12-27 10:47
我也有此疑问,今天又做了一次试验
我把初值改成100 定时次数改为10 总的中断时间还是1mS 频率794Hz

把中断函数里的先关中断再开中断的这两句注释掉看看频率能不能更大些,我看别人代码是直接赋值不用先关再开的:
  1.    //TR0=0;
  2.    .......
  3.    //TR0=1;
复制代码
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-12-27 13:52:33 | 显示全部楼层

好详细                    
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-12-27 13:53:39 | 显示全部楼层
mmxx2015 发表于 2023-12-27 00:33
如果是12MHz的STC89C52,10μS确实跑不完中断中的程序。
即使忽略中断响应过程,从中断跳转开始算,中断 ...

厉害,每条语句的执行时间都能看到,这是用什么软件分析出来的?
回复 支持 反对

使用道具 举报

发表于 2023-12-27 13:58:38 | 显示全部楼层
猪小呆 发表于 2023-12-27 13:53
厉害,每条语句的执行时间都能看到,这是用什么软件分析出来的?

对着指令表数的。

打赏

参与人数 1家元 +20 收起 理由
猪小呆 + 20

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2023-12-27 15:10:57 | 显示全部楼层
本帖最后由 hellozwt 于 2023-12-27 15:12 编辑

我把程序做了最简化
//程序功能:P1.0口输出PWM信号

#include <reg52.h>                //头文件
typedef unsigned char uchar;        //类型定义
typedef unsigned int uint;

sbit PWM=P1^0;        //P1.0口输出PWM信号
uchar time;         //定义中断次数         

void time0init();        //声明定时器初始化子函数

void main ()          //主函数
{
        time0init();        //调用初始化子函数
        while(1)
        {
        }
}

void time0init()         //定时器初始化函数
{
    TMOD=0X01;        // 定时器模式1
        TH0=(65536-100)/256;          //10us初值高8位
        TL0=(65536-100)%256;          //10us初值低8位
        EA=1;                //开总中断
        ET0=1;                //开定时器0            
        TR0=1;                //定时器0开始
}

void time0 ()interrupt 1        //定时器中断函数
{        
        TH0=(65536-100)/256;                //
        TL0=(65536-100)%256;                 //
        time++;        //
         if(time>=10)          //如果定时满了10次
           {
                        time=0;                //定时清零
                        PWM=!PWM;
                }
}         

如果TH0=(65536-10)/256  定时100次,出来的频率是177HZ 因为是定时翻转,所以频率应该177*2=354HZ
如果TH0=(65536-100)/256  定时10次,出来的频率是404HZ 因为是定时翻转,所以频率应该404*2=808HZ

我又尝试了TMOD=0X02;模式
TH0=0X9C;  //256-100
定时次数10次
出来的频率是460HZ 因为是定时翻转,所以频率应该460*2=920HZ


回复 支持 反对

使用道具 举报

发表于 2023-12-27 15:13:27 | 显示全部楼层
得出的经验是定时器初值太大,精度越低,
回复 支持 反对

使用道具 举报

发表于 2023-12-27 15:20:52 | 显示全部楼层
hellozwt 发表于 2023-12-27 10:47
我也有此疑问,今天又做了一次试验
我把初值改成100 定时次数改为10 总的中断时间还是1mS 频率794Hz

这个示波器不错,这种显示效果看着很有质感
回复 支持 反对

使用道具 举报

发表于 2023-12-28 11:23:15 | 显示全部楼层
楼主重新学习一下定时器的模式吧,有自动重载方式的,就是TMOD的设置,那么定时器的初始化工作就不用在中断中做,
  1. TR0=0;                 //
  2.     TH0=(65536-10)/256;
  3.     TL0=(65536-10)%256;
  4.     TR0=1;                 
复制代码

这些都是不需要的。
回复 支持 反对

使用道具 举报

发表于 2023-12-28 11:40:08 | 显示全部楼层
  1. #include <reg52.h>
  2. #define PWM_OUT P1

  3. // 定义占空比
  4. unsigned char PWM_duty_ratio = 50;  // 50%占空比

  5. void main()
  6. {
  7.     // 设置定时器1的计数初值
  8.     TH1 = 0xFC;
  9.     TL1 = 0x66;
  10.     // 设置定时器1为工作模式2
  11.     TMOD |= 0x10;
  12.     // 启动定时器1
  13.     TR1 = 1;
  14.     // 设置PWM输出的IO口为输出模式
  15.     PWM_OUT = 0;
  16.     // 开始输出PWM波
  17.     while(1)
  18.     {
  19.         if(PWM_OUT >= PWM_duty_ratio)
  20.             PWM_OUT = 0;
  21.         else
  22.             PWM_OUT = 1;
  23.     }
  24. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2023-12-31 23:02:27 | 显示全部楼层
就不要让51跑10微秒的中断了。。换个快点的撒
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-5-3 07:29 , Processed in 0.374401 second(s), 15 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表