STM32控制LED灯

544 阅读5分钟

  本文练习硬件使用STM32F103C8芯片开发板,软件使用Keil5-5.23版本安装对应Keil4兼容包,使用Keil4标准库函数开发,练习目的为掌握STM32单片机操作方法,最终独立完成相关项目,为巩固学习并分享学习经验现分享如下:

LED相关操作

1.1 相关函数定义

  1. 相关函数定义
    1. LED.h
      #ifndef __LED_H
      #define __LED_H	 
      #include "sys.h"
      
      //#define LED1 PBout(0)// PB0
      //#define LED2 PBout(1)// PB1	
      #define LEDPORT	GPIOB	//定义IO接口
      #define LED1	GPIO_Pin_0	//定义IO接口
      #define LED2	GPIO_Pin_1	//定义IO接口
      
      void LED_Init(void);//初始化
                                  
      #endif
      
    2. LED.C
      #include "led.h"
      
      void LED_Init(void){ //LED灯的接口初始化
      GPIO_InitTypeDef  GPIO_InitStructure; 	
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
      GPIO_InitStructure.GPIO_Pin = LED1|LED2; //选择端口号(0~15或all)                        
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式       
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
      GPIO_Init(LEDPORT, &GPIO_InitStructure);			
      }
      
    3. delay.h
      #ifndef __DELAY_H
      #define __DELAY_H 			   
      #include "sys.h"
      
      void delay_s(u16 s);
      void delay_ms(u16 ms);
      void delay_us(u32 us);
      
      #endif
      
    4. delay.c
      #include "delay.h"
      #define AHB_INPUT  72  //请按RCC中设置的AHB时钟频率填写到这里(单位MHz)
      
      void delay_us(u32 uS){ //uS微秒级延时程序(参考值即是延时数,72MHz时最大值233015)	
          SysTick->LOAD=AHB_INPUT*uS;      //重装计数初值(当主频是72MHz,72次为1微秒)
          SysTick->VAL=0x00;        //清空定时器的计数器
          SysTick->CTRL=0x00000005;//时钟源HCLK,打开定时器
          while(!(SysTick->CTRL&0x00010000)); //等待计数到0
          SysTick->CTRL=0x00000004;//关闭定时器
      }
      
      void delay_ms(u16 ms){ //mS毫秒级延时程序(参考值即是延时数,最大值65535)	 		  	  
          while( ms-- != 0){
              delay_us(1000);	//调用1000微秒的延时
          }
      }
      
      void delay_s(u16 s){ //S秒级延时程序(参考值即是延时数,最大值65535)	 		  	  
          while( s-- != 0){
          delay_ms(1000);	//调用1000毫秒的延时
      }
      } 
      
    5. KEY.h
      #ifndef __KEY_H
      #define __KEY_H	 
      #include "sys.h"
      
      //#define KEY1 PAin(0)// PA0
      //#define KEY2 PAin(1)// PA1
      #define KEYPORT	GPIOA	//定义IO接口组
      #define KEY1	GPIO_Pin_0	//定义IO接口
      #define KEY2	GPIO_Pin_1	//定义IO接口
      
      void KEY_Init(void);//初始化				    
      #endif
      
    6. KEY.c
      #include "key.h"
      
      void KEY_Init(void){ //微动开关的接口初始化
      GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);       
      GPIO_InitStructure.GPIO_Pin = KEY1 | KEY2; //选择端口号(0~15或all)                        
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
      //    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
      GPIO_Init(KEYPORT,&GPIO_InitStructure);			
      }
      

1.2 LED相关操作

  1. 端口信息 LED电路图.png

