“使用STM32控制LED灯实现闪烁效果”

STM32寄存器和库函数点灯

  • 一、寄存器操作
  • 1、新建工程,新建一个目录存放以后所有的工程stmproject,在这个目录下新建文件夹寄存器点灯,文件名为LED。
  • 2、新建文件main.c并双击source group文件夹添加进去
  • 3、添加启动文件,取官方固件库中根据FLASH大小选择型号,我选大容量hd
  • 4、编写main程序通过寄存器控制I/O口输入输出点灯
  • 使能时钟
  • 配置端口寄存器,再通过端口寄存器输出数据
  • 总结
  • 二、库函数点灯
  • 1 建立库函数工程模板
  • 2 新建库函数点灯工程
  • 3 调用库函数编写程序点亮
  • 4 程序升级
  • 一、寄存器操作


    1、新建工程,新建一个目录存放以后所有的工程stmproject,在这个目录下新建文件夹寄存器点灯,文件名为LED。

    接下来选择所用的软件包,之前安装软件时安装的,选择对应型号即可。

    如果需要安装别的安装包可以在这个网址下载。点击打开文件

    点击next,再点击finish就安装成功了。可以在包安装里找到。

    2、新建文件main.c并双击source group文件夹添加进去


    3、添加启动文件,取官方固件库中根据FLASH大小选择型号,我选大容量hd



    复制到点灯文件夹后再添加进source group

    框架搭好

    4、编写main程序通过寄存器控制I/O口输入输出点灯

    使能时钟


    本人开发板LED等分别连到PB5和PE5上,所以点亮这两个灯需要使第三位和第六位置一开启时钟。上图给出了偏移地址,还需要知道RCC寄存器的起始地址,由中文参考手册里的存储器映射图可得,

    下面写出代码

    	*(unsigned int *) 0x40021018 |=(1<<3);  //使能端口B时钟
    	*(unsigned int *) 0x40021018 |=(1<<6);  //是能端口E时钟
    

    配置端口寄存器,再通过端口寄存器输出数据

    stm32因为引脚太多,所以把引脚分成了几组,每组最多16个引脚,其中32位端口配置低寄存器控制低八位引脚,32位端口配置高寄存器控制高八位引脚。本次只用到了低八位


    每个引脚分为配置位和模式位4位来配置,首先确定模式位是输入还是输出,再确定输入和是输出模式下具体的模式。上图可以得到端口的起始地址和偏移地址。代码如下:

    	*(unsigned int *) 0x40010C00 &=~(0x0F<<(4*5));  //把配置B端口5引脚那四位清零
    	*(unsigned int *) 0x40010C00 |=(0x01<<(4*5));   //把PB5配置成通用推挽输出,最大速度10MHz
    	*(unsigned int*)0x40011800 &=~(0x0F<<(4*5));  //把配置E端口5引脚那四位清零
    	*(unsigned int*)0x40011800 |=(0x03<<(4*5));   //把PE5配置成通用推挽输出,最大速度10MHz
    
    

    再用端口输出寄存器使PB5和PE5输出低电平(由pcb图可直),由下图可知偏移地址0x0C

    io口输出低电平电流才能流过二极管点灯

    代码如下:

    *(unsigned int*)0x40010C10 &=~(1<<5);    //B端口5清零
    *(unsigned int*)0x4001180C &=~(1<<5);    //E端口5清零
    

    最后把程序下载到单片机里即可点亮两个小灯。

    总结

    1、首先新建工程时,需要启动文件,main里需要一个系统时钟。
    2、STM32引脚多,为节约资源,每个引脚都需要开启对应时钟。
    3、点亮LED主要是控制GPIO输出高电平或低电平,需要GPIO寄存器来实现。
    4、置零,置一技巧
    某几位置零 &=~(x<<y)
    某位置一 |=(x<<y)
    5、各种地址可以用宏定义表示出来放在一个头文件里,这样程序可读性更高,以后库函数变成就这样,结构相同需要重复的还可用结构体表示。如

    	RCC_APB2ENR |=(1<<3);  //是能端口B时钟
    	
    	GPIOB_CRL &=~(0x0F<<(4*5));  //把配置B端口5引脚那四位清零
    	GPIOB_CRL |=(1<<(4*5));   //把PB5配置成通用推挽输出,最大速度10MHz
    	
    	GPIOB_ODR &=~(1<<5);    //B端口5清零
    

    二、库函数点灯


    1 建立库函数工程模板

    1. 在stmproject文件夹下新建Template文件夹,并新建 USER,CORE,OBJ ,STM32F10x_FWLib这几个文件夹用于装需要的库文件。

    2. CORE文件夹需要从固件库中找到三个文件并复制进来,分别在这两条文件路径下寻找:
      1 STM32F1xx固件库 \STM32F10x_StdPeriph _Li b_V3.5. 0\ Libraries \CMSIS \CM3\CoreSupport
      2 STM32F1xx固件库 \STM32F10x_StdPeriph_Lib_V3.5.0 \Libraries\ CMSIS \CM3 \DeviceSupport\ST\STM32F10x\startup\arm里的启动文件

      注:hd代表大容量,根据自身的芯片容量选取。

    3. STM32F10x_FWLib文件夹下需要找到两个文件夹并复制进来,在这条路径下:
      STM32F1xx固件库\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries \STM32F10x_StdPeriph_Driver

    4. USER文件夹下需要找到6个文件并复制进来,文件路径如下:
      1 STM32F1xx固件库\STM32F10x_StdPeriph_Lib_V3.5.0\Project\ STM32F10x_StdPeriph_Template
      2 STM32F1xx固件库\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3 \DeviceSupport\ST\STM32F10x

    5. OBJ里存放编译完的文件。
      到此为止,模板就建成了,以后需要建立工程之前可以直接复制一份改个名字。

    2 新建库函数点灯工程

    1. 把Template复制一份并改名为库函数点灯
    2. 打开keil5,点击new uVision project ,把工程文件放在库函数点灯下的USER里,取名为LED,然后弹出界面选择自己的芯片,点击ok。
    3. 右键点击Target1,选择manage project items,把group栏和files栏变成下图的样子:



      这三个组分别把之前复制到同名文件夹下的.c文件添加进来,CORE多添加了一个启动文件。
    4. 点击魔术棒进入工程配置
      先配置文件输出,点击output,选择OBJ为输出文件夹

      再配置C/C++选项卡,在Define中填写STM32F10X_HD,USE_STDPERIPH_DRIVER 配置全局宏定义变量,再把带有头文件的文件夹添加到include paths


    5. 再USER文件夹中添加main.c文件,写下如下代码,点击编译,不报错就可以进行代码编写了。

    6. 在库函数点灯文件夹下,新建HARDWARE文件夹,再从HARDWARE里新建LED文件夹,LED文件夹里新建两个文件led.c和led.h。

    7. 再按照第三步添加HARDWARE组,组里添加led.c,
    添加完写下#include "led.h"编译一下点左边加号就会有led.h,还需要配置一下C/C++选项卡include paths使得可以被找到。

    3 调用库函数编写程序点亮

    由上面寄存器点灯可知,点亮小灯就是控制GPIO引脚输出高电平或低电平(根据硬件电路决定),电流流通二极管就发光了。
    所以控制GPIO引脚输出低电平需要的函数要去STMF10x_FWLib中的rcc和gpio文件中找。

    1. 先进入rcc的头文件寻找APB2外设时钟使能的函数


      点击右键可以回到函数的定义看到函数的参数怎么填
    2. 去gpio里找到配置GPIO的函数,也是同上,先去头文件最下找到函数的声明,再右键跳到函数定义。


    由于这个函数第二个参数是一个结构体,所以我们需要用这个结构体实例化一个对象,所以可以右键转到结构体的函数定义。

    结构体中参数的取值也都在头文件里用枚举和宏定义表示了。


    最后还需要一个置位复位函数

    3. 编写led.c,把PB5和PE5时钟使能,并配置输入输出模式

    #include "led.h"
    void LED_Init(void)
    {
    	//实例化结构体
    	GPIO_InitTypeDef GPIO_InitStruct;
    	
        //使能B和E端口的时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
        
        //配置端口引脚5输入输出模式和速度
        GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
        
        //把B和E端口按照上面配置的进行初始化
        GPIO_Init(GPIOB, &GPIO_InitStruct);
        GPIO_Init(GPIOE, &GPIO_InitStruct);    
    }
    
    1. 编写led.h
    #ifndef __LED_H  //避免头文件交叉引用后被重复编译
    #define __LED_H
    
    #include "stm32f10x.h"
    
    void LED_Init(void);  //声明函数
    #endif
    
    1. 编写main.c,调用初始化函数,并通过复位和置位函数控制灯的亮灭
    #include "stm32f10x.h"
    #include "led.h"
    
    //写一个简单的延时函数
    void Delay( uint32_t count)
    {
        for(;count!=0;count--);
    }
    
    int main()
    {	
    	LED_Init();     //调用初始化函数
        while(1)
        {
        	//复位低电平灯亮,置位高电平灯灭
            GPIO_ResetBits(GPIOB,GPIO_Pin_5);    
            GPIO_SetBits( GPIOE,GPIO_Pin_5);
            Delay(0xFFFFF);
            GPIO_SetBits( GPIOB,GPIO_Pin_5);
            GPIO_ResetBits(GPIOE,GPIO_Pin_5);               
            Delay(0xFFFFF);
        }   
    }
    

    此时就可以把程序下载到芯片里,(程序下载见我第一篇文章),此时就可以看到两个等交替亮灭。

    4 程序升级

    上述程序可以达到要求,但是可移植性和查看性不强,可以把GPIO端口、引脚、时钟和控制灯亮灭的置位复位函数都写成宏定义,具体代码如下:
    led.h

    #ifndef __LED_H
    #define __LED_H
    
    #include "stm32f10x.h"
    
    //定义两个灯的端口、引脚和时钟,LED0是PB5,LED1是PE5
    #define LED_0_PIN   GPIO_Pin_5
    #define LED_0_PORT  GPIOB
    #define LED_0_CLK   RCC_APB2Periph_GPIOB
    
    #define LED_1_PIN   GPIO_Pin_5
    #define LED_1_PORT  GPIOE
    #define LED_1_CLK   RCC_APB2Periph_GPIOE
    
    //用LED(ON/OFF)控制两灯亮灭
    #define ON  1
    #define OFF 0
    #define LED0(x) if (x) GPIO_ResetBits(LED_0_PORT,LED_0_PIN);\
                    else  GPIO_SetBits( LED_0_PORT,LED_0_PIN);
    
    #define LED1(x) if (x) GPIO_ResetBits(LED_1_PORT,LED_1_PIN);\
                    else  GPIO_SetBits( LED_1_PORT,LED_1_PIN) ;               
                  
    void LED_Init(void);  //声明初始化函数
    #endif
    

    led.c

    #include "led.h"
    
    void LED_Init(void)
    {
        //实例化配置GPIO模式和速度结构体
    	GPIO_InitTypeDef GPIO_InitStruct;  
    	
        //使能两灯的时钟
        RCC_APB2PeriphClockCmd(LED_0_CLK, ENABLE);
        RCC_APB2PeriphClockCmd(LED_1_CLK, ENABLE);
        
        //配置LED0引脚为推挽输出,速度为50MHz
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStruct.GPIO_Pin = LED_0_PIN;
        GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
        //调用GPIO初始化函数,对LED0端口按上面配置初始化
        GPIO_Init(LED_0_PORT, &GPIO_InitStruct); 
        
        //配置LED1引脚为推挽输出,速度为50MHz
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStruct.GPIO_Pin = LED_1_PIN;
        GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
        //调用GPIO初始化函数,对LED1端口按上面配置初始化
        GPIO_Init(LED_1_PORT, &GPIO_InitStruct);
          
    }
    

    main.c

    #include "stm32f10x.h"
    #include "led.h"
    
    //一个简单的延时函数
    void Delay( uint32_t count)
    {
        for(;count!=0;count--);
    }
    
    int main()
    {
    	//调用初始化函数
    	LED_Init();
        
        //两个灯交替亮灭
        while(1)
        {
            LED0(1);
            LED1(0);
            Delay(0xFFFFF);
            LED0(0);
            LED1(1);        
            Delay(0xFFFFF);
        }  
    }
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » “使用STM32控制LED灯实现闪烁效果”

    发表评论