STM32官方固件库代码解读--GPIO
最近闲得无聊,又把 stm32 拿了出来。之前学的时候是看的库函数版本,现在和寄存器版本的一起看感觉比一开始接触的时候看得顺畅多了,详细了解了底层寄存器的功能。之前用 stm32 只是调用函数,看了寄存器版本后对那些实现具体细节的库函数代码产生了兴趣,所以稍微看了看库函数的代码,同时把一些难理解的地方也记录下来。
我用的是 ALIENTEK 的战舰版,看的也是他们的开发指南,这里我就写对库函数代码的理解,寄存器种类和功能等指南里面有的我基本不写了。
一开始是 GPIO,学任何单片机一开始都是流水灯。stm32 的每组 GPIO 有 7 个寄存器,功能这里就不介绍了,网上很多。官方库里关于 GPIO 有 18 个函数,看了几个常用的,如下
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
这些函数除了第一个初始化函数以外其他都很简单,了解一下寄存器的功能就能看明白代码了,这里只写一下初始化函数。
代码如下:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;uint32_t tmpreg = 0x00, pinmask = 0x00;/* Check the parameters */assert_param(IS_GPIO_ALL_PERIPH(GPIOx));assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)); /*---------------------------- GPIO Mode Configuration -----------------------*/currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00){ /* Check the parameters */assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));/* Output mode */currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;}
/*---------------------------- GPIO CRL Configuration ------------------------*//* Configure the eight low port pins */if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00){tmpreg = GPIOx->CRL;for (pinpos = 0x00; pinpos < 0x08; pinpos++){pos = ((uint32_t)0x01) << pinpos;/* Get the port pins position */currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;if (currentpin == pos){pos = pinpos << 2;/* Clear the corresponding low control register bits */pinmask = ((uint32_t)0x0F) << pos;tmpreg &= ~pinmask;/* Write the mode configuration in the corresponding bits */tmpreg |= (currentmode << pos);/* Reset the corresponding ODR bit */if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD){GPIOx->BRR = (((uint32_t)0x01) << pinpos);}else{/* Set the corresponding ODR bit */if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU){GPIOx->BSRR = (((uint32_t)0x01) << pinpos);}}}}GPIOx->CRL = tmpreg;}
/*---------------------------- GPIO CRH Configuration ------------------------*//* Configure the eight high port pins */if (GPIO_InitStruct->GPIO_Pin > 0x00FF){tmpreg = GPIOx->CRH;for (pinpos = 0x00; pinpos < 0x08; pinpos++){pos = (((uint32_t)0x01) << (pinpos + 0x08));/* Get the port pins position */currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);if (currentpin == pos){pos = pinpos << 2;/* Clear the corresponding high control register bits */pinmask = ((uint32_t)0x0F) << pos;tmpreg &= ~pinmask;/* Write the mode configuration in the corresponding bits */tmpreg |= (currentmode << pos);/* Reset the corresponding ODR bit */if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD){GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));}/* Set the corresponding ODR bit */if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU){GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));}}}GPIOx->CRH = tmpreg;}
}
每个 GPIO 端口有四个配置为,分别是 CNF1,CNF0,MODE1,MODE0,初始化主要就是配置这四位,如下表
可以看到输入模式中上拉输入与下拉输入 CNF1 和 CNF0 是一样的,那这两种模式在配置时是怎么区分的呢?其实这是通过配置输出数据寄存器 ODR 来区分的,对应位置 1 为上拉,对应位置 0 为下拉。那这在库函数里又是怎么实现的呢?
先介绍固件库 GPIO 头文件里的两个枚举类型,如下
typedef enum
{ GPIO_Speed_10MHz = 1,GPIO_Speed_2MHz, GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;typedef enum
{ GPIO_Mode_AIN = 0x0,GPIO_Mode_IN_FLOATING = 0x04,GPIO_Mode_IPD = 0x28,GPIO_Mode_IPU = 0x48,GPIO_Mode_Out_OD = 0x14,GPIO_Mode_Out_PP = 0x10,GPIO_Mode_AF_OD = 0x1C,GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;
这两个是初始化函数的第二个结构体参数里的两个参数,用于速率和输入输出模式的初始化。第一个速率还好理解,01、10、11 分别对应上表中代表不同的输出速率。第二个就不太看得懂了,模式配置明明只要 4 位数据,怎么有 8 位,这个 8 位数据又到底是什么涵义?我也在网上看到有人问这个问题,他说对着寄存器的配置怎么也看不懂,其实对着寄存器配置是肯定看不懂的,因为这个要对着库函数的具体实现方法来看。
初始化函数里先定义了几个参数,如下
uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
uint32_t tmpreg = 0x00, pinmask = 0x00;
看变量名就知道大概是用来干嘛的,就不专门解释了,讲具体用法再说。
下面一段用来模式配置
currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
{ /* Check the parameters */assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));/* Output mode */currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
}
这里用到了之前看不懂的那个枚举量,现在就可以看它的值到底是什么意思了。先把这个 8 位数保留低 4 位,高四位置 0 ,保存到 currentmode 这个变量,然后判断这个 8 位数的第 4 位是不是 1,如果是 1,把 currentmode 和速率按位或之后保存到 currentmode 这个变量中。乍一看这段代码根本不知道是什么意思,只知道现在 currentmode 大概保存的是要配置的模式参数。其实这要和之前那个枚举量结合起来看。
既然检测了第四位是否为 1,那看之前的枚举类型中的数值,四种输出模式对应的值为 0x14、0x10、0x1C、0x18,四种输入模式对应的值为 0x0、0x04、0x28、0x48。可以发现输出模式的第四位都为 1,输入模式的第四位都不为 1,所以只有输出模式会进入 if 语句,因为只有输出模式要配置速率,所以再将 currentmode 和速率按位或就得到了输出模式的配置参数。以 0x14 为例,高四位 0001 是用来检测是否为输出模式,第 2 位和第 3 位是输出模式参数,第 0 位和第一位为 0,在 if 语句里和速率参数拼成一个完整的参数。至于输入模式的参数,它不用进入 if 语句,它的低四位就是它的模式参数,同时下拉和上拉输入枚举值高四位的不同也起到区分的作用,因为之前说过它们的配置参数是一样的。
下面是 CRL 寄存器的配置,对应 GPIO_x的 0~7,每个口四位参数。代码如下:
if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00){tmpreg = GPIOx->CRL;for (pinpos = 0x00; pinpos < 0x08; pinpos++){pos = ((uint32_t)0x01) << pinpos;/* Get the port pins position */currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;if (currentpin == pos){pos = pinpos << 2;/* Clear the corresponding low control register bits */pinmask = ((uint32_t)0x0F) << pos;tmpreg &= ~pinmask;/* Write the mode configuration in the corresponding bits */tmpreg |= (currentmode << pos);/* Reset the corresponding ODR bit */if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD){GPIOx->BRR = (((uint32_t)0x01) << pinpos);}else{/* Set the corresponding ODR bit */if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU){GPIOx->BSRR = (((uint32_t)0x01) << pinpos);}}}}GPIOx->CRL = tmpreg;}
先判断是不是低 8 位的端口,若是就进入继续判断。pinpos 控制判断次数,因为有 8 个口,所以要循环 8 次。pos 的值表示当前判断的是哪一个端口,只有 1 位为 1。接着 pos 和输入参数 GPIO_Pin 按位与赋值给 currentpin,如果 currentpin 的值和 pos 相等,说明要配置当前这个端口,进入 if 语句进行配置。下面这句pos = pinpos << 2,就是把 pinpos 乘上 4 赋给 pos,pos 之后控制移位次数,因为一个口的配置参数有 4 位,所以计数参数要乘 4。下面两句pinmask =((uint32_t)0x0F) << pos;tmpreg &= ~pinmask,是把要配置的这个口对应的四位清零。然后一句tmpreg |= (currentmode << pos),大功告成,要配置的那个端口对应的四位参数设置好了。下面还有两个判断语句是用来判断是否为上拉或下拉输入模式,若是则设置相应的 ODR 位。
下面还有一段是设置 CRH 寄存器的,和设置 CRL 差不多,只是它是端口 8~15。
OK,对 GPIO 的库函数先写这么多吧。
STM32官方固件库代码解读--GPIO相关推荐
- STM32官方固件库
1 基于标准外设库的软件开发 1.1 STM32标准外设库概述 STM32标准外设库之前的版本也称固件函数库或简称固件库,是一个固件函数包,它由程序.数据结构和宏组成,包括了微控制器所有外设的性能特征 ...
- 【STM32】获取STM32官方固件库
以STM32F103ZET6为例: 第一步:进入ST官网 http://www.st.com/content/st_com/en.html 第二步:搜索需要下载固件库芯片型号,例如: 第三步:找到对 ...
- STM32基于固件库学习笔记(11)RTC实时时钟
实时时钟(RTC) 小容量产品是指闪存存储器容量在16K至32K字节之间的STM32F101xx.STM32F102xx和STM32F103xx微控制器. 中容量产品是指闪存存储器容量在64K至128 ...
- STM32基于固件库学习笔记(4)(通用定时器)TIM3定时1S中断
STM32F103ZE系列定时器资源 有 TIME1 和 TIME8 等高级定时器,也有 TIME2~TIME5 等通用定时器,还有 TIME6 和TIME7 等基本定时器. 通用定时器:是一个通过可 ...
- USB学习笔记(4)STM32关于USB库的解读
USB学习笔记(4)STM32关于USB库的解读 为了方便在STM32上实现USB功能,本文对USB库的文件进行解读. USB-FS_Device peripheral interface(外围接口) ...
- STM32 串口固件库中定义的几个中断标志什么意思?
STM32 串口固件库中定义的几个中断标志位什么意思? #define USART_IT_PE ((uint16_t)0x0028) #define USA ...
- #STM32标准固件库的硬件SPI(NSS为软件)封装函数库
#基于STM32标准固件库的硬件SPI(NSS为软件)封装函数库: 最近来回顾之前写过的SPI_Flash,打算重写一次SPI配置文件 spi协议最大的特点大概就是全双工了,因此stm32硬件spi的 ...
- STM32如何下载官方固件库
ST官网拥有好多权威的资料库和例程固件库.在ST官网获取stm32f103c8t6标准例程固件库 1)登陆ST官网 官网参考网页 2)点击右上角登陆或者注册 3)当登陆成功时,点击Products-& ...
- STM32之固件库的调用(参考洋桃电子视频)
STM32固件库中有许多函数,每个函数代表着不同的意义. 在工程中,后缀为.s的是汇编语言编写的文件,后缀为.c的是C语言编写的文件. 在整个工程中,只有Startup中的startup_stmf10 ...
最新文章
- 7种不同的使用AI聊天机器人的方式
- UML类关系(依赖,关联,聚合,组合,泛化,实现)
- 数据段描述符和代码段描述符(二)——《x86汇编语言:从实模式到保护模式》读书笔记11
- kotlin学习笔记——lambda表达式
- Linux date命令的用法
- 几种并发服务器模型的实现:多线程,多进程,select,poll,epoll
- SAP MM模块-实施顾问岗位-面试手册-项目管理经验面试清单-英文
- linux防火墙安装httpd配置,CentOS7下 Apache的安装配置方法
- 回调函数透彻理解Java
- MySQL两阶段加锁
- HTML教程笔记(菜鸟教程)
- 运行metamascara时出现的一些错误
- Go strings.ToLower函数
- Java获取当前时间到凌晨12点剩余秒数
- 2021-2027全球与中国5G核心网市场现状及未来发展趋势
- 线段树 hdu3265 Posters
- 【海量数据学院】DBA学习方法论系列之:明确的学习目标
- UiPath估值超百亿!这家来自罗马尼亚深耕中国市场的公司已进入市场高增长期...
- Windows键失灵的三种处理方式
- html $符号的作用,javascript 中$符号是代表什么意思!
热门文章
- 社会趣谈之无良媒体篇:北京一男子与充气娃娃结婚
- 含文档+PPT+源码等]精品微信小程序慢性疾病+后台管理系统|前后分离VUE[包运行成功]
- Life, Art, and Mysticism | Vita, arte e mistica | 布劳威尔 生活艺术和神秘主义翻译,第一章 | I - The sad world
- 四款 GTD 任务管理软件试用对比
- Excel表格打开文件提示内存或磁盘空间不足怎么解决
- matlab 加等深线
- 2023年回顾与计划
- 单片机原理及应用技术课后答案(5)
- sql服务器虚拟内存不足,解决SQL Server虚拟内存不足情况
- laravel导出Excel表格提示内存超出