[其他ST产品] 基于STM32+LED库函数和寄存器点灯

[复制链接]
1969|37
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:12 | 显示全部楼层 |阅读模式
前言
       本次我们学习一下利用库函数和寄存器点亮LED,寄存器操作全程直接参考STM32手册,直接对照寄存器讲解,大部分是自己收集和整理,如有侵权请联系我删除。

本次实验板子使用的是正点原子精英版,芯片是STM32F103ZET6,需要资料可以@我拿取。

本博客内容原创,创作不易,转载请注明

1.利用库函数点亮LED
1)查看原理图的LED引脚
        点亮LED灯我们需要了解原理图的引脚,看下LED的对应引脚是什么,再进行配置,本次使用的是正点原子精英版,所以引脚分别是:LED0 --- PB5 , LED1 --- PE5 。

955556566b9ee37356.png

2)加入驱动LED需要的 rcc 和 gpio 的头文件

280046566ba04731e5.png

那么这些文件在哪里可以添加呢? 不懂文件放在那里的,可以参考我的博客---新建工程专栏
我在这里再说一次:就在这个inc 的文件夹里,我们在工程里面添加就行。、


793766566ba17cbda3.png

接下来我们添加需要的H文件:其中 stm32f10x_rcc.h 头文件在每个实验中都要引入,因为系统时钟配置函数以及相关的外设时钟使能函数都在这个其源文件 stm32f10x_rcc.c 中。




 楼主| 花间一壶酒sd 发表于 2023-11-29 12:12 | 显示全部楼层
621076566ba2c6e720.png
新建一个 HARDWARE 的文件夹,用来存储以后与硬件相关的代码,当然你也可以随意放在一个文件夹,不过为了后期能快速找到对应文件的代码,我建议还是创建一个。

驱动LED灯,我们需要创建两个文件,分别是LED.c 和 LED.h 的文件,然后放在HARDWARE 的文件夹里面。

LED.h:新建一个led.h的文件,.h中一般放的是同名.c文件中定义的变量、数组、函数的声明,需要让.c外部使用的声明。
  1. #ifndef __LED_H
  2. #define __LED_H         
  3. #include "sys.h"

  4. #define LED0 PBout(5)// PB5
  5. #define LED1 PEout(5)// PE5       

  6. void LED_Init(void);//初始化
  7.                                     
  8. #endif
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:13 | 显示全部楼层
很多人可能不了解sys.h文件,和两个#define的声明,其实这个是一个位操作的文件,就是为了方便控制LED,相信学过51的同学都了解。我们也可以不用这种方式,等会我会直接展示库函数输出的方法来使能LED。这里放下sys.h 的代码:

31326566ba5818cce.png
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:13 | 显示全部楼层
LED . c : 新建一个led.c的文件,存放驱动LED的初始化代码

  1. #include "led.h"
  2. #include "delay.h"


  3. //初始化PB5和PE5为输出口.并使能这两个口的时钟                    
  4. //LED IO初始化
  5. void LED_Init(void)
  6. {
  7.         //定义一个结构体变量为GPIO_InitStructure,是为了下面初始化变量使用
  8. GPIO_InitTypeDef  GPIO_InitStructure;
  9.        
  10. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);         //使能PB,PE端口时钟
  11.        
  12. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;                                 //LED0-->PB.5 端口配置
  13. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //推挽输出
  14. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                 //IO口速度为50MHz
  15. GPIO_Init(GPIOB, &GPIO_InitStructure);                                         //根据设定参数初始化GPIOB.5
  16. GPIO_SetBits(GPIOB,GPIO_Pin_5);                                                 //PB.5 输出高

  17. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;                             //LED1-->PE.5 端口配置, 推挽输出
  18. GPIO_Init(GPIOE, &GPIO_InitStructure);                                           //推挽输出 ,IO口速度为50MHz
  19. GPIO_SetBits(GPIOE,GPIO_Pin_5);                                                  //PE.5 输出高
  20. }
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:13 | 显示全部楼层
1) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);     //使能PB,PE端口时钟
这个函数的意思是打开GPIOB和GPIOE的时钟,我们每次使用外设的时候,都需要打开相应的外设时钟,通过查看手册时钟框图可以看到,GPIOB和GPIOE的时钟在APB2总线上:
967316566ba787027f.png
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:14 | 显示全部楼层
2) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;          //推挽输出
结论:IO口需要配置为通用推挽输出,输出低电平0时,灯亮。输出高电平1,灯灭

