了解并利用STM32寄存器和HAL库完成LED流水灯实践练习

本文内容为:
  1.原理学习,学习STM32F103系列寄存器映射和地址映射原理;了解GPIO端口初始化设置三步骤,即时钟配置、输入输出设置、最大速率设置。
 2.分别利用C语言和HAL库编程实现一个LED流水灯程序的编写。


一、原理学习

1.STM32F103系列芯片的地址映射和寄存器映射原理

  寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的;一个触发器可存储1位二进制代码。
  按照功能的不同,可将寄存器分为基本寄存器和移位寄存器两大类。基本寄存器只能并行输入输出,而移位寄存器可以在移位脉冲作用下依次逐渐右移或左移,既可以并行输入输出,也可以串行输入输出,并且可以互相组合,灵活且用途广。
  宏观上来说,寄存器就是存储数据的,这个数据既可以是指令,也可以是地址等。如果要提取里面的数据,就要访问寄存器,在STM32中,寄存器根据存放的数据不同,分为许多种类型,而区分不同寄存器的方式就是通过地址,每个寄存器都有自己的地址。

  以访问(即输入数据)PB3引脚为例,需要查询PB3的地址,而PB3是在GPIOB端口内的,因此需要先找到GPIOB的地址范围,通过查看数据手册,可以找到GPIOB相关的地址范围为0x4001 0C00 ---0x4001 0FFF之间。在这里插入图片描述

  查看参考手册,下图所示,发现该端口输入数据寄存器地址偏移为0x08(由于操作位数与各寄存器存储规格不一,通常会设置地址偏移区分各寄存器),因此该寄存器实际地址为0x4001 0C08(GPIOB相关地址的起始地址加上寄存器地址偏移即该寄存器起始地址)。并且PB3数据在第4位(IDR3)。
在这里插入图片描述

  至此,得到PB3引脚的电平数据位于0x4001 0C08 第4位;我们可以直接访问这个地址:

 unsigned int *pGPIOB_IDR = (unsigned int *)0x40010C08;
 unsigned char PB3 = *pGPIOB_IDR & 0x8;//取出从右往左数的第4位

  

2.GPIO端口的初始化设置三步骤(时钟配置、输入输出模式设置、最大速率设置)

 在操作寄存器输出时,总是需要先初始化,接下来是初始化GPIO端口从而操作PB3为低电平,最终点亮LED灯的步骤。


2.1 时钟配置

 任何外设都需要配置时钟,因为寄存器是由D触发器组成的,往触发器里编写数据的前提是有时钟输入。简单来说,时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的命令。
 51单片机是不需要配置时钟的,因为其只有一个时钟控制着所有的功能,且这个时钟是默认打开的,这就有一个问题,其他没有用到的功能时钟也是开着的,即也在一直耗能。而STM32之所以是低功耗,就是因为其每个功能都对应着一个时钟,而这些时钟默认关闭,这就避免了非必要的功损。因此,当我们需要操作时,需要先配置打开时钟。下面以对PB3输出低电平为例
 通过查询参考手册,如下图,得出:复位和时钟控制位于AHB总线;GPIO端口B位于APB2总线
在这里插入图片描述

 AHB总线与APB2总线关系如下,由图可见AHB包含桥接了APB2和APB1,而APB2接入了GPIOB,而在AHB种还接入了RCC即复位和时钟控制
在这里插入图片描述

接下来在手册中找到对应的APB2外设时钟使能寄存器
在这里插入图片描述

  • 由上两张图可知,RCC即复位和时钟控制的地址从0x4002 1000开始,而APB2外设时钟使能寄存器偏移地址为0x18,且GPIOB即PB引脚时钟开关在第4位即IOPB。至此,得出PB引脚时钟地址为0x4002 1018 第4位,取此地址、赋值为1即开启GPIOB时钟。

2.2 输入输出模式和最大速率设置

 上面我们打开了IOPB的时钟开关,既然是IO,那么就有input和output即输入和输出,那么该如何区分呢?这就涉及到要对输入输出模式进行设置。
 在本案例中,控制LED的亮灭要输出高低电平,则需要设置输出模式。在STM32中,每4位配置1个IO,所以一个32位寄存器最大配置8个IO。因此,STM32分了两个寄存器来分别配置Px0–Px7和Px8–Px15引脚,即端口配置低寄存器(GPIOx_CRL)和端口配置高寄存器(GPIOx_CRH)。
 我们此处配置的是PB3引脚,使用的寄存器是端口配置低寄存器(GPIOx_CRL)。如下图:
