AVR单片机教程——数字IO寄存器

发布者:炉火旁的Yye最新更新时间:2019-11-30 来源: eefocus关键字:AVR  单片机  数字IO寄存器 手机看文章 扫描二维码
随时随地手机看文章

前两篇教程中我们学习了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 (在创建项目时自动生成的代码中就有),然后就可以使用 PORTA 、 DDRB 、 PINC 等寄存器了。它们是宏定义,你不必去探究它们展开后是怎样的,只需知道这些宏可以读取,可以赋值,可以位操作,就像 uint8_t 类型变量一样。


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


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


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


先点亮红色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

2 #include

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

 2 #include

 3 

 4 int main(void)

 5 {

 6     reset_bit(DDRA, 4);

 7     set_bit(DDRC, 4);

 8     while (1)

 9     {

10         cond_bit(!read_bit(PINA, 4), PORTC, 4);

11     }

12 }


再结合按键动作的知识,你应该知道怎样直接通过寄存器操作来判断按键动作了吧。


顺便说一句,以上两个程序都不必在项目属性中给linker加上libee1库。虽然代码中使用了 ,但其中都是宏定义,与linker无关。


之前留了一个问题,就是位操作是如何实现的。以下为 中部分代码:


1 #define set_bit  (r, b) ((r) |=  (1u << (b)))

2 #define reset_bit(r ,b) ((r) &= ~(1u << (b)))

3 #define read_bit (r, b) ((r) &   (1u << (b)))

4 #define flip_bit (r, b) ((r) ^=  (1u << (b)))


写那么多括号是为了防止出现运算符优先级的问题。假设r就是一个寄存器,比如PORTC,b就是一个数字,比如4,也可以是一个变量,那么 (r) |= (1u << (b)) 就相当于 r = r | 1u << b (后缀u表示无符号数,位操作的运算数一般都是无符号数)。对于二进制表示下的每一位,如果不是第b位,那么位或运算符右边此位为0,运算结果等于左边,即r的这些为保持不变;对于第b位,右边此位为1,无论左边此位的值是多少,结果一定是1,即这一位被置1;这样就实现了将一位置为1的功能。


reset_bit 的实现还要绕一个弯。1u << b 是一个第b位为1,其余位为0的数,那么 ~(1u << b) ,即位与赋值号右边,是一个第b位为0而其余为都是1的数。仿照上面的分析可得,运算结果的第b位一定是0而其余位与r中原来的值相同。类似的分析也可应用于 flip_bit :两个bit进行异或运算的结果,若相同则为1,不同则为0;当一个运算数是1时,结果就是另一个运算数取反;当一个运算数是0时,结果与另一个运算数相同;因此 flip_bit 就使r的第b为取反而其他为不变。


以上是向寄存器中的位写入的操作。用于读取位的 read_bit 的原理也大致相同,用寄存器的值与 1u << b 相与,仅当第b位为1时结果是 1u << b ,这是个非零数,否则结果为0。read_bit 语句可以直接放在 if 语句的条件部分,但如果是根据其结果决定一个变量是否加1,不能直接加上其运算结果,可以转换成 bool 类型或用 if 语句判断。


这篇教程有点长。好好消化一下,然后把以前写过的程序用寄存器重新写一遍,以此巩固所学的知识。


从本教程开始至今,我们先了解了LED灯、按键、拨动开关、数字输入输出的使用方法,然后学习C语言位操作与数字IO寄存器,终于打通了一条从底层到应用的路。而网络上很多教程都是反过来讲的,即先介绍寄存器,然后直接通过寄存器来驱动LED、检测按键等,甚至有直接写诸如 DDRB |= 0x0C; 或 if (PINB & 0x40) 这样的代码的,初学者怎么看得明白?站在我的角度,我觉得以上都是常识,都不用讲,尽管我学习的时候也颇费周折(正是因为那些反过来的教程)。现在我站在初学者的角度,认为本教程的讲解顺序是更容易理解的。


