最近闲得无聊,又把 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相关推荐

  1. STM32官方固件库

    1 基于标准外设库的软件开发 1.1 STM32标准外设库概述 STM32标准外设库之前的版本也称固件函数库或简称固件库,是一个固件函数包,它由程序.数据结构和宏组成,包括了微控制器所有外设的性能特征 ...

  2. 【STM32】获取STM32官方固件库

    以STM32F103ZET6为例: 第一步:进入ST官网  http://www.st.com/content/st_com/en.html 第二步:搜索需要下载固件库芯片型号,例如: 第三步:找到对 ...

  3. STM32基于固件库学习笔记(11)RTC实时时钟

    实时时钟(RTC) 小容量产品是指闪存存储器容量在16K至32K字节之间的STM32F101xx.STM32F102xx和STM32F103xx微控制器. 中容量产品是指闪存存储器容量在64K至128 ...

  4. STM32基于固件库学习笔记(4)(通用定时器)TIM3定时1S中断

    STM32F103ZE系列定时器资源 有 TIME1 和 TIME8 等高级定时器,也有 TIME2~TIME5 等通用定时器,还有 TIME6 和TIME7 等基本定时器. 通用定时器:是一个通过可 ...

  5. USB学习笔记(4)STM32关于USB库的解读

    USB学习笔记(4)STM32关于USB库的解读 为了方便在STM32上实现USB功能,本文对USB库的文件进行解读. USB-FS_Device peripheral interface(外围接口) ...

  6. STM32 串口固件库中定义的几个中断标志什么意思?

    STM32 串口固件库中定义的几个中断标志位什么意思? #define USART_IT_PE                       ((uint16_t)0x0028) #define USA ...

  7. #STM32标准固件库的硬件SPI(NSS为软件)封装函数库

    #基于STM32标准固件库的硬件SPI(NSS为软件)封装函数库: 最近来回顾之前写过的SPI_Flash,打算重写一次SPI配置文件 spi协议最大的特点大概就是全双工了,因此stm32硬件spi的 ...

  8. STM32如何下载官方固件库

    ST官网拥有好多权威的资料库和例程固件库.在ST官网获取stm32f103c8t6标准例程固件库 1)登陆ST官网 官网参考网页 2)点击右上角登陆或者注册 3)当登陆成功时,点击Products-& ...

  9. STM32之固件库的调用(参考洋桃电子视频)

    STM32固件库中有许多函数,每个函数代表着不同的意义. 在工程中,后缀为.s的是汇编语言编写的文件,后缀为.c的是C语言编写的文件. 在整个工程中,只有Startup中的startup_stmf10 ...

最新文章

  1. 7种不同的使用AI聊天机器人的方式
  2. UML类关系(依赖,关联,聚合,组合,泛化,实现)
  3. 数据段描述符和代码段描述符(二)——《x86汇编语言:从实模式到保护模式》读书笔记11
  4. kotlin学习笔记——lambda表达式
  5. Linux date命令的用法
  6. 几种并发服务器模型的实现:多线程,多进程,select,poll,epoll
  7. SAP MM模块-实施顾问岗位-面试手册-项目管理经验面试清单-英文
  8. linux防火墙安装httpd配置,CentOS7下 Apache的安装配置方法
  9. 回调函数透彻理解Java
  10. MySQL两阶段加锁
  11. HTML教程笔记(菜鸟教程)
  12. 运行metamascara时出现的一些错误
  13. Go strings.ToLower函数
  14. Java获取当前时间到凌晨12点剩余秒数
  15. 2021-2027全球与中国5G核心网市场现状及未来发展趋势
  16. 线段树 hdu3265 Posters
  17. 【海量数据学院】DBA学习方法论系列之:明确的学习目标
  18. UiPath估值超百亿!这家来自罗马尼亚深耕中国市场的公司已进入市场高增长期...
  19. Windows键失灵的三种处理方式
  20. html $符号的作用,javascript 中$符号是代表什么意思!

热门文章

  1. 社会趣谈之无良媒体篇:北京一男子与充气娃娃结婚
  2. 含文档+PPT+源码等]精品微信小程序慢性疾病+后台管理系统|前后分离VUE[包运行成功]
  3. Life, Art, and Mysticism | Vita, arte e mistica | 布劳威尔 生活艺术和神秘主义翻译,第一章 | I - The sad world
  4. 四款 GTD 任务管理软件试用对比
  5. Excel表格打开文件提示内存或磁盘空间不足怎么解决
  6. matlab 加等深线
  7. 2023年回顾与计划
  8. 单片机原理及应用技术课后答案(5)
  9. sql服务器虚拟内存不足,解决SQL Server虚拟内存不足情况
  10. laravel导出Excel表格提示内存超出