在这里插入图片描述
 可以得出偏移地址为0x00,起始地址即为GPIOB基地址0x4100 0C00;复位值0x4444 4444,0x代表16进制,每个4转换为2进制为0100,表示其CNFMODE的默认模式分别为01和00。而我们要的PB3口的输入输出模式控制就在第二排前两框内,即CNF3MODE3,其具体模式如下:
在这里插入图片描述
 可见有1种输入模式(也是默认模式),3种不同最大速度的输出模式,而输入和输出模式还分别对应着4种状态。以下是对这些状态的解释:
输入模式下:
   1. 模拟输入:对模拟信号读取

   2. 浮空输入:对数字信号读取

   3. 上拉输入:对数字信号读取,输入电平不会上下浮动而导致输入信号不稳定,在没有信号
         输入时维持在高电平

   4. 下拉输入:对数字信号读取,输入电平不会上下浮动而导致输入信号不稳定,在没有信号
         输入时维持在低电平

输出模式下:
   1. 推挽输出:输出高低电平,两个不同的MOS管实现输出,输出时一个导通另一个截止

   2. 开漏输出:给予低电平,输出浮空电压(不稳定电压);给予高电平,输出低电平,开漏
         输出通常需要一个上拉电阻防止电流流向另一MOS管。

   3.复用推挽/开漏输出:顾名思义,复用此端口,将输出转移到其他外设上

  • 我们此处选择推挽输出模式,最大速率为50MHz,即0b0011=0x3,配置端口配置低寄存器GPIOB_CRL为0x4444 3444

2.3 代码实现与步骤总结

 最后,找到端口输出数据寄存器中PB3口的地址,为0x4001 0c0c 第4位(下图偏移地址为中文手册BUG,英文原为0x0c,故地址为0x4001 0c0c)。在上述初始化完成后直接对此赋值即可
在这里插入图片描述

  • 至此,就算完成了所有步骤,以下为程序代码:
/*****************点亮PB3口的LED灯*****************/
    int main(void)
    {
        unsigned int *pRCC_APB2ENR = (unsigned int *)0x40021018;//指向时钟地址
        unsigned int *pGPIOB_CRL = (unsigned int *)0x40010c00;//输入输出模式设置地址
        unsigned int *pGPIOB_ODR = (unsigned int *)0x40010c0c;//PB3输出地址
        *pRCC_APB2ENR = 0x00000008;//第4为赋值为1,开启PB时钟
        *pGPIOB_CRL = 0x44443444;//将PB3口模式设置为00 11
        *pGPIOB_ODR = 0x00000000;//赋值为0,LED低电平亮
         return 0;             
    }



二、实践练习

 假设你手中已有STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED,并搭建了电路,分别在GPIOA-5、GPIOB-9、GPIOC-14这3个引脚上控制LED灯(最高时钟2Mhz),轮流闪烁,间隔时长1秒。

 1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;

 2)用C语言 寄存器方式编程实现。
 3)安装 stm32CubeMX,用cubemx完成初始化过程,采用HAL库编程实现。
 4)在Keil下用软件仿真运行上面代码,并用虚拟逻辑分析仪观察 对应管脚上的输出波形(高低电平转换),看是否是1秒的周期。

1. 程序设计思路及C语言编程实现(第(1)、(2)问回答)

  • 配置各寄存器时钟,上文中提到了时钟寄存器地址为0x4002 1018,其中,控制GPIOA、GOIOB、GPIOC的分别位于第3、4、5位。因此,赋值为1即可,如下:
