本帖最后由 GCT 于 2022-7-17 23:28 编辑
入门第十二课《GPIO输出—使用固件库点亮LED》 第十三课《GPIO输入—按键检测》 课后作业汇总 入门篇 第十二课题目:
之前几节课讲的都是从寄存器方式“进化”到库函数方式的过程,如何自己创建库。 ST官方也有它自己的库,对于不同型号开发板、不同内核外设文件都写进了库中,可以说很完备。 在对于寄存器方式有了一个基本了解以后,是时候使用官方的库效率更高地进行开发了。
按照课上讲的步骤创建了一个库函数开发模板,不得不承认创建过程比寄存器方式略微复杂,需要导入很多官方文件。 创建方式步骤可以看火良大大的第十一课《新建工程—固件库版本》
开发者自己创建的文件都放在User文件夹里
仍然是熟悉的点LED灯,在User文件夹创建一个文件夹led,里面创建两个文件:bsp_led.c & bsp_led.h (BSP:Board Support Package 板级支持包 由开发者编写,只兼容特定开发板) bsp_led.c:定义初始化函数。 bsp_led.h:对于硬件的宏定义(提高可移植性)、其他宏定义、bsp_led.c中函数的申明。
在 bsp_led.c 创建一个函数用来初始化时钟、一个函数用来初始化GPIO:
里面使用的函数、结构体、参数的定义都可以在官方库对应的.c/.h文件里找到 比如
的定义就在 stm32f10x_rcc.c 中能找到定义:
输出模式的定义(stm32f10x_gpio.h)
怎么知道外设有多少函数? 方法1.查找官方手册(英文 固件库自带)
方法2.看固件库文件的函数申明部分(推荐)
stm32f10x_gpio.h包含了全部的gpio函数申明:
每个函数的定义、用什么参量、什么作用可以在stm32f10x_gpio.c找到
根据官方函数定义的注释,RCC时钟使能的参数:
时钟使能函数编写:
CLK形参意为RCC寄存器要使能相应外设所要操作的寄存器位,定义也在rcc的头文件。 官方已经写好每一位对应什么,与参考手册一致。
初始化GPIO函数编写(参数:GPIO端口号(GPIO类型地址)、引脚号): 初始化内容和之前写的完全一致:定义新的初始化结构体类型,确定引脚=PIN、输出模式=推挽输出、输出速度=50MHz,初始化相应GPIO。
为了便于移植,在头文件里定义了引脚别名:
如果使用setbits和resetbits函数控制LED的开和关,如果不看参考手册无法得知用哪个开LED哪个关LED,可读性还是不够高。 火良大大教了一招,用宏定义来定义一个新的函数,通过形参的状态量判断使用哪个setbit函数。 状态量定义为ON=True,OFF=False。
定义完以后LED()函数就可以使用了,参数:某颜色的LED、开/关,方便了很多。
做了这么多准备工作以后,可以开始写main.c了 头文件引用"stm32f10x.h"(上帝之手 总宏定义)和bsp_led.h(对于led的宏定义)
将控制灯的步骤封装整合到一个函数中,并加入延时:
流水灯完成(红->绿->蓝 交替)
实现代码: bsp_led.c: - //bsp: board support package 板级支持包
- #include "bsp_led.h"
- void GPIO_RCC_Config(int CLK)
- {
- RCC_APB2PeriphClockCmd(CLK, ENABLE);
- }
- void GPIO_LED_Config(GPIO_TypeDef* PORT, int PIN)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
-
- GPIO_InitStruct.GPIO_Pin = PIN;
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(PORT, &GPIO_InitStruct);
- }
复制代码
bsp_led.h: - #ifndef __BSP_LED_H
- #define __BSP_LED_H
- #include "stm32f10x.h"
- #define LED_Red GPIO_Pin_5
- #define LED_Green GPIO_Pin_0
- #define LED_Blue GPIO_Pin_1
- #define LED_GPIO_PORT GPIOB
- #define LED_GPIO_CLK RCC_APB2Periph_GPIOB
- #define ON 1
- #define OFF 0
- // \C语言 里面叫续行符,后面不能有任何东西
- #define LED(color,stat) if(stat)\
- GPIO_ResetBits(LED_GPIO_PORT, color);\
- else GPIO_SetBits(LED_GPIO_PORT, color);
- void GPIO_RCC_Config(int CLK);
- void GPIO_LED_Config(GPIO_TypeDef* PORT, int PIN);
- #endif /*__BSP_LED_H*/
复制代码
main.c:
- #include "stm32f10x.h" // 相当于51单片机中的 #include <reg51.h>
- #include "bsp_led.h"
- void LED_Blink(int LED_Color);
- void delay( uint32_t count);
- int main(void)
- {
- // 来到这里的时候,系统的时钟已经被配置成72M。
- GPIO_RCC_Config(LED_GPIO_CLK);
- while(1)
- {
- LED_Blink(LED_Red);
- LED_Blink(LED_Green);
- LED_Blink(LED_Blue);
- }
- }
- void LED_Blink(int LED_Color)
- {
- GPIO_LED_Config(LED_GPIO_PORT, LED_Color);
- LED(LED_Color, ON);
- delay(0xFFFFFF);
- LED(LED_Color, OFF);
- //delay(0xFFFFFF);
- }
- void delay( uint32_t count)
- {
- for(;count!=0;count--);
- }
复制代码
第十三课题目:
在User文件夹再创建一个文件夹key,里面创建两个文件:bsp_key.c & bsp_key.h bsp_key.c:定义初始化函数,按键检测函数。 bsp_key.h:对于硬件的宏定义(提高可移植性)、bsp_key.c中函数的申明。
如图为按键的原理图:
可知 按钮1 使用GPIOA 0号引脚;按钮2 使用GPIOC 13号引脚。
和bsp_led.c的中的初始化相同,bsp_key也需要一个初始化函数配置输入
内容:定义新的初始化结构体类型,确定引脚=PIN、输入模式=浮空输入,初始化相应GPIO。 按键时钟使能直接用led定义的时钟使能函数即可,不用再定义一个了。
还需定义一个函数用于检测按键
其中GPIO_ReadInputDataBit用来读取输入时输入寄存器对应位状态
先判断是否按下(寄存器对应的位为1),如果条件成立进入循环。循环的作用是判断是否松手,只有松手才跳出循环。
返回值KEY_ON、KET_OFF的宏定义:
对于按键端口引脚的宏定义和函数的声明: 按键1和按键2使用不同的GPIO端口,所以时钟也不同。
led的程序大体上不需要改变,头文件需要增加一个宏定义的函数定义:
异或是为了让相应位的状态一直取反,因为异或1会一直改变那一个位。 其他的部分和上面的点亮LED灯部分一致
这里唯一有区别的地方就是初始化LED函数最后加了一个SetBits()。 因为指南针开发板的LED灯是置0有效,初始化以后的值就是0,所以一开始灯是亮的,需要把它关掉。
主函数的初始化部分
使用|来控制多个引脚,这样就不用重复写相同的函数了
使用按键检测函数判断状态是否为开,然后执行先前定义的led状态切换。
最终代码: bsp_led.c - #include "bsp_led.h"
- void GPIO_RCC_Config(int CLK)
- {
- RCC_APB2PeriphClockCmd(CLK, ENABLE);
- }
- void GPIO_LED_Config(GPIO_TypeDef* PORT, int PIN)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
-
- GPIO_InitStruct.GPIO_Pin = PIN;
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(PORT, &GPIO_InitStruct);
-
- GPIO_SetBits(PORT, PIN);
- }
复制代码
bsp_led.h - #ifndef __BSP_LED_H
- #define __BSP_LED_H
- #include "stm32f10x.h"
- #define LED_Red GPIO_Pin_5
- #define LED_Green GPIO_Pin_0
- #define LED_Blue GPIO_Pin_1
- #define LED_GPIO_PORT GPIOB
- #define LED_GPIO_CLK RCC_APB2Periph_GPIOB
- //^= 异或运算符 与1异或改变 与0异或不变
- #define LED_TOGGLE(color) {LED_GPIO_PORT->ODR ^=color;}
- void GPIO_RCC_Config(int CLK);
- void GPIO_LED_Config(GPIO_TypeDef* PORT, int PIN);
- #endif /*__BSP_LED_H*/
复制代码
bsp_key.c - #include "bsp_key.h"
- void GPIO_KEY_Config(GPIO_TypeDef* PORT, int PIN)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
-
- GPIO_InitStruct.GPIO_Pin = PIN;
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
-
- GPIO_Init(PORT, &GPIO_InitStruct);
- }
- uint8_t KEY_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
- {
- if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin)==KEY_ON)
- {
- //松手检测
- while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin)==KEY_ON);
- return KEY_ON;
- }
- else
- return KEY_OFF;
- }
复制代码
bsp_key.h - #ifndef __BSP_KEY_H
- #define __BSP_KEY_H
- #include "stm32f10x.h"
- #define KEY_ON 1
- #define KEY_OFF 0
- #define KEY1_GPIO_PIN GPIO_Pin_0
- #define KEY1_GPIO_PORT GPIOA
- #define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
- #define KEY2_GPIO_PIN GPIO_Pin_13
- #define KEY2_GPIO_PORT GPIOC
- #define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
- void GPIO_KEY_Config(GPIO_TypeDef* PORT, int PIN);
- uint8_t KEY_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
- #endif /*__BSP_KEY_H*/
复制代码
main.c - #include "stm32f10x.h" // 相当于51单片机中的 #include <reg51.h>
- #include "bsp_led.h"
- #include "bsp_key.h"
- int main(void)
- {
- // 来到这里的时候,系统的时钟已经被配置成72M。
- GPIO_RCC_Config(LED_GPIO_CLK|KEY1_GPIO_CLK|KEY2_GPIO_CLK);
- GPIO_LED_Config(LED_GPIO_PORT,LED_Green|LED_Red);
- GPIO_KEY_Config(KEY1_GPIO_PORT,KEY1_GPIO_PIN);
- GPIO_KEY_Config(KEY2_GPIO_PORT,KEY2_GPIO_PIN);
- while(1)
- {
- if(KEY_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) ==KEY_ON)
- LED_TOGGLE(LED_Green);
- if(KEY_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN) ==KEY_ON)
- LED_TOGGLE(LED_Red);
- }
- }
复制代码
|