cubeIDE开发, stm32的GPIO原理、cubeMX配置及底层源码实现分析

news2024/11/26 23:32:59

一、GPIO介绍

        1.1 GPIO 简述

        GPIO(General purpose input/output,通用型输入输出),一个引脚可以用于输入、输出或其他特殊功能,PIN脚依现实需要可作为通用输入(GPI)或通用输出(GPO)或通用输入与输出(GPIO),并多个GPIO一起组合可实现诸如UART、SPI等外设功能。

        GPIO是通过寄存器操作来实现电平信号输入输出的,对于输入,通过读取输入数据寄存器(IDR,Input Data Register)来确定引脚电位的高低;对于输出,通过写入输出数据寄存器(ODR,Output Data Register)来让这个引脚输出高电位或者低电位;对于其他特殊功能,则有另外的寄存器来控制它们。GPIO的输出通常可以输出0/1信号,用来实现如LED灯、继电器、蜂鸣器等控制,而GPIO的输入可识别0/1信号,用来实现开关、按键等等动作或状态判定。

       1.2 STM32 GPIO寄存器

         STM32中GPIO的寄存器是MCU分配的一个连续存储区域,按存储地址划分出不同功能寄存器,如下图所示,有Port mode寄存器、Output Type寄存器等等。

         在HLA库中,这些寄存器的数值(宏定义)通常会放在/Drivers/CMSIS/Device/ST/STM32L4xx/Include/stm32XXX.h头文件中,供开发者调用,例如STM32L496VGTx的开发板,该头文件具体名是stm32l496xx.h,自8977行起到10008行,具是按上述表宏定义了GPIO各寄存器的各个数据位可设置数据值(偏移(POS)及相应数值)。

        1.3 GPIO引脚与标记

         在一个STM32 MCU芯片中,所有引脚本质都是GPIO引脚,只是鉴于全GPIO引脚开发者使用起来比较不方便,于是才通过各个GPIO组合形成针对各个外设的IO接口。在CubeMX中,以STM32L496VGTx芯片为例,可以直观看到该芯片有100个引脚:LQFP100,LQFP是封装,100指的是引脚数。

        STM32的MCU标记一般有两种,直插IC以半圆条口为标记,贴片IC以小圆点为标记,也有缺口及小点都有标记的。通常将标记圆点或缺口正面朝左上角,左上角的下方脚为第一脚,依次围绕芯片中心逆时针计数,在旋转360度以后,左上角右方角为最后一脚。如上图所示,STM32L496VGTx引脚序号如蓝色字体及箭头所示标记。STM32的MCU的引脚标记有三种方式:

        一种是基于上面所述的100个序号排序P1、Pn等,这种标记目前一般是在直接操作寄存器地址或使用标准库中使用,目前HLA库、LL库已经不提供直接地址偏移方式的API了,而是给没一组分组提供了独立寄存器来处理该组引脚;

        第二种是标准库或HLA库宏定义了引脚分组,一般为A、B、C、...,按大写字母次序网线,目前支持到16组类别,每组为PX0~PX15,其标记就是分组名+0~15序号,例如GPIOB+GPIO_PIN_3。

        注意,目前STM32的MCU设计是最大16组类别,支持到256引脚(16*16),通过STM32命名规范可以得知,STM32最做引脚支持到256个,但一般会少于引脚数/16的组数,因为不少引脚在芯片设计时就特别支持了其引脚标识,如VSS、VDD等。另外也不是每组必须0~15序号全用完,分组主要是方便设计及使用时进行引脚区分而已。

         目前HLA库主要就是这种方式,例如来看看GPIO读取数据的API(HAL_GPIO_ReadPin)实现,主要就是根据引脚序号(0~15)来读取IDR寄存器内对应数据位的数值。

         GPIO分组引脚地址在HLA宏定义数值如下。

#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected    */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected    */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected    */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected    */
#define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected    */
#define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected    */
#define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected    */
#define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected   */
#define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected   */
#define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected   */
#define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected   */
#define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected   */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected   */
#define GPIO_PIN_All               ((uint16_t)0xFFFF)  /* All pins selected */

        而GPIO各个分组的IDR寄存器为32bit宽度的数据,目前只用到低16位字段。

