1.1 GPIO工作原理 要想实现流水灯,首先必须了解CPIO的工作原理。GPIO的基本结构如图1-1。
图1-1 GPIO的基本结构
STM32 的 IO 口可以由软件配置成如下 8 种模式:
输入模式
浮空输入:浮空(floating)就是逻辑器件的输入引脚即不接高电平,也不接低电平。由于逻辑器件的内部结构,当它输入引脚悬空时,相当于该引脚接了高电平。一般实际运用时,引脚不建议悬空,易受干扰。 通俗讲就是让管脚什么都不接,浮空着。信号进入芯片内部后,既没有接上拉电阻也没有接下拉电阻,经由触发器输入。配置成这个模式后,用电压变量引脚电压为1点几伏,这是个不确定值。由于其输入阻抗比较大,一般把这种模式用于标准的通讯协议,比如IIC、USART的等。该模式是STM32复位之后的默认模式。
图1-2输入浮空模式
上拉输入:上拉就是把电位拉高,比如拉到Vcc。上拉就是将不确定的信号通过一个电阻嵌位在高电平,电阻同时起限流作用,弱强只是上拉电阻的阻值不同,没有什么严格区分。上拉输入就是信号进入芯片后加了一个上拉电阻,再经过施密特触发器转换成0、1信号,读取此时的引脚电平为高电平;
图1-3输入上拉模式
下拉输入:就是把电压拉低,拉到GND。与上拉原理相似。下拉输入就是信号进入芯片后加了一个下拉电阻,再经过施密特触发器转换成0、1信号,读取此时的引脚电平为低电平;
图1-4输入下拉模式
模拟输入:信号进入后不经过上拉电阻或者下拉电阻,关闭施密特触发器,经由另一线路把电压信号传送到片上外设模块。模拟输入是指传统方式的输入,数字输入是输入PCM数字信号,即0、1的二进制数字信号,通过数模转换,转换成模拟信号,经前级放大进入功率放大器,功率放大器还是模拟的。比如传送给ADC模块,由ADC采集电压信号。所以可以理解为模拟输入的信号是未经处理的信号,是原汁原味的信号。
图1-5模拟输入模式
输出模式
开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)。
图1-6开漏输出模式
复用开漏输出:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用)。端口必须配置成复用开漏功能输出模式。
图1-7开漏复用功能
推挽式输出:可以输出高、低电平,连接数字器件;推挽结构一般是指两个三极管分别受两个互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源决定。推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。
图1-8推挽式输出
推挽式复用输出
图1-9推挽式复用功能
1.2 I/O复用和重映射1.2.1 I/O复用STM32 有很多的内置外设,这些外设的外部引脚都是与 GPIO 复用的。也就是说,一个 GPIO如果可以复用为内置外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时候,就叫做复用。当I/O端口被配置为复用功能时:
● 在开漏或推挽式配置中,输出缓冲器被打开
● 内置外设的信号驱动输出缓冲器(复用功能输出)
● 施密特触发输入被激活
● 弱上拉和下拉电阻被禁止
● 在每个APB2时钟周期,出现在I/O脚上的数据被采样到输入数据寄存器
● 开漏模式时,读输入数据寄存器时可得到I/O口状态
● 在推挽模式时,读输出数据寄存器时可得到最后一次写的值
图1-10复用功能配置
大家都知道, MCU 都有串口, STM32 有好几个串口。比如说 STM32F103ZET6 有 5 个串口,我们可以查手册知道,串口 1 的引脚对应的 IO 为 PA9,PA10.PA9, PA10 默认功能是 GPIO, 所以当PA9,PA10 引脚作为串口 1 的 TX,RX 引脚使用的时候,那就是端口复用。
表1-1串口1的复用
复用端口初始化有几个步骤:
1) GPIO 端口时钟使能。要使用到端口复用,当然要使能端口的时钟了。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
2) 复用的外设时钟使能。比如你要将端口 PA9,PA10 复用为串口,所以要使能串口时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
3) 端口模式配置。 1.2.2 I/O重映射为了使不同器件封装的外设 IO 功能数量达到最优,可以把一些复用功能重新映射到其他一些引脚上。 STM32 中有很多内置外设的输入输出引脚都具有重映射(remap)的功能。 我们知道每个内置外设都有若干个输入输出引脚,一般这些引脚的输出端口都是固定不变的,为了让设计工程师可以更好地安排引脚的走向和功能,在 STM32 中引入了外设引脚重映射的概念,即一个外设的引脚除了具有默认的端口外,还可以通过设置重映射寄存器的方式,把这个外设的引脚映射到其它的端口。
表1-2 USART1重映像
从表中可以看出,默认情况下,串口 1 复用的时候的引脚位 PA9、PA10,同时我们可以将 TX 和 RX 重新映射到管脚 PB6 和 PB7 上面去。所以重映射我们同样要使能复用功能的时候讲解的 2 个时钟外,还要使能 AFIO 功能时钟,然后要调用重映射函数。 详细步骤为:
1) 使能 GPIOB 时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
2) 使能串口 1 时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
3) 使能 AFIO 时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
4) 开启重映射:
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
这样就将串口的 TX 和 RX 重映射到管脚 PB6 和 PB7 上面了。
1.3 GPIO流水灯硬件电路分析发光二极管是属于二极管的一种,具有二级管单向导电特性,即只有在正向电压(二极管的正极接正,负极接负)下才能导通发光。PB0引脚接发光二极管(LED1)的正极,所以PB0引脚输出高电平LED1亮,PB0引脚输出低电平LED1熄灭, LED2, LED3同理。
图1-11 LED电路图
1.4 GPIO流水灯寄存器分析每个GPIO端口都有两个32位配置寄存器(GPIOx_CRL ,GPIOx_CRH) ,两个32位数据寄存器 (GPIOx_IDR和GPIOx_ODR),一个32位置位/ 复位寄存器(GPIOx_BSRR),一个16位复位寄存器(GPIOx_BRR),一个32位锁定寄存器(GPIOx_LCKR)。每个I/O端口位可以自由编程,然而I/O端口寄存器必须按32位字被访问(不允许半字或字节访问) 。
点亮LED,基本步骤是:配置寄存器;控制寄存器。
我们常用的 IO 端口寄存器只有 4 个: CRL、 CRH、 IDR、 ODR。其中CRL 和 CRH 控制着每个 IO 口的模式及输出速率。
表1-3端口位配置表
表1-4输出模式位
低配置寄存器 CRL 的描述,如图1-12和表1-5所示。
CRL和CRH类似,在此就不在赘述了,读者朋友可以参看《STM32F10XXX参考手册》。
数据输入输出寄存器是将对应的IO口置位,从而进行数据的输入与输出。
图1-12 CRL寄存器
表1-5 CRL寄存器描述
1.5 GPIO 流水灯具体代码分析笔者在上文已经分析了GPIO的原理及操作步骤,现在我们就来写代码吧。本书是用库来对STM32来开发的,这是本书的第一个实例,笔者为了读者比较直接配置寄存和库开发的区别,笔者在此用了两种方式进行开发,希望读者能自己体会两种方式的优劣。
GPIO是开发STM32最基本的配置,所以掌握GPIO的配置显得尤为重要。要实现流水灯,一般步骤可以总结为如下:
1) GPIO 时钟使能;
2) GPIO 端口模式设置;
3) 初始化IO口;
4) 编写处理函数;
GPIO库函数
GPIO_DeInit 将外设 GPIOx 寄存器重设为缺省值;
GPIO_AFIODeInit 将复用功能(重映射事件控制和 EXTI 设置)重设为缺省值;
GPIO_Init 根据 GPIO_InitStruct 中指定的参数初始化外设 GPIOx 寄存器;
GPIO_StructInit 把 GPIO_InitStruct 中的每一个参数按缺省值填入;
GPIO_ReadInputDataBit 读取指定端口管脚的输入;
GPIO_ReadInputData 读取指定的 GPIO 端口输入;
GPIO_ReadOutputDataBit 读取指定端口管脚的输出;
GPIO_ReadOutputData 读取指定的 GPIO 端口输出;
GPIO_SetBits 设置指定的数据端口位;
GPIO_ResetBits 清除指定的数据端口位;
GPIO_WriteBit 设置或者清除指定的数据端口位;
GPIO_Write 向指定 GPIO 数据端口写入数据;
GPIO_PinLockConfig 锁定 GPIO 管脚设置寄存器;
GPIO_EventOutputConfig 选择 GPIO 管脚用作事件输出;
GPIO_EventOutputCmd 使能或者失能事件输出;
GPIO_PinRemapConfig 改变指定管脚的映射;
GPIO_EXTILineConfig 选择 GPIO 管脚用作外部中断线路; 1.5.1直接操作寄存器方式IAR版本完整代码
KEIL版本完整代码 1.5.2使用固件库方式IAR版本完整代码
KEIL版本完整代码 小贴士1上拉电阻
【1】当 TTL 电路驱动 CMOS 电路时,如果 TTL 电路输出的高电平低于 CMOS电路的最低高电平(一般为 3.5V),这时就需要在 TTL 的输出端接上拉电阻,以提高输出高电平的值。
【2】OC(集电极开路)门电路必须加上拉电阻,才能使用。
【3】为加大输出引脚的驱动能力,有的单片机管脚上也常使用上拉电阻。
【4】在 CMOS 芯片上,为了防止静电造成损坏,不用的管脚不能悬空,一般接上拉电阻产生降低输入阻抗,提供泄荷通路。
【5】芯片的管脚加上拉电阻来提高输出电平,从而提高芯片输入信号的噪声容限增强抗干扰能力。
【6】提高总线的抗电磁干扰能力。管脚悬空就比较容易接受外界的电磁干扰。
【7】长线传输中电阻不匹配容易引起反射波干扰,加上下拉电阻是电阻匹配,有效的抑制反射波干扰。
上拉电阻阻值的选择原则包括:
从节约功耗及芯片的灌电流能力考虑应当足够大;电阻大,电流小。
从确保足够的驱动电流考虑应当足够小;电阻小,电流大。
对于高速电路,过大的上拉电阻可能边沿变平缓。综合考虑
以上三点,通常在 1k 到 10k 之间选取。对下拉电阻也有类似道理对上拉电阻和下拉电阻的选择应结合开关管特性和下级电路的输入特性进行设定,主要需要考虑以下几个因素:
驱动能力与功耗的平衡。以上拉电阻为例,一般地说,上拉电阻越小,驱动能力越强,但功耗越大,设计是应注意两者之间的均衡。
下级电路的驱动需求。同样以上拉电阻为例, 当输出高电平时,开关管断开,上拉电阻应适当选择以能够向下级电路提供足够的电流。
高低电平的设定。不同电路的高低电平的门槛电平会有不同,电阻应适当设定以确保能输出正确的电平。以上拉电阻为例,当输出低电平时,开关管导通,上拉电阻和开关管导通电阻分压值应确保在零电平门槛之下。
频率特性。以上拉电阻为例,上拉电阻和开关管漏源级之间的电容和下级电路之间的输入电容会形成 RC 延迟,电阻越大,延迟越大。上拉电阻的设定应考虑电路在这方面的需求。
下拉电阻的设定的原则和上拉电阻是一样的
2 assert_param
在STM32的固件库和提供的例程中,到处都可以见到assert_param()的使用。如果打开任何一个例程中的stm32f10x_conf.h文件,就可以看到实际上assert_param是一个宏定义;在固件库中,它的作用就是检测传递给函数的参数是否是有效的参数。
所谓有效的参数是指满足规定范围的参数,比如某个参数的取值范围只能是小于3的正整数,如果给出的参数大于3,则这个assert_param()可以在运行的程序调用到这个函数时报告错误,使程序员可以及时发现错误,而不必等到程序运行结果的错误而大费周折。这是一种常见的软件技术,可以在调试阶段帮助程序员快速地排除那些明显的错误。它确实在程序的运行上牺牲了效率(但只是在调试阶段),但在项目的开发上却帮助你提高了效率。
当你的项目开发成功,使用release模式编译之后,或在stm32f10x_conf.h文件中注释掉对USE_FULL_ASSERT的宏定义,所有的assert_param()检验都消失了,不会影响最终程序的运行效率。在执行assert_param()的检验时,如果发现参数出错,它会调用函数assert_failed()向程序员报告错误,在任何一个例程中的main.c中都有这个函数的模板,如下: void assert_failed(uint8_t* file, uint32_t line){while (1){}}你可以按照自己使用的环境需求,添加适当的语句输出错误的信息提示,或修改这个函数做出适当的错误处理。
1、STM32F10xD.LIB是DEBUG模式的库库文件。
2、STM32F10xR.LIB是Release模式的库库文件。
3、要选择DEBUG和RELEASE模式,需要修改stm32f10x_conf.h的内容。#define DEBUG 表示DEBUG模式,把该语句注释掉,则为RELEASE模式。
4、要选择DEBUG和RELEASE模式,也可以在Options,C/C++,Define里填入DEBUG的预定义。这样,就不需要修改stm32f10x_conf.h的内容。
5、如果把库加入项目,则不需要将ST的库源文件加入项目,比较方便。但是,库的选择要和DEBUG预定义对应。 参考资料:http://www.makeru.com.cn/ 嵌入式学习交流群:561213221
|