1.2.1 使用库函数点亮LED

  1. 使用库函数点亮LED
    1. 初始化时钟RCC_Configuration
    2. 初始化LED端口 LED_Init
      void LED_Init(void){ //LED灯的接口初始化
         GPIO_InitTypeDef  GPIO_InitStructure; 	//定义结构体变量
         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE); //启动APB2总线上的功能   
         GPIO_InitStructure.GPIO_Pin = LED1 | LED2; //选择端口号(0~15或all)                        
         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式       
         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
         GPIO_Init(LEDPORT, &GPIO_InitStructure);	
         /*
         选择IO接口工作方式:
         GPIO_Mode_AIN 模拟输入
         GPIO_Mode_IN_FLOATING 浮空输入
         GPIO_Mode_IPD 下拉输入
         GPIO_Mode_IPU 上拉输入
         GPIO_Mode_Out_PP 推挽输出
         GPIO_Mode_Out_OD 开漏输出
         GPIO_Mode_AF_PP 复用推挽输出
         GPIO_Mode_AF_OD 复用开漏输出
         */		
      注意:
      1. GPIO端口速度只有在输出模式才需要设置
      
    3. 点亮方法1: GPIO_Writebit void GPIO_WriteBit(GPIO_TypeDef* GPIOx, u16 GPIO_Pin, BitAction BitVal) 功能: 设置或者清除指定的数据端口位 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 GPIO_Pin:待设置的端口位,0-15 BitVal:该参数指定了待写入的值,该参数必须取枚举 BitAction的其中一个值 Bit_RESET: 清除数据端口位 Bit_SET: 设置数据端口位 返回值: 无
    4. 点亮方法2: GPIO_ReadOutputDataBit u8 GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, u16 GPIO_Pin) 功能: 读取指定端口管脚的输出 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 GPIO_Pin:待读取的端口位 返回值: 输出端口管脚值
    5. 点亮方法3: GPIO_SetBits void GPIO_SetBits(GPIO_TypeDef* GPIOx, u16 GPIO_Pin) 功能: 设置指定的数据端口位 1 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 GPIO_Pin:待设置的端口位,0-15 返回值: 无 GPIO_ResetBits void GPIO_ResetBits(GPIO_TypeDef* GPIOx, u16 GPIO_Pin) 功能: 清除指定的数据端口位 0 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 GPIO_Pin:待设置的端口位,0-15 返回值: 无
    6. 点亮方法4(难度较大): GPIO_Write void GPIO_Write(GPIO_TypeDef* GPIOx, u16 PortVal) 功能: 向指定 GPIO 数据端口写入数据(整组操作) 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 PortVal: 待写入端口数据寄存器的值,0x0001 返回值: 无

1.2.2 自定义端口点亮LED

  1. 自定义端口点亮LED
    1. sys.h中封装BIT_ADDR函数
    #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
    #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
    #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
    
    1. sys.h中封装PBout函数控制LED1端口输出电平
    #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出
    #define LED1 PBout(0)// PB0
    
    1. LED.h中封装LED1函数 #define LED1 PBout(0)// PB0
    2. 修改原LED_Init函数中相应端口组,端口号
    3. 调用PBout函数点亮LED1 LED1=1;

