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

13560567638 发表于 2020-3-1 22:54

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

本帖最后由 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

      
      • 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 熔丝位已编程。
      


以下是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判断语句了!
      
      降低时钟频率同时也可以降低工作电压,将大幅度的降低功耗。除了,利用系统时钟预分频器降低时钟频率,还可以通过修改熔丝位同样可以降低时钟频率,待续 ......
      





页: [1]
查看完整版本: ATmega328 利用系统时钟预分频器降低时钟频率