如何控制单片机的IO引脚输出高电平和低电平?

控制/MCU

1878人已加入

描述

单片机的IO引脚就像人的手脚一样,单片机通过IO引脚与外界进行数据交换。可以输出电压信号来控制外部电路,也可以读取外部的电压信号。我们这一节主要讲解如何控制单片机的IO引脚输出高电平和低电平。

使用单片机的内部模块,一般需要写五部分代码:

(1)库的初始化(如果使用了HAL库)

(2)系统时钟配置代码

(3)单片机引脚的配置

(4)外设的初始化配置

(5)外设功能实现代码

关于单片机库的由来,这个库是什么东西呢?其实库就是之前别人写好的代码,以函数的形式存在于文件中。比如,黄昏哥之前做过很多项目,其中数码管用的比较多。那么,黄昏哥把驱动数码管相关的代码单独写在一个文件里。以后做项目用到数码管,那就把那个文件复制过去。然后在使用时,直接调用相关的函数就可以了。

我们都知道单片机编程,需要和很多寄存器打交道。这些寄存器数量很多,黄昏哥很难都记住这些寄存器的用法。那怎么办呢?查手册啊,去看看每个寄存器是干嘛的。但是,黄昏哥每次都去查手册太麻烦了。所以,黄昏哥想了下,把单片机内部模块经常使用的功能提前用寄存器写好。然后,把这些函数功能记住,以后使用就方便多了。那么,这里就出现了另一个问题。黄昏哥自己写的函数,黄昏哥自己肯定知道这些函数是干嘛用的。但是,其他人就不一定知道了。所以,使用别人写的函数,你首先要通过函数名来大概猜测一下函数的功能。然后就是去看函数前面的注释,最后看函数的内部代码。也就是说,你要熟悉函数,你才能更好的使用库。

库函数有很多很多,好多初学者都不知道使用哪一个库函数,也没法都把所有的库函数看一遍。毕竟不现实,就算是黄昏哥,也没那个耐心去把所有的库函数看一遍。这里强调一遍,你用到单片机哪个功能,你就去看单片机的用户手册相对应的那个部分。

不管是用寄存器编程还是用库编程你都要去用户手册,你懂了单片机你才能知道如何编程。为什么这样说,那是因为,不管是库还是寄存器,本质上就是配置表。我们知道库函数的底层代码就是寄存器编写的。寄存器就是相当于电脑的配置表,单片机内部模块是根据寄存器的内容来运行的。寄存器就像一个菜谱一样,厨师根据菜谱来做菜。所以,黄昏哥想做酸菜鱼,那黄昏哥要去看菜谱,然后才好做酸菜鱼。那问题来了,菜谱是谁设计的?黄昏哥偷偷告诉你,是单片机厂家设计的。单片机厂家有一个菜谱,上面描述了,不同的菜需要配置哪些原材料,需要加什么调味品等等。单片机内部不同的模块相当于不同的菜类,比如鱼类,猪肉类,蔬菜类。同样的菜类,不同的配料可以做不同的具体的菜。

那么,进入到今天的主题。单片机IO引脚控制这道菜这么做?黄昏哥想控制IO输出低电平,这个时候LED灯就亮了。那么,这个程序怎么写呢?

1.黄昏哥先把锅洗好,做菜前的准备工作--库和时钟初始化,先让单片机满足正常运行的条件。

2.黄昏哥开始点火--IO引脚时钟要打开。

3.开始放置各种调料,以及把鱼准备好--IO配置成输出模式,速度为低速,推挽模式。

4.把鱼和调料下锅--具体的功能实现,输出低电平。

黄昏哥是怎么知道控制IO需要写这4个步骤的呢?通过用户手册知道的呗,所以黄昏哥一直强调不管你用什么编程你都要看手册。你只有熟悉了内部功能,你才能知道如何配置内部模块。尤其是在功能函数编写这块,你不熟悉单片机内部功能,你也是无法编程的,你也用不好库。在实际工作中,黄昏哥都是库与寄存器混合编程的。在这里,黄昏哥建议初学者,在单片机的早期学习中最好用寄存器编程。当然了,这里我是指简单的内部模块。复杂的模块还是用库编程吧。

