目录
前言
为了保证学有所得,在这里记录STM32F103RCT6V4板的自学历程,包括每个模块的开发流程及其共性问题,和我参照开发手册和官方源码自己重写的代码(会有warning)持续丰满内容和更新,主要目的,是让我可以快速回顾STM32的开发工作流,并尽快上手摆脱开发手册和源代码实现自己开发。
一、GPIO_LED(STM32开发的基本流程)
GPIO头文件基本流程:
头文件的作用是提供驱动,为xxx.c文件提供宏定义,引用库函数
1、另定义引脚端口。2、定义GPIO时钟使能函数。3、定义GPIO控制函数。4、定义led.c文件的led初始化函数
/1、另定义端口和引脚,如:
#define LED0_PORT GPIOA
#define LED0_PIN GPIO_PIN_8
包括一些函数,都尽量重新定义名字,一方面是后续的程序书写更加直观更加直观,另一方面是避免直接使用库文件的函数,以免在特殊语句(如结构体中)出现语法错误
如:
/2、令定义函数:
#define LED0_clk_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); } while(0) // 时钟使能
时钟使能函数在 stm32f1xx_hal_rcc.h 文件中
直接使用库函数出错:GPIO_InitTypeDef gpio_init_type;
//GPIO_InitTypeDef只是头文件定义的一个GPIO的结构体,不能直接用,需要定义结构体变量 没定义‘gpio_init_type’的话就会出错
另行定义函数的另一个好处就是对库函数进行包装,使用起来便捷直观而且简单
如:
/3、
#define LED0(x) do{x ? HAL_GPIO_WritePin(LED0_PORT,LED0_PIN,GPIO_PIN_SET):\
HAL_GPIO_WritePin(LED0_PORT,LED0_PIN,GPIO_PIN_RESET);}while(0)
//控制LED开闭的函数
/4、
void led_INIT(void);
GPIO的led.c文件基本流程:
xxx.c 文件的作用就是给main()函数提供函数,如led的初始化函数
1、GPIO时钟使能,(直接写库函数,而不在头文件定义当然也可以但是容易出错)
如:
LED0_clk_ENABLE();
LED1_clk_ENABLE();
2、初始化GPIO口
如:
GPIO_InitTypeDef gpio_init_type;
//另行定义结构体gpio_init_type,其结构体类型为GPIO_InitTypeDef,根据GPIO_InitTypeDef中的元素依此填入
另行定义结构体gpio_init_type,其结构体类型为GPIO_InitTypeDef,根据GPIO_InitTypeDef中的元素依此填入
HAL_GPIO_Init(LED0_PORT, &gpio_init_type); //GPIO初始化
确定初始化的GPIO引脚地址
3、关灯(led初始化为熄灭状态)
main()文件的具体流程
1、初始化HAL库
如:
HAL_Init();
2、设置时钟,延时
如:
sys_stm32_clock_init(RCC_PLL_MUL9);
delay_init(72);
3、初始化led
4.执行函数
代码全貌:
led.h
/*定义引脚和使能*/
#ifndef _led_h
#define _led_h
#include ".\SYSTEM\sys\sys.h"
#define LED0_PORT GPIOA
#define LED0_PIN GPIO_PIN_8
#define LED0_clk_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); } while(0)
#define LED1_PORT GPIOD
#define LED1_PIN GPIO_PIN_2
#define LED1_clk_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); } while(0)
/*引脚定义完了,接下来需要定义什么时候led亮和灭*/
#define LED0(x) do{x ? HAL_GPIO_WritePin(LED0_PORT,LED0_PIN,GPIO_PIN_SET):\
HAL_GPIO_WritePin(LED0_PORT,LED0_PIN,GPIO_PIN_RESET);}while(0)
#define LED1(x) do{x ? HAL_GPIO_WritePin(LED1_PORT,LED1_PIN,GPIO_PIN_SET):\
HAL_GPIO_WritePin(LED1_PORT,LED1_PIN,GPIO_PIN_RESET);}while(0)
/*定义x为1时亮(GPIO_PIN_SET)否则灭*/
/*定义LED取反函数*/
////#define LED0_Toggle() do{HAL_GPIO_TogglePin(LED0_PORT,LED0_PIN);}while(0)
////#define LED1_Toggle() do{HAL_GPIO_TogglePin(LED1_PORT,LED1_PIN);}while(0)
void led_INIT(void);
#endif
led.c
#include "./BSP/led.h"
//定义LED初始化函数
void led_INIT(void)
{
//使能GPIO口
LED0_clk_ENABLE();
LED1_clk_ENABLE();
GPIO_InitTypeDef gpio_init_type; //GPIO_InitTypeDef只是头文件定义的一个GPIO的结构体,不能直接用,需要定义结构体变量
//GPIO初始化
gpio_init_type.Pin = LED0_PIN; //引脚口为LED0的引脚,即8号引脚
gpio_init_type.Mode = GPIO_MODE_OUTPUT_PP; //模式设置为推挽输出
gpio_init_type.Pull = GPIO_PULLUP; //设置为上拉电阻
gpio_init_type.Speed = GPIO_SPEED_FREQ_HIGH; //设置为高速
HAL_GPIO_Init(LED0_PORT, &gpio_init_type); //GPIO初始化
gpio_init_type.Pin = LED1_PIN;
HAL_GPIO_Init(LED1_PORT, &gpio_init_type);//这里是对HAL_GPIO_Init的LED_PORT赋值,所以必须先给LED0附上,再把gpio_init_type.Pin的值改为LED1
LED0(1); //关闭LED
LED1(1); //关闭LED
}
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/led.h"
int main(void)
{
HAL_Init(); //初始化HAL库
sys_stm32_clock_init(RCC_PLL_MUL9); //设置时钟72Mhz
delay_init(72); //设置延时倍数为72/8=9
led_INIT(); //初始化led
while(1)
{
LED0(0);
LED1(1);//初始设置LED0亮,LED1灭
delay_ms(500); //延时半秒
LED0(1);
LED1(0);
delay_ms(500);
}
}
二、按键KEY
头文件:
HAL_GPIO_ReadPin 函数:用于读取引脚口的电平状态信息
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
定义按键被按下的状态,其余与GPIO无二。
c文件有两个函数,一个就是普通的KEY初始化和执行功能的函数,另外还有一个消除按键抖动的函数 uint8_t key_scan(uint8_t Mode) 他的作用是保证按键不会多次触发或误触发,方法是定义一个描述按键键值的变量key_up和keyval,分别判断按键是否真的被按下和哪一个按键被按下,以此来保证后续的程序执行
uint8_t key_scan(uint8_t Mode)
{
//定义静态变量
static uint8_t key_up = 1; //松开标志
uint8_t keyval = 0; //按键按下种类判断
if (Mode) key_up = 1; /*当mode=1时支持连按,相当于每循环一遍都会将key_up复位为1,
否则,第一下没按完,key_up为0,不会触发后续函数*/
if (key_up && (KEY0 == 0 || KEY1 == 0 || WKUP == 1))//key_up为1,且有任意一个按键按下
{
delay_ms(10);//消抖
key_up = 0;//松开前不会置位
if (KEY0 == 0) keyval = KEY0_tr;
if (KEY1 == 0) keyval = KEY1_tr;
if (WKUP == 1) keyval = WKUP_tr;
}
else if(KEY0 ==1 && KEY1 ==1 && WKUP ==0 )
{
key_up = 1;//没有按键按下,松开标志置位
}
return keyval;
}
代码全貌:
key.h
#ifndef _key_h
#define _key_h
#include ".\SYSTEM\sys\sys.h"
/*定义按键和LED的PORT和引脚 按键:wk_up --> PA0
key1-->PA15
key0-->PC5
*/
#define WKUP_GPIO_PORT GPIOA
#define WKUP_GPIO_PIN GPIO_PIN_0
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_PIN GPIO_PIN_15
#define KEY0_GPIO_PORT GPIOC
#define KEY0_GPIO_PIN GPIO_PIN_5
/*定义时钟使能*/
//定义C口使能
#define KEY0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)
//定义A口使能
#define KEY1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)
/*定义IO口数据读取函数*/
#define KEY0 HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)
#define KEY1 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_15)
#define WKUP HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
/*定义按键状态*/
#define KEY0_tr 1
#define KEY1_tr 2
#define WKUP_tr 3
void KEY_init(void);
uint8_t key_scan(uint8_t Mode); /* 按键扫描函数 */
#endif
key.c
#include ".\BSP\KEY\key.h"
#include ".\SYSTEM\delay\delay.h"
#include ".\stm32f1xx_it.h"
#include ".\SYSTEM/usart\usart.h"
void KEY_init(void)
{
/*定义初始化GPIO*/
GPIO_InitTypeDef gpio_def_struct;
/*时钟使能*/
KEY0_GPIO_CLK_ENABLE();
KEY1_GPIO_CLK_ENABLE();
/*定义gpio初始特性*/
gpio_def_struct.Pin = KEY0_GPIO_PIN;
gpio_def_struct.Mode = GPIO_MODE_INPUT; //上拉输入
gpio_def_struct.Pull = GPIO_PULLUP; //上拉电阻,KEY0是高有效输入
gpio_def_struct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_def_struct.Pin); //初始化串口
//key1也是高有效,故其他都一样
gpio_def_struct.Pin = KEY1_GPIO_PIN;
HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_def_struct.Pin);
//WKUP是低有效,故要改为下拉
gpio_def_struct.Pin = WKUP_GPIO_PIN;
gpio_def_struct.Pull = GPIO_PULLDOWN; //下拉电阻,WKUP是低有效输入
HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_def_struct.Pin);
//初始化完成
}
//接下来要进行按键消抖
uint8_t key_scan(uint8_t Mode)
{
//定义静态变量
static uint8_t key_up = 1; //松开标志
uint8_t keyval = 0; //按键按下种类判断
if (Mode) key_up = 1; /*当mode=1时支持连按,相当于每循环一遍都会将key_up复位为1,
否则,第一下没按完,key_up为0,不会触发后续函数*/
if (key_up && (KEY0 == 0 || KEY1 == 0 || WKUP == 1))//key_up为1,且有任意一个按键按下
{
delay_ms(10);//消抖
key_up = 0;//松开前不会置位
if (KEY0 == 0) keyval = KEY0_tr;
if (KEY1 == 0) keyval = KEY1_tr;
if (WKUP == 1) keyval = WKUP_tr;
}
else if(KEY0 ==1 && KEY1 ==1 && WKUP ==0 )
{
key_up = 1;//没有按键按下,松开标志置位
}
return keyval;
}
main.c
其中的led函数用的就是上一节所编写的代码
#include ".\SYSTEM\delay\delay.h"
#include ".\SYSTEM\sys\sys.h"
#include ".\BSP\led.h"
#include ".\BSP\KEY\key.h"
int main()
{
uint8_t key;
HAL_Init();//初始化HAL库
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72Mhz */
delay_init(72); /* 延时初始化 */
led_INIT(); /* 初始化 LED */
KEY_init(); /* 初始化按键 */
LED0(0); /* 先点亮 LED0 */
while(1)
{
key = key_scan(0); //不支持连按
if(key != 0)
{
switch(key)
{
case KEY0_tr: //按下key0点亮/熄灭LED0
LED0_Toggle();
break;
case KEY1_tr:
LED1_Toggle(); //按下KEY1点亮/熄灭LED0
break;
case WKUP_tr:
LED1(1);
LED0(0); //按下WKUP恢复初始
break;
}
}
else delay_ms(10);
}
}