目录
前言
这也是比较简单的一部分,我们就从这里正式开始咱们的32之路。
上面这些是基础,如果想看懂程序,了解即可。
一、GPIO是什么?
所有的嵌入式处理器内部均集成了通用输入输出(General Purpose Input Output,GPIO)接口,通俗地讲呢,其实就是你的开发版上面的插针,那个东西就是和你的单片机上面的IO口(GPIO)连起来了(连起来好用,方便开发)。 STM32的IO口被分成了若干组,PA、PB、PC等,每一组有16个管脚,PA0、PA1、PA2、PA3等等。
二、GPIO干什么
从 Output Input 这两个单词也可以看出来,他是做输入输出作用的。
我们的单片机一般就是做主控,来控制其他电路或者是电机,既然是控制肯定就要输出控制信号,所以说就需要IO口,来做输出。同样,单片机肯定也需要外部的信息,所以说也需要输入功能。
最基本的输出功能是由STM32控制引脚输出高、低电平,实现开关控制,如把GPIO引脚接入到LED灯,那就可以控制LED灯的亮灭,引脚接入到继电器或三极管,那就可以通过继电器或三极管控制外部大功率电路的通断。
三、GPIO怎么干
简单地说一下,这个图呢可以被分为两部分, 上面是输入,下面是输出。
从右往左看,先是引脚然后是保护二极管(作用是保护你的管脚不会被一个比3V3该有可能是5V高一点,或者比0V低一点的电压伤害,但要记住是一点),让后就分别进入到两个部分。
输入部分是,进入后经过上拉或者下拉电阻,然后是TTL肖特基触发器(TTL肖特基触发器其实可以理解为用肖特基管构成的施密特触发器,作用简单说就是将相对缓慢变化的模拟信号变成矩形信号,便于后面读取。)然后就可以被读取。
输出部分,是经过MOS管的导通与断开来控制输出电平的高低。可以看到,输出最高电平时VDD(单片机工作电压,2.0~3.6V,一般是3.3V),最低电平是Vss(单片机电源地,0V)。
三、如何用GPIO点亮一个LED灯
毫无疑问,我们肯定需要写代码嘛,通过我们写的代码,告诉单片机我们想干的事。
如果我们选择的功能是输入,那么我们需要的就是读取相关寄存器(可以理解为一个存储器,我们可以往里面写数,就可以控制单片机。同样,单片机的一些数据也存放在这里。)的值,就可以的得到相关IO口的电平,如果我们选择的是输出,我们需要做的就是控制MOS管的导通和关闭。
首先我们要做的确定我们要使用的IO口,我们应该查找原理图,然后找到LED灯对应的管脚。
我们可以看到,LED0对应的是PB5这个管脚,下面要做的就是写程序了。
1. GPIO_InitTypeDef
首先,我们要定义一个结构体,上面有提到,我们是通过往寄存器里面写数字来控制单片机,寄存器转化到程序里面被分成了许多结构体,我们要往里面写数字,也要通过结构体的方式。
GPIO_InitTypeDef GPIO_InitStructure;
这个 GPIO_InitTypeDef是通过typedef得到的一个宏定义,方便用它来定义结构体中的变量和GPIO寄存器类型结构相符合结构体。后面的那个GPIO_InitStructure就是我们定义的结构体(名字随意,我学习的时候就叫这个)。
typedef struct
{
uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */
GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;
下面让我们来分析这个结构体里面的成员,
1. GPIO_Pin的意思是哪个管脚,例如GPIO_Pin_5,代表的是PX5这个管脚,其中X=A、B、C、D等。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
2.GPIOSpeed_TypeDef GPIO_Speed,这里是一个的枚举定义,前面是枚举的类型,后面是枚举的名字。
GPIO 引脚速度: GPIO_Speed_2MHz (10MHz, 50MHz) ;
又称输出驱动电路的响应速度:(芯片内部在I/O口的输出部分安排了多个响应速度不同的输出驱动电路,用户可以根据自己的需要选择合适的驱动电路,通过选择速度来选择不同的输出驱动模块,达到最佳的噪声控制和降低功耗的目的。)
简单的理解就是,IO口的响应速度。
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
3.GPIOMode_TypeDef GPIO_Mode显而易见,这里又是一个枚举的定义,前面是类型,后面是名称。
typedef enum
{ GPIO_Mode_AIN = 0x0,
GPIO_Mode_IN_FLOATING = 0x04,
GPIO_Mode_IPD = 0x28,
GPIO_Mode_IPU = 0x48,
GPIO_Mode_Out_OD = 0x14,
GPIO_Mode_Out_PP = 0x10,
GPIO_Mode_AF_OD = 0x1C,
GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;
这里有不同的模式,如果我们选择输出的话,GPIO_Mode_Out_PP就是我们想要的,叫推挽输出,还有很多种方式,我就不细讲了,可以参考其他文章。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
2.GPIO_Init
当我们配置好上面的结构体成员后,我们就完成了大部分,但是还没有结束,你会发现,我们只是把我们的定义的结构体里面的值确定了,并没有放到单片机的寄存器里面,这时候肯定不行啊。
怎么放进去呢,别人在就想到了这一点,直接调用函数就可以了。
GPIO_Init(GPIOB, &GPIO_InitStructure);
可以看到,这个函数需要输入两个参数,一个就是A、B、C、D等,确定我们要控制哪一组管脚。
另一个参数是我们定义的结构体的地址。
举一个例子,我们定义的结构体就更好比是一个包裹,这个函数就是快递员,我们要告诉的我们寄哪个快递,寄到哪。
到这你可能感觉差不多了,其实还没有。我们是不是要告诉收快递的那个人,你有快递了,怎么告诉呢,来一句函数就OK了。
3.RCC_APB2PeriphClockCmd
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
可以看到,这个函数也有两个参数,第一个参数是使能哪一组管脚的时钟,第二个管脚是使能(ENABLE)。
使能外设时钟的原因https://blog.csdn.net/weixin_43217963/article/details/97792677注意:一定要先使能PB端口的时钟然后在配置GPIO
void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
}
四、初始化
初始化是一个比较重要的问题,很多新手在经历过千难万险把函数写好之后,把程序下进去,发现灯不亮。BBQ了,大部分原因都是初始化的原因。
可能很多新手创建了一个新的文件LED,里面有 led.c 和 led.h 这两个文件,把自己写的函数放到led.c里面了,这样也是可以的,不过大可不必,我还是建议直接写到 main.c 这个文件里面,毕竟我们只是点个灯。
就想这样
虽然没有写到 led.c里面简洁,但是初始化会方便很多(避免很多坑)。
如果你非要建立一个文件的话,千万记得在 led.h 里面声明 ,main.c文件要包括相关的头文件。
五、合成
把我们之前写的合到一起,像下面这样
但是你会发现,灯是一直在亮,这是因为单片机的执行速度的是很快的,一瞬间就完成了高低电平的设置,人的肉眼反应不过来。
所以需要延时函数,就是让灯等一会亮,也等一会灭。
延时
延时的方法有两种,第一种是我们可以直接调用延时函数(别人写好的函数),也可以自己写一个循环进行延时。
我们就是用第一种吧,需要做的就是包括对应的头文件,和初始化延时函数。
头文件
直接在主函数里面包括就行
初始化
加上一句 delay_init(); 即可
delay_ms(200); 就是延时200毫秒
delay_ms(500); 就是延时500毫秒
注意:单次延时时间不能超过1864ms
把延时函数加上后
灯就闪了起来 ~~~
六、两个灯
就像这样
简单讲解一下,首先就是端口时钟多了一个PE的,直接使用一个函数就可以,但是注意有一个 或 的步骤。
第二个地方就是,在配置PE5时,仍然使用了上一次的结构体。因为第二个灯也是五号管脚,不需要做其他修改。
第三个地方就是主函数,这里也很简单,就是多了两句话。