其实吧,库一开始出现是为了节省时间,是给熟悉单片机的人使用的,并不是为了给初学者使用。不同的单片机它们的库也是不一样的,比如GD32,LKS32,PIC32,AVR这些单片机。那么,我们学单片机学的究竟是什么?肯定是学内部模块的工作原理和使用方法啊,并不是学库的使用。单片机内部模块的原理基本上是一样的,我们要达到学会一种单片机就能使用各种单片机的能力。不管什么单片机,我们都是根据那几个步骤来编写程序的。你是标准库也好,是HAL库也好,或者其他厂家自己的库也好,哪怕是没有库,我们也能够根据那几个步骤用寄存器来写程序。

下面我们来看看点灯程序怎么写?

电压信号

LED接的是单片机的PB15引脚,GPIO_PIN_RESET输出低电平,GPIO_PIN_SET输出高电平。我们主要看看MX_GPIO_Init();这个函数。

电压信号

我建议大家使用库函数时,要总结库函数的一些使用规则。可以看看库函数内部是怎么写的,有时候为了提高效率,我们会自己重新改写库函数。就像HAL_GPIO_WritePin这个函数用起来很不方便。黄昏哥是直接操作寄存器来控制IO输出电平的,我们先看看库函数里关于IO相关寄存器的定义,看下图。

电压信号

这个ODR就是输出寄存器,它的0-15位对应IO的0-15,ODR寄存器写入0或1就可以控制IO输出对应的电平。IDR是输入寄存器,读取这个寄存器就可以知道对应IO的电平。下面这幅图也是在STM32F103xb.h里定义好了,用户不需要自己定义。

电压信号

学过C语言的都知道,结构体的成员在内存里都是按照顺序连续依次排列的,而我们IO的寄存器也是连续依次排列的。因此我们可以把IO相关的寄存器封装成结构体的形式,GPIOB_BASE就是B口相关寄存器的首地址,B口相关的寄存器就是从这个地址依次排列的,这里我们定义成结构体指针。比如,输出寄存器,我们可以这样访问。GPIOB->ODR = 0x00;表示PB0到PB15全部输出低电平。PB15输出高电平可以这样写,GPIOB->ODR |= 1<15,输出低电平可以这样写,GPIOB->ODR &= ~(1<15);有没有发现,这样写,好像也挺麻烦的。那我们继续简化。

电压信号

我们可以定义一个结合体类型GPIO_Bit_TypeDef,它的成员变量是位变量,这个结构体一共有16个位变量。再用这个结构体定义一个指针,这个指针指向输出寄存器GPIOB->ODR。我们可以把这些定义写在头文件IO.h里,以后直接复制到项目里面就可以使用了。这个时候PB15输出低电平,可以这样写:PORTB_OUT->bit15 = 0。C语言预处理器有一个连接符##可以把宏定义里的数字连接起来:

#define PBout(n) PORTB_OUT->bit##n

当我们在程序中写PBout(15)时,预处理器会直接把PBout(15)用PORTB_OUT->bit15替换,因此,当我们PB15想输出高电平时,可以直接写成PBout(15)=1,如果要让LED闪烁,那就写成PBout(15) =! PBout(15)就行了。

库函数都是别人写的,有时候我们自己使用起来不爽的,就可以自己写一个函数来实现。黄昏哥前面,提到过学单片机之前最好先学一下C语言最好,就是这个道理,不然,这一节你不一定看得懂。

我们这一节主要掌握的知识点是:C语言中如何定义一个位结构体,如何把一个寄存器地址定义成结构体指针。掌握IO如何配置成输出模式,并实现IO输出函数。下面一节,将讨论如何采用寄存器的方式实现声光报警程序以及LED流水灯程序。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分