不太了解GPIO模式的,可以去看看我的博客:

基于STM32+认识GPIO引脚的8种输入输出及引脚配置
https://blog.csdn.net/qq_49015270/article/details/126336600?spm=1001.2014.3001.5501、

928406566ba8eb0b37.png
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:15 | 显示全部楼层
3)使能LED0 和 LED1 ,让他们交替闪烁
  1. #include "led.h"
  2. #include "delay.h"
  3. /*******************************

  4. //LED0 和  LED1  都是低电平有效
  5. //电平输出为0时灯亮,输出为1时灯灭
  6. //低电平:0 , 高电平:1
  7. ********************************/

  8. int main(void)
  9. {

  10.         delay_init();                     //延时函数初始化          
  11.         LED_Init();                                  //初始化与LED连接的硬件接口
  12.         while(1)
  13.         {
  14.         GPIO_SetBits(GPIOB,GPIO_Pin_5);                                 //PB.5 输出高 - - - LED0灭
  15.         GPIO_ResetBits(GPIOE,GPIO_Pin_5);                        //PE.5 输出低        - - - LED1亮
  16.         delay_ms (1000);
  17.                
  18.         GPIO_SetBits(GPIOE,GPIO_Pin_5);                                //PE.5 输出高- - - LED1灭
  19.         GPIO_ResetBits(GPIOB,GPIO_Pin_5);                        //PB.5 输出低- - - LED0亮
  20.         delay_ms (1000);               
  21.         }
  22. }
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:15 | 显示全部楼层
GPIO_SetBits()和 GPIO_ResetBits()两个函数分别是把对应的位置1和置0控制LED的亮灭
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:15 | 显示全部楼层
2.利用寄存器点亮LED
        利用寄存器来驱动使能LED,我们就需要参考STM32中文参考手册来对寄存器的对应位进行位运算,这个就需要同学们会一点C语言的位操作,一般是使用置1操作:|= ,然后置0操作:&=~,然后就是 << 和 >> 这几种,我在这里手把手教学打架看寄存器和位操作使能这些位。
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:15 | 显示全部楼层
1)打开对应的RCC时钟
和库函数一样,我们的第一步也是需要打开RCC时钟,我们知道IO口的RCC时钟在AHB总线的APB2上,所以我们查看手册,找到  APB2 外设时钟使能寄存器(RCC_APB2ENR)
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:15 | 显示全部楼层
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:16 | 显示全部楼层
我们可以看到,在这个寄存器的第3位和第6位分别是端口B和端口E的时钟,我们要使能这两个时钟,就需要给这两个位置1 ,才能开启IO口的时钟。

        RCC->APB2ENR|=1<<3;    //使能PORTB时钟                    
        RCC->APB2ENR|=1<<6;    //使能PORTE时钟       
从这里我们能看到,我们分别是把1左移3位和左移6位,然后利用置1 |=,就成功把 端口B和端口E成功使能开启了,是不是觉得很简单呢。
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:16 | 显示全部楼层
2)配置引脚模式
和上面的库函数类似,我们也要给引脚配置模式和引脚速率,所以我们直接参考手册,找到GPIO寄存器的说明看看,我们需要用到 端口配置低寄存器(GPIOx_CRL) (x=A..E) 。
当然他这里还有一个  端口配置高寄存器(GPIOx_CRH) (x=A..E)

