STM32 - 2
之前我们已经了解了一些STM32的基础知识,下面就开始正式的编程了。我们主要会使用STM32的HAL库,这些库函数已经在创建工程的时候初始化好了,一般来说,直接使用即可。
GPIO
片上外设的使能时钟
首先了解一下STM32的片上外设的使能时钟。STM32几乎所有的片上设备都有使能时钟,包括输入输出GPIO。不把使能时钟打开,对应的外设就不能用。STM32这么设计是为了降低功耗。
那怎么样启动使能时钟呢?以最小系统板上面的PC13为例,这个IO实际上就是GPIOC的13号引脚,那么我们就在main.c
里面使用如下代码就可以开启GPIOC的使能时钟:
|
|
初始化GPIO
在打开GPIO的使能时钟之后,就要初始化GPIO了。可以使用如下API初始化GPIO:
|
|
其中,GPIOC就是GPIO的类型,而后面的那个GPIO_InitStruct
则是各种初始化参数,如输入输出模式、输入输出引脚等。下面是初始化设置的代码:
|
|
向GPIO写入值(输出)
在初始化完毕之后,就可以使用HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
向对应的GPIO写入值了。参数都很容易理解,最后一个参数,如果是SET
,那么就是写1;如果是RESET
,就是写0。
OK,到这里,一个最简单的亮灯程序就写完了。后面,我们就深入了解一下GPIO的各种设置属性。
GPIO的设置
GPIO的设置主要是使用GPIO_InitTypeDef
来声明:GPIO_InitTypeDef GPIO_InitStruct = {0};
。GPIO_InitTypeDef
主要包含4个设置:
-
Mode:IO引脚的模式
我们在上面的示例代码中用到的,就是
GPIO_MODE_OUTPUT_PP
,即输出推挽模式(Output Push-Pull Mode)。推挽模式就是高低电平均有驱动能力的输出模式。此外,输出模式还有
GPIO_MODE_OUTPUT_OD
,即输出开漏模式。开漏输出高电平相当于高阻态,是没有驱动能力的。另外,还可以选择输入模式,即
GPIO_MODE_INPUT
和GPIO_MODE_AF_INPUT
。具体这些模式的区别,可以参考下 https://www.bilibili.com/video/BV1th411z7sn 15分钟左右的讲解,很详细 -
Pin:就是第几号IO引脚
如果要设置第13号引脚,就可以使用:
GPIO_InitStruct.Pin = GPIO_PIN_13;
,如果要设置多个引脚,可以使用|
:1 2
// 同时设置13和14号引脚 GPIO_InitStruct.Pin = GPIO_PIN_13 | GPIO_PIN_14;
-
Pull:用在输入模式里面,即上拉/下拉电阻,用来设置输入悬空的时候,输入口是高电平(上拉电阻PULLUP)还是低电平(下拉电阻PULLDOWN)还是就把它悬空(NOPULL),详见 https://www.bilibili.com/video/BV1th411z7sn 9分钟30秒左右的讲解
-
Speed:设置IO引脚允许的最大速度
看f411的源代码可以到这里可以设置4个级别的IO速度,分别是2M、12.5M~50M、25M~100M和50M~200M。速度越快,IO的读取频率就越高,也就越耗电。
1 2 3 4
#define GPIO_SPEED_FREQ_LOW 0x00000000U /*!< IO works at 2 MHz, please refer to the product datasheet */ #define GPIO_SPEED_FREQ_MEDIUM 0x00000001U /*!< range 12,5 MHz to 50 MHz, please refer to the product datasheet */ #define GPIO_SPEED_FREQ_HIGH 0x00000002U /*!< range 25 MHz to 100 MHz, please refer to the product datasheet */ #define GPIO_SPEED_FREQ_VERY_HIGH 0x00000003U /*!< range 50 MHz to 200 MHz, please refer to the product datasheet */
最后,需要注意传进去的GPIO_InitTypeDef
是一个指针,所以需要使用&GPIO_InitStruct
传地址进去。
GPIO输入
我们就用最简单的开关来学习GPIO口的输入。
首先复习一下在配置输入模式的时候,有一个Pull
字段。这个字段代表着在输入悬空的时候,默认是上拉电阻还是下拉电阻或者是悬空。这玩意有什么用呢?我们看一下下面的开关电路:
很好理解,开关按下的时候,GPIO输入就接地,即低电平0。那这个上下拉电阻有什么关系呢?
看下左边的图,如果开关断开,这个时候PA0输入就悬空了。如果这个时候GPIO口的配置为悬空,那这个引脚就真的悬空了,其输入就不确定。而如果这个时候,GPIO口的输入配置为下拉电阻,此时PA0悬空,输入就被下拉电阻拉到了低电平,那这个按键实际上就没用了。因此,在按键一边接地的时候,如果没有手动配置上拉电阻(右边的图),那就必须把GPIO的输入设置为上拉电阻模式(PULLUP),来保证开关断开时输入为高电平。
当然,如果像右边一样,外面单独配置了上拉电阻,那么PA0的输入模式配置为上拉电阻模式或者悬空时都可以的。
❗ 注意:由于STM32系列并不是所有的开关都有下拉电阻模式,因此,在习惯上,只要是开关都采用低电平触发的方式,即一边接地一边接GPIO。
输入模式
按键消抖
一般来说,按下按键之后,按键会有一个5-10ms的抖动,然后才会稳定在高电平。消除抖动的策略很简单,在检测到按键之后,延迟20ms再进入完全按下时的那个while循环。然后就只要按下就一直在这个while循环里,直到松开按键,跳出while循环,然后再delay20ms。对于按键的要求比较高的场合,可能还需要后续的滤波。
GPIO输出
上面我们已经了解了GPIO输出模式的推挽模式(PP),即输出的时候,上下两个MOS管均可以导通(同时只有一个能通),然后输出就能够被下推到0V或上拉到3.3V,这样高低电平都有强驱动能力。
除了推挽模式之外,输出还有开漏模式(OD)。其实开漏模式和推挽模式的区别就在于,开漏模式的上面接高电平的MOS管一直是断开的。这样的话,只有下面的MOS可以导通,那就低电平有强驱动能力。而在输出1的时候,上下MOS均断开,输出就是一个高阻抗状态,就相当于这个输出直接断开了(不是输出高电平)。
两个输出模式的区别:
开漏模式的应用场景
驱动IED
PP和OD模式都能驱动LED,驱动电路略有不同:
区别是除了电路不同之外,两个模式驱动LED还有如下区别:
- PP模式是输出1灯亮,而OD是输出0灯亮
- PP模式是芯片向外供电,而OD是外面的电源供电,芯片吸电
- OD模式相对于PP模式,理论上能够承载的最大电流多一些
- OD模式能够降低芯片功耗
逻辑线与
逻辑线与最多是用在I2C通信总线上。
I2C是一种通信总线,它有两个线:一个是SDA用于传数据,一个是SCL用于传时钟。其接线如图:
I2C的所有SDA和SCL的IO都是OD模式,这样的话,只要有一个输出是0,那么对应的SDA就会被拉到0V输出低电平。而只有所有的SDA或者SCL全是1,由于是高阻态,对应输出的SDA或SCL就会被上拉电阻拉到VDD,输出高电平。这就是逻辑线与。
匹配不同电压等级
STM32输出的高电平一般是3.3V,但是如果我们想要去驱动需要5V输入的芯片应该如何做呢?下面就是使用开漏模式驱动5V输入的原理图:
可以看到,在STM32输出0的时候,那右边芯片的DIN输入就是0V低电平。如果STM32输出1的时候,由于输出口为高阻态,那么DIN就会被外接电源拉到5V,也就是右边芯片需要的高电平。这就实现了不同电压水平的匹配。