typedef struct
{
  __IO uint32_t MODER;       /*!< GPIO port mode register,               Address offset: 0x00      */
  __IO uint32_t OTYPER;      /*!< GPIO port output type register,        Address offset: 0x04      */
  __IO uint32_t OSPEEDR;     /*!< GPIO port output speed register,       Address offset: 0x08      */
  __IO uint32_t PUPDR;       /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
  __IO uint32_t IDR;         /*!< GPIO port input data register,         Address offset: 0x10      */
  __IO uint32_t ODR;         /*!< GPIO port output data register,        Address offset: 0x14      */
  __IO uint32_t BSRR;        /*!< GPIO port bit set/reset  register,     Address offset: 0x18      */
  __IO uint32_t LCKR;        /*!< GPIO port configuration lock register, Address offset: 0x1C      */
  __IO uint32_t AFR[2];      /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
  __IO uint32_t BRR;         /*!< GPIO Bit Reset register,               Address offset: 0x28      */

} GPIO_TypeDef;

        第三种引脚标记其实就是在第二种方式基础上,为引脚设置别名,在cubeMX设置引脚mode后,会开启该引脚,在引脚参数设置列表可以设置该引脚标识名,例如下图,设置PE3(P2)为输出模式(GPIO_Output),的别名为LED1。

         那么在生成输出代码时,会通过#define实现标识名替换。

         1.4 GPIO原理框图及CubeMX配置

        进一步,再看看GPIO结构原理图,它支持4种GPIO输入模式(浮空输入、上拉输入、下拉输入、模拟输入)和4种GPIO输出模式(开漏输出、开漏复用输出、推挽输出、推挽复用输出)。因此才能通过多种输入输出模式组合来实现更复杂的其他外设功能。

         在cubeMX中在GPIO配置界面中,可以配置GPIO引脚的GPIO输入模式以及其他参数设置。

         同样地,在cubeMX中在GPIO配置界面中,可以配置GPIO引脚的GPIO输出模式以及其他参数设置。

        另外各种基于GPIO组合实现的外设,也可在GPIO栏的各个外设页面下,单独设置各个引脚的GPIO输入或输出模式。

 二、GPIO的HLA源码分析

        2.1 HLA库GPIO初始化

        通过cubeMX生成输出源码,如果配置GPIO引脚功能,则cubeMX生成代码时,设置了为每个外设生成独立.h/.c文件时,会在Core源码目录下的Inc及Src目录,分别生成gpio.h和gpio.c驱动文件,否则将会GPOI相关输出函数放置main.h和main.c中。

        在gpio.c文件中,主要定义了MX_GPIO_Init函数,该主要做三件事情,一是启动各个GPIO分组寄存器的时钟,二是对于输出GPIO引脚实现初始值写入,三是将在cubeMX上配置引脚参数写入到参数缓存区域,并依据缓存参数,调用HLA库的HAL_GPIO_Init来实现真正的初始化设定。如下面代码所示。

//main.h
/* Private defines -----------------------------------------------------------*/
#define LED1_Pin GPIO_PIN_3
#define LED1_GPIO_Port GPIOE
#define KEY2_Pin GPIO_PIN_10
#define KEY2_GPIO_Port GPIOE
#define KEY0_Pin GPIO_PIN_11
#define KEY0_GPIO_Port GPIOE
#define KEY1_Pin GPIO_PIN_14
#define KEY1_GPIO_Port GPIOE
#define LED2_Pin GPIO_PIN_15
#define LED2_GPIO_Port GPIOD
#define LED0_Pin GPIO_PIN_6
#define LED0_GPIO_Port GPIOB
/* USER CODE BEGIN Private defines */
//gpio.c
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : PEPin PEPin PEPin */
  GPIO_InitStruct.Pin = KEY2_Pin|KEY0_Pin|KEY1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(LED2_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED0_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(LED0_GPIO_Port, &GPIO_InitStruct);

}

        在stm32l4xx_hal_gpio.c源文件中定义了HAL_GPIO_Init函数,它做以下事情:1)诊断先前缓存配置参数是否合规,2)再将缓存配置参数写入到GPIO的寄存器内,完成GPIO的初始化设定(GPIO 的Mode、Pull、Analog、Alternate、Direction、EXTI等)。

        2.2 GPIO的开发应用接口API简述

        除了提供GPIO初始化API外,STM32的HLA针对GPIO还外开发这提供如下API:

         GPIO对数据的读取主要是HAL_GPIO_ReadPin函数,写入主要是HAL_GPIO_WritePin、HAL_GPIO_TogglePin函数,其参数都是GPIO_TypeDef* GPIOx和 uint16_t GPIO_Pin,前者就是前面参数的分组如GPIOA、GPIOB等,后者就是Pin0~Pin15,例如GPIO_PIN_3。

        关于外部中断回调函数HAL_GPIO_EXTI_Callback,该函数是弱函数

        若需要使用该函数,使其生效,首先需要在cubeMX配置界面,将GPIO引脚调整为GPIO_EXTI类型

        并开启该引脚的中断支持

         就可以重新实现stm32l4xx_hal_gpio.c文件中的HAL_GPIO_EXTI_Callback函数来实现GPIO外部中断回调功能,例如设置如下,则实现按键KEY0按下异常,触发回调函数HAL_GPIO_EXTI_Callback调用,从而实现LED0灯状态反转(点亮或熄灭):

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin==KEY0_Pin)
	{
		static uint32_t key0_count = 0;
		printf("this is %lu count for HAL_GPIO_EXTI_Callback!\r\n",key0_count++);
		Toggle_led0();//LED0等状态反转一次
	}
}
/* USER CODE END 0 */

         同样地对于外部中断请求函数HAL_GPIO_EXTI_IRQHandler也可以类似地进行调整实现GPIO外部中断触发器调用,从而实现我们需要的业务应用。

        GPIO的HLA库中,HAL_GPIO_LockPin函数,主要是操作lock寄存器数值的,lock寄存器会针对GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR等GPIO寄存器配置对应的状态标记,而其他寄存器操作时,会先去lock寄存器读取其使用状态来确定是否能进行相关操作,从而确保防止冲突及达成一致性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/153662.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鑫磊股份开启申购:资产负债率较高,实控人控制企业借款高企