那么这两个寄存器为啥一个高一个低呢?

原来在M3的架构是32为寄存器的芯片,所以这个引脚的一个脚占了4位。低寄存器的引脚范围在0-7之间,就是GPIOA...E的0-7引脚使用低寄存器,然后8-15引脚使用高寄存器

不过这种情况是在M3架构是这样分配的,我了解的M4架构就只要一个寄存器,具体情况配置一切按照寄存器说明来使能。
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:16 | 显示全部楼层
639656566bb1f56422.png 我们可以一个一个位来分析,首先我们先看20-21位,这个位是控制端口的输入输出位,22-23是端口相应的模式位,这里我们可以分为两个部分来看。
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:16 | 显示全部楼层
1)当MODEy[1:0]:这个位设置为00,表示为输入,这个时候相对应的CNFy[1:0]:的配置是上面那个,比如我需要浮空输入模式,那么20-23位就是:0100 ,如图: 842306566bb33cfcaf.png 380746566bb38300d8.png
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:17 | 显示全部楼层
2)当MODEy[1:0]:这个位设置为 01 或者 10 或者 11,表示为输出,这个时候相对应的CNFy[1:0]:的配置是下面那个,比如我需要通用推挽模式,速度为50Mhz 的输出模式,那么20-23位就是:0011 ,如图: 176736566bb44ccb45.png
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:17 | 显示全部楼层
驱动LED的模式:通用推挽模式,速度为50Mhz 的输出模式
  1.         GPIOB->CRL &=~(0XF<<20);
  2.         GPIOB->CRL |= (3<<20);
  3.                
  4.         GPIOE->CRL &=~(0XF<<20);
  5.         GPIOE->CRL |= (3<<20);
  6.        
  7. //        GPIOB->CRL&=0XFF0FFFFF;
  8. //        GPIOB->CRL|=0X00300000;//PB.5 推挽输出            
  9.                                                                                   
  10. //        GPIOE->CRL&=0XFF0FFFFF;
  11. //        GPIOE->CRL|=0X00300000;//PE.5推挽输出
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:18 | 显示全部楼层
其实这两个代码的意思是一样的,我们需要配置的是20-23位 为  0011
所以我们第一步应该先把这四个位清0,然后再对相应位置1 ,具体操作如图所示:

   GPIOB->CRL &=~(0XF<<20);   - - - 先对20-23 位  清0操作
    GPIOB->CRL |= (3<<20);       - - - 然后对0011左移20位  置1操作

那么这两串代码又是什么意思呢?

其实他和上面的功能一样,我们都知道这是一个32位寄存器,那么一个0XF就是1111,所以
0X FFFF FFFF,其实每一位都是对应一个引脚,也相当于控制四位,那么我们控制20-23清零操作就是//    GPIOB->CRL&=0XFF0FFFFF; 在第三位为0
然后再在第三位置1 ,就是GPIOB->CRL|=0X00300000; 在第三位写入3就是
0000 0000 0011 0000 0000 0000 0000 0000 ,然后就把对应位置为1了

//    GPIOB->CRL&=0XFF0FFFFF;
//    GPIOB->CRL|=0X00300000;//PB.5 推挽输出  
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:18 | 显示全部楼层
3)把引脚输出配置为高,这样使能位0的时候LED亮
我们这里就需要使用到端口输出数据寄存器(GPIOx_ODR) (x=A..E)  这个寄存器,因为这个寄存器只有前16位有效,所以我们只需要控制第5位就是引脚5的输出了。 401886566bb87a5959.png
 楼主| 花间一壶酒sd 发表于 2023-11-29 12:18 | 显示全部楼层
具体操作代码是:

//  GPIOB->ODR|=1<<5;      //PB.5 输出高
//                                                                                          
//        GPIOE->ODR|=1<<5;      //PE.5输出高
您需要登录后才可以回帖 登录 | 注册

本版积分规则

101

主题

1219

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部