我学习计算机之前,总对计算机抱有特殊的幻想,觉得它什么都能干,很神奇。现在这些想法都没有了,尤其是在学习单片机的过程中。学习计算机教会我们分析问题、解决问题,而学习单片机让我们更好地理解计算机是如何按照我们的想法来解决问题的。这篇教程带你了解了寄存器,在你学习单片机的全过程中,它都会伴随着你。寄存器是硬件和软件之间的一个重要纽带,计算机的任何功能都离不开寄存器。CPU?有寄存器。总线通信?通过寄存器。内存分页?需要寄存器。万物基于寄存器。又有更多像寄存器一样的纽带,在电子空穴与丰富多彩的计算机世界之间建立起联系。它们看起来如此复杂,却又清晰明了,就算一夜之间所有计算机都突然消失,人类也能从电子管和打孔纸带开始,一层一层地构建起计算机的世界。而我们了解的只不过是这个巨大体系中的沧海一粟。


初入计算机世界,你想着计算机能干什么,学完计算机我能干什么。而计算机世界是如此高深,在逐渐深入后,你会明白计算机不能干什么,我不能干什么。数码管、蜂鸣器,它们一直在你的开发板上,你却不知道如何使用它们;更不用说那些高级到你没听说过的东西。学得越多,你会发现虽然原本不会的减少了,但脑海中萌生出的“不切实际”的想法更多——学习的速度永远赶不上认知的速度。本系列教程不能帮你消除所有“不会”,而是要在带你一步步消除一些“不会”的过程中让你学会发现更多“不会”并消除的方法。

关键字:AVR  单片机  数字IO寄存器 引用地址:AVR单片机教程——数字IO寄存器

上一篇:AVR单片机教程——数码管
下一篇:AVR单片机教程——数字输出

推荐阅读最新更新时间:2024-11-16 21:09