1月10日&#xff0c;鑫磊压缩机股份有限公司&#xff08;下称“鑫磊股份”&#xff0c;SZ:301317&#xff09;开启申购&#xff0c;发行价格20.67元/股&#xff0c;市盈率58.65倍。据贝多财经了解&#xff0c;鑫磊股份将在深圳证券交易所创业板上市。 本次上市&#xff0c;鑫磊…

LinuxC—进程

进程 1 进程标识符pid 基本概念 类型pid_t&#xff0c;是一个有符号16位整数&#xff0c;进程号是顺次向下使用(fd是优先使用当前可用最小的) shell中的ps命令能够查看当前系统的进程信息快照 相关函数 getpid(2)获取当前进程的进程号 /* Get the process ID of the calling…

中断处理程序

文章目录前言前置知识中断概念中断分类实验操作实验一实验二实验三前言 博客记录《操作系统真象还原》第七章实验的操作~ 实验环境&#xff1a;ubuntu18.04VMware &#xff0c; Bochs下载安装 实验内容&#xff1a; 编写中断处理程序&#xff08; 操作 8259A 打开中断&…

2023年浙江食品安全管理员考试真题题库及答案

百分百题库提供食品安全管理员考试试题、食品安全管理员考试预测题、食品安全管理员考试真题、食品安全管理员证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 一、多选题 1.餐具清洗消毒水池与以下哪些水池应分开&#xff…

机器学习— —导入数据

DAY1集成开发环境原始数据展示主要函数介绍data.iloc()函数dataset.iloc[0]dataset.iloc[:,:-1]dataset.iloc[:,3]dataset.iloc[1:3,1]可执行代码导入结果展示我的写在最后集成开发环境 Spyder (前身是 Pydee) 是一个强大的交互式 Python 语言开发环境&#xff0c;提供高级的代…

如何搭建Python环境和安装Pycharm

1、 搭建Python的基础环境 Anaconda简介 Anaconda指的是一个开源的Python发行版本&#xff0c;其包含了conda、Python等180多个科学包及其依赖项。 因为包含了大量的科学包&#xff0c;Anaconda 的下载文件比较大&#xff08;约 531 MB&#xff09;&#xff0c;如果只需要某些…

【Kotlin】标准库函数 ② ( run 标准库函数 | run 函数传入 Lambda 表达式作为参数 | run 函数传入函数引用作为参数 )

文章目录一、run 标准库函数1、run 函数传入 Lambda 表达式作为参数2、run 函数传入函数引用作为参数Kotlin 语言中 , 在 Standard.kt 源码中 , 为所有类型定义了一批标准库函数 , 所有的 Kotlin 类型都可以调用这些函数 ; 一、run 标准库函数 1、run 函数传入 Lambda 表达式作…

Odoo丨5步轻松实现在Odoo中打开企微会话框

文章目录一、前言二、实现方案三、接口调用步骤一、前言 企业微信作为一个很好的企业级应用发布平台&#xff0c;尤其是提供的数据和接口&#xff0c;极大地为很多企业级应用提供便利&#xff0c;在日常中应用广泛&#xff01; 最近在项目中就遇到一个与企业微信相关的场景开…

