江协科技GPIO输入输出hal库实现
首先先介绍一下GPIO在hal库里面的函数
GPIOhal库函数介绍
GPIO在hal库里面有两个文件,一个hal_gpio.h一个hal_gpio_ex.h
第一个文件主要存放的就是hal库里面对gpio的相关函数以及GPIO配置的结构体,还有hal库与标准库的一大区别回调函数。以及一些对gpio配置的检测宏定义
第二个文件主要存放的就是AFIO的相关宏定义,比如说AFIO的事件输出中断引脚设置,AFIO的引脚重映射功能宏定义。(AFIO的两个主要功能就是重映射以及输出中断引脚的选择。重映射:对于一个GPIO口,硬件会定义它的相关外设功能,如果两个外设同时用到一个引脚,就得看他有没有重映射功能了,如果有,就可以通过AFIO的重映射功能把另一个功能放到别的GPIO口上,但是如果没有就得对引脚的设计进行修改了。输出中断引脚的选择:对于多个GPIOX对应的PIN口,同一时刻只有一个能正常中断输出,我们通过AFIO来对这几个GPIOX的PIN口进行仲裁判断,选择我们要运用的GPIOX口,比如说Pin8的中断引脚,我们可以选择为GPIOA口也可以选择为GPIOB口,但是PIN8一次性只能选择一个GPIOX口)。
如图为STM32F103C8T6的引脚定义图
hal库的外设初始化,比标准库的初始化更加的严谨和丰富。
typedef struct
{uint32_t Pin; /*GPIO对应的Pin引脚*/uint32_t Mode; /*GPIO输入输出模式*/uint32_t Pull; /*GPIO对应的引脚上下拉设置*/uint32_t Speed; /*GPIO输出速率*/
} GPIO_InitTypeDef;
首先就是一样的结构体配置GPIO口,下面就是GPIO在hal库里面的所有库函数了
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
这个函数就是用来初始化我们GPIO口的,参数为GPIOx和GPIO的初始化结构体,和标准库的几乎一模一样,DeInit函数也和标准库的一样是恢复到默认状态,释放资源并清除配置。
而对于标准库的GPIO口的读取和写入,hal库只剩下Read_Pin和Write_Pin两个函数,且多了一个电平翻转函数TogglePin。
如果我们需要锁定GPIO的端口配置的话,和标准库一样给我们提供了一个LockPin的函数
接着就是对应的GPIO_Pin的中断函数,GPIO_EXTI_IRQHandler,学完了标准库再来看这个就不会很陌生了。
相比于标准库,hal库多了一个Callback回调函数,在hal库里面,回调函数才是真正处理数据的地方,而中断服务函数IRQHandler只是在函数里面调用回调函数而不对数据进行处理。
GPIOCubeMx初始化步骤
GPIO的hal库初始化和标准库是一模一样的,就是函数代码改变了
思路是一样的:首先打开GPIOX的时钟,然后结构体初始化GPIO口
这个我们通过CubeMx能很快速的设置好我们需要的GPIO口的初始化代码
在创建文件的界面下找到我们的STMF103C8T6芯片。可以设置为收藏方便我们的使用
双击右下角的芯片,进入芯片设置界面
首先就是点开System Core 里面设置SYS里面的Debug内容为SW,也就是江科大里面的USB转串口的串行线下载方式,如果不设置会导致芯片的锁死,当你遇到编译的时候芯片报错无法连接,在MDK里面看Debug的设置,可以检测到Stlink但是检测不到芯片,大概率就是这个问题了。解决方法的问题就是修改完Debug方式后,通过Bootloader的下载方式进行一次程序的下载。
然后对于我们要设置的引脚在右侧的芯片上直接点击在配置即可,这里我们按照江科大的引脚来配置,选择PA0,推挽输出模式,速度为高速(50Mhz,hal库还可以设置为超高速100Mhz)
在CubeMx里面我们还可以对引脚进行命名,GPIO output level是用来设置GPIO口上电时的电平的,我们按照标准库默认为低电平。
这样就完成了GPIO的初始化,相比于标准库的初始化配置,速度和效率快上了无数倍。
接着就是配置我们的时钟树,也可以直接一步到位
首先在RCC这里把HSE和LSE都配置成CryStall.ceramic Resonator模式即可,也可以不需要配置LSE,因为我们现在只需要对CPU的主时钟进行配置。
接着直接在蓝色框的HCLK里面输入72点击回车,CubeMx就能帮你自动配置好系统的时钟树了。
设置好项目名称和Toolchain即可,我们使用的MDK,所以这边就选择MDK-ARM即可,版本按照你的MDK版本设置即可,CubeMx的下载和固件配置去看正点原子的课程即可,视频总会比文字讲的更加清楚。
Code Generator推荐这个配置,只使用当前运用到的文件,且生成对应的外设.c.h文件
再点击右上角的GENERATE CODE即可生成成功。
如果生成不成功就是你的固件和ARM版本不适应,自己重装升级ARM就行了,教程CSDN上很多
GPIO的输出:LED闪烁
我们只需要在CubeMx给我们配置好的文件里面,在主函数while循环实现我们的闪烁代码即可。
在while循环里面,对电平进行翻转且延时一段时间。
我们直接使用 HAL_GPIO_TogglePin函数即可,在使用hal库内部自己的延时函数HAL_Delay来进行延时,hal_Delay里面的数字对应延时的毫秒数。
while (1){/* USER CODE END WHILE */HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_0);HAL_Delay(500);/* USER CODE BEGIN 3 */}
我们的代码最好是写在他给我们定义的地方,这样我们再次进行CubeMx初始化这个文件的时候就不会对我们的代码进行覆盖。
已经会点一个灯了剩下的LED流水灯程序也是如法炮制即可,就不再写相关程序了。
GPIO的输入:按键控制LED灯
首先就是通过GPIO口的输入功能实现按键功能,接着在主函数里面对按键键值进行判断即可。
CubeMx初始化
通过PB1和PB11作为按键的输入,控制PA1PA2,两个端口的输出,进而实现按键控制Led灯亮灭的功能。
这边我就使用了用户自定义变量名称,这样也有利于我们的代码的复用,输出和上面的设置一样,输入为上拉输入模式,输入就不用设置输出速率了,但是不能不设置,CubeMx里面会先把结构体全部初始化为0,再开始赋值,避免因为有的结构体变量没有赋值而出现问题。
输入输出配置完成后,我们再完成按键的文件:
对于按键的输入,我们就得读取GPIO口的电平,上拉输入,所以默认为高电平,按键按下后变为低电平被检测。
我们使用到了ReadPin函数来读取GPIO口的状态,Delay用来延时消除按键抖动,这部分江科大应该是讲的很详细的。
uint8_t Key_Read(){uint8_t temp = 0;if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET){HAL_Delay(10);temp = 1;while(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET);HAL_Delay(10);}if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET){HAL_Delay(10);temp = 2;while(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET);HAL_Delay(10);}return temp;
}
Key底层文件的定义和调用就不多说了,我们定义的用户变量名都会在main.h里面显示,我们要查询就可以在这里进行查询.
这样的编程习惯有助于我们以后修改变量,维护代码,只需要在这边对变量名定义的部分进行修改就行了。
引用头文件以及变量定义的部分我就不多赘述了。
while (1){/* USER CODE END WHILE */Key_Num = Key_Read();if(Key_Num == 1){HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);}else if(Key_Num == 2){HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);}/* USER CODE BEGIN 3 */}
效果很好,完成!