unsigned int *pRCC_APB2ENR = (unsigned int *)0x40021018;//指向时钟地址
*pRCC_APB2ENR=0x0000001c;//将3、4、5位赋值为1
  • 配置输出模式,3个引脚中,有2个需要用到端口配置高寄存器(Px8~Px15),端口配置低寄存器见上文,现附上端口配置高寄存器。由上文,GPIOA/B/C基地址分别为:0x4001 0800/0C00/1000
    在这里插入图片描述
    综上,计入偏移地址,PA/PB/PC端口输入输出模式配置寄存器的起始地址分别为:0x4001 0800/0C04/1004,接下来就是对其赋值,如下:
 unsigned int *pGPIOA_CRL = (unsigned int *)0x40010800;//GPIOA端口配置低输入输出模式设置地址
 unsigned int *pGPIOB_CRH = (unsigned int *)0x40010c04;//GPIOB端口配置高输入输出模式设置地址
 unsigned int *pGPIOC_CRH = (unsigned int *)0x40011004;//GPIOC端口配置高输入输出模式设置地址

 *pGPIOA_CRL = 0x44344444;//将PA5口输出模式设置为00 11
 *pGPIOB_CRH = 0x44444434;//将PB9口输出模式设置为00 11
 *pGPIOC_CRH = 0x43444444;//将PC14口输出模式设置为00 11

  至此3个引脚的初始化已完成,接下来是找到端口输出寄存器的地址,并对其进行编程进行延迟输出而达到流水灯的效果。以下为完整代码:

 /****************************流水灯代码,此代码默认高电平灯灭,低电平灯亮****************************/
 
 void Delay_ms(uint16_t time)//延时函数,延迟time毫秒,Delay(1000*1)即延迟1秒
  {
    u16 i=0;
    while(time--)
    {
      i=10000;
      while(i--);
    )
  }
  
  int main()//主函数
  {
    //初始化过程
    //时钟配置
    unsigned int *pRCC_APB2ENR = (unsigned int *)0x40021018;//指向时钟地址
    *pRCC_APB2ENR=0x0000001c;//将3、4、5位赋值为1
 
    //输入输出模式配置
    unsigned int *pGPIOA_CRL = (unsigned int *)0x40010800;//GPIOA端口配置低输入输出模式设置地址
    unsigned int *pGPIOB_CRH = (unsigned int *)0x40010c04;//GPIOB端口配置高输入输出模式设置地址
    unsigned int *pGPIOC_CRH = (unsigned int *)0x40011004;//GPIOC端口配置高输入输出模式设置地址

    *pGPIOA_CRL = 0x44344444;//将PA5口输出模式设置为00 11
    *pGPIOB_CRH = 0x44444434;//将PB9口输出模式设置为00 11
    *pGPIOC_CRH = 0x43444444;//将PC14口输出模式设置为00 11
 
    //指针指向PA/PB/PC端口输出数据寄存器地址
    unsigned int *pGPIOA_ODR = (unsigned int *)0x4001080c;//指向PA
    unsigned int *pGPIOB_ODR = (unsigned int *)0x40010c0c;//指向PB
    unsigned int *pGPIOC_ODR = (unsigned int *)0x4001100c;//指向PC
    
    //将引脚设为高电平,即设置灯初始为灭
    *pGPIOA_ODR =0xFFFFFFFF;
    *pGPIOA_ODR =0xFFFFFFFF;
    *pGPIOA_ODR =0xFFFFFFFF;

   while(1)//循环实现流水灯
     {
       //PA5亮灭/01
       *pGPIOA_ODR =0xFFFFFFdF;//设PA5为0,灯亮
       Delay_ms(1000*1);//延迟1秒
       *pGPIOA_ODR =0xFFFFFFFF;//灯灭
     
        //PB9亮灭/01
       *pGPIOB_ODR =0xFFFFFdFF;//设PB9为0,灯亮
       Delay_ms(1000*1);//延迟1秒
       *pGPIOB_ODR =0xFFFFFFFF;//灯灭
     
        //PC14亮灭/01
       *pGPIOC_ODR =0xFFFFbFFF;//设PC14为0,灯亮
       Delay_ms(1000*1);//延迟1秒
       *pGPIOC_ODR =0xFFFFFFFF;//灯灭
     }
 }

2. 安装 stm32CubeMX,用cubemx完成初始化过程,采用HAL库编程实现流水灯

  • 首先,去官网下载stm32CubeMX:stm32CubeMX官网安装地址(注意安装时路径不能有中文且安装的文件夹要为空)

  • 安装完成后打开软件,选择HELP中的Manage…,下载软件包,选中后install即可,此处我已下好
    在这里插入图片描述
    在这里插入图片描述

  • 下载完成后,返回主界面,选择第一项
    在这里插入图片描述

  • 选择所需要的芯片,创建新项目(左上角框内查询目标芯片,中间下方选择具体类型,双击或右上角建项)
    在这里插入图片描述

  • 选择SYS,设置调试方式为Serial Wire
    在这里插入图片描述

  • 接下来我们观察时钟构造,我们需要的连接GPIOA/B/C端口的APB2总线由HSE控制,此处先将中间连接切换为PLLCLK
    在这里插入图片描述

  • 回到Pinout,将时钟控制RCC中的HSE设为Crystal/Ceramic Resonator
    在这里插入图片描述

  • 配置完后,接下来就是选择GPIOA5/B9/C14引脚为output输出模式,选择GPIO,在芯片视图上选择对应的引脚并选择GPIO_Output
    在这里插入图片描述

  • 接下来进入Project Manager,并在Project中的IDE选择MDK-ARM,设置项目保存路径
    在这里插入图片描述

  • 最后,进入Code Generate中勾选生成如图Keil可编辑文件(第二栏第一项),再点击右上角生成即可
    在这里插入图片描述

  • 至此,在STM32CubeMx中的操作已完成,此步骤利用HAL库完成了STM32 GPIO初始化的过程,并最终生成Keil可编辑的文件,之后可以在Keil中直接编辑主函数来控制这三个引脚输出流水灯,再利用Keil生成HEX文件,即可直接烧录到硬件中实现。

