本文练习硬件使用STM32F103C8芯片开发板,软件使用Keil5-5.23版本安装对应Keil4兼容包,使用Keil4标准库函数开发,练习目的为掌握STM32单片机操作方法,最终独立完成相关项目,为巩固学习并分享学习经验现分享如下:
LED相关操作
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
- 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); }
- 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
- 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毫秒的延时 } }
- 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
- 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); }
- LED.h
1.2 LED相关操作
- 端口信息
1.2.1 使用库函数点亮LED
- 使用库函数点亮LED
- 初始化时钟RCC_Configuration
- 初始化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端口速度只有在输出模式才需要设置
- 点亮方法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: 设置数据端口位 返回值: 无 - 点亮方法2:
GPIO_ReadOutputDataBit
u8 GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
功能: 读取指定端口管脚的输出 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 GPIO_Pin:待读取的端口位 返回值: 输出端口管脚值 - 点亮方法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_ResetBitsvoid GPIO_ResetBits(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
功能: 清除指定的数据端口位 0 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 GPIO_Pin:待设置的端口位,0-15 返回值: 无 - 点亮方法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
- 自定义端口点亮LED
- 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))
- sys.h中封装PBout函数控制LED1端口输出电平
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出 #define LED1 PBout(0)// PB0
- LED.h中封装LED1函数
#define LED1 PBout(0)// PB0
- 修改原LED_Init函数中相应端口组,端口号
- 调用PBout函数点亮LED1
LED1=1;
1.2.3 延时点亮LED
- 延时点亮LED灯
- 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;//关闭定时器 }
- delay_ms
void delay_us(u16 ms
功能: 延时闪灯 参数: mS:毫秒//最大65535 返回值: 无void delay_ms(u16 ms){ //mS毫秒级延时程序(参考值即是延时数,最大值65535) while( ms-- != 0){ delay_us(1000); //调用1000微秒的延时 }
- delay_s
void delay_us(u16 s)
功能: 延时闪灯 参数: S:秒//最大65535 返回值: 无void delay_s(u16 s){ //S秒级延时程序(参考值即是延时数,最大值65535) while( s-- != 0){ delay_ms(1000); //调用1000毫秒的延时 }
- delay_uS
1.2.4 LED呼吸灯
- LED呼吸灯
- 初始化时钟,LED端口
- 设置变量与变暗程序,循环执行
- 设置变量t用于控制占空比
- 设置变量JUMP用于循环的转换
- 变亮循环
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; } }
- 变暗循环
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; }
- 相关概念
- 占空比 一个频率周期内高电平与低电平时间的比值,可以控制LED亮度
- 通过调整占空比改变LED亮度,占空比:点亮时间和熄灭时间的比值
- 主函数while循环内嵌套变量循环和变暗循环
- 变亮循环占空比由低到高,变暗循环占空比由高到低
- 可用for循环设置循环次数放大单次变亮,变暗时长
1.2.5 按键控制LED
- 按键控制LED灯光
- 按键端口信息
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); }
- 无锁存形式
GPIO_ReadInputDataBit
u8 GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
功能: 读取指定端口管脚的输入 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 GPIO_Pin:待读取的端口位 返回值: 输入端口管脚值- 方案1:
判断按键电平值,为1则LED熄灭,为0则LED点亮
if(GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //读按键接口的电平 GPIO_ResetBits(LEDPORT,LED1); //LED灯都为低电平(0) }else{ GPIO_SetBits(LEDPORT,LED1); //LED灯都为高电平(1) }
- 方案2:
将按键接口电平取反,赋值给LED
GPIO_WriteBit(LEDPORT,LED1,(BitAction)(!GPIO_ReadInputDataBit(KEYPORT,KEY1)));
- 方案1:
判断按键电平值,为1则LED熄灭,为0则LED点亮
- 有锁存形式
GPIO_ReadOutputDataBit
u8 GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
功能: 读取指定端口管脚的输出 参数: GPIOx:x 可以是 A,B,C,D 或者 E,来选择 GPIO 外设 GPIO_Pin:待读取的端口位 返回值: 输出端口管脚值- 方案1:
- 判断按键导通则去抖动20ms
- 判断按键导通将LED输出电平取反,赋值给LED
- 判断按键松开
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:
- 判断按键导通则去抖动20ms
- 判断按键导通则a++,a>3则a=0,将a赋值给LED端口状态
- 判断按键松开
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)); //等待按键松开 } }
- 方案1:
- 相关概念
- 按键工作原理
- 按下时导通,低电平
- 松开时断开,高电平
- 锁存
- 无锁存:按下时点亮,松开后熄灭
- 有锁存:按下后点亮,松开后熄灭
- 延时去除按键抖动,20ms左右
- 按键工作原理