商标异议解读

商标异议解读《商标法》第三十五条规定&#xff0c;对初步审定公告的商标提出异议的&#xff0c;商标局应当听取异议人和被异议人陈述事实和理由&#xff0c;经调查核实后&#xff0c;自公告期满之日起十二个月内做出是否准予注册的决定&#xff0c;并书面通知异议人和被异议人…

Python初次实现MapReduce——WordCount

前言 Hadoop 本身是用 Java 开发的&#xff0c;所以之前的MapReduce代码小练都是由Java代码编写&#xff0c;但是通过Hadoop Streaming&#xff0c;我们可以使用任意语言来编写程序&#xff0c;让Hadoop 运行。 本文用Python语言实现了词频统计功能&#xff0c;最后通过Hadoo…

Redis从青铜到王者,从环境搭建到熟练使用

一、常见的非关系型数据库NOSQL分类NOSQL类型主要数据库产品类型特色K-V键值对存储类型Redis、Memcached使用key可以快速的查询到value&#xff0c;Memcached可以支持String类型的值value&#xff0c;Redis支持的值的数据类型很多如&#xff1a;String\set\hash\sortset\list等…

1月重磅福利——Softing在线培训课程上线

尽管有标准化和界面友好的工具&#xff0c;但车辆诊断的复杂性仍需要或多或少的专业知识支持&#xff0c;其具体取决于应用领域和要求。无论是初学者还是高级工程师&#xff0c;我们都很乐意为您日常遇到的问题提供相关支持&#xff0c;并就车辆诊断、ODX和OTX标准以及工具的使…

LeetCode 179. 最大数

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 179. 最大数&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称 LeetCode 179. …

01Hashmap并发问题-ConcurrentHashMap-线程安全集合类-并发编程(Java)

1 概述 线程安全类可以分为3个大类&#xff1a; 遗留的的线程安全集合Hashtable&#xff0c;Vector使用Collections装饰的线程安全集合&#xff0c;如&#xff1a; Collections.syncronizedCollectionCollections.syncronizedListCollections.syncronizedMapCollections.sync…

Linux系列文章 —— vim的基本操作(误入vim退出请先按「ESC」再按:q不保存退出,相关操作请阅读本文)

系列文章目录 文章目录系列文章目录前言一、vim的基本概念二、vim的基本操作1.退出vim编辑器2.进入vim编辑器3.模式功能及切换三、vim命令模式命令集1.光标移动2.查找字符3.复制粘贴删除4.撤销、重做与重复做5.插入模式6.保存与退出7.环境修改四、总结1.vim的三种基本模式2.vim…

springboot之webmvc和webflux浅析

webmvc和webflux作为spring framework的两个重要模块&#xff0c;代表了两个IO模型&#xff0c;阻塞式和非阻塞式。 1、webmvc webmvc是基于servlet的阻塞式模型&#xff0c;一个请求到达服务器后会单独分配一个线程去处理请求&#xff0c;如果请求包含IO操作&#xff0c;线程…

车载以太网 - DoIP时间参数 - 06

时间参数在所有的协议定义中都无法被忽略的一块重要部分,之前的CAN&CANFD诊断协议总,ISO 16765就有相关的诊断时间参数的定义;在DoIP中,也有同样的时间参数定义,不过他是放在ISO 13400 - 2中,今天我们一起来看下这一块的内容。 ISO 13400原文文档 中文释义 时间参数定…

北大硕士LeetCode算法专题课-基础算法之排序

接连上篇&#xff1a;北大硕士LeetCode算法专题课---算法复杂度介绍_骨灰级收藏家的博客-CSDN博客 冒泡排序 冒泡排序&#xff08;Bubble Sort&#xff09;是一种很原始的排序方法&#xff0c;就是通过不断地交换“大数”的位置达到排序的目的。 因为不断出现“大数”类似于水…

HTML实现狗屁不通文章生成器

演示 实现 css html, body {background: radial-gradient(#181818, #000000);margin: 0;padding: 0;border: 0;-ms-overflow-style: none;}.btn {display: inline-block;color: #fff;cursor: pointer;font-size: 1em;font-weight: 400;max-weight: 20%;position: relative;tex…

【零基础】学python数据结构与算法笔记8

文章目录前言46.数据结构介绍47.列表48.栈的介绍49.栈的应用&#xff1a;括号匹配问题50.队列的介绍51.队列的实现52.队列的内置模块总结前言 学习python数据结构与算法&#xff0c;学习常用的算法&#xff0c; b站学习链接 46.数据结构介绍 数据结构是指相互之间存在着一种…