atmega328p引脚图_ATmega328P定时器详解

阅读: 评论:0

atmega328p引脚图_ATmega328P定时器详解
写这篇⽂章,纯粹是想为博客拉点点击量。在博客园,游客访问好像是不计⼊阅读量的,⽽作为⼀个⼗⼋线博主,注册⽤户的访问应该以搜索引擎为主,博客园⾸页为次,个位数的粉丝就别谈了。
所以,希望各位从搜索引擎点进来的朋友,多多评论,有问题咱们⼀起讨论。
我写过AVR单⽚机教程,设计过⾃⼰的Arduino板,希望你相信我能给你带来收获。
我不想听你放那么多屁,我只想知道周期为1ms的定时器中断怎么写!
什么是定时器
在ATmega328P单⽚机中,定时/计数器(Timer/Counter)是这样的组件:它需要⼀个时钟源,驱动⼀个8或16位的计数器递增或递减,当计数器等于⼀个值时,会触发⼀些操作,如产⽣中断、翻转引脚电平等。由于定时器的时钟源是系统时钟或外接晶振(⼀种产⽣频率精准的波的器件)分频得到的,⼀旦设置好定时器的⼯作参数,直到下次调整参数,定时器都会按照预期⼯作,与CPU执⾏的代码⽆关。
为什么要⽤定时器
之前有过这样的经历:跟⼀个优秀作品设计者聊了⼏句,他说同时控制舵机和扬声器很难控制好延时,扬声器输出的⾳乐节奏会乱。我第⼀反应当然是他没有⽤定时器中断,⼀问果然如此,并且他不知道中断也不知道定时器。
还有⼀位同学,写TI计算器的程序。在他的⼀个作品中,每次循环的计算量不定,循环间隔也不定,导致游戏效果不好。他的解决⽅法是根据计算量计算出循环最后需要的延时,使得循环间隔基本保持不变。
这种思路是相当优秀的。但是如果有定时器可⽤的话,编程难度会降低,循环间隔的⼀致性也会更好,是更加优秀的解决⽅案。
其实你⼀直在⽤定时器
Arduino Uno Rev3的3、5、6、9、10、11号端⼝可以使⽤analogWrite和tone函数,它们的功能都是利⽤定时器实现的。⽤函数确实⽅便,但是只知使⽤⽽不知其原理就只能停留在技术的表⾯——Arduino的强⼤封装对开发者的学习有两⾯性。
定时器其实不知道什么3号端⼝,它只知道OC2B。两种表⽰之间的对应关系如下表:端⼝编号硬件符号3PD3(PCINT19/OC2B/INT1)
5PD5(PCINT21/OC0B/T1)
6PD6(PCINT22/OC0A/AIN0)
9PB1(PCINT1/OC1A)
10PB2(PCINT2/SS/OC1B)
11PB3(PCINT3/OC2A/MOSI)
寄存器是开发者与硬件打交道的⽅式。从编程的语法上,可以把寄存器当作是变量,可以对它赋值,也可以读取它的数值。
寄存器中的位有⼏种不同的组织结构,它们的存取⽅式也不尽相同:
TCCR1B寄存器中有4组参数:ICNC1、ICES1、WGM1[3:2]、CS1[2:0]。现在你完全⽆需理解这些字母的含义,但是得对这些数字有个概念:WGM1[3:2]表⽰从WGM13到WGM12;TCCR1B中的1表⽰该寄存器属于定时器1,ICNC1和WGM13等名字中的1也是;CS12中的2表⽰该位为CS1[2:0]位域(bitfield)中的第2位(最低位为第0位)。
ICNC1和ICES1都是1位的位域,它们的值可以是0或1;WGM1[3:2]是2位的位域,它的值可以是00、01、10、11;CS1[2:0]同理。
你也许⼀眼就能看出⼆进制的11在⼗进制中是3,但是你很可能看不出23对应10111。在Arduino编程中(语⾔为C++),⼆进制数可以直接写,⽆需与⼗进制或⼗六进制转换。Arduino提供的⽅法是B10111,GCC提供的是0b10111(0b前缀字⾯量是C++14标准才规定的)。后者是我⼀直以来的习惯。
假如我要把这4个参数分别写为1、0、0b00、0b101,就要写:
TCCR1B =    1 << ICNC1
|    0 << ICES1
|  0b00 << WGM12
| 0b101 << CS10
;
全是0的可以不写,写是为了可读性。ICNC1是寄存器的第7位,所以代码中它的值就是7,其他位同理。
如果要判断ICNC1位是否为1:
if (TCCR1B & 1 << ICNC1)
// ...
如果要读取WGM1[3:2]位:
uint8_t wgm = (TCCR1B & 0b11 << WGM12) >> WGM12;
有的位因为不存在⽽不能写,如TCCR1B的第5位;有的位即使存在但是只读所以也不能写;有的位域分布于多个寄存器中,如
WGM1[3:0],低两位在TCCR1A,⾼两位在TCCR1B。
除了⼀个或多个位的位域以外,有些寄存器是整体使⽤的:
可以直接当变量读写:
OCR0A = 233;
uint8_t ocr0a = OCR0A;
还有16位寄存器,虽然读写不能⽤⼀句汇编搞定,但是⾼级语⾔层⾯上可以:
TCNT1 = 10086;
uint16_t tcnt1 = TCNT1;
不超过255的话可以只写低字节TCNT1L。
定时器相关寄存器总览:
定时器的⼯作模式
读数据⼿册⽆疑是深⼊了解单⽚机的最好⽅法,可惜很多⼈没这个耐⼼,⼏⼗页的英语也不是每个⼈都吃得消的。有些中⽂书打着介绍AVR 单⽚机的幌⼦翻译数据⼿册,不仅没有营养还漏洞百出,我不也推荐。写这篇⽂章,也有避免后⼈重蹈覆辙的⽬的。
当然,除了有代码⽰例以外,本⽂再“详解”也详细不过数据⼿册,不过⾄少可以让你对定时器有个⼤致的印象,不致于让你读的时候⼀头雾⽔。
ATmega328P有3个定时器:定时器0、定时器1和定时器2(简单粗暴)。0和2都是8位的,2⽀持异步⼯
作;1是16位的,精度更⾼,⽀持更多⼯作模式。我接触过其他型号的单⽚机,AVR的定时器是相对简单的。
定时器有3种⼯作模式:普通模式、CTC模式、PWM模式,其中PWM还分快速PWM、相位矫正(波形居中)PWM、相位与频率矫正
PWM(频率可以任取,仅限定时器1)。
先讲各种模式中共通的部分。定时器需要⼀个时钟源,它可以是:时钟源适⽤范围⽆所有
clkI/O/N,N=1,8,64,256,1024clkI/O/N,N=1,8,64,256,1024
(clkI/OclkI/O为系统时钟,16MHz)定时器0/1
T0(4)引脚上升/下降沿定时器0
T1(5)引脚上升/下降沿定时器1
clkT2S/N,N=1,8,32,64,128,256,1024clkT2S/N,N=1,8,32,64,128,256,1024
(clkT2SclkT2S为系统时钟或外置32kHz晶振)定时器2
⼯作模式之间的区别在于计数器的变化⽅向与范围,介绍之前需要先下3个定义:名称描述BOTTOM0,计数器的最⼩值
MAX对8位定时器为0xFF,对16位定时器为0xFFFF,计数器的最⼤可能值
TOP计数器达到这个值时,可能会被清零,或变化⽅向改变
对定时器0和2,可以为MAX或OCRnA
对定时器1,可以为0x00FF、0x01FF、0x03FF、OCR1A或ICR1普通模式中,计数器从0开始增长到MAX,然后溢出回到0,周⽽复始。频率为(clkclk为定时器时钟频率)
clkMAX+1clkMAX+1
CTC模式和快速PWM模式中,计数器从0开始增长到TOP,然后不再继续增长⽽是直接回到0,重新开始增长。频率为
clkTOP+1clkTOP+1
两种相位矫正PWM模式中,计数器从0到TOP,再从TOP回到0,如此循环。频率为
clk2TOPclk2TOP
计数器⽐较
当计数器的值与OCRnA或OCRnB相等时,可以对OCnx的电平进⾏⼀些操作。所有模式下,OCnx都可以不连接定时器。
⾮PWM模式下,可以把OCnx置为低电平、⾼电平或翻转电平,tone就是这样实现的;
PWM模式下,有正相和反相两种模式,正相为OCRnx越⼤占空⽐越⾼,analogWrite就是这样实现的;反相反之;有些配置下OCnA可以被翻转,请参考数据⼿册。
由于引脚电平可以有宏观表现,我们终于可以开始写代码了。
先试试tone。在9号端⼝上连接⼀个蜂鸣器,使⽤定时器1的CTC模式,产⽣440Hz⽅波:
void setup() {
pinMode(9, OUTPUT);
TCCR1A = 0b01 << COM1A0 | 0b00 << WGM10;
TCCR1B = 0b01 << WGM12 | 0b001 << CS10;
OCR1A = 18181;
}
void loop() {
}
OCR1A = 18181是怎么来的呢?每次计数器与OCR1A相等电平翻转,两次为⼀周期,频率为clk2(OCR1A+1)clk2(OCR1A+1)。先取clkclk为不分频试试,算出OCR1A为18181,没有超过最⼤值65535,因此就取这个。如果超过了,就要把定时器频率下调,直到OCRnx合理为⽌。
void setup() {
pinMode(3, OUTPUT);
TCCR2A = 0b10 << COM2B0 | 0b11 << WGM20;
TCCR2B = 0 << WGM22 | 0b100 << CS20;
}
int brightness = 0;
int fadeAmount = 5;
void loop() {
OCR2B = brightness;
brightness = brightness + fadeAmount;
if (brightness <= 0 || brightness >= 255)
fadeAmount = -fadeAmount;
delay(30);
}
如果要让程序以频率为参数计算出合适的分频系数与OCRnx值,可以参考tone的实现。
再试试analogWrite。在3号端⼝上连接⼀个LED,使⽤定时器2的快速PWM模式,实现呼吸灯的效果:
在快速PWM模式中,正相输出占空⽐不能为0,反相输出占空⽐不能为1,如果要达到这两个值,需要断开引脚与定时器的连接,⽤digitalWrite等⽅法输出。
定时器中断
懒得写了,我抄我⾃⼰:中断,是单⽚机的精华。
当⼀个事件发⽣时,CPU会停⽌当前执⾏的代码,转⽽处理这个事件,这就是⼀个中断。触发中断的事件成为中断源,处理事件的函数称为中断服务程序(ISR)。
中断在单⽚机开发中有着举⾜轻重的地位——没有中断,很多功能就⽆法实现。⽐如,在程序⼲别的事时接受UART总线上的输⼊,⽽
uart_scan_char等函数只会接收调⽤该函数后的输⼊,先前的则会被忽略。利⽤中断,我们可以在每次接受到⼀个字节输⼊时把数据存放到缓冲区中,程序可以从缓冲区中读取已经接收的数据。
AVR单⽚机⽀持多种中断,包括外部引脚中断、定时器中断、总线中断等。每⼀个中断被触发时,通过中断向量表跳转到对应ISR。如果⼀个中断对应的ISR不存在,链接器会把复位地址
[1] [2]

本文发布于:2023-05-05 22:54:24,感谢您对本站的认可!

本文链接:https://patent.en369.cn/patent/1/89410.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:中断   寄存器   计数器   模式   循环   频率   时钟
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 369专利查询检索平台 豫ICP备2021025688号-20 网站地图