1.2.3 延时点亮LED

  1. 延时点亮LED灯
    1. delay_uS void delay_us(u32 us) 功能: 延时闪灯 参数: uS:延时时长,单位us微秒//72MHz最大值233015 返回值: 无
      void delay_us(u32 uS){ //uS微秒级延时程序(参考值即是延时数,72MHz时最大值233015)	
         SysTick->LOAD=AHB_INPUT*uS;      //重装计数初值(当主频是72MHz,72次为1微秒)
         SysTick->VAL=0x00;        //清空定时器的计数器
         SysTick->CTRL=0x00000005;//时钟源HCLK,打开定时器
         while(!(SysTick->CTRL&0x00010000)); //等待计数到0
         SysTick->CTRL=0x00000004;//关闭定时器
      }
      
    2. delay_ms void delay_us(u16 ms 功能: 延时闪灯 参数: mS:毫秒//最大65535 返回值: 无
      void delay_ms(u16 ms){ //mS毫秒级延时程序(参考值即是延时数,最大值65535)	 		  	  
         while( ms-- != 0){
            delay_us(1000);	//调用1000微秒的延时
      }
      
    3. delay_s void delay_us(u16 s) 功能: 延时闪灯 参数: S:秒//最大65535 返回值: 无
      void delay_s(u16 s){ //S秒级延时程序(参考值即是延时数,最大值65535)	 		  	  
         while( s-- != 0){
            delay_ms(1000);	//调用1000毫秒的延时
      }
      

1.2.4 LED呼吸灯

  1. LED呼吸灯
    1. 初始化时钟,LED端口
    2. 设置变量与变暗程序,循环执行
      1. 设置变量t用于控制占空比
      2. 设置变量JUMP用于循环的转换
      3. 变亮循环
        if(JUMP == 0){
        for(i = 0; i < 10; i++){//i放大频率周期
           GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //亮
           delay_us(t); //亮占比
           GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); //暗
           delay_us(501-t); //暗占比
        }
        t++;
        //转换循环
        if(t==500){
           JUMP = 1;
        }
        }
        
      4. 变暗循环
        if(JUMP == 1){
        for(i = 0; i < 10; i++){
           GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //亮
           delay_us(t); //亮占比
           GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); //暗
           delay_us(501-t); //暗占比
        }
        t--;
        //转换循环
        if(t==1){
           JUMP = 0;
        }
        
      }
    3. 相关概念
      1. 占空比 一个频率周期内高电平与低电平时间的比值,可以控制LED亮度
      注意:
      1. 通过调整占空比改变LED亮度,占空比:点亮时间和熄灭时间的比值
      2. 主函数while循环内嵌套变量循环和变暗循环
      3. 变亮循环占空比由低到高,变暗循环占空比由高到低
      4. 可用for循环设置循环次数放大单次变亮,变暗时长

1.2.5 按键控制LED

  1. 按键控制LED灯光
    1. 按键端口信息
    按键电路图.png 2. 初始化时钟,LED 3. 初始化按键KEY_Init KEY_Init
    void KEY_Init(void){ //微动开关的接口初始化
     GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);       
    GPIO_InitStructure.GPIO_Pin = KEY1 | KEY2; //选择端口号(0~15或all)                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
    //    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输入模式不需要设置端口速度
     GPIO_Init(KEYPORT,&GPIO_InitStructure);			
    }
    
    1. 无锁存形式 GPIO_ReadInputDataBit u8 GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, u16 GPIO_Pin) 功能: 读取指定端口管脚的输入 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 GPIO_Pin:待读取的端口位 返回值: 输入端口管脚值
      1. 方案1: 判断按键电平值,为1则LED熄灭,为0则LED点亮
        if(GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
           GPIO_ResetBits(LEDPORT,LED1); //LED灯都为低电平(0) 
        }else{	
           GPIO_SetBits(LEDPORT,LED1); //LED灯都为高电平(1) 
        }
        
      2. 方案2: 将按键接口电平取反,赋值给LED
        GPIO_WriteBit(LEDPORT,LED1,(BitAction)(!GPIO_ReadInputDataBit(KEYPORT,KEY1)));
        
    2. 有锁存形式 GPIO_ReadOutputDataBit u8 GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, u16 GPIO_Pin) 功能: 读取指定端口管脚的输出 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 GPIO_Pin:待读取的端口位 返回值: 输出端口管脚值
      1. 方案1:
        1. 判断按键导通则去抖动20ms
        2. 判断按键导通将LED输出电平取反,赋值给LED
        3. 判断按键松开
        if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
           delay_ms(20); //延时去抖动
           if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
              GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1-GPIO_ReadOutputDataBit(LEDPORT,LED1))); //LED取反
           while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开 
           }
        }
        
      2. 方案2:
        1. 判断按键导通则去抖动20ms
        2. 判断按键导通则a++,a>3则a=0,将a赋值给LED端口状态
        3. 判断按键松开
        if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
           delay_ms(20); //延时20ms去抖动
           if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平
              //在2个LED上显示二进制加法
              a++; //变量加1
              if(a>3){ //当变量大于3时清0
                 a=0; 
              }
              GPIO_Write(LEDPORT,a); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上)
              while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开 
           }
        }
        
    3. 相关概念
      1. 按键工作原理
        1. 按下时导通,低电平
        2. 松开时断开,高电平
      2. 锁存
        1. 无锁存:按下时点亮,松开后熄灭
        2. 有锁存:按下后点亮,松开后熄灭
      3. 延时去除按键抖动,20ms左右 按键抖动.png