基于Arduino,了解AVR单片机的底层工作原理,像学8051一样学习AVR,仅仅利用Arduino廉价且普及的硬件
Microchip很多芯片的官方文档存在错误,一定要注意甄别
由浅入深的硬件分析
UNO作为Arduino最经典的一款开发板,分为很多个版本,因为各种克隆版、授权代工、改良版等等实在太多了。
这里遵循TB商家的命名习惯做一些定义:
官方版代指所有遵循官方电路以及PCB设计生产的开发板,这些开发板包括正版开发板(TB商家一般称为原装进口正版、意大利官方英文版等),国内授权代工版(目前已停产,一般称为官方中文版等),以及一些国内厂家根据官方网站提供的PCB文件生产的官方盗版(一般称为官方版本),这些开发板共同的特点就是usb电路部分都是使用一片正方形的ATmega16u2或8u2的单片机(需要下载固件),并且一般会印上Arduino的logo
改良版代指国内一些厂家由官方电路更改而来,重新自行设计PCB之后生产的开发板(一般称为改良版或改进版)。这些改良版的共同特点是usb部分使用了南京沁恒的CH340芯片,是一个16脚长方形芯片,可以大大降低成本(ATmega16u2和ATmega328P一样是个完整功能的AVR单片机,价格较高)。而主控芯片一般使用贴片ATmega328P,也有少部分和官方版一样使用插槽安装直插式DIP芯片,并且一般不会打印Arduino的logo
克隆版代指所有非官方工厂(包括官方代工)来源的开发板,包括了官方盗版以及改良版。现在新款的原装和代工版的PCB一般使用蓝绿色油墨
官方版
官方版UNO有新版UNO R3和旧版UNO两种,其中旧版已经非常少见。在Arduino中AVR单片机基本都是使用的16Mhz的晶振
新版UNO R3电路查看
旧版UNO电路查看
官方新版与旧版电路区别主要在5V稳压芯片以及13脚的LED电路上
旧版5V稳压芯片采用一片MC33269D-5.0,这个5V三端稳压有两种不同的封装,所以图中画了两个,实际只安装一种封装的芯片
新版R3的5V稳压芯片采用一片AMS1117-5.0
旧版13脚LED直接和328P的IO(PB5)相连,通过电阻限流
新版R3的13脚LED利用了板载运放LM358中未使用的一个运放,作为一个跟随器用,利用运放输入高阻抗特性隔离LED负载,可以减小PB5口的电流负载,同时13脚设定为输入时理论阻抗无穷大
此外,新版相对于旧版在电源口部分添加了IOREF引脚,用于为扩展板提供参考IO电平
原装英文版(引自Arduino官网)
背面
进口的价格非常高,同样价格可以买一打克隆版了。当然土豪不差钱无所谓
官方中文版(自摄)
背面
Arduino官方授权中国厂商代工。这块中文版前主人将其用于小车电机控制,由于未知原因导致16u2芯片固件异常无法烧录,而328P芯片已被取下转移至其他UNO上使用。之后有修复教程,以及16u2的开发(事实上UNO没有328P也可以当开发板用,功能和之后提到的Leonardo类似,可以模拟键盘,但是可扩展性稍差)
官方盗版(自摄)
背面
陪伴我时间最长的一块开发板,从刚上高中用到现在。这些克隆版缺点就是焊接工艺较差,元器件歪斜,但是工作稳定性没什么区别
旧版官方盗版(自摄)
背面
捡到的板子。字体形状比较奇怪,应该是当时的厂商没有相应的字体文件。比新版少一些接口,usb是使用的ATmega8u2而非16u2,复位键在中间,三端稳压不一样,L13的LED没有经过运放直接由单片机IO驱动。其他功能和新版R3基本相同
改良版
改良版电路查看
改良版就是将原ATmega16u2部分换成较为廉价的CH340,并且使用
/DTR
作为复位信号
改良版(自摄)
背面
和UNO一样,Nano也有多种。Nano事实上是缩小版的UNO,同样使用ATmega328P,区别在usb部分
官方版
原装英文版(引自Arduino官网)
官方盗版(自摄)
背面
官方版usb转串口都采用FTDI的FT232RL
改良版
改良版(自摄)
背面
改良版usb部分使用CH340
Leonardo没有usb转串口(32u4自带usb),所以没有所谓改良版。主控采用ATmega32u4,封装可能不同。另有Arduino Micro,就是Leonardo的缩小版,它和Leonardo的关系类似于UNO和Nano的关系
官方版电路查看
官方版
原装英文版(引自Arduino官网)
背面
官方盗版(自摄)
背面
Mega2560使用ATmega2560-16AU作为主控芯片,本质和328P差不多,IO较多,但是RAM依然和大部分8位机一样很小。所以Mega2560使用的场合一般是3D打印机等,这些CNC设备一般要使用到大量的IO(尤其是步进电机控制)。而在信号处理等场合,限于Mega2560的内存大小,其应用十分有限(1024点FFT都难以实现)。有信号处理的需求一般还是使用32位ARM单片机
这里只给出官方图片
官方版
原装英文版(引自Arduino官网)
背面
Arduino DUE主控芯片为SAM3X8E,使用32位ARM Cortex-M3核心而非AVR,IO电平为3.3V,不属于常规AVR开发板,比起UNO、Mega 2560等传统Arduino,和STM32更加相近,这里仅展示其新旧两种版本
新版DUE电路查看
旧版DUE电路查看
DUE功耗较大,新版以及旧版使用的5V供电电路不同。旧版使用的是LM2734Y构建的分立DC-DC,有一个较大的黑色方形电感,缺点是容易啸叫。新版采用全集成式MPM3610
官方版
原装英文版(引自Arduino官网)
背面
原装进口一样也是很贵,一般要350左右,不差钱的可以考虑
旧版官方盗版(自摄)
背面
相比官方原版,没有安装RTC晶振(32.768kHz)。入手时全新50多米,比很多2560还便宜一些
只给出UNO以及Leonardo两种开发板的电路分析,其他经典AVR衍生版基本同理
这里再放一下UNO R3的pdf
官方版UNO使用了两片AVR单片机,一片是直插式DIP28的ATmega328P,另一片是贴片TQFP32的ATmega16u2。ATmega16u2作为usb芯片拥有usb IP核以及寄存器,而其他方面较328P较弱。
ATmega16u2相比328P拥有usb模块,而缺少了计数器/定时器2,I2C模块以及AD模数转换
电源
由上图分析。Arduino UNO支持从DC口以及USB供电,主要电路以及MCU全部使用5V供电,3.3V供电仅仅用于为LM358(作为比较器使用)提供参考电压(左上角),当然也可以通过3.3V接口向外设供电。通过DC供电时,设AMS1117输出5V正常,LP2985输出3.3V,那么只有当VIN大于6.6V时,MOS管FDN340P才会截止,此时完全使用1117的5V供电。如果此时VIN输入电压小于6.6V(或VIN未连接),此时MOS管导通,主要由USB供电
设置这个电路就是为了防止在同时连接VIN和USB时USB口电流倒灌损坏电脑
328P分为数字部分供电以及模拟部分供电,这里都使用5V,其中模拟部分AVCC输入串联一个10uH电感
GPIO
UNO引出了328P所有的引脚。这些引脚可以做普通IO功能。其中,PC兼做AD输入以及I2C;PD包含了UART,外部中断,计数器输入以及比较器输入;PB包含了SPI
UART通过1k电阻直接和16u2相连。AREF为ADC模拟参考电压输入,IOREF直连5V为扩展板提供IO参考电压。SPI另外引出到一个6pin接口ICSP,用于AVRISP烧录,一般用于烧录空芯片、修复BootLoader功能
USB电路以及复位电路
USB部分以ATmega16u2为中心。16u2同样引出了专门的ISP接口可用于烧录固件,另外的4个引脚在ISP(ICSP1)接口旁边,一般不焊接排针。usb接口通过22欧电阻连接到16u2,16u2没有专门的复位按钮,只能通过ICSP1接口复位。16u2和328P一样使用了一个16MHz的晶振,封装不同。
16u2和328P之间有3条线连接,除UART之外(TX、RX指示灯使用16u2另外的GPIO),16u2的PD7还连接到了328P的复位电路部分,在通过usb下载程序时16u2会自动复位328P
通过观察可以发现,328P的复位引脚通过10k电阻上拉到5V,同时通过100nF电容连接到32u4的PD7,PD7通过1k电阻接地。
上电后,16u2的PD7输出低电平,328P的RESET脚为0V,之后10k电阻为100nF电容充电,#RESET失效,328P开始工作。下载时,16u2的PD7给出一个正脉冲即可实现328P的复位。此时电容C5两端都为5V而被放电。在正脉冲的下降沿后,328P的RESET引脚瞬间又被拉低到0V,328P复位,之后328P内部的BootLoader进入到ISP模式,烧录开始
Leonardo R3的pdf
Leonardo只有一片ATmega32u4,基本相当于拥有了16u2和328P的功能,下载程序原理和UNO完全不同,32u4的BootLoader可以直接从usb读取并更新代码,同时32u4可以编程变为任意的usb设备(UNO的16u2也可以,但是由于IO很多未引出所以利用价值不大)。另外32u4支持JTAG
电源
Leonardo的电源部分和UNO基本相同,不再赘述
GPIO
Leonardo除用于UART指示灯的IO之外引出了32u4其他所有的IO。除GPIO作用外,PF还包括了ADC输入以及JTAG功能;PD部分包含了4个外部中断,以及部分ADC输入,UART,I2C,计数器输入,PWM输出(OCxx),互补PWM输出(OC4x);PE包含了比较器输入,6号外部中断以及BootLoader的选择性执行(可通过熔丝位配置);PC包含了PWM输出,互补PWM输出以及AD输入0;PB包含了SPI,PWM输出,互补PWM输出,AD输入11,外部中断等
同样,Leonardo也将32u4的ISP单独引出,用于ISP烧录
USB电路以及复位电路
同样,usb接口通过22欧电阻连接到32u4的usb输入。而32u4在下载时的复位机制和UNO完全不同,在通过usb下载时32u4使用的是软件复位而不是通过硬件复位,所以在Windows下面开发会发现Leonardo会自动插拔。而经过观察发现,RESET在默认状态下只有通过一个10k电阻上拉到5V,复位只有通过按下按钮或将ISP端口的RST拉低
主要以ATmega328P/16U2/32u4为例,参考数据手册
AVR CPU核心结构如下
主要的存储设备包括通用寄存器,特殊寄存器,RAM,ROM,IO寄存器,熔丝位等
AVR使用了RISC设计以及哈佛结构,将数据(SRAM)以及指令存储区(Flash)分开并且独立编址。内部可以看作使用了简单的两级流水(预取指和执行),仅支持LOAD/STORE访存,有专门的IN/OUT指令可以用于IO内存区域的读写,并且使用了大量的通用寄存器,多达32个。这和8051中的32个寄存器不同,这32个寄存器都可以参加运算,而8051中基本的运算只能使用ACC和B寄存器。另外8051中的寄存器分为4组,同时只能使用一个寄存器组。而AVR的32个寄存器不分组,可以同时使用
AVR单片机系统结构如下
可以看到,SRAM和程序Flash使用不同的数据总线,各自单独编址
内部寄存器
8位版AVR拥有32个8位通用寄存器(另有AVR32,是32位机),编号从R0到R31,其中R27:R26组成X寄存器可以用于寻址,同理R29:R28组成Y寄存器,R31:R30组成Z寄存器
SREG状态寄存器:拥有I中断使能、T位操作寄存器、H半进位标记、S符号标记(N xor V)、V溢出标记、N负值标记、Z零值标记、C进位标记。发生中断时CPU不会自动保存和恢复现场,需要由中断服务软件实现
PC程序计数器:指令地址,长度和单片机Flash容量有关(328P有32k字节的Flash,PC为14位长(16k words)),用户不可直接访问。注意,PC每增加1,对应Flash中的2字节,而不是1字节,这和Flash的数据寻址不同,数据寻址以字节为单位
位于IO地址空间的寄存器
RAMPX RAMPY RAMPZ:分别用于和X寄存器R27:R26,Y寄存器R29:R28,Z寄存器R31:R30连接,用于支持64kB以上空间的间接寻址(SRAM或Flash)
RAMPD:用于和Z寄存器连接,支持64kB以上空间的直接寻址
EIND:用于和Z寄存器连接,用以支持长跳转指令
SP栈寄存器:堆栈指针,长度16位,有的只有8位(SPL)。堆栈自上向下增长,使用到堆栈操作的指令主要有
PUSH POP ICALL RCALL RET RETI
程序Flash
由于AVR指令长度为16或32,CPU使用PC取指时在程序Flash中也是2字节对齐的。Flash分为两个部分,Bootloader区和应用程序区,两个区域相对独立,有各自的熔丝控制位,以及不同的安全保护措施
Flash可以通过ELPM LPM SPM
指令进行字节数据访问,使用Z寄存器作为指针(地址寄存器),而ELPM
会添加上RAMPZ寄存器,可以访问大于64k的空间
在不同单片机中SPM
指令的执行流程可能不相同,Flash只能以Page为单位进行擦除,有些单片机可以以word(2字节)为单位写入,而另一些单片机不支持word为单位的写入,只能以Page为单位写入,这样的单片机中会有一个Page Buffer,需要将该Buffer写满以后一次性写入到一个Page。访问Flash时一般使用Z寄存器作为指针,而使用R1:R0作为数据寄存器
实际的使用中,由于Program Memory的擦写次数有限,一旦程序出现问题容易导致擦写寿命耗尽,所以建议在用户程序中不要出现写入操作,而使用EEPROM记录数据。写入指令一般只在Bootloader中出现
这些指令也可以用于熔丝位的访问
和STC的51单片机直接将Bootloader固件写死不同,AVR的Bootloader可以更改,Flash同样支持读时写技术(Read While Write)。Bootloader也是一段程序,如果复位后进入下载模式,CPU在读取执行同时对Flash本身数据单元进行擦除与烧写操作,甚至是Bootloader本身的数据。这样CPU理论上可以从任意接口更新Flash的数据,比如UART,USB,TWI等。而通过ICSP的下载方式由硬件实现,和Bootloader无关。ICSP高压编程(从RST输入12V)是解决单片机变砖的终极方法
SRAM
SRAM分为4个区间,分别为通用寄存器、IO寄存器、扩展IO寄存器、内部SRAM
区间 | 物理地址范围 | 解释 |
---|---|---|
通用寄存器 | 0x0000 ~ 0x001F | 32个通用寄存器R0 ~ R31 |
IO寄存器 | 0x0020 ~ 0x005F | 共计64个,可以使用IO指令IN/OUT直接访问,使用地址0x0000 ~ 0x003F,也可以使用普通访存指令访问,使用地址0x0020 ~ 0x005F。这片区域的低32字节(共256bit)同样支持8051一样的位寻址,位操作指令有SBI CBI SBIS SBIC |
扩展IO寄存器 | 0x0060 ~ 0x00FF | 共计160个,只能使用普通访存指令访问,使用地址0x0060 ~ 0x00FF |
内部SRAM | 0x0100 ~ | 只能使用普通访存指令访问 |
通用寄存器之间只能通过MOV MOVW
传送数据,分别为传送单个寄存器和传送两个寄存器(word)
整个SRAM数据区可以通过LD ST
指令进行字节数据访问,使用X Y Z寄存器作为指针,最多可以访问当前数据区64kB空间,如需要访问其他空间需要更改相应的RAMP寄存器
可以访问SRAM的还有堆栈指令
立即数装载指令LDI
只能将立即数装载到R16 ~ R31,例如LDI R30,0x3D
,而直接地址装载指令LDS
(长32位)可以将数据装载到所有的32个通用寄存器,例如LDS R3,0x0F33
EEPROM
EEPROM不可直接寻址,只能通过寄存器操作。EEPROM有数据寄存器,地址寄存器,控制寄存器三种寄存器。EEPROM擦写寿命大约是100000次
由于AVR是8位机,所以不能直接处理16位外设寄存器,16位寄存器的读写需要通过两次操作。为保证16位寄存器的所有位同时被读写,AVR在内部使用了16位的临时寄存器,该寄存器对用户不可见
AVR对16位寄存器高位和低位的读写顺序有明确要求
写操作:写16位寄存器时,需要先写高位,再写低位。高位被写入时被存到临时寄存器中,之后在写入低位同时高位和低位共2字节数据同时被自动写入到16位寄存器中
读操作:读16位寄存器时,需要先读低位,再读高位。读取低位时高位会在同一时刻被自动传输到临时寄存器中,只要再次读取高位即可
328P的引脚定义
32u4的引脚定义
16u2的引脚定义
不同AVR单片机的中断向量表也不同,因为不同单片机带有的外设模块不同
AVR中所有中断的优先级都是固定的不可更改的,优先级以中断向量表的地址为参考,地址越低的拥有越高的优先级,RESET复位中断拥有最高优先级
328P拥有2k字节的内部SRAM以及32k字节的Flash
16u2拥有512字节的内部SRAM以及16k字节的Flash
32u4拥有2.5k字节的内部SRAM以及32k字节的Flash
因为Flash分为Bootloader以及应用程序两个区域,可以通过熔丝位BOOTRST BOOTSZ以及MCUCR寄存器位IVSEL更改中断向量指令的实际位置,这在ATmega系列的单片机中通用,如下图,可以分别设置复位中断和其余中断的位置。BOOTRST用于设置RESET中断的位置,BOOTSZ用于设置Bootloader区域的大小。具体可以参考数据手册
其中IVSEL位需要在IO地址区寄存器MCUCR设置,地址为0x35(使用
IN OUT
)或0x55(使用LD ST
)。在对IVSEL进行更改之前,需要首先向IVCE写入1,此时会自动禁止中断,之后需要在4个时钟以内向IVSEL写值,同时需要将IVCE置0(写0xN2/0xN0)。如果超出4个时钟,IVCE会自动置0,写入失败
AVR的外部中断原理都是相通的,这里只集中说明。外部中断引脚分为两种,一种是普通的INT引脚,使用的是普通的INTx中断,一个引脚对应一个中断。另一种是PCINT(Pin Change)引脚,使用的是PCINTx中断,多个引脚共用一个中断
INT支持的4种中断形式有低电平中断,以及上升沿/下降沿/边沿触发中断,而PCINT仅仅支持引脚变化中断
注意这些引脚即便是配置成输出也会触发中断
328P | 16u2 | 32u4 | |
---|---|---|---|
PCINT | PCI0..PCI2 | PCI0,PCI1 | PCI0 |
INT | INT0,INT1 | INT0..INT7 | INT0..INT3,INT6 |
328P中一共有3个PCI中断,23个PCINT引脚,PCI0对应引脚PCINT(7..0),PCI1对应引脚PCINT(14..8),PCI2对应引脚PCINT(23..16)
16u2中有2个PCI中断,13个PCINT引脚,PCI0对应引脚PCINT(7..0),PCI1对应引脚PCINT(12..8)
32u4中有1个PCI中断,8个PCINT引脚,PCI0对应引脚PCINT(7..0)
PCINT中断中的每一位可以通过对应的PCMSK0/PCMSK1/PCMSK2寄存器进行配置
外部中断控制寄存器主要有EICRx,EIMSK,EIFR,PCICR,PCIFR,PCMSKx六种寄存器
EICRx寄存器
用于控制INTx外部中断的触发方式,有4种可用方式
328P只有一个EICRA,2个INT中断,其中ISC0x配置INT0中断,ISC1x配置INT1中断,定义如下,00代表低电平触发,01代表边沿触发,10代表下降沿触发,11代表上升沿触发
16u2拥有EICRA和EICRB两个寄存器,8个INT中断,配置定义和上表相同
32u4拥有EICRA和EICRB两个寄存器,5个INT中断,地址和16u2相同
EIMSK寄存器
用于设置INTx中断屏蔽位
将328P的EIMSK寄存器中的INT0或INT1设为1,并且使能状态寄存器中的全局中断位I,允许相应中断
16u2的EIMSK,对应8个中断
32u4的EIMSK,对应5个中断,寄存器地址同上
EIFR寄存器
用于指示触发中断的INTx引脚号
在INTx触发中断时,328P的EIFR中的相应位会置1,在跳转进入中断服务程序之后相应位会自动置0,也可以向该位写1(注意不是写0)软件清除该位。另外在低电平触发模式时这些位都不会置位
16u2的EIFR,8个中断
32u4的EIFR,5个中断,寄存器地址同上
PCICR寄存器
从这里开始是PCINT中断相关,和以上内容没有关联
一整组PCINT的中断屏蔽
向328P的PCICR寄存器相应位PCIEx设为1可以使能相应的PCI中断,单独的引脚屏蔽位在PCMSKx设置
16u2的PCICR,2个中断
32u4的PCICR,1个中断,寄存器地址同上
PCIFR寄存器
指示PCI中断号
328P中的PCIFR寄存器,中断触发时对应位置1,并且会在跳转至中断服务程序时自动清0,也可以写1软件清零
16u2中的PCIFR,2个中断
32u4中的PCIFR,1个中断,寄存器地址同上
PCMSKx寄存器
一组PCI中单独引脚的中断屏蔽
328P拥有3个寄存器PCMSK0,PCMSK1,PCMSK2,可以将相应的位置1使能对应引脚中断
16u2拥有2个寄存器PCMSK0,PCMSK1
32u4只拥有1个寄存器PCMSK0,寄存器地址同上
AVR的时钟系统相比8051单片机要复杂,带USB功能的型号(16u2和32u4)有PLL锁相环用以提供USB所需48Mhz的高频时钟
如上图,时钟系统以控制单元为核心,分5路输出到定时器,GPIO,ADC,CPU和RAM部分以及Flash和EEPROM部分,切断CPU时钟可以使CPU停止工作,而IO时钟提供给SPI,I2C,UART,定时器,外部中断使用(I2C的地址识别和时钟无关),定时器可以使用系统主时钟源也可以使用异步的外部时钟或低速的32.768khz外部振荡器(作为RTC使用)
系统时钟源可以选择外部时钟输入,外接晶振,低频外接晶振以及内部RC振荡器。而定时器和看门狗可以使用独立的振荡器
328P支持多种不同的主时钟源,可以通过熔丝位设置,用于配置时钟源的熔丝位都位于三个熔丝字节的最低一个字节(有关熔丝位的基本介绍参考1.15熔丝位),如下
其中,CKDIV8用于配置是否对输入的系统时钟进行8分频,置1不分频。
CKOUT用于配置是否在引脚PB0进行时钟输出。
而SUT指Startup Time,因为单片机的振荡器在开始工作的时候有一个逐渐稳定的过程,此时单片机还不能开始工作,所以需要设定一个在上电到复位信号失效的延时,通过SUT设置。SUT一般设为默认的最大延时即可,在使能BOD时需要改为对应配置,参考数据手册
补充:AVR单片机一般都支持Brown-Out-Detection(BOD)即掉电检测,为防止单片机的异常工作会及时进行复位。BOD的功能实际和SUT重复,所以熔丝位提供了相应的配置选项,需要在使能BOD时使用
328P一共可配置使用6种不同的主时钟源,通过熔丝位CKSELx配置时钟,出厂时默认0010,使用内部的RC振荡器(8.0Mhz),定义如下
选择External Clock外部时钟需要在XTAL1引脚输入时钟,并且时钟不能有超过2%的变化,如果需要调节频率要通过预分频器调节,SUT设为10
选择128k内部RC振荡器会使用单片机自带的低功耗RC振荡器,此时单片机引脚XTAL1和XTAL2可以当作GPIO使用。此种模式振荡器精准度较低,SUT设为10
选择内部RC振荡器会使用单片机自带的高速RC振荡器,频率为8Mhz左右,此时单片机引脚XTAL1和XTAL2可以当作GPIO使用,可以通过OSCCAL寄存器进行校准,是328P出厂默认设置,SUT设为10
选择低频晶体振荡器会使用到外接的晶振,一般为32.768khz,连接在TOSC1和TOSC2引脚(和XTAL1和XTAL2共用引脚),这种模式一般用在启动时震荡频率不敏感的场合。该种模式下CKSEL一般设为0101,SUT设为10
以下描述2种使用外部振荡器的常用配置
配置定义参考
配置1:是最常用的配置方式,选择Full Swing(满幅)全功率晶体振荡器同样需要使用到外部晶振,比如Arduino使用的是16Mhz的晶振,此时振荡放大器以满功率工作(XTAL2输出满幅),晶振连接在XTAL1和XTAL2引脚,适用于干扰较重的场合。该种模式下一般使用400k到20Mhz晶振,12到22pF电容,CKSEL设为0111,SUT设为11(不使用BOD)或01(使用BOD)
配置2:选择低功耗晶体振荡器晶振连接和常用的Full Swing模式类似,区别是此时振荡放大器不是满功率输出,较为省电,但是容易受干扰影响。该种模式需要根据振荡频率设定CKSEL,一般使用900k到16Mhz晶振,12到22pF电容。SUT设为11(不使用BOD)或01(使用BOD)。使用900k到3Mhz振荡器CKSEL3..0设为1011,3Mhz到8Mhz振荡器CKSEL3..0设为1101,8Mhz到16Mhz振荡器CKSEL3..0设为1111
OSCCAL寄存器
OSCCAL寄存器用于设置RC振荡器的校正系数
OSCCAL寄存器有一个出厂设定值,这个值在单片机复位时会自动写入到该寄存器。可以在运行时通过程序调节,大约在7.3Mhz到8.1Mhz之间调节,一般用不上
CLKPR寄存器
CLKPR寄存器用于设置时钟的预分频系数
分频系数定义如下
其中,CLKPCE位是更改使能,CLKPSx为分频系数设置。在复位时熔丝位CKDIV8的值会决定CLKPSx的初值,如果CKDIV8为0那么默认CLKPSx=0011(8分频)。设置分频的步骤如下
步骤1:向CLKPR写入0x80(即置CLKPE为1,其余位为0)
步骤2:在之后4个周期以内同时向寄存器写入0x0N(即设置CLKPS的值同时置CLKCE为0)
设置该寄存器时建议关闭中断防止过程被打断
16u2相比328P去掉了定时器的异步时钟源,并且由于没有ADC模块所以也没有ADC时钟输出。16u2有usb模块,所以需要使用PLL提供48Mhz的高频时钟。PLL的时钟虽然和系统时钟使用同一个时钟源,但是相对系统时钟独立,拥有自己的预分频配置。由于PLL始终将时钟乘以6,而USB的标准频率是48Mhz,所以PLL预分频之后需要提供一个8Mhz的时钟
和328P只能通过熔丝位配置时钟源不同,16u2支持使用软件通过寄存器配置时钟源
16u2也不支持异步时钟源
16u2熔丝位低字节位定义和328P完全相同,这里不再赘述
由于16u2不支持低速32.768khz的时钟源,所以CKSELx熔丝位的定义也稍有不同,0101 ~ 0100不再有用,其余有关系统主时钟源配置的定义和328P完全相同
出厂时16u2的CKSELx熔丝位默认配置为0010使用内部RC振荡器,SUT默认配置为10为最大,CKDIV默认配置为0使用8分频
这是16u2相比328P增加的部分
PLL的结构如下,相关配置位定义见寄存器配置
16u2除了拥有CLKPR以及OSCCAL寄存器以外,还添加了CLKSEL0,CLKSEL1(注意不是熔丝位CKSEL),CLKSTA,PLLCSR共4个寄存器
OSCCAL寄存器
和328P定义完全相同,略
CLKPR寄存器
略
CLKSEL0寄存器
和328P不同,16u2支持通过寄存器软件更改时钟和振荡器参数。CLKSEL0用于设置SUT参数以及振荡器的选择与开关,定义如下
其中,RCSUTx用于设置使用RC振荡器时的SUT参数,在启动时熔丝位SUTx会自动装载到此处。设置这两位没有任何作用所以维持默认值即可
EXSUTx寄存器同理,用于设置使用外部振荡器或晶振时的SUT参数,区别是更改该两位之后重启振荡器会生效(因为RC振荡器不可重启所以RCSUTx没有用)
RCE和EXTE分别作为RC振荡器和外部/晶体振荡器的开关,置1开启置0关闭
CLKS用于设置时钟源类型,置1使用外部/晶体振荡器,置0使用RC振荡器。该位会在复位后根据熔丝位的设置自动选择
CLKSEL1寄存器
该寄存器用于设置使用相应时钟(内部RC振荡器或外部时钟/晶体振荡器)时的CKSELx参数(启动时从熔丝位自动装载到相应位)
RCCKSELx用于设置在使用RC振荡器作为时钟源时的CKSEL。由于RC模式只能设置为0010,所以更改无效,维持默认值即可
EXCKSELx用于设置在使用外部/晶体振荡器作为时钟源时的CKSEL。区别是可以更改SUT参数,在clock switch之后生效
CLKSTA寄存器
该寄存器用于指示时钟源状态,1为开启0为关闭,定义如下
RCON指示RC振荡器状态,EXTON指示外部/晶体振荡器状态
PLLCSR寄存器
该寄存器专用于PLL配置,以及指示PLL状态,划重点
注意,Atmel官方文档对于16u2的PLL配置寄存器的描述存在失误,目前也没有找到Errata。截图如下
之后参考了Atmel Studio中相关的头文件,分别找到了16u2和32u4有关PLL设置的寄存器定义
// 16u2
#define PLLCSR _SFR_IO8(0x29)
#define PLOCK 0
#define PLLE 1
#define PLLP0 2
#define PLLP1 3
#define PLLP2 4
// 32u4
#define PLLCSR _SFR_IO8(0x29)
#define PLOCK 0
#define PLLE 1
#define PINDIV 4
#define PLLFRQ _SFR_IO8(0x32)
#define PDIV0 0
#define PDIV1 1
#define PDIV2 2
#define PDIV3 3
#define PLLTM0 4
#define PLLTM1 5
#define PLLUSB 6
#define PINMUX 7
Arduino软件维护者在代码中也提到了相关问题,参考github上代码的第697行开始,由此发现问题所在
#elif defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega8U2__)
// for the u2 Series the datasheet is confusing. On page 40 its called PINDIV and on page 290 its called PLLP0
#if F_CPU == 16000000UL
// Need 16 MHz xtal
PLLCSR |= (1 << PLLP0);
#elif F_CPU == 8000000UL
// Need 8 MHz xtal
PLLCSR &= ~(1 << PLLP0);
#endif
16u2和32u4的PLL部分不相同,32u4使用了2个寄存器控制PLL(多了PLLFRQ)
PLL电路框图也是错误的,实际16u2的时钟系统和AT90USB162是基本一样的
16u2文档中的错误图片,多出了很多不存在的信号
AT90USB162的PLL时钟控制架构
最后根据Arduino官方的代码,参考了AT90USB162的资料如下。这是16u2的PLLCSR寄存器的正确定义。该图中PLLPx的读写权限的标记应当是R/W
PLLE是PLL的开关,置1启动。注意如果使用的是RC振荡器,将PLLE置位时RC振荡器会自动启动
PLOCK是PLL锁定指示位,在PLL启动之后需要经过大约1ms到100ms该指示位才会置位,表示PLL输出和输入参考时钟已经锁定相位,此时PLL才可以使用
PLLPx是PLL的预分频参数,PLLP2..0为000时不分频,为001时使用2分频。换言之有两位设置是无效的
32u4的时钟系统和16u2的主要区别在于PLL部分,PLL既可以使用系统时钟也可以直接使用RC振荡器,另外由于32u4比16u2多了一个高速定时器,PLL除了给usb提供时钟以外还要为高速定时器提供时钟
32u4的时钟熔丝位配置和16u2又是不同的。和16u2相反,32u4相比328P没有振荡器的满幅模式,只有低功耗模式。32u4支持低频32.768khz的外部振荡器
32u4分为两种,最普通的32u4默认出厂熔丝设置为使用低功耗振荡器并且设置系统时钟8分频(CKSELx=1110,SUTx=01),而32u4RC默认出厂熔丝设置为使用RC振荡器
32u4支持4种时钟源,这些时钟源的熔丝位设置和328P完全相同,这里不再赘述
32u4的PLL和16u2的不同,支持将8Mhz时钟倍频后输出32 ~ 96Mhz的时钟,为usb以及高速定时器提供时钟。并且32u4的PLL模块有预分频也有输出分频,可以分别单独设置
32u4的PLL时钟模块使用了2个寄存器PLLCSR和PLLFRQ进行配置。PLL电路结构如下
OSCCAL寄存器
略
CLKPR寄存器
略
CLKSEL0寄存器
同16u2,以下同理,略
CLKSEL1寄存器
略
CLKSTA寄存器
略
PLLCSR寄存器
32u4的PLLCSR定义和16u2不同。这里的PLLCSR只有3个有效位,如下
PINDIV为PLL输入预分频,置0不分频,置1对输入时钟(振荡器等)进行2分频,需要在启动PLL之前进行设置
PLLE和PLOCK分别为PLL使能(启动)和PLL锁定指示位,同16u2
PLLFRQ寄存器
该寄存器用于设置PLL的输出分频,以及高速定时器的时钟源
PINMUX用于选择PLL的时钟输入,置0使用PLL专用预分频器(独立于系统时钟预分频器),置1直接使用内置8Mhz的RC振荡器,这样可以将usb和系统时钟分离开来,系统使用晶振而USB使用RC振荡器
PLLUSB用于设置usb的输入分频(相对PLL输出时钟),置0不分频(此时PLL应当输出48Mhz),置1二分频(此时PLL应当输出96Mhz)
PLLTMx用于设置高速定时器TCNT4的输入分频(相对PLL输出时钟),当PLLTM1..0为00时断开时钟,01时不分频,10时进行1.5分频,11时进行2分频
PDIVx用于设置PLL的时钟输出频率,见下
Atmel建议在正常5v供电时,将PDIV3..0设置为1010,输出96Mhz时钟,将PLLUSB置1进行2分频之后得到48Mhz的时钟
AVR的GPIO结构相比8051也要复杂很多,功能更加强大
一般的AVR都有多组GPIO,比如328P的GPIO有PORTB,PORTC,PORTD等。每一组IO最多可以包含8个IO,单个IO的结构如下。以下对于GPIO做一个详细的分析
在一般模式下面,一组GPIO有3个主要的控制寄存器,分别为DDRx(Data Direction Register,用于设置IO为输入或输出模式),PINx(Port Input,用于读取引脚输入),PORTx(GPIO输出高低电平)。以下没有特殊说明,寄存器名称都代指一组IO中的1位
和最传统的8051单片机相比,AVR的GPIO由于其更加复杂严谨的设计,并且可以设置输入输出模式,输出时类似强推挽输出,所以输出高电平和低电平的负载驱动能力是基本相近的(足以点亮LED),不像8051输出高电平基本没有驱动能力可以轻易被拉低,需要加一个Buffer
仔细观察之后可以大致将该电路分为5个部分,分别为上拉电阻,数据方向,输出数据,引脚读取以及休眠控制
首先从上拉电阻(左上角)开始分析。上拉电阻只有在GPIO数据寄存器PORTx为1,并且数据方向DDRx为0(作为输入,关闭Buffer)相应的上拉才会生效。另外,建议将不使用的引脚上拉
数据方向寄存器DDRx置1使能输出,置0禁止输出,此时如果没有上拉电阻那么引脚处于高阻态模式
输出数据寄存器PORTx可以直接置位,置1输出高电平置0低电平,也会受到PINx寄存器的影响,见下
引脚读取寄存器PINx一般是作为只读寄存器读取引脚状态使用,并且读取的是Buffer输出之后的电平状态,和PORTx寄存器的输出状态没有必然联系。也可以向PINx寄存器写1,可以翻转PORTx相应位,改变输出。另外,为了消除寄存器亚稳态的影响,输入端使用了两个寄存器(Synchronizer),注意这会带来0.5到1.5个IO时钟的延迟
休眠控制和休眠模式有关。休眠模式时SLEEP信号为高电平,此时施密特触发器输入端会被拉低到0,Buffer关闭,这样可以保证信号的确定性,降低功耗(对于CMOS电路来说中间电平是最消耗能源的)
最终可以得到各种配置模式定义如下
注意:由于从输入高阻态(PORTx为0,DDRx为0)到输出高电平(PORTx为1,DDRx为1)一定会经历一个中间状态01或10,在这两个状态中建议使用输入上拉(PORTx为1,DDRx为0)模式,此时如果有需要也可以在MCUCR临时禁用上拉。从01到10的转换同理,建议通过00中间状态跳转
另外GPIO会和单片机中的一些外设模块如SPI,I2C,UART,中断,ADC输入等复用,此时实际的GPIO电路会变成类似如下所示的结构(仅作为示意)
该种模式事实上和各外设的使能与否有关,相关的配置需要参考之后的各章节。由电路分析,其实就是在之前除引脚读取之外的4个部分(都和输出有关)添加了复用接口,如果使能了其他功能就会Override覆盖原始的普通GPIO功能。另外引出了单独的模拟输入输出以及数字输入接口
以下为328P的GPIO复用情况
PORTB
在PB7和PB6作为振荡器接口使用时,所有寄存器PORTx,PINx以及DDRx读取都为0
SPI接口的SCK在Master模式时为输出,Slave模式时为输入(此时上拉电阻依然可以配置)。在Slave模式时#SS配置为输入。同时SPI接口兼具ICSP下载功能
OC2A和OC1A OC1B分别为定时器2的比较输出A和定时器1的比较输出AB。ICP1为定时器1的捕获输入
CLKO用于输出系统时钟,通过熔丝位配置
PORTC
复位输入#RESET通过熔丝位RSTDISBL配置,此时PC6相应寄存器读取都为0
当TWCR的TWEN置位时,TWI使能,此时SDA以及SCL为开集输出无上拉电阻
ADCx为模数转换器输入,理想输入电阻无穷大。不同输入通道使用的电源不同(328P使用了两个电源,一个为数字电路部分供电,一个为模拟电路部分供电)。ADC4和ADC5使用数字电源,其余使用模拟电源
PORTD
AIN0和AIN1分别为比较器的正相以及反相输入,理论输入电阻无穷大
OC2B和OC0A OC0B分别为定时器2的分别为定时器2的比较输出B和定时器0的比较输出AB。T0和T1分别为定时器0和1的外部时钟输入
XCK为UART外部时钟输入,TXD和RXD分别为UART发送和接收端口。使能UART的发送和接收模块时会分别自动将这些端口配置为发送和接收
MCUCR寄存器
寄存器MCUCR中的PUD用于控制IO的上拉电阻
将PUD位置1可以禁用所有的上拉电阻
PORTB寄存器
DDRB寄存器
PINB寄存器
PORTC寄存器
DDRC寄存器
PINC寄存器
PORTD寄存器
DDRD寄存器
PIND寄存器
以下为16u2的GPIO复用情况
PORTB
PORTC
- dW是Debug Wire,在熔丝位DWEN使能以后启用。一般用不上
PORTD
- #CTS和#RTS分别为UART1的发送流控制信号以及接收流控制信号,XCK为UART1外部时钟
MCUCR寄存器
寄存器MCUCR中的PUD用于控制IO的上拉电阻
将PUD位置1可以禁用所有的上拉电阻
PORTB寄存器
DDRB寄存器
PINB寄存器
PORTC寄存器
DDRC寄存器
PINC寄存器
PORTD寄存器
DDRD寄存器
PIND寄存器
以下为32u4的GPIO复用情况,32u4有5组GPIO
PORTB
#RTS为UART的时钟接收流控制信号
OC.4B和#OC.4B分别为定时器T4的比较输出B,是一对互补信号
PORTC
OC.4A和#OC.4A分别为定时器T4的比较输出A,是一对互补信号
OC.3A为定时器T3的比较输出A
PORTD
OC.4D和#OC.4D分别为定时器T4的比较输出D,是一对互补信号
#CTS为UART的时钟发送流控制信号
PORTE
- 复位时将#HWB接地,可以允许在复位之后执行Bootloader(在非复位状态下可以使用正常功能),通过设置熔丝位HWBE使能
PORTF
- TDI、TDO、TMS以及TCK为JTAG引脚,使能JTAG时不能使用普通功能
MCUCR寄存器
同16u2,略
PORTB寄存器
同16u2,略
DDRB寄存器
略
PINB寄存器
略
PORTC寄存器
地址0x08(0x28)
DDRC寄存器
地址0x07(0x27)
PINC寄存器
地址0x06(0x26)
PORTD寄存器
略
DDRD寄存器
略
PIND寄存器
略
PORTE寄存器
地址0x0E(0x2E)
DDRE寄存器
地址0x0D(0x2D)
PINE寄存器
地址0x0C(0x2C)
PORTF寄存器
地址0x11(0x31)
DDRF寄存器
地址0x10(0x30)
PINF寄存器
地址0x0F(0x2F)
AVR中的定时器有多种用途,可以用于PWM输出,为UART提供时钟,外部计数,精准定时任务等
首先引入几个定义
TOP代指计数器达到最大值
BOTTOM代指计数器达到0
MAX代指计数器达到0xFF或0xFFFF
328P一共有3个通用定时器,分别为8位定时器TCNT0,16位带外部触发定时器TCNT1以及8位异步定时器TCNT2
定时器TCNT0拥有2个单独的比较寄存器(输出引脚分别为OC0A和OC0B),3个不同的中断源(TOV0,OC0A和OC0B),可以用于PWM输出以及频率脉冲输出,计数,定时等
使用TCNT0时需要将PRTIM0置零,见休眠模式
8位定时器的结构如下,其中标粗体的寄存器是可以使用CPU访问的
由上图分析,定时器1拥有TCNT0,OCR0A,OCR0B,TCCR0A以及TCCR0B一共5个主要的寄存器,另外有中断控制寄存器TIFR0和TIMSK0
其中,TCNT0寄存器为计数器,可以向上计数或向下计数,可以通过CPU强行置数,注意置数后一个计数周期内将会屏蔽比较输出(这样可以在计数器初始化时将TCNT0和OCR0x初始化为相同的值而不触发比较输出)。OCR0A可以作为比较寄存器A使用,也可以用于设置计数TOP值,OCR0B只能作为比较寄存器B使用。在PWM模式下OCR0x寄存器会启用双缓冲。
当比较器检测到TCNT0计数器数值和OCR0A以及OCR0B相同时,在下一个时钟比较标记会置位,可以通过软件写入1复位。此时如果使能中断就会产生中断,执行中断时标记位会自动硬件复位。另外输出引脚也是可以配置不同的输出模式的,如PWM,脉冲等,也可以通过强制置位改变比较器输出引脚的状态
TCCR0A和TCCR0B分别用于控制比较器输出A和B
定时器TCNT0有3个外部引脚,分别为T0时钟输入,OC0A和OC0B为比较输出。输出引脚必须在定时器初始化之前配置为输出,具体原因参考下图
定时器的寄存器配置会影响到IO,OC0A和OC0B为输出寄存器,而DDRx寄存器不受影响
工作模式
定时器TCNT0一共支持3种工作模式,分别为一般模式(Normal),高速PWM模式(Fast PWM),相校正PWM模式(Phase Correct PWM)
- 一般模式下定时器向上计数,当计数溢出之后自动从0开始重新计数,同时TOV0溢出标记置位(需要软件复位)。如果使能了TOV0中断,执行中断服务程序时标记位会自动清零。一般模式下还有一种CTC模式(相同时清零),定时器工作示意图如下,每一次触发比较都会使得OC0x置位。在当定时器TCNT0当前计数值大于想要的OCR0x比较值时,设置OCR0x后定时器会继续计数直到溢出,而并不会立即触发比较。另外,输出寄存器OC0x也可以配置为Toggle模式,也就是每触发一次比较就切换一下状态
- 高速PWM模式事实上为单坡(Single Slope)PWM模式,工作示意图如下(相比之后的相校正PWM定时器只递增计数,所以可以输出两倍频率的PWM信号)。定时器的最大计数值TOP可以是0xFF也可以是寄存器OCR0A。PWM方波信号输出端OC0A或OC0B(正相)在计数到达BOTTOM以后就置1,而在触发比较到计数到TOP的区间就会清0,这就是PWM的基本原理。还可以将输出设置为反相输出。比较寄存器可以是OCR0A或OCR0B,用于设置PWM的占空比,启用了双缓冲可以随时赋值,每次计数溢出才更新真正的OCR0x。注意,这种模式下计数溢出中断TOV0依然起作用(可以屏蔽),可以通过该中断设置下一次的比较值
- 相校正PWM(Phase Correct PWM)模式为双坡(Dual Slope)PWM模式,工作示意图如下。这种模式的特征在于占空比改变时相位不变(适用于电机控制),最大只能达到高速PWM一半的频率。同高速PWM,定时器的最大计数值TOP可以是0xFF也可以是寄存器OCR0A。定时器在递增计数到TOP时(停留1个时钟)就会自动开始递减计数。OC0A或OC0B输出端(正相)会在递增计数区间比较触发之后清0,在递减计数区间比较触发之后置1。可以通过寄存器设置反相输出,同理。比较寄存器可以是OCR0A或OCR0B,有双缓冲可以随时赋值,该种模式下每次计数到TOP会将缓冲中的数据存储到真正的OCR0x寄存器中。但是注意该种模式下溢出中断TOV0会在计数到BOTTOM(即0x00)时触发
可以注意到上图中OC0x第一次的状态翻转。比较寄存器OCR0x初始值等于TOP,在计数到TOP同时寄存器OCR0x被更改,此时发现输出状态发生了由高转低的翻转,而此时其实并未触发比较。这是一种特殊情况,为了保证这种相校正PWM的波形对称性
TCCR0A和TCCR0B寄存器
这两个寄存器是控制定时器1的主要配置寄存器
其中,WGM02:0用于设置定时器的工作模式(Waveform Generation Mode),各模式定义如下,观察可知计数模式无非就3种,每种模式可以使用2种不同的计数TOP(0xFF或OCR0A)
而COM0A1:0和COM0B1:0分别用于控制输出OC0A以及OC0B
Normal模式
OC0A的COM0A1:0定义如下,COM0B1:0的定义完全相同。置00可以断开定时器输出,使用引脚的原始功能;这种模式下可以生成占空比50%的方波,也可以用于连续/单次输出正/负脉冲或定时电平
高速PWM模式
该种模式下AB两个输出端的配置定义有所不同。当COM0x1:0为10时正相输出,为11时反相输出。而OC0A在COM0A1:0为01时,当WGM02为1(此时计数TOP值为OCR0A)时,可以输出占空比为50%的方波,基频为一般的PWM输出模式的1/2
相校正PWM模式
该种模式同理。当COM0x1:0为10时正相输出,为11时反相输出。而OC0A在COM0A1:0为01时,当WGM02为1时,可以输出占空比为50%的方波,基频和一般的相校正PWM模式相等
除以上配置位外,还有CS0x位用于设置定时器的预分频,定义如下
其中,可以将CS0x设置为000停止时钟,而正常的使用中,时钟一般取自IO时钟clkI/O的某个分频,或使用T0引脚作为时钟输入,这在外部计数中应用比较广泛
另外,FOC0x可以用于强制触发一次比较,一般的使用中没有什么用,只能在Normal模式下使用,在PWM模式下只能写入0。FOC0x的读取数值永远为0
TCNT0寄存器
该寄存器为定时器的最主要部分,可以直接读写。向该寄存器写入之后会导致下一次比较信号的失效
OCR0A和OCR0B寄存器
比较寄存器,作用已经在上文讲述,在PWM模式下有双缓冲
TIMSK0寄存器
定时器TCNT0有3个不同的中断,该寄存器是这3个中断的屏蔽位
当OCIE0x置1时就会使能TCNT0相应的A或B比较中断,比较触发时就会转到相应的中断向量地址。而当TOIE0置1时就会使能TCNT0的计数溢出中断
TIFR0寄存器
该寄存器为3个中断的指示位
相应位置1时指示相应中断的触发,在执行中断服务时会自动清零,在禁用中断时也可以通过使用软件写1的方式置0
16位定时器TCNT1拥有更长的计数器以及外部捕获引脚ICP1
使用TCNT1时需要将PRTIM1置零,见休眠模式
16位定时器的结构如下
通过观察发现,16位定时器相对于8位定时器的主要区别就是支持捕获,可以使用边沿触发,也可以使用电压比较器(见1.6ADC以及比较器)触发,其余基本相同。多出的ICR0为输入捕获寄存器,长度16位。另外图中省略了中断寄存器TIMSK1和TIFR1
而16位寄存器的读写操作在前文已经讲述过,写时先写高字节再写低字节,读时先读低字节再读高字节,并且访问16位寄存器时最好关闭中断。高字节的临时寄存器为TCNT1中所有寄存器共用
定时器的捕获电路部分如下
定时器所谓的捕获其实就是检测到触发信号时(外部输入的边沿触发或电压比较器触发,可以配置),立即将此时TCNT1中的值复制到寄存器ICR1中,此时如果使能了捕获中断也会触发中断ICF1,会在执行中断程序时自动清0,也可以使用软件写1清0。边沿触发捕获也可以通过将引脚软件置1实现软件触发
捕获寄存器ICR1只能在作为计数的TOP值寄存器时可以写入,其余状态下不可写入
注意在上图中还有输入降噪电路,在使能降噪时输入触发延时有4个时钟
另外在软件设计中应当注意:在定时器捕获中断触发以后,应该在中断程序中尽早读取ICR1的值
实际应用中,在使用定时器TCNT1测量脉冲的脉宽(Duty Cycle)时,需要在每次中断触发以后立即读取ICR1的值并更改捕获器的触发方式,此时捕获中断标记位ICF1只能通过软件写1清0。这个操作在使用SR04超声波传感器时比较关键。而如果在触发捕获中断以后不需要更改触发方式,ICF1会自动硬件清0
工作模式
16位寄存器的工作模式和8位寄存器基本相同,由于多了输入捕获功能,在寄存器的使用方面有所不同
一般模式的使用方式(包括CTC模式)和8位定时器完全相同,注意高低字节的读写顺序即可。CTC模式
高速PWM模式的计数TOP可以设置为固定的0x00FF,0x01FF或0x03FF,也可以使用OCR1A或ICR1作为计数TOP。这两个寄存器作为计数TOP值在写入值的操作方面稍有不同。由于OCR1A有双缓冲,所以可以在任意时间写入,TCNT1计数到本周期结束才会对真正的OCR1A进行数值更新。之前TCNT0的OCR0A寄存器同理在PWM模式下也有双缓冲。而ICR1没有双缓冲,所以写入的就是实际的寄存器,如果新值比当前的TCNT1小,定时器就会继续计数直到溢出,这在实际应用中容易导致问题
相校正PWM模式的计数TOP可以设定为3个固定值或使用寄存器OCR1A和ICF1。比较寄存器OCR1x的缓冲在计数到TOP时对实际的OCR1x进行赋值
频率与相校正PWM模式和相校正PWM的主要区别就是OCR1x寄存器被双缓冲Buffer更新的时机,比较寄存器OCR1x的缓冲在计数到BOTTOM时对实际的OCR1x进行赋值
TCCR1A,TCCR1B和TCCR1C寄存器
主要的配置寄存器,如下
各模式WGM1x的设置定义如下,一共15种配置可用,普通Normal模式(包括CTC)有3种,高速PWM模式有5种(5种不同的计数TOP),相校正PWM模式有5种,频率与相校正PWM模式有2种
COM1Ax和COM1Bx用于控制输出OC1A和OC1B
Normal模式
高速PWM模式
校正PWM模式
时钟选择CS1x的配置定义如下
ICNC1和ICES1是设置外部捕获的关键配置位。ICNC1用于配置降噪电路(Noise Canceler),置1使能降噪。ICES1用于配置上升或下降沿触发(Edge Select),置1上升沿触发,置0下降沿触发
FOC1x用于强制触发比较,这里不再赘述
TCNT1寄存器
寄存器长度16位,分为TCNT1H和TCNT1L高低位共两个寄存器
OCR1A和OCR1B寄存器
长度16位
ICR1寄存器
长度16位,每次触发捕获之后会将当前TCNT1的数字截取
TIMSK1寄存器
定时器TCNT1有4个不同的中断,该寄存器是中断的屏蔽位
相比定时器TCNT0多出了捕获中断屏蔽位ICIE1,置1使能捕获中断
TIFR1寄存器
中断指示,原理不再赘述
TCNT0和TCNT1定时器使用相同的分频器,而TCNT2异步定时器可以使用单独的异步分频器。这两个分频器都可以通过寄存器GTCCR进行复位
GTCCR寄存器
该寄存器可以用于对分频器进行复位,一般用于同步不同的计数器,使计数器可以同时开始计数或停止
一般先向TSM置1使能预分频复位设置,之后向PSRASY或PSRSYNC置1复位相应的预分频器,此时所有定时器停止工作,可以进行一些定时器的参数设定。向TSM置0时PSRASY和PSRSYNC会自动置0,同时定时器立即开始工作
8位异步定时器TCNT2和TCNT0 TCNT1不共用一个预分频器
TCNT2的电路结构和TCNT0完全相同,只是在时钟的选择方面还可以使用低频异步时钟(32.768khz),并且不支持外部时钟输入,具体电路这里不再赘述
工作模式
定时器TCNT2的3种工作模式和TCNT0完全相同,不再赘述
这里只讲述TCNT2的异步工作模式下需要注意的问题
建议CPU时钟至少是TCNT2异步模式计数频率的4倍
异步模式下向定时器TCNT2的寄存器TCNT2,OCR2x以及TCCR2x的写入操作要经过2个异步时钟之后才会真正写入,在这期间不能再次写入新值。这在使用TCNT2定时器作为休眠唤醒定时器时是需要重点关注的问题
时钟源在异步时钟以及系统时钟之间切换时,建议使用以下步骤对寄存器进行初始化
- 禁用TCNT2中断(指示位和屏蔽位全部清零)
- 通过AS2设置想要的时钟源
- 向TCNT2,OCR2x以及TCCR2x写入新值
- 如果是要转向异步模式,就要等待TCN2xUB,OCR2xUB以及TCR2xUB
- 清除中断指示位
- 如果需要可以启用中断
TCCR2A和TCCR2B寄存器
只有时钟选择位CS2x的定义和TCNT0有所不同,其余相同
时钟/预分频的选择位CS2x的定义如下
TCNT2寄存器
OCR2A和OCR2B寄存器
TIMSK2寄存器
TIFR2寄存器
ASSR寄存器
该寄存器是定时器TCNT2相对于TCNT0多出的配置寄存器,专用于异步时钟的配置
重要的配置位(可写)只有2位
EXCLK位置1可以将振荡器模式设置为外部时钟输入,此时需要从TOSC1引脚输入时钟。如果使用晶体振荡器就需要将该位置0。注意振荡器的设置需要在置位AS2之前进行
AS2置0时TCNT2使用内部IO时钟,置1使用独立异步时钟。注意状态切换时TCNT2的主要数据寄存器会混乱,需要按照之前所述步骤进行初始化
其余都是等待标志位(Update Busy),置1时分别用于指示当前TCNT2 OCR2A OCR2B TCCR2A TCCR2B寄存器正处于未更新完成的状态,不可再次写入
16u2一共有2个通用定时器,分别为8位定时器TCNT0和16位带外部触发定时器TCNT1。不支持异步时钟源
16u2的定时器TCNT0和328P的TCNT0完全相同,这里不再赘述
寄存器和328P也完全相同。只是在高速PWM模式下双缓冲寄存器OCR0x的更新时机不同(在TOP更新而不是BOTTOM),不过作用等价
工作模式寄存器WGM0x配置定义
16u2的16位定时器相比328P多了一个比较寄存器OCR1C,所以多了一个输出接口OC1C
电路框图如下
通过观察可以发现定时器TCNT1除了多了一个比较寄存器,其余和328P的完全相同。输出OC1C和OC1B拥有一样的功能(不支持占空比50%的输出)
TCCR1A,TCCR1B和TCCR1C寄存器
主要的配置寄存器,如下
工作模式配置位WGM1x的定义如下
通过观察可以发现16u2的定时器TCNT1和328P功能基本相同,同样只在高速PWM模式有细微区别(寄存器双缓冲)
TCNT1寄存器
长度16位,略
OCR1A,OCR1B和OCR1C寄存器
这里只给出OCR1C寄存器的地址
ICR1寄存器
输入捕获寄存器
TIMSK1寄存器
中断屏蔽
TIFR1寄存器
中断指示
因为16u2不支持异步时钟源,所以GTCCR寄存器只有两个有效位,在向PSRSYNC置1之前需要先将TSM置1
32u4的时钟系统较为复杂,一共有4个通用定时器,分别为8位定时器TCNT0,两个16位带外部触发定时器TCNT1和TCNT3,以及10位高速定时器TCNT4。另外32u4相比传统的AVR添加了定时器比较输出调制模块
32u4的定时器TCNT0和16u2的TCNT0完全相同,这里不再赘述
同16u2,略
16位定时器TCNT1和TCNT3结构和16u2的完全相同,略
这里主要记录定时器TCNT3的寄存器,定时器TCNT1的配置参考之前的16u2,这里不再赘述
TCCR3A,TCCR3B和TCCR3C寄存器
定时器TCNT3和TCNT1在强制比较触发方面有所不同,其余基本相同
地址0x90
地址0x91
地址0x92,注意TCCR3C只引出了OC3A输出引脚
TCNT3寄存器
地址0x94(低位L)0x95(高位H)
OCR3A,OCR3B和OCR3C寄存器
比较寄存器,地址0x98(OCR3AL)到0x9D(OCR3CH)
ICR3寄存器
地址0x96开始
TIMSK3寄存器
地址0x71
TIFR3寄存器
地址0x18(0x38)
10位高速定时器和之前的定时器都不相同,可以使用PLL作为时钟源,最高计数频率不超过64Mhz
电路框图如下
分析以上框图,TCNT4相比之前的定时器不支持捕获输入以及外部时钟,并且有OC4A,OC4B,OC4D以及对应的互补输出共6个输出(当使能OC4x输出时,这些IO的输出方向依然需要设置),分别对应比较寄存器OCR4A,OCR4B,OCR4D。而OCR4C寄存器没有对应的输出,也没有对应的中断,只作为计数TOP寄存器使用。注意这些寄存器长度为8位,而计数器TCNT4以及OCR4x的高2bit(增强模式下为3bit)可以通过TC4H读取(所有寄存器共用),访问方式和16位寄存器方法类似
TCNT4可以作为10bit定时器使用也可以作为8bit定时器使用,作为8bit定时器使用时通过TC4H将OCR4C高2bit全部清0即可
TCNT4可以使用高速异步时钟(在PLL中设置),和328P的异步时钟不是一个概念,这里的异步时钟是比系统时钟更快的时钟(PCK),也是由系统时钟倍频而来(多于2x)
另外,TCNT4的输出端还带有死区时间控制器(仅在PWM模式有效,且在PWM模式下无法禁用,只能设置延时为0)。由于32u4的TCNT4支持两个互补信号的输出所以可以用于逆变器或电机控制器中,驱动晶体管桥。
所谓PWM死区就是为了防止晶体管桥上下两个晶体管同时导通导致短路损坏,在上下管切换时加上的一段空闲时间,使得两个晶体管不会同时导通。
3个输出端口都支持死区控制,电路结构如下。死区生成器本质是一个4bit计数器,两个互补输出的死区时间可以分别设置(通过DT4寄存器配置)
定时器TCNT4相比之前的定时器还有增强模式(Enhanced Mode,将TCCR4E中的ENHC4置位)。所谓的增强模式寄存器定义如下
增强模式下,TC4H的最低3bit有效(一般模式是2bit),此时OCR4x寄存器的最低(LSB)位用于配置触发输出变化的时机,置0和一般模式相同,引脚在总时钟上升沿变化,置1在总时钟下降沿变化。相当于将PWM分辨率提高了一倍(提高到1/2时钟),PWM输出在总时钟上升沿或下降沿都可以变化
定时器TCNT4还有一个重要特性是异常保护(Fault Protection),可以在外部异常发生时(触发一个中断,可以是外部引脚中断INT0或模拟比较器输出)及时切断PWM引脚输出(其实就是将COM4xx全部清0断开连接)
将FPEN4置1可以使能异常保护功能。此时可以由INT0或模拟比较器触发一个异常保护事件,定时器TCNT4进入保护模式,将COM4x全部清0,同时FPEN4也会自动清0,比较器输出会被断开恢复原始功能(寄存器PORTx的输出)。如果将FPIE4置1使能中断,此时还会自动触发相应中断,FPEN4在此时也会自动清0
异常保护的触发源INT0和模拟比较器同样支持降噪,通过置位FPNC4使能,会增加触发的延迟(4个时钟周期)
工作模式
32u4的TCNT4定时器有4种基本工作模式,前3种原理类似不再赘述。该定时器还支持一种PWM6模式
所谓PWM6模式一般用于控制无刷电机,直接看下面的图片可能更好理解一点
在PWM6模式下所有6个输出端口的波形事实上都由比较器A生成,通过OCR4A设置比较值,OCR4C设置计数TOP。计数模式同样有单坡和双坡两种模式,单坡模式下计数到TOP置位TOV4中断,双坡模式下计数到BOTTOM置位TOV4中断。需要通过相应的OC4OEx寄存器设置比较器输出使能。如果在PWM6模式下没有使能,引脚恒定输出0
TCCR4A,TCCR4B,TCCR4C,TCCR4D和TCCR4E寄存器
定时器TCNT4共使用5个配置寄存器,如下
地址0xC0
COM4Ax和COM4Bx分别为输出端OC4A,OC4B比较器的输出模式配置
FOC4x为强制比较触发。PWM4x为对应输出端口的PWM模式使能
地址0xC1
PWM4x为PWM反相输出使能。PSR4为TCNT4预分频器的复位信号,置1复位。DTPS4x为PWM死区生成器的预分频(相对于TCNT4的输入时钟分频),如下图所示
而CS4x为定时器TCNT4的预分频选择,时钟源通过PLLTMx设定(见1.3.3.2 PLL时钟源),如下
地址0xC2
COM4AxS和COM4BxS都是TCCR4A寄存器中相应COM4xx寄存器位的影子寄存器(Shadow Register),相当于原寄存器,没有什么用,写入操作时注意
COM4Dx是输出端口OC4D的比较器输出模式配置。FOC4D强制比较触发。PWM4D为输出端口FOC4D的PWM模式使能
地址0xC3
寄存器TCCR4D主要用于配置异常保护模块(Fault Protection)
将FPEN4置1使能PWM模式下的异常保护功能,FPIE4置1使能异常保护中断(在触发异常保护事件之后产生的中断)。FPNC4置1使能降噪(会增加延迟)
FPES4用于设置引脚INT0的触发方式,置1使用上升沿触发置0使用下降沿触发
将FPAC4置1可以使能模拟比较器触发,否则模拟比较器和定时器TCNT4之间没有任何联系
FPF4是异常保护中断标志位,在允许中断时,触发保护时会置1,执行中断服务程序时会和FPEN4一起自动清0,也可以写1清0
WGM41和WGM40是基本工作模式设置,和各输出端口的PWM4x一起决定比较输出的模式,见下
地址0xC4
TLOCK4为定时器4的比较寄存器锁。在将TLOCK4置1时,向比较寄存器OCR4x写入的值不会马上生效,要将该位清0新值才会生效,一般用于同时更改多个比较寄存器
将ENHC4置1可以使能增强PWM模式
OC4OEx用于控制在PWM6模式下各引脚的输出与否,对应关系如下
综合以上分析,定时器TCNT4的基本输出配置和之前的有所不同,三个带互补信号输出端OC4A,OC4B和OC4D,通过WGM41,WGM42以及对应的PWM4x设置模式,如下所示。将PWM4x置1可以将对应的OC4x输出切换为PWM模式。所有的输出端使用只能相同的PWM模式
而输出端口COM4xx设置如下
PWM6模式
TCNT4寄存器
地址0xBE
TC4H寄存器
地址0xBF
这个寄存器是TCNT4实现10bit操作的关键,事实上TCNT4寄存器以及OCR4x寄存器长度都为10bit,当OCR4C高2bit为0时相当于8bit计数模式
OCR4A,OCR4B,OCR4C和OCR4D寄存器
地址0xCF
地址0xD0
地址0xD1
上图应为OCR4C,该寄存器和其他3个寄存器不同,一旦TCNT4和OCR4C相等就会将TCNT4清0,且OCR4C只有这个功能
地址0xD2
TIMSK4寄存器
地址0x72
中断屏蔽位,置1使能中断,OCIE4x分别为对应比较器的比较中断,TOIE4为计数溢出中断
TIFR4寄存器
地址0x39
DT4寄存器
该寄存器用于设置PWM输出的死区时间。在普通模式下无效
地址0xD4
DT4Hx为正相输出OC4x相比OCWxx上升沿死区延迟(下降沿不可调整),DT4Lx为其反相输出相比OCWxx下降沿死区延迟(上升沿不可调整),单位为时钟,可以设为0到15之间的值。示意图如下
32u4同样有同步时钟以及高速异步时钟预分频器的复位寄存器,使用方法同328P
GTCCR寄存器
地址0x23(0x43)
32u4的波形调制模块基于定时器TCNT0的OC0A输出以及定时器TCNT1的OC1C的输出。由于这两个输出共用引脚,在同时使能这两个比较器输出时会自动启用波形调制功能
输出波形的调制方法由PORTB7决定,此时IO输出方向依然由DDRB7决定。PORTB7为0时使用与门调制,为1时使用或门调制,如下
集成ADC是现代单片机常见的部件。只有328P和32u4有ADC,16u2只有模拟比较器
328P拥有一个精度为10bit的逐步逼近型ADC,输入通道有8个,与一般IO复用,建议信号源阻抗在10kΩ以下,可以工作在自动运行或单次转换模式,最高可以支持76.9kSPS的采样率(非满精度,保证满精度最高只有约15kSPS)
328P使用单独的AVcc模拟电源,和数字电源电压Vcc之差不能多于0.3V
注意,ADC拥有一个参考电压代表测量的最高电压,从AREF引脚输入,也可以设置为内部的1.1V参考电压或AVcc
使用ADC需要将PRADC清0,同时需要置位ADCSRA中的ADEN
ADC的电路框图如下所示
ADC转换得来的10bit数据存储在两个寄存器ADCH和ADCL中,可以设置为左对齐或右对齐。右对齐是最传统的对齐方式,必须依次读取ADCL和ADCH,原理和16位寄存器的操作相同。而左对齐常用于8bit精度够用的情况,这样只要使用1个CPU指令周期读取ADCH的内容即可
使用右对齐时,尽量连续读取ADCL以及ADCH保证操作的原子性。在读取了ADCL而还未读取ADCH时ADC无法将新转换的数据存入到寄存器,但是此时转换结束中断依然会触发,容易导致异常
单次转换模式可以向ADSC置1触发,转换结束以后ADC将数据写入到寄存器,ADSC自动清0,同时置位中断标志位ADIF
自动触发模式可以使用多种时钟源作为触发,需要将ADATE置1。使用指定信号源的上升沿触发
如果在一次ADC转换未完成时时钟源再次出现上升沿触发,该次触发会被忽略
注意,中断指示位即便是在禁用中断的情况下也会置位,这种情况下需要在下一次触发转换前将中断指示位清0才能进行下一次转换
ADC支持的自动转换触发电路如下所示
可以注意到在自动触发信号源中可以使用中断标志位ADIF作为触发信号。此时ADC工作在自由模式(Free Running Mode),在一次转换结束以后会自动进行下一次转换同时不断更新寄存器,在这种工作模式下ADC的工作不受ADIF影响,所以基本可以随时读取数据。这种模式一般在配置完相关寄存器以后需要将ADSC置1触发第一次转换(类似多米诺骨牌效应)
在使用触发信号源时,单次转换依然可以通过将ADSC置1实现。在运行中,一次转换是否完成也可以通过检查ADSC是否为1实现(和触发转换的方式无关)
ADC另外可以选择驱动时钟的预分频(逐次逼近型ADC都有一个驱动时钟),电路如下
输入时钟直接由系统时钟分频而来(和CPU时钟频率相同)。328P的官方文档建议,如果需要一个准确的10bit精度测量值,最好将时钟控制在50 ~ 200kHz。如果要求精度不是很高那么可以使用200kHz以上的时钟。分频可以通过ADPS3:0设置
由以上电路分析,一旦将ADEN置0,分频器就一直处于复位状态,ADC就永远不会工作
在ADC的转换时间方面,单次转换模式下ADC转换一次需要13个时钟,而第一次开始转换(使能ADEN)由于需要初始化模拟电路(多出了12个时钟用于采样与保持),所以需要25个时钟
而在自动触发模式下,每次触发事件发生以后还会加上多余的2个ADC时钟的延迟,之后ADC才会开始采样保持。另外最终的逻辑时序同步还需要加上多余的3个CPU时钟
单次转换模式下第一次的工作时序如下所示,在25个时钟后会自动置位ADIF,其余略
以下补充一些通道和参考电压选择方面的细节
通道以及参考电压选择通过ADMUX寄存器实现
ADMUX寄存器为单缓冲(和之前的双缓冲相同)设计,向ADMUX写入新值以后ADC会在合适的时间自动更改配置(在开始转换之前)。所以更改ADMUX的配置应该尽量避开这段时间(从ADSC置1到之后1个ADC时钟)
使用自动触发模式时,Atmel建议在以下时间写ADMUX
当ADATE和ADEN全部置0(禁用自动触发,禁用ADC)
在转换期间,在触发事件以后1个周期以后
一次转换完毕,在ADIF清0之后
无论如何,最简单的方法就是在每一次转换之后再选择想要使用的通道
而参考电压可以使用AREF引脚输入外部基准电压,AVcc或内部1.1V基准电源。Atmel建议在每次切换参考电压之后舍去第一次转换的值(一般误差较大)
另外ADC支持MCU休眠(Sleep)模式下的输入信号降噪功能,此时MCU所有的外设以及CPU核心都停止工作
降噪采样的使用步骤如下
使能ADC,同时需要确保ADC不处在转换过程中。需要使用单次触发模式,同时使能ADC中断
进入ADC降噪或Idle模式,ADC会自动在CPU休止后开始一次转换
在转换完成以后触发ADC中断,ADIF置1,CPU被唤醒。如果在此之前有其他的中断发生,CPU会首先唤醒执行此中断,而此时的ADC转换不受影响
也是ADC的特殊性,MCU进入其他睡眠模式时不会自动切断ADC电源,需要软件将ADEN清0,否则会导致MCU休眠状态下过度的耗电
328P还有一个内置的温度传感器,同样可以通过ADC读取测量(通过通道8,同时使用内置1.1V参考电压)
计算公式如下,k为固定系数,TOS为偏移,都是常量,可以校准以后存储在EEPROM中
相关寄存器
ADMUX寄存器
该寄存器用于设置VREF参考或输入通道
其中VREF参考设置REFSx定义如下,有3种可选的参考源
将ADLAR置1时ADC会将转换结果在ADCH和ADCL中左对齐存放
MUXx用于设置用于输入的模拟通道,定义如下
ADCSRA和ADCSRB寄存器
这两个寄存器是主要的ADC工作控制寄存器
ADEN是ADC的总开关,将ADEN置0会终止当前的转换
ADSC置1开始转换,同时用于当前ADC的转换状态
ADIE和ADIF分别为ADC的中断使能和中断标记。ADIE置1使能中断,ADIF在转换结束以后置1,在执行中断服务程序时自动清0,否则需要软件写1清0
ADATE置1使用自动触发模式
ADPS2:0用于设置ADC输入驱动时钟的预分频,定义如下
ACME和模拟比较器有关,见之后的内容
ADTS2:0用于设置自动触发信号源,定义如下
ADCL和ADCH寄存器
转换结果存储
DIDR0寄存器
该寄存器用于禁用对应ADC引脚的数字输入缓冲(Digital Input Buffer)。当使用对应输入通道时建议将对应ADCxD置1,降低IO功耗
模拟比较器的电路如下
在外部引脚方面,比较器可以使用AIN0作为同相输入端,使用AIN1作为反相输入端。同相输入端另外可以选择使用内部1.1V参考电压,而反相输入端可以使用ADC的任意输入通道。此时需要关闭ADC(要将ADEN置0),模拟输入通道是ADC和比较器互斥使用的
相关寄存器
ADSRB寄存器
关键在于ACME
将ACME位置1同时关闭ADC可以设置比较器的反相输入端
ACSR寄存器
ACD是模拟比较器的总开关(Disable),置1切断比较器的电源。注意更改该bit时需要禁用比较器中断
将ACBG置1,比较器同相输入端使用内部1.1V基准源,否则使用AIN0作为输入
ACO为比较器输出
ACIE为比较器中断使能,ACI为中断标志位,当比较器触发事件时会置1。清0过程略
ACIC和定时器TCNT1有关。之前提到过定时器1可以使用模拟比较器输出作为捕获触发信号,将ACIC置1可以将定时器TCNT1捕获触发设置为比较器
ACISx可以用于设置模拟比较器的中断触发方式,定义如下,有3种触发方式可用。注意更改该bit时需要禁用比较器中断
DIDR1寄存器
将AINxD置1可以禁用AIN1和AIN0对应IO数字输入缓冲
16u2没有ADC,其比较器正相输入端可以使用AIN0或内部基准电源,反相输入端可以使用引脚AIN1到AIN6一共6个输入端,如下
相关寄存器
ACSR寄存器
定义和328P完全相同,略
ACMUX寄存器
用于选择反相输入端通道
定义如下
DIDR1寄存器
一共控制7个通道输入端口
地址0x7F
32u4的ADC支持12个通道的输入,以及额外添加的差分放大器功能,可以用于差分测量。另外注意32u4的内部基准源为2.56V
ADC的电路框图如下所示
32u4的ADC除了添加了差分放大器以外其余部分的使用以及工作原理和328P相同,这里只对差分放大器模式做一些说明
使用差分放大器时,ADC的转换时间和普通转换模式相等,但是在使用差分放大器时开始转换的时机是对准时钟CKADC2的(由ADC输入时钟2分频而来)。在时钟CKADC2为低电平时转换可以即时触发而无延迟,转换耗费13个时钟;为高电平时会等待1个ADC时钟之后才会触发,需要耗费14个时钟。在**自动连续转换(Free Running)**模式下由于每次转换结束以后时钟都是高电平所以每次都需要14个时钟。
注意,在使用自动触发模式下如果使用了差分放大器,由于起始时钟的稳定性问题,需要在每一次转换结束以后开关一次ADC(将ADEN清0之后再置1),这样测量到的才是有效的值
另外,差分放大器的增益是可以调节的,通过ADMUX设置
差分放大器计算公式如下
相关寄存器
ADCSRA和ADCSRB寄存器
ADC的控制寄存器,定义和之前不同
地址0x7A
地址0x7B
ADCSRA寄存器中ADPS2:0用于设置分频,定义如下
32u4支持ADC高速模式,这种模式下ADC的转换速率会变快,通过将ADHSM置1启用
而ADTSx自动触发源的选择定义如下
ADCL和ADCH寄存器
地址0x78,0x79
ADMUX寄存器
地址0x7C
参考电压选择REFSx定义如下
ADLAR置1使用左对齐
可以通过MUX5:0选择输入通道以及差分放大器的增益,MUX5在ADCSRB寄存器中,定义如下
DIDR0和DIDR2寄存器
地址0x7E
地址0x7D
置1禁用对应模拟通道引脚的数字输入缓冲
32u4的模拟比较器同样和ADC互斥使用。32u4的比较器的同相输入端可以使用内部基准电压源或AIN0,反相输入端可以使用基准电压源或ADC的输入
相关寄存器
ADCSRB寄存器
将ACME置1同时关闭ADC(将ADEN置0)可以选择比较器反相输入端的信号源
ACSR寄存器
定义和328P相同。触发信号选择ACISx定义如下
DIDR1寄存器
只有1位有效,控制AIN0的数字输入缓冲
UART是最通用的串行通信接口之一
328P、16u2和32u4的UART模块基本完全相同,16u2和32u4相比328P只是多出了RTS和CTS流控制,这里不再分开讲解
使用USART时需要将PRUSART0置0使能
和8051一样,AVR的UART同样支持同步模式,所以称为USART。区别是AVR还多出了标准的SPI模式,并且在同步以及标准SPI模式下使用了单独的时钟引脚XCK(和PD4共用),有独立的发送接收端口
USART模块包含了一个时钟发生器,可以工作在4种模式下,有一般异步模式,倍频异步模式,主机同步模式以及从机同步模式。通过UMSEL0位选择同步或异步模式,通过U2X0选择是否使用倍频模式
在同步模式下通过引脚XCK0的方向设定DDR_XCK0设定主从模式(使用外部时钟还是内部时钟),在该模式下最好将U2X0置0
以上为时钟电路。时钟电路的基频通过波特率发生器生成,可以通过UBRR0配置。可以发现使用异步模式时(UMSEL0为0),发送模块的时钟可以使用发生器输出的16分频(U2X0为0)或8分频,而接收器直接使用了发生器的输出作为基频。
而在同步模式时(UMSEL0为1),主机模式下(DDR_XCK0为1)整个串口系统(发送器,接收器,XCK引脚)都是使用了波特率发生器的2分频。而在从机模式下,需要从XCK引脚输入时钟。注意在同步模式下输入时钟最大不能超过系统时钟的1/4。另外,同步模式的数据采样模式可以通过UCPOL0配置,置1使用XCK时钟的下降沿发送、上升沿采样,置0反之。主机以及从机一般使用相同的配置,同时收发
波特率发生器实质是一个递减计数器,使用系统时钟源。每次计数器计数到0时会产生一个脉冲,同时立即将UBRR0中的值载入。这个脉冲就是USART的基本时钟
AVR的UART支持多种帧格式,排列组合可以有30种,如下
AVR的UART帧有1个固定的起始位
之后的数据位可以配置为5-9bit长
数据位之后的奇偶校验位可以配置为无奇偶校验,以及奇校验(连带校验位奇数个1)或偶校验(连带校验位偶数个1)
最后的结束位长度可以为1或2
建议在初始化时禁用UART中断或全局中断
初始化步骤如下
禁用中断
通过UBRR0设置波特率
使能发送器以及接收器
设置帧格式
数据发送模块可以通过使能TXEN开启,此时TXD所在引脚会切换到串口输出模式。如果使用的是同步模式那么此时XCK引脚也会覆盖原来的功能
数据发送是一个自动的过程。只要向收发寄存器UDR0写入新值发送器就会自动发送一次。如果发送完发现没有新值,就会进入到空闲模式,不再发送。如果发送完发现有新值,就会立即进行下一帧的发送
注意,发送长度为9的帧时,需要在向UDR0写低8bit之前,将最高的第9位写入TXB8
发送中断
每一次发送开始之后数据从UDR0转移到发送的移位寄存器中,UDRE0会自动置1,代表可以向UDR0写新值。每一次发送结束以后TXC0会自动置1,代表一次发送完成。这两个位都可以用于生成中断,分别通过UDRIE0以及TXCIE0使能。Atmel建议每一次写UCSR0A时都将UDRE0清零
在使用UDRE0中断时,需要在中断服务程序中向UDR0写新值(此时UDRE0自动清零)或将UDRIE0清零禁用中断,否则会循环触发中断
而TXC0触发的中断在执行中断服务程序时会自动将TXC0置0,用户一般无需操作(可以写1置0)
数据接收同理,可以通过将RXEN0置1使能数据接收
接收器模块一旦接收到一个起始位,就触发一次接收,直到接收到结束位,代表接收完毕,此时RXC0置1。从第二个结束位开始都会被接收器忽略
接收器有一个长度为2字节的缓冲,如果缓冲已满,那么之后的数据到来后就会丢失。缓冲区可以通过连续的读取UDR0进行清除(flush,直到RXC0变为0)
接收长度为9的帧时,需要首先在RXB80读取第9位,之后再读取UDR0
另外,接收器还有3个纠错指示位,分别为帧错误FE0,数据丢失DOR0以及奇偶校验错误UPE0。和RXB80一样,这些位也需要在UDR0之前读取
FE0为1时表示当前接收到的帧结束位有错误(为0),可以用于侦测不同步的情况。Atmel建议写入寄存器时将该位置0
DOR0表示当前已经有数据丢失,在之后顺利转移读取之后会清0
UPE0为1时表示本次接收到的帧奇偶校验错误(此时使能奇偶校验功能)
接收中断
接收器有1个中断RXC0,可以通过RXCIE0使能,表示当前UDR0中还有未取走的数据,读取UDR0后会自动清0
异步接收时钟调节
UART模块会自动根据输入对UART的时钟进行调节,使得时钟同步,接收器事实上会对传输过来的1bit数据进行3次采样并舍入,和8051相同
多机通信
使用多机通信需要涉及到地址的使用。AVR的多机通信原理和8051基本相似,需要将从机的MPCM0置1用于地址帧以及数据帧的区分过滤
当UART的帧长度设置为5-8bit时,UART使用第一个结束位区分当前是数据帧(0)还是地址帧(1)。当工作在帧长度为9bit时UART使用最后第9位区分当前是数据还是地址帧。如果是数据帧那么会被使能MPCM0的从机忽视,如果是地址帧就会被所有从机接收。建议在多机通信模式中使用9位长度的帧,这也能和8051等其他设备的UART兼容
主机发送的帧类型都需要通过软件设定
UDR0寄存器
该寄存器为存储发送以及接收数据的主寄存器
328P地址0xC6,16u2地址0xCE,32u4地址0xCE(32u4中为UDR1,以下所有寄存器同理)
发送和接收使用两个物理上独立的寄存器,使用同一个地址,通过读写操作区分
UCSR0A寄存器
该寄存器为UART的主要控制寄存器,注意每次读取UCSR0A都要在读取UDR0之前,因为读取UDR0会改变UCSR0A中的值
328P地址0xC0,16u2地址0xC8,32u4地址0xC8
RXC0为接收结束标志位,置1表示当前接收缓冲(2字节)有未读取的数据,在读取后会自动清0,可以屏蔽该中断
TXC0为发送结束标志位,置1表示当前发送移位寄存器中的数据已经全部输出并且UDR0中不再有新值,在执行中断服务时会自动清0,可以屏蔽该中断
UDRE0为发送寄存器空闲标志,置1表示当前发送寄存器UDR0可以接收新数据,在向UDR0写新值时会自动清0,可以屏蔽该中断
FE0为帧错误标志位,结束位出错,不能触发中断。向UCSR0A写值时需要对该位写0
DOR0为数据丢失标志位,一般在接收缓冲满时再次接收新数据触发,不能触发中断。向UCSR0A写值时需要对该位写0
UPE0为奇偶校验错误标志位,不能触发中断。向UCSR0A写值时需要对该位写0
U2X0为异步模式下发送模块的分频,置0使用波特率定时器溢出频率的16分频,置1使用8分频
MPCM0用于异步多机通信,置1可以使从机忽略数据帧,只接收地址帧
UCSR0B寄存器
328P地址0xC1,16u2地址0xC9,32u4地址0xC9
RXCIE0,TXCIE0以及UDRIE0分别为之前RXC0,TXC0以及UDRE0中断的屏蔽位,置1使能中断
RXEN0以及TXEN0分别为接收模块以及发送模块的使能总开关
RXB80以及TXB80分别为9bit帧长模式下接收数据的第9位以及发送数据的第9位,都需要在UDR0之前读或写
UCSZ02见下
UCSR0C寄存器
该寄存器为UART的主要状态控制寄存器
328P地址0xC2,16u2地址0xCA,32u4地址0xCA
UMSEL0x用于设置基本的工作模式,如下,00为一般的异步模式,01为同步模式,11为标准SPI模式
UPM0x用于设置奇偶校验,定义如下,00禁用奇偶校验,10使用偶校验,11使用奇校验
USBS0用于设置结束位位数,置0使用1位结束位,置1使用2位校验位
UCSZ0x用于设置一帧数据的位数,定义如下,可以设置为5到9位
UCPOL0用于控制同步模式下数据的发送以及接收相对时钟的关系。异步模式下需要置0
UCSR0D寄存器
16u2地址0xCB,32u4地址0xCB
该寄存器只在16u2以及32u4有,用于流控制使能RTS以及CTS引脚
分别用于使能CTS以及RTS引脚,在#CTS输入为0时表示本机可以发送,在RTS输出有效时通知接收方本机可以接收
UBRR0H和UBRR0L寄存器
波特率时钟发生器加载值
328P地址0xC4,16u2地址0xCC,32u4地址0xCC
这种模式被称为MSPIM模式,并且只支持SPI主模式,所以该模式下时钟输出引脚DDR_XCK0需要配置为1(输出)才能正常使用
另外这种模式下的时钟为固定的波特率发生器频率的1/2
在这种模式下,UART的发送以及接收控制逻辑都已经被禁用,但是在寄存器中需要使能TXEN0,而RXEN0可以选择性开启。3个中断依然可用,但是错误校验功能在该模式下不再有用,错误校验位永远为0
可以配置4种SPI模式如下。其中Sample指的是输入采样,Setup指的是输出改变。而Leading Edge以及Trailing Edge指的是传输开始以及结束时的边沿类型
UDR0寄存器
略
UCSR0A寄存器
中断位,略
328P地址0xC0
UCSR0B寄存器
略
328P地址0xC1
UCSR0C寄存器
328P地址0xC2
将UMSEL0x都设为1
UDORD0用于设置数据输出顺序,置0首先输出MSB,置1首先输出LSB
UCPHA0以及UCPOL0见前
UBRR0H和UBRR0L寄存器
略
SPI是一种非常简单的同步串行通信协议,是一种业界标准。AVR中的SPI只有两个状态控制寄存器以及一个数据寄存器
SPI拥有一个同步时钟。两个数据IO接口分别为MOSI(主机输出从机输入)以及MISO(主机输入从机输出)。单片机的SPI工作在主机模式(Master)时,MOSI配置为输出端口而MISO配置为输入端口,从机模式(Slave)时相反,MOSI配置为输入端口而MISO配置为输出端口。主机的MOSI连接从机的MOSI,MISO同理。时钟由主机输出
#SS信号为SPI的从机选择信号,低电平有效,在主机模式时为输出,在从机模式时为输入。主机模式下#SS需要使用软件控制输出高低电平,而在从机模式下#SS作为输入会自动控制从机的数据传输
SPI在主机模式以及从机模式下的工作流程如下
主机模式下,想要发送一帧数据之前,可以使用软件使能#SS(如果需要使用从机选择功能的话),之后向发送数据寄存器SPDR写入想要发送的数据,发送器会自动输出时钟并开始发送数据。在输出一个字节以后,输出时钟自动中断同时将发送结束标记位SPIF置1。如果此时使能了传输结束中断SPIE那么此时会触发一次中断。之后可以继续向发送寄存器写入数据进行发送或将#SS置为无效结束传输,此时发送的最后一字节数据依然会留存在发送寄存器中
从机模式下,如果#SS输入无效,那么从机的MISO输出会保持为高阻态,此时就可以向发送寄存器SPDR写入想要发送的值,在主机使能#SS同时向从机发送时钟,从机的SPDR中的数据就会在主机时钟的控制下依次输出。和主机模式相同,如果将SPIE置1,SPIF在传输完一帧数据以后也会置1并产生中断
SPI拥有1字节的发送缓冲,以及2字节的接收缓冲,所以接收数据时需要在下一字节完全进入之前取走当前字节
主机模式
主机模式下的初始化需要使用软件将MOSI以及SCK引脚置为输出模式,#SS看情况而定,而MISO已经自动置为输入模式无需配置。之后可以使能SPI并设置SPI的工作模式
注意,在主机模式下如果#SS被设置为输入(不输出),该引脚需要上拉保证发送的正常工作。如果该引脚被拉低,主机会认为是另一个主机在向从机发送数据从而变为从机模式,同时也会触发SPIF中断(使能SPIE时)
从机模式
从机模式下的初始化可以使用软件将MISO引脚置为输出模式,其余引脚#SS,MOSI,SCK已经自动置为输入模式
在从机模式下使用#SS引脚有个优势,就是方便数据包同步。在#SS无效时从机的控制逻辑会自动进行复位,同时丢弃接收缓冲中的不完整数据
SPCR寄存器
地址0x2C(0x4C)
SPIE为SPIF中断使能,置0屏蔽,见下
SPE为SPI使能总开关,置1使能
DORD为SPI数据帧发送顺序设定。置0先发送最高位MSB,置1先发送最低位LSB
MSTR为主机模式选择,置1使用主机模式。在主机模式下将#SS拉低(此时配置为输入)会导致该位清0从而进入从机模式
CPOL以及CPHA用于配置收发数据的时序,定义见下图
SPR1以及SPR0和SPSR中的SPI2X一起可以用于设置SPI工作时钟的分频,定义如下
SPSR寄存器
地址0x2D(0x4D)
SPIF为中断标志位,也是SPI唯一的中断,在一次传输结束以后会置1,执行中断服务程序时会自动清0。手动清0需要首先读取一次该寄存器,之后读或写数据寄存器SPDR
WCOL为写冲突标记。如果数据寄存器SPDR在一次数据传输时被写入,该位就会置1。手动清0方法同上,该位不会产生中断
SPDR寄存器
地址0x2E(0x4E)
发送以及接收数据寄存器,主机模式下向该寄存器写入数据会自动启动一次发送
I2C为原荷兰飞利浦(半导体部门独立后成为恩智浦半导体NXP)的专利。为规避风险,Atmel/Microchip等其他半导体厂商将I2C称为TWI,和I2C兼容。而NXP自家产品的文档都使用I2C
I2C为半双工类型总线,相比SPI等其他串口通信协议要复杂,优点是信号线少,同时使用地址进行从机的区分,可以只使用两条信号线支持最多128台从机的通信。主机以及从机通过同一条总线连接,两条信号线一条为数据(SDA),另一条为时钟(SCL),同时这两条信号线都要有上拉电阻(此时IO自动配置为开漏输出),示意图如下
TWI的特性就是,无论有几个设备将数据线拉低,数据线总是呈现低电平状态。换句话说,只有所有设备都输出1(晶体管不导通),总线才会呈现高电平的状态,这也是TWI的与属性。使用开漏输出加上拉电阻的优势就是永远不会短路,极大方便了多机通信
TWI可以说是AVR中最复杂的通信协议之一。其本质还是基于状态机的设计,寄存器TWSR中就是该状态机的状态指示
另外,16u2不具有TWI模块,而328P和32u4的TWI完全一致,所以不再分开讲解
这里首先引入TWI通信中的几个基本概念:
主机(Master):产生SCL时钟的设备。所有传输的启动以及结束也是由主机管理。一条总线上可以有多台主机
从机(Slave):主机发送一个地址并与其通信的设备
发送者(Transmitter):正在向总线传输数据的设备
接收者(Receiver):正在从总线读取数据的设备
TWI可以算是一种基于中断的通信协议,电路结构如下
TWI的引脚SCL以及SDA作为输出使用时,都为开漏输出,同时设有有压摆限制。作为输入使用时都设有毛刺消除。另外,在AVR中这两个引脚可以使用对应GPIO的内置上拉电阻,这样可以省去外置的上拉电阻
主机TWI的传输速率(时钟SCL)由比特率生成器以及预分频决定,分别通过TWBR以及TWSR寄存器设置。从机模式下该设置无效。计算公式如下
有关数据传输部分,数据以及地址通过TWDR寄存器进行收发。另外传输时的ACK可以在接收模式下可以通过TWCR寄存器设置,而在发送模式下可以通过TWSR寄存器读取
有关传输控制部分,开始/结束的侦测功能由硬件电路实现。在电路侦测到一个开始或结束事件时可以触发中断,可以用于休眠状态的唤醒
在主机模式下TWI还支持在发送模式下的仲裁/冲突检测。如果当前主机失去当前仲裁的总线控制权,会转到执行相应处理代码
工作在从机模式时,本机地址通过TWAR设定(1-127)。另外TWAR中还有一位TWGCE,置1可以使能广播地址的响应(接收到广播地址0x00以后也会产生中断做出响应)。另外还可以通过TWCR设置从机是发送ACK还是NACK
TWI工作模式的控制主要由TWCR负责,中断标志位为TWINT。另外注意状态寄存器TWSR只有在TWINT为1时才会包含有效数据,与此同时时钟输出SCL输出低电平
触发中断的情况有以下几种
作为主机发送了一个开始信号(START OR REPEATED START)
作为主机发送了从机地址帧以后
作为主机失去总线控制权
作为从机被主机选中(私有地址或广播地址)
作为接收方接收到一个数据帧
作为从机接收到重复开始以及结束信号(REPEATED START OR STOP)
接收到非法起始/结束信号
TWI要求在传输数据时,数据SDA在时钟SCL为高电平的时候始终保持稳定,如下
如果SDA在SCL为高电平时发生了翻转(一般由主机发出),就代表一个传输开始或传输结束信号,如下
在SCL为高电平时如果SDA出现一个下降沿,那么就代表一次传输的开始。一个传输过程中可以有多次开始,多次传输(称为重复开始),但是一旦出现结束信号(SCL高电平时SDA出现一个上升沿)那么就代表当前传输过程的结束,总线被释放。在此之前认为总线处于忙(busy)状态
TWI的数据包格式如下,分为地址包以及数据包两种
地址包
地址包长度为9位,包括7位地址位,1位读写位,1位ACK。读写位为0代表写操作,为1代表读操作。起始以及结束信号永远由主机发出(主机永远控制整个传输过程)。主机选中的从机在发现自己被选中后如果没有异常情况,需要在第9位ACK将数据线拉低表示可以响应请求。如果当前从机无法响应,应当保持SDA为高电平,之后主机可以发送结束信号或再次开始一次传输。
地址帧发送时首先发送MSB,最后发送LSB。0-127的从机地址中,0x00为广播地址(所有从机都要响应,并同时在ACK将SDA拉低,一般用于所有从机接收相同数据,所以主机一般将读写位置0)
TWI规范建议从机地址取0x01到0x77之间的地址
数据包
数据包长度为9位,包括8位数据位,1位ACK。数据包发送方(可以是主机或从机)随主机时钟开始发送数据。接收方(可以是从机或主机)需要在第9个时钟发送ACK(将SDA拉低)。如果接收方不想再接收数据,发送NACK即可(保持SDA为高电平,一般在接收方接收完最后一字节数据以后)
数据帧同样采用首先发送MSB的方式
一个传输过程如下
一个地址帧之后可以跟多少数据帧由具体的通信协议决定,需要由软件实现
另外,如上图,从机还可以主动将SCL拉低延长时间用于处理数据,准备继续传输。而SCL高电平时间固定,只由主机决定
多主机通信系统
TWI的设计本身就为多主机的通信提供了可能
多主机通信中涉及到一个仲裁的过程。这个仲裁的过程需要决定本次传输中唯一的一个主机(TWI通信中同时刻只允许一台主机运行)。其余未竞争得总线控制权的主机可以进入从机模式。注意这个仲裁的过程不能影响到从机而导致从机数据的损失。所有仲裁机制都由软件实现
另外,本仲裁过程也需要解决不同主机之间TWI通信时钟不统一的问题,在此之后才能开始仲裁过程
由于TWI总线的电路结构特性,在多主机系统中SCL的占空比取决于输出SCL占空比最小的那台主机。所有主机都可以同时监视SCL,调节自己的输出
仲裁的基本原理也是依赖于TWI总线的电路特性,主机可以在输出数据同时监视SDA引脚的状态。仲裁时,所有想要通信的主机各自输出不同的数据帧。如果一台主机发送数据同时发现接收到的SDA数据和自己发送的数据不同,那么就代表该主机已经失去本次使用总线的资格,主机需要切换到从机模式,不再参与仲裁。一次仲裁可能需要多次迭代。并且仲裁用的数据帧也是需要特别设计的,不能是任意的数据帧(否则大概率会导致一次仲裁后所有主机都变为从机的情况)。巧妙设计的仲裁数据帧甚至可以实现具有优先级的通信,比如0x07,0x0F,0x1F,优先级依次降低
仲裁中时钟同步的大致流程如下
一个典型的工作流程如下
首先是发送一个起始信号。起始信号的发送需要通过写TWCR触发,注意写入该寄存器时需要将TWINT对应位置1(会将TWINT置0),这样才会触发起始信号的发送
发送完起始信号,TWCR中的TWINT会重新置1触发中断,此时状态寄存器TWSR已经被更新,指示当前状态
检查状态标志位TWSR是否是想要的值,如果异常就要处理。如果检查正常,接下来就可以向TWDR写入从机地址帧。之后向控制寄存器TWCR写新值触发该地址帧的发送,同理此时需要向TWINT对应位写1清除中断位才能成功触发
地址帧发送完毕,此时TWINT再次置1触发中断,同时TWSR状态被更新
检查状态标志位TWSR,看是否成功发送地址帧,以及接收到的ACK。如果没有问题,可以继续向TWDR写想要发送的数据,之后写TWCR(TWINT写1)触发一次数据传输
数据帧传输结束,此时TWINT再次置1触发中断,同时可以在TWSR读取当前状态(检查ACK)
最后整个传输过程结束,可以写TWCR触发传输结束信号(TWINT写1)
可以发现,TWI每一步工作的共同特点是,每一小步结束以后都会触发中断,之后CPU需要首先检查状态寄存器TWSR的值,之后向数据寄存器TWDR以及控制寄存器TWCR写入新值(同时向TWINT写1清除该标记位),以进行下一步的传输
TWI有4种工作模式,分别为主机发送(MT),主机接收(MR),从机发送(ST),从机接收(SR)
TWBR寄存器
比特率寄存器,见之前公式
TWCR寄存器
主要的控制寄存器
TWEN为TWI总开关,置1使能TWI,同时对应SDA以及SCL引脚会取代原IO功能
TWINT为中断标志位。如果启用全局中断同时TWIE中断使能为1,那么在TWINT置1时会自动触发一次中断
TWEA用于控制ACK位,如果在传输之前将该位置1,TWI就会在输出的最后第9位置0输出ACK。(在3种情况下会输出,一种是作为从机接收到自己的地址,一种是在TWGCE置1时受到广播地址,还有一种是接收方接收到数据帧)
TWSTA为传输开始控制位,写入寄存器时将该位置1可以使得进入主机模式,同时如果总线空闲会立即输出一个开始信号。如果当前总线忙,会等待至总线空闲以后才会数据开始信号。之后TWSTA需要使用软件清0
TWSTO为传输结束控制位,写入寄存器时置1会输出一个结束信号。在成功输出一个结束信号以后该位会自动清0。另外,在从机模式下将该位置1可以使得TWI从错误中恢复,此时该从机会将SDA以及SCL置为高阻态
TWWC为写入冲突检测。在TWINT为0时写入TWDR会导致该位置1(即在一次传输过程中写入)。之后只要在TWINT为1时再次向TWDR写入新值即可将该位清0
TWDR寄存器
收发数据寄存器
该寄存器事实上是两个寄存器,使用了同一个地址,发送模式下该寄存器存储下一个想要传输的数据。接收模式下该寄存器存储当前最后接收到的数据帧。只要TWINT为1,从该寄存器读取的值就保持不变
另外,在本机(发送方)发送数据时,数据也会回过来同步输入到TWDR。换句话说,该寄存器总是存储上一次在总线上出现的数据。这也为输出数据校验提供了可能
TWSR寄存器
状态寄存器
该寄存器的TWS7..3用于指示当前的TWI状态,具体定义见1.9.5
TWPS1..0为时钟预分频设置,定义如下。注意在一般的状态读取中需要将这两位屏蔽
TWAR寄存器
从机地址寄存器
该位用于设置从机模式下的本机地址。TWGCE为广播地址使能,置1时从机如果接收到0x00也会产生中断
TWAMR寄存器
从机地址掩码寄存器
TWAM6..0对应TWAR相应地址位。如果置1,那么从机在接收到地址帧时会忽略相应位的不同,产生中断
首先介绍主机发送模式(MT)
主机发送模式首先要确保当前设备是主机,通过发送起始信号(START)进入。其次地址帧决定了是进入发送还是接收模式,需要将地址帧的读写位R/W置0表示写操作,发送起始信号需要向TWCR写以下值
当起始信号传输完毕,中断标志TWINT会自动置位,同时TWSR应当变为0x08。接下来先向TWDR写新值(从机地址以及末位0代表写操作),之后将TWINT写1清0触发地址帧的发送,如下
在地址帧的末尾会收到相应从机的ACK,此时TWINT会置1,TWSR会更新,值可能为0x18,0x20,0x38,需要采取相应的处理措施。如果发送成功,接下来就可以开始发送数据帧
发送数据帧时,首先向TWDR写入想要发送的数据,之后将TWINT清0触发数据帧的发送。注意,如果在TWINT为0时写TWDR,会导致发送冲突,TWWC会置位,而TWDR不会更新值
传输完所有字节以后,需要发送一个传输终止信号(STOP)或重复开始信号(REPEATED START),分别如下
附:MT模式下状态寄存器TWSR各代码含义以及状态转移图
主机接收模式(MR)
主机接收模式同样需要首先发送起始信号(START),其次在地址帧将读写位置1表示读操作,发送起始信号需要向TWCR写以下值
之后TWINT置位,TWSR变为0x08。之后向TWDR写入从机地址(末位R/W置1读),之后将TWINT写1清0触发
在地址帧末尾会受到相应从机的ACK。此时TWINT会再次置1,同时TWSR中的值会更新(0x38,0x40或0x48)。运行相应的处理过程以后,TWI会自动进入到接收模式。一旦TWI接收到新的数据包,就会将TWINT置1
主机在接受到最后一个字节以后需要向从机(数据发送方)发送NACK表示已经接收到所有要求的数据帧
最后接收主机需要向从机发送一个传输终止信号(STOP)或重复开始信号(REPEATED START)
附:MR模式下状态寄存器TWSR各代码含义以及状态转移图
从机发送模式(ST)
从机发送模式首先需要进行初始化,设置从机地址TWAR,以及控制寄存器,分别设置如下。将TWEA置1,那么本设备就会在检测到自己被寻址后自动回复ACK
从机在接收到地址帧(读操作)以后自动进入到ST模式,同时TWINT会置1,同时TWSR中的值会更新。一台主机在总线仲裁失败后也会进入到ST模式(0xB0)
如果在一次数据传输过程中将TWEA置0,就代表从机发送最后一帧数据。在这帧数据之后会接收到MR发来的确认位,从机会进入到TWSR为0xC0(接收到NACK)的模式或0xC8(接收到ACK)的模式。如果是0xC0,在此之后从机会进入到未选中模式,并忽略主机发来的所有数据。如果是0xC8,就代表主机还需要额外数据
在TWEA置0的模式下,作为从机不会响应主机的寻址
附:ST模式下状态寄存器TWSR各代码含义以及状态转移图
从机接收模式(SR)
SR模式的寄存器初始化和ST模式相同
从机接收到地址帧(写操作)后会自动进入到SR模式,同时置位TWINT,更新TWSR。一台主机在总线仲裁失败后也会进入到SR模式(0x68或0x78)
如果在一次数据传输过程中将TWEA置0,那么当前从机就会在下一个接收到的数据帧之后回复NACK表示不再继续接收数据。
在TWEA置0的模式下,作为从机不会响应主机的寻址
附:SR模式下状态寄存器TWSR各代码含义以及状态转移图
其他模式
BootLoader作为AVR单片机启动时运行的第一个程序,具有很重要的作用。通过UART或USB的在线程序下载就是由BootLoader软件实现
Atmel的AVR单片机通用的下载协议,通过SPI(ICSP)接口下载,由硬件支持所以和BootLoader无关
熔丝位一般用于配置一些基础参数以及安全设置
事实上STC的8051单片机也有熔丝位,比如指令时钟的调节,以及ALE引脚的设置,就是通过编程软件更改熔丝位设置实现
更改熔丝位一定要三思,否则单片机很容易变砖,只能通过高压编程(通过专用的高压编程接口,需要在#RST输入12V)或强行输入时钟的方法修复
包括ATmega328P,16u2,32u4的熔丝位,以及作用
熔丝位设置错误一般可以通过输入外部时钟修复。但是如果设置使用了内部时钟,就只能通过高压编程器恢复了(这是解决熔丝位设置错误的最终解决方法,如果这种方式依然无法修复那么就是单片机本身损坏了)
这里提供这两种方案的具体实现,并且会提供一种可行的高压编程器的DIY方案
从零开始的AVR开发环境,基于VSCode,包括了软件的安装,环境的配置,工具链的使用,Makefile的编写等
使用两种方法,和Arduino IDE一样使用串口(依赖于Bootloader),以及使用ISP编程。基于开源工具avrdude