当前位置: 首页 > news >正文

江协科技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 */}

效果很好,完成!

相关文章:

  • 每日Prompt:生成自拍照
  • YOLOv8 目标检测算法深度解析
  • flutter长列表 ListView、GridView、SingleChildScrollView、CustomScrollView区别
  • 汽车Wafer连接器:工业设备神经网络的隐形革命者
  • 豆粕ETF投资逻辑整理归纳-20250511
  • c++函数调用运算符及类型转换运算符重载
  • 线程同步学习
  • JSP与JSTL:EL表达式与MVC分层模式的完美结合
  • 3D个人简历网站 5.天空、鸟、飞机
  • 院校机试刷题第五天:1912循环位移、1913礼炮车
  • SQL语句执行问题
  • 使用 Kaniko来构建镜像
  • Mysql 索引概述
  • Vue-事件修饰符
  • 怎么用Origin画出MATLAB效果的3D时频图
  • 哈希表(2):
  • 劳特巴赫trace32烧录方法
  • Compose笔记(二十三)--多点触控
  • JWT令牌验证
  • STM32入门笔记(06):STM32Cube 生态系统 (STM32CubeMX图形工具STM32CubeIDE 集成开发环境)(HAL库)
  • 陕西:未来一周高温持续,继续发布冬小麦干热风风险预警
  • 体坛联播|水晶宫队史首夺足总杯,CBA总决赛爆发赛后冲突
  • 上海国际碳中和博览会下月举办,首次打造民营经济专区
  • 中方是否计划解除或调整稀土出口管制?外交部回应
  • 既是工具又是食物,可食用机器人开启舌尖上的新科技
  • 贝壳一季度收入增长42%:二手房市场活跃度维持在高位