3. 在Keil下用软件仿真运行上面代码,并用虚拟逻辑分析仪观察对应管脚上的输出波形(高低电平转换),看是否是1秒的周期。

  • 打开上一步生成的Keil文件,在左侧所示路径打开main.c,将主函数全部替换成如下代码(PA5/PB9/PC14流水灯代码)
    在这里插入图片描述
SystemClock_Config();//系统时钟初始化
  MX_GPIO_Init();//gpio初始化
  while (1)
  {		
		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);//PA5亮灯
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);//PC14熄灯
		HAL_Delay(1000);//延时1s
		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);//PA5熄灯
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);//PB9亮灯
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);//PC14熄灯
		HAL_Delay(1000);//延时1s		
		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);//PA5熄灯
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_RESET);//PC14亮灯
		HAL_Delay(1000);//延时1s
	}
  • 接下来进行仿真(下面会展示最终生成波形之前,我遇到的三个问题及解决方法)
    第一个问题,无法仿真,点击仿真按钮会显示错误。
    第二个问题,仿真过程中命令框会显示如下,无读写权限。
    在这里插入图片描述
    第三个问题,在虚拟逻辑分析仪中,无法添加目标引脚观察。
    接下来是所有仿真步骤,以上问题将会逐一解决。

  • 打开Project Option for target,在Device中查看所用的芯片类型。切换到Debug,右上选择Use Simulator使用虚拟仿真器,在下方Dialog DLL和Parameter分别输入DARMSTM.DLL-p+芯片类型,左右两别都要输入!(此步骤解决第一、三个问题,匹配芯片对应得虚拟仿真器
    参考文章:https://blog.csdn.net/bean_business/article/details/116456981)
    在这里插入图片描述

  • 然后,还是上面的界面,在Initialization File中,创建一个.ini文件,记事本打开编写以下内容保存退出即可(解决第二个问题,给予读写权限
    参考文章:https://blog.csdn.net/qq_41610710/article/details/121675571
    在这里插入图片描述

  • 至此,返回主界面,Build编译后即可仿真。打开仿真后,打开虚拟逻辑分析仪
    在这里插入图片描述

  • 打开左上角Setup,点击右上角叉叉旁边的虚线方框添加要观察的引脚的高低电平,依次输入PORTA.5 PORTB.9 PORTC.14即可,添加后再选中这些引脚,将Display Type设置为Bit即可。
    在这里插入图片描述

  • 最后,在上方设置栏内设置Grid(此处设置的为1s即虚线间隔1s),并且勾选signal infocursor
    在这里插入图片描述

  • 以下是观察的波形图,从上到下依次为PA5、PB9、PC14,低电平代表灯亮,高电平代表灯灭,可以观察出,三个灯是流水灯输出,且每个灯亮的间隔刚好是1s
    在这里插入图片描述


总结

 本文较为全面的学习了STM32寄存器映射的原理以及对GPIO初始化的过程,同时也了解了STM32的总线框架。最后利用了一道流水灯实践练习题巩固了控制GPIO输出初始化的步骤,还利用了HAL库,直观地对GPIO输出进行初始化操作,最终在Keil软件上,对流水灯输出地的三个引脚进行了仿真,利用虚拟逻辑分析仪观察到了三个引脚的输出波形,验证了程序的可行性。以上就是大致的全部内容,期待下次的深入学习。


参考文章

https://blog.csdn.net/geek_monkey/article/details/86291377
https://blog.csdn.net/qq_47281915/article/details/120812867
https://blog.csdn.net/weixin_46129506/article/details/120780184
https://blog.csdn.net/bean_business/article/details/116456981
https://blog.csdn.net/qq_41610710/article/details/121675571

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值