-
初始框架
转述不利于理解,我们直接上图上操作,操作起来
下面图来自于我去年写的一个初学者按键点灯简洁的代码新手易于理解,易懂又可以看出效果
但是实际项目中不可能这么用这里边有很多坑。
这里采用之前的文章为切入点熟悉一下正常操作流程。。。
#include "stm32f1xx_hal.h" #include "gpio.h" #define LED_R GPIO_PIN_0; #define KEY_SW8 GPIO_PIN_13; #define LED_RR() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET) #define sw8 HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) void led_init(void) { GPIO_InitTypeDef gpiotypedef; __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); gpiotypedef.Mode = GPIO_MODE_OUTPUT_OD; gpiotypedef.Pin = LED_R; gpiotypedef.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB,&gpiotypedef); LED_RR(); gpiotypedef.Mode = GPIO_MODE_AF_INPUT; gpiotypedef.Pin = KEY_SW8; gpiotypedef.Pull = GPIO_PULLDOWN; HAL_GPIO_Init(GPIOC,&gpiotypedef); }
上述是GPIO初始化代码,配置结构体,传入结构体初始化,然后设置引脚的逻辑电平。
这是正常写法但是如果项目后加led灯你也要不停的在里边添加代码显得很麻烦
所以来看下图
这是stm32f411的一个工程
重构了GPIO结构体方便自己添加
下图结构体参数为GPIO总控、那个引脚、什么模式、以及上边的枚举类型时钟
LEDLISTMAX 这是一个宏来计算结构体大小,这是c语言知识不细说了
我们循环初始化结构体然后传入参数来利用HAL库底层帮我们做初始化后续这块代码不需要动
下边我把文件代码传上去先一个个看
#define LEDLISTMAX sizeof(ledlist) / sizeof(LedInfo) /** *********************************************************** * @brief 初始化led为关闭 * @param * @return *********************************************************** */ void Led_Init(void) { for(uint8_t i = 0;i<LEDLISTMAX;i++) { switch(ledlist[i].clock) { case A: __HAL_RCC_GPIOA_CLK_ENABLE(); break; case B: __HAL_RCC_GPIOB_CLK_ENABLE(); break; case C: __HAL_RCC_GPIOC_CLK_ENABLE(); break; default: break; } GPIO_InitTypeDef inittypedef; inittypedef.Pin = ledlist[i].pin; inittypedef.Mode = ledlist[i].mode; inittypedef.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(ledlist[i].gpio,&inittypedef); HAL_GPIO_WritePin(ledlist[i].gpio, ledlist[i].pin, GPIO_PIN_RESET); } }
传入对应的序号,结构体数组0号成员就传入0,数组下标为1就传入1依次类推
这是反转电平函数
这是置位高电平函数,以及低电平函数
这是完整代码
#include "hal_led.h" /** *********************************************************** * @brief 定义枚举类型的GPIO时钟 * @param 0=A;1=B;C=2;D=3; * @return *********************************************************** */ typedef enum{ A = 0, B, C, }GPIO_CLOCK; /** *********************************************************** * @brief 定义端口结构体类型 * @param * @return *********************************************************** */ typedef struct{ GPIO_TypeDef *gpio; uint32_t pin; uint32_t mode; GPIO_CLOCK clock; }LedInfo; LedInfo ledlist[] = { {GPIOB,GPIO_PIN_4,GPIO_MODE_OUTPUT_PP,B}, //{GPIOB,GPIO_PIN_5,GPIO_MODE_OUTPUT_PP,B}, }; #define LEDLISTMAX sizeof(ledlist) / sizeof(LedInfo) /** *********************************************************** * @brief 初始化led为关闭 * @param * @return *********************************************************** */ void Led_Init(void) { for(uint8_t i = 0;i<LEDLISTMAX;i++) { switch(ledlist[i].clock) { case A: __HAL_RCC_GPIOA_CLK_ENABLE(); break; case B: __HAL_RCC_GPIOB_CLK_ENABLE(); break; case C: __HAL_RCC_GPIOC_CLK_ENABLE(); break; default: break; } GPIO_InitTypeDef inittypedef; inittypedef.Pin = ledlist[i].pin; inittypedef.Mode = ledlist[i].mode; inittypedef.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(ledlist[i].gpio,&inittypedef); HAL_GPIO_WritePin(ledlist[i].gpio, ledlist[i].pin, GPIO_PIN_RESET); } } /** *********************************************************** * @brief 反转Led * @param led编号 * @return *********************************************************** */ void Led_toggle(uint8_t LedIndex) { if(LedIndex >= LEDLISTMAX) { return; } HAL_GPIO_TogglePin(ledlist[LedIndex].gpio, ledlist[LedIndex].pin); } /** *********************************************************** * @brief 熄灭Led * @param led编号 * @return *********************************************************** */ void Led_off(uint8_t LedIndex) { if(LedIndex >= LEDLISTMAX) { return; } HAL_GPIO_WritePin(ledlist[LedIndex].gpio, ledlist[LedIndex].pin,GPIO_PIN_RESET); } void Led_on(uint8_t LedIndex) { if(LedIndex >= LEDLISTMAX) { return; } HAL_GPIO_WritePin(ledlist[LedIndex].gpio, ledlist[LedIndex].pin,GPIO_PIN_SET); }
到时候只需在初始化结构体时候添加自己的结构体成员放到数组中即可,不过这种代码还是和底层代码混在一起并不是最高优的led驱动框架,真正的需要和底层硬件库解耦。
不过这种做基础的项目也够用了,有利于扩展和维护性。
-
寄存器驱动led
寄存器的话代码比较少我们只需掌握指针既可以操作指定位置
int a = 10; int *b = &a; //这样大家应该不陌生吧,标准指针指向变量 //这里思考一下&a = 多少? //。。。 //&a = 内存中某一块地址,所以*b = 这块地址 //大家要有一个概念c语言地址,地址上有东西,地址 = 你家小区,你住在小区上这是俩个东西
我们打开103的手册可以看到
我们发现了有个偏移量的东西这是什么?
所以我们来看这里
//先应该定义一个指针 //指向一块地址 //配置为输出模式 //从新指向一块地址 //使能相应的GPIO //再次从新指向一块地址 //然后利用(置位高低电平) uint8_t *temp; temp = (uint8_t *)(0x40010C00 + 0x00); *temp |= (1<<3); temp = (uint8_t *)(0x40021000 + 0x18); *temp |= (1<<3); temp = (uint8_t *)(0x40010C00 + 0x0C); *temp |= (1<<0); //这三步利用指针可以更改数据的道理,在你家小区里不同的人家中篡改他家东西你可以这么理解 //你在疯狂作案所以小心被抓到,所以(谨慎行事!) //所以说寄存器点灯很简单这里我就不传手册图片了,大家可以自行看看地址思考一下
寄存器点灯代码量少没有函数掺杂执行效率高
但是不利于项目开发,周期太长你要不停的去对手册浪费时间
而现在芯片执行效率很快也就一行代码一个机器周期的时间而已