ATmega328 利用系统时钟预分频器降低时钟频率-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 2832|回复: 0

ATmega328 利用系统时钟预分频器降低时钟频率

[复制链接]
发表于 2020-3-1 22:54 | 显示全部楼层 |阅读模式
本帖最后由 13560567638 于 2020-3-1 22:56 编辑

        ATmega328 可以通过设置时钟预分频寄存器CLKPR来得到分频的系统时钟。当需要的系统处理能力比较低时可以利用这个特性来降低功耗。预分频对所有时钟源都适用,并且影响 CPU 及所有同步外设的时钟频率
        当在预分频器设置转换时,系统时钟预分频器保证时钟系统不会出现毛刺。同时保证中频低于设置前后的时钟频率。脉动计数器使预分频器在未分频时钟频率下运行,这可能比CPU 时钟频率快。因此即使预分频器可读,我们也无法确定其状态,所以我们也无法得到准确的转换时间。 CLKPS 值的写入时间介于 T1 + T2 与 T1 + 2*T2 之间。在此间隔中,产生 2 个时钟边沿。其中 T1 为前一个时钟周期, T2 为新设置后相应的时钟周期。

为避免时钟频率的无意改变,对 CLKPS 位的写入必须按照如下步骤进行:
        1. 将 CLKPCE 位写 "1”,而 CLKPR 寄存器的其他位写 "0”。
        2. 在四个时钟周期内,将期望值写入 CLKPS,并在 CLKPCE 位写 "0”。
        在改变预分频器设置时必须禁止中断,以保证在写入过程中不会出现中断。
        
时钟预分频寄存器- CLKPR
CLKPR.JPG
        
        • Bit 7 – CLKPCE:
        CLKPCE 位必须置"1”使能 CLKPS位。只有当CLKPR寄存器的其他位同时写"0”时, CLKPCE 位改变。CLKPCE 在
        写入四个周期后或当 CLKPS 位写入后由硬件清零。在暂停周期中重新写 CLKPCE 位,既不扩展暂停周期,也不清除
        CLKPCE 位。
        • Bits 3..0 – CLKPS3..0:  时钟预分频器选择位 3 - 0
        这几位定义所选时钟源与内部系统时钟所分频因子。这几位写入运行时间来改变时钟频率以适应运行需要。当作为
        MCU 主时钟输入分频器,使用分频因子时,所有同步外设速度将会下降。分频因子见 Table 17。
        CKDIV8熔丝位决定CLKPS位的初始值。若CKDIV8未编程,CLKPS位复位为“0000”;若CKDIV8 已编程,CLKPS
        位复位为 “0011”,给出启动时分频因子为 8。若所选时钟源频率大于当前工作状态下器件最大频率时,应利用该
        特性分频。注意, CLKPS 位写入值不受CKDIV8 熔丝位设置影响。若所选时钟源频率大于当前工作状态下器件最大
        频率,应用程序必须保证选择一个足够大的分频因子。芯片出厂时 CKDIV8 熔丝位已编程。
        
时钟预分频器选择.JPG

以下是ATmega328测试代码:

/*  ====================  Pin8 连接低电平,开启时钟预分频器  ====================  */

void setup() {
  pinMode(13, OUTPUT);           //  Pin13 为输出模式
  pinMode(8, INPUT_PULLUP);      //  Pin8  为输入上拉模式

/*  在没有了解系统时钟预分频器之前, 不要删除if判断语句, 否则有可能造成烧录报错, Pin8 连接低电平开启时钟预分频  */
/*  如果由于频率设置过低出现烧录报错, 只要把Pin8 悬空(建议连接高电平), 然后复位单片机, 再次烧录就可以解决问题  */
  if(digitalRead(8) == 0) {
      cli();                      //  禁止所有中断
      CLKPR = 0b10000000;         //  时钟预分频器变化使能
      CLKPR = 0b10;               //  时钟频率设置为4 分频
      sei();                      //  重新启用中断
  }
}

void loop() {
  digitalWrite(13, !digitalRead(13));
  delay(1000);              //  延时1秒,由于降频的原因变为延时4秒
}

/*  ====================  Pin8 连接高电平,关闭时钟预分频器  ====================  */

解决烧录报错:
        报错原因是时钟频率不符合要求造成的,主要出现在ArduinoISP作为烧录器的情况(ISP烧录器应该都有这个问题),关于如何把Arduino变成AVRISP烧录器网上有很多资料,这里不再赘述。Arduino开发版直接上传和TTL上传均不会出现报错,这与上传及烧录的原理有关,上传程序是依靠BootLoader自编程,而烧录是单片机自身的功能。当然,与MCU对SPI串行下载的频率要求也有关系,串行时钟 (SCK) 的高电平最小持续时间低电平最小持续时间要满足如下要求:
        
        高:> f ck < 12 MHz 时为 2 个 CPU 时钟周期, f ck >= 12 MHz 时为 3 个 CPU 时钟周期。
        低:> f ck < 12 MHz 时为 2 个 CPU 时钟周期, f ck >= 12 MHz 时为 3 个 CPU 时钟周期。
        
        从要求中可以看出,高低电平的最小持续时间分别为2个CPU时钟周期3个CPU时钟周期,因此,取3个CPU时钟周期。SPI高电平时间等于3个CPU时钟周期、SPI低电平时间也等于3个CPU时钟周期,那么,1个SPI时钟周期应该等于6个CPU时钟周期,高低电平的持续时间相加才是1个SPI时钟周期。
               
        假设: CPU时钟频率为16MHz,将CPU时钟频率256分频等于62500Hz (即 16000000/256=62500Hz)        
                 ArduinoISP默认SPI时钟频率等于166666Hz (即 1000000/6=166666Hz)
        
                 62500Hz(CPU) < 166666Hz(SPI)

        但是,根据要求SPI时钟频率必须小于等于CPU时钟频率的1/6(或CPU时钟频率的1/4)。
        所以,SPI时钟频率应该小于等于62500/6=10416Hz,因此,需要修改SPI时钟频率。
        那么,怎么修改ArduinoISP的SPI时钟频率呢?

        打开Arduino -> 点击 "文件" -> "示例" -> "11.ArduinoISP" -> "ArduinoISP"

        点击 "编辑" -> "查找" -> 寻找 "#define SPI_CLOCK"
        把 #define SPI_CLOCK (1000000/6) 改为 #define SPI_CLOCK (62500/6)
        
        SPI时钟频率越小烧录速度越慢,耐心等待...
        重新上传ArduinoISP程序,然后再次烧录,发现问题已经迎刃而解。

到此已经可以放心删除if判断语句了!
        
        降低时钟频率同时也可以降低工作电压,将大幅度的降低功耗。除了,利用系统时钟预分频器降低时钟频率,还可以通过修改熔丝位同样可以降低时钟频率,待续 ......
        





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

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 11:56 , Processed in 0.080427 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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