51 单片机编程:双路计数器
利用 51 单片机构成计数器,计数的上限,做的大一些,也不难,几千几万都可以。 下面的电路,小了一点,是个两路两位的计数器。 用来记录乒乓球比赛,还算可以,用于篮球比赛,分数超过 99,这个电路就不够用了。 电路图中,用的是共阳数码管;还设置了六个按键,用途,都已经标出。 用 C 语言编写驱动程序,是比较简单的,全部代码如下: #include reg51.h unsigned char n1, n2; //----------------------------------------------- void delay(unsigned int i) //1ms延时程序 { unsigned
[单片机]
51 <font color='red'>单片机</font>编程:双路计数器
STM32单片机—编码器测速
一、实验工具:STM32开发板一块、L298N电机驱动、直流电机 以及用到的软件(STM32CubeMX、keil4) 二、编码器原理 1.概述:编码器是一种将角位移或者角速度转换成一串电数字脉冲的旋转式传感器。编码器又分为光电编码器和霍尔编码器,我们这里用到的是霍尔编码器。 2.霍尔编码器工作原理:一种通过磁电转换将输出的机械几何位移量转换成脉冲或数字量的传感器,霍尔编码器室友霍尔马盘和霍尔元件组成。霍尔马盘是在一定直径的圆板上等分的布置有不同的磁极。霍尔马盘与电动机同轴,电动机旋转时,霍尔元件检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号。简单示意图如下: 3.编码器接线图: 4.测速原理:
[单片机]
第6课 数码管静态显示
1、数码管显示原理 数码管是单片机应用系统中常用的一种显示器件,由于其价格低廉、操作简单,而被广泛的应用于各种数字显示系统中,常见的数码管如图1所示。 根据外观的不同,数码管又为分1位数码管、2位数码管、3位数码管、四位数码管等种类,如图2所示。 但不论是几位一体的数码管,其显示原理都是一样的,都是靠内部发光二极管发光来进行显示的。下面我们以1位数码管为例介绍其显示原理。 1、数码管显示的原理 数码管内部的电路如图3所示, 图3 数码管内部电路 图3中,显示一个完整的8字,需要7个小段,外加一个小数点,共8段,分别称为a段、b段、c段、d段、e段、f段、g段、dp段,每段内部都集成了一个发光二
[单片机]
第6课 数码管静态显示
NEC电子推出2款集成驱动功能的8位微控制器
NEC电子日前推出适用于LED照明器件、厨房家电、充电器等各种系统控制的8位通用全闪存微控制器产品,即日起开始提供样品。 新产品是NEC电子的8位全闪存系列微控制器“78K0/KB2”,是将保持电流设定值的电流驱动功能封装在一起的微控制器,根据闪存容量的不同,分为8KB的”uPD78F8024”以及32KB的”uPD78F8025”两款产品。新产品的主要特征有:(1)将全闪存微控制器及驱动功能封装在一起,可减少器件数量,缩减安装面积,用户能轻松构筑系统;(2)内置于全闪存微控制器的A/D转换器及I2C以及UART等各种通信功能可实现更加精细的多功能控制;(3)内置了定电流控制电路和各种保护电路,可构筑高效率、高信赖性
[单片机]
NEC电子推出2款集成驱动功能的8位<font color='red'>微控制器</font>
单片机与电脑串口通信控制,附带遥控控制
/****** 单片机接收电脑串口发出的指令并执行相应动作。同时,也接收无线遥控发出的信号,并执行动作**************/ #include REG51.H #define uchar unsigned char #define uint unsigned int uchar code SEG7 ={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//数码管编码,预留     uchar code ACT ={0xfe,0xfd,0xfb,0xf7};//数码管显示相关,预留 /*********************************************
[单片机]
2020年预计投产3亿颗MCU,灵动微为啥逆势大涨?
受全球消费电子市场整体放缓以及中美贸易战的影响,MCU市场在持续两年高景气后出现萎缩。与全球MCU市场不同的是,在“国产代替”的背景下,国内MCU厂商迎来了难得一遇的发展机遇。 近日,“2019灵动MM32协作大会”在深圳成功举办。国内杰出的MCU厂商灵动微在会上公布,从2016年MM32 MCU品牌面世,2017年形成规模至今,灵动MM32 MCU年复合增长率高达400%!与去年同期相比,灵动MM32 MCU客户数增加67%,订单数增加79%,为未来灵动MCU的业务增加打下了坚实的基础。 做更好用的MCU平台 据了解,灵动微已经连续四年举办MM32 协作大会了,2018年“灵动MM32协作大会”的主要聚焦4点即:应用(A
[手机便携]
2020年预计投产3亿颗<font color='red'>MCU</font>,灵动微为啥逆势大涨?
基于STM32F4单片机USART寄存器控制的设计
USART又叫通用同步异步收发器,塔提供了一种灵活的方法与工业使用标准NRZ异步春航数据格式的外部设备之间进行全双工数据交换。USART利用分数波特率发生器提供宽范围的波特率选择,支持同步单向通信和半双工单线通信,也支持LIN(局部互联网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范以及调制解调器(CTS/RTS)操作,它还允许多处理器通信,使用多换成器配置的DMA方式,可以实现高速数据通信。 USART寄存器控制框图如下 可通过对 USART_CR1 寄存器中的 M 位进行编程来选择 8(置0) 位或 9(置1) 位的字长。TX 引脚在起始位工作期间处于低电平状态。在停止位工作期间处于高电平状态。
[单片机]
基于STM32F4<font color='red'>单片机</font>USART<font color='red'>寄存器</font>控制的设计
图形点阵式液晶显示模块与51单片机的接口设计
引 言 液晶作为一种显示器件,以其特有的优势正广泛应用于仪器、仪表、电子设备等低功耗产品中。以往的测控仪器的显示部分大都采用LED式液晶显示屏进行参数设定和结果显示,其显示信息量少、形式单一、人机交互性差、操作人员要求较高。而液晶显示器(LCD)具有功耗低、体积小、质量轻、超薄和可编程驱动等其他显示方式无法比拟的优点,不仅可以显示数字、字符,还可以显示各种图形、曲线、及汉字,并且可实现屏幕上下左右滚动、动画、闪烁、文本特征显示等功能;人机界面更加友好,使用操作也更加灵活、方便,使其日益成为智能仪器仪表和测试设备的首选显示器件。本文在介绍以ST7920为驱动器的WGM-12832液晶显示模块的引脚、结构、功能的基础上,详述了与AT
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved