设为首页 加入收藏

TOP

AVR单片机教程——数字IO寄存器(一)
2019-10-09 15:08:11 】 浏览:73
Tags:AVR 单片机 教程 数字 寄存器

前两篇教程中我们学习了LED、按键、开关的基本原理,数字输入输出的使用以及两者之间的关系。我们用到了 pin_mode 、 pin_read 和 pin_write 这三个函数,实际上它们离最底层(至少是单片机制造商允许我们接触到的最底层)就只有一步之遥了。而学单片机要是不了解一点底层,那跟Arduino玩家还有什么区别?(为防止有忠实的Arduino粉丝骂我,我得承认还是有一小部分Arduino玩家是知道本篇教程所介绍内容的。)根本不好意思说自己学过单片机好吧。这所谓的最底层,就是数字IO寄存器了。

在开始之前,你需要下载两份文档:

单片机的数据手册。官网链接极慢,我在国内平台上传了一份,在本篇教程写成之时是最新的。

开发板的原理图。本应在教程之初就放出来,但事实证明没有原理图也不影响使用。现在是肯定需要的。

 

等等,你可能还不知道寄存器是什么。那我们就从寄存器开始吧。

寄存器是一类CPU内部的存储器,分为通用寄存器与特殊功能寄存器(8086对特殊功能寄存器还有细分)。通用寄存器,顾名思义是通用的,可以存储操作数、运算结果、内存地址等数据,在使用C语言编程时,一般不直接接触通用寄存器,而由编译器负责安排其使用。特殊功能寄存器有特定的功能,一些作用于CPU内部,如PC存放下一条指令的地址,SP记录内存中栈顶的位置(现在无需了解这些);另一些与IO模组相连接,单片机程序通过这类寄存器来控制各种外设,我们今天要学习的数字信号IO寄存器就属于这一类,并且应该是其中最简单的了。

我们使用的单片机的型号是ATmega324PA,它有多种封装,引脚(pin)数不尽相同,但都有32个通用输入输出(GPIO)引脚。由于AVR架构是8位字长的,CPU一次处理1位数据和8位数据所需的时间是一样的,这32个引脚被组织为4个端口(port),分别是PA、PB、PC和PD。

在AVR架构tiny与mega系列的单片机中,每个端口都有3个寄存器控制数字信号IO,分别是PORTx、DDRx和PINx。这里的x是A、B、C或D,由于这4个端口在数字IO方面完全相同,就把它们合并起来讲。相应地,对于每个引脚Pxn,有PORTxn、DDxn(没有R)和PINxn三个bit控制其数字IO。

DDxn控制引脚方向:当DDxn为1时,Pxn为输出;当DDxn为0时,Pxn为输入。

当Pxn为输入时,如果PORTxn为1,则该引脚通过一个上拉电阻连接到VCC;否则引脚悬空。

当Pxn为输出时,如果PORTxn为1,引脚输出高电平;否则输出低电平。

PINxn的值为Pxn引脚的电平。如果给PINxn写入1,PORTxn的值会翻转。

还有很多细节问题,如MCUCR寄存器中PUD位的功能、复位后寄存器的值、输入输出切换的方法、读取引脚电平的延迟、未连接引脚的处理方法等,留作今天的作业,阅读数据手册I/O-Ports一章中除Alternate Port Functions一节以外的内容(一共8页不到,不多吧),找出这些问题的答案,并以此为基础回答上一篇教程最后的问题。

 

讲了这么多,相信你也没记住多少,而且你也不知道去哪里用这些寄存器。

要使用寄存器,你需要在C语言程序中写 #include <avr/io.h> (在创建项目时自动生成的代码中就有),然后就可以使用 PORTA 、 DDRB 、 PINC 等寄存器了。它们是宏定义,你不必去探究它们展开后是怎样的,只需知道这些宏可以读取,可以赋值,可以位操作,就像 uint8_t 类型变量一样。

但是诸如 PORTA0 和 DDB7 等宏定义却不代表寄存器上的那一位,它们实际上就是字面值常量,如 PORTAx 的意义是寄存器 PORTA 的第x位(第0位为最低位,第7位为最高位),它的值就是x。因此,直接对这些宏复制是不正确的(不仅意义不正确,编译也不会通过)。

在开发板的库函数中的 <ee1/bit.h> 提供了包含几个用于位操作的宏函数。我们先按照手册来用,稍后来看它们是如何实现的。

 

我们先返璞归真一下,回到最初的例子,点亮一个LED,不过这次我们不再使用 <ee1/led.h> 提供的函数,而是直接操作寄存器。

先点亮红色LED吧。在原理图的第2页左上角,红色LED通过一个电阻连接到网络LED0,而在第1页中LED0连接的是单片机PC4引脚,因此我们需要让PC4引脚输出高电平。回到上面看一下三个寄存器的功能,输出高电平需要DDxn和PORTxn同时为1。这里把x和n分别用C和4带入,即我们要让DDC4和PORTC4为1。

将一个寄存器的一位置为1可以由 set_bit 实现。它需要两个参数,要操作的整型变量与表示第几位的整数。把DDC4置为1应该写 set_bit(DDRC, 4); ,4 可以用 DDC4 替换,这个定义就是这么用的。类似地也可以将PORTC4置为1。点亮红色LED的整个程序如下:

1 #include <avr/io.h>
2 #include <ee1/bit.h>
3 
4 int main(void)
5 {
6     set_bit(DDRC , 4);
7     set_bit(PORTC, 4);
8 }

相信聪明的你已经知道闪烁和流水灯怎么写了。翻转输出电平可以使用 flip_bit(PORTC, 4); ,也可以使用 set_bit(PINC, 4); 。

 

下面来看数字输入。还是用第一个与按键相关的例子,让LED状态与按键保持一致,即按下亮起。

读取一个寄存器中的一位可以使用 read_bit。如果引脚上电平为高,read_bit 的运算结果非0(但不一定是布尔值1)。如果你没有忘记的话,按键按下时引脚电平为低,因此对读取引脚电平的结果取非才是按键是否按下。

在原理图中,按键一端连接在BTN0网络上,进而连接到单片机的PA4引脚。因此按键是否按下应该写为:!read_bit(PINA, 4) 。

在读取之前应该先把引脚配置为输入。尽管复位后默认为输入,在这个例子中没有必要向DDA4写0,但明确写出来可以让看这段代码的人(可能别人也可能是你自己)明白PA4是作输入的,这样做是一种良好的习惯。至于PORTA4,由于这一引脚在外部有连接上拉电阻,就没有必要启用内部上拉电阻了。

 1 #include <avr/io.h>
 2 #include <ee1/bit.h>
 3 
 4 int main(void)
 5 {
 6     reset_bit(DDRA, 4);
 7     set_bit(DDRC, 4);
 8     while (1)
 9     {
10         cond_bit(!read_bi
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C语言入门-字符串 下一篇零基础怎样学习好C/C++语言?

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目