如果不想看的可以直接使用git把我的代码下载出来,里面工程挺全的,后期会慢慢的补注释之类的
stm32学习笔记: stm32学习笔记源码 (gitee.com)(出差是公司电脑改的代码,有锁所以不能上传,只能回去了拿自己电脑上传了)
如果不会使用git快速下载可以选择直接下载压缩包或者去看看git的使用
git的使用git的使用(下载及上传_gitcode怎么下载代码-CSDN博客
有错误的地方欢迎大家多多指出,方便我修改错误,以及提升自己
目录
本系列主要为了学习安富莱源码的规范写法以及思想。
1 确定自己板子的硬件连接
我的板子硬件如下:为低电平导通,硬件连接为PB5 PB0 PB1
2 宏定义
附上安富莱的宏定义:写在bsp_led.c 里面的我个人还是喜欢宏定义和结构图在h文件,我给移到H文件去了,
这里改成了我自己的硬件GPIOB的时钟通道,以及引脚更改。
/*
该程序适用于安富莱STM32-V4 开发板
如果用于其它硬件,请修改GPIO定义
如果用户的LED指示灯个数小于4个,可以将多余的LED全部定义为和第1个LED一样,并不影响程序功能
*/
/* 按键口对应的RCC时钟 */
#define RCC_ALL_LED (RCC_APB2Periph_GPIOB)
#define GPIO_PORT_LED1 GPIOB
#define GPIO_PIN_LED1 GPIO_Pin_5
#define GPIO_PORT_LED2 GPIOB
#define GPIO_PIN_LED2 GPIO_Pin_0
#define GPIO_PORT_LED3 GPIOF
#define GPIO_PIN_LED3 GPIO_Pin_1
#define GPIO_PORT_LED4 GPIOB
#define GPIO_PIN_LED4 GPIO_Pin_5
3 GPIO初始化
/*
*********************************************************************************************************
* 函 数 名: bsp_InitLed
* 功能说明: 配置LED指示灯相关的GPIO, 该函数被 bsp_Init() 调用。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitLed(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 打开GPIO时钟 */
RCC_APB2PeriphClockCmd(RCC_ALL_LED, ENABLE);
/*
配置所有的LED指示灯GPIO为推挽输出模式
由于将GPIO设置为输出时,GPIO输出寄存器的值缺省是0,因此会驱动LED点亮.
这是我不希望的,因此在改变GPIO为输出前,先关闭LED指示灯
*/
bsp_LedOff(1);
bsp_LedOff(2);
bsp_LedOff(3);
bsp_LedOff(4);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽输出模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_LED1;
GPIO_Init(GPIO_PORT_LED1, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_LED2;
GPIO_Init(GPIO_PORT_LED2, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_LED3;
GPIO_Init(GPIO_PORT_LED3, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_LED4;
GPIO_Init(GPIO_PORT_LED4, &GPIO_InitStructure);
}
第一句:创建一个结构体变量 类型为GPIO_InitTypeDef 名字为GPIO_InitStructure
第二句:RCC_APB2PeriphClockCmd(RCC_ALL_LED, ENABLE);
库函数:可以在官方的rcc.c 里面找到,首先你需要清楚你使用的IO口是挂载在哪个总线下面:可以·看见所有的GPIO都在APB2下面,所有我们开启APB2的时钟
后面的这几句,就是关闭LED灯了,这个之前说过我们的LED是低电平点亮,然而GPIO输出寄存器默认值为0,所有开启后会默认点亮LED,所以这里直接关闭了LED。
bsp_LedOff(1);
bsp_LedOff(2);
bsp_LedOff(3);
bsp_LedOff(4);
之后就是往结构体写入数据:这是gpio结构体:往里面写入数据
1 GPIO的数据 2 GPIO的速度 3 GPIO的模式
1 直接写入GPIO引脚号 这里有上面结构体的定义 类型是16位短整型 这里我们之前用了宏定义:
2 类型为GPIOSpeed_TypeDef 可以选以下类型 我们选GPIO_Speed_50MHz
3 GPIO的模式:变量类型为GPIOMode_TypeDef 里面有这些类型,我们选择为推完输出:各个输出的类型区别可以去这个链接了解:
这里就写入了:GPIO_Init() 大部分外设都会将你自己创的结构体写入到init这个函数里面,他会帮你把你创的数据,赋值给各个寄存器。
这里LED2上面没有再写speed和mode,因为结构体的值是一直存在的,你没有更改的话他就是上面我们设置的这两个。
这样我们的LED灯就初始化完成了。
4 LED控制函数
4.1 LED开启函数
/*
*********************************************************************************************************
* 函 数 名: bsp_LedOn
* 功能说明: 点亮指定的LED指示灯。
* 形 参: _no : 指示灯序号,范围 1 - 4
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_LedOn(uint8_t _no)
{
_no--;
if (_no == 0)
{
GPIO_PORT_LED1->BRR = GPIO_PIN_LED1;
}
else if (_no == 1)
{
GPIO_PORT_LED2->BRR = GPIO_PIN_LED2;
}
else if (_no == 2)
{
GPIO_PORT_LED3->BRR = GPIO_PIN_LED3;
}
else if (_no == 3)
{
GPIO_PORT_LED4->BRR = GPIO_PIN_LED4;
}
}
函数类型无 输入参数为一个8位无符号整形 这里输入后做了一个--操作:这里我也不清楚为什么这样写:清楚的朋友可以给我留个言哈。我们led灯输入为1-4,--之后就是0-3了。
这里我们举例一个,假设输入参数为1,进来之后就为0,然后这里我们直接往GPIOB的BRR写入数据。BRR是清楚寄存器
这里往里写入1 就会清除ODR的数据。
这里我们可以看见假设我们是GPIO_PIN_5 官方库里面就是 ((uint16_t)0x0020) 这里就是0010 0000 第五位 ,所以就是将第五位写入1 从而将ODR清楚。
4.2 LED关闭函数
/*
*********************************************************************************************************
* 函 数 名: bsp_LedOff
* 功能说明: 熄灭指定的LED指示灯。
* 形 参: _no : 指示灯序号,范围 1 - 4
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_LedOff(uint8_t _no)
{
_no--;
if (_no == 0)
{
GPIO_PORT_LED1->BSRR = GPIO_PIN_LED1;
}
else if (_no == 1)
{
GPIO_PORT_LED2->BSRR = GPIO_PIN_LED2;
}
else if (_no == 2)
{
GPIO_PORT_LED3->BSRR = GPIO_PIN_LED3;
}
else if (_no == 3)
{
GPIO_PORT_LED4->BSRR = GPIO_PIN_LED4;
}
}
还是一样_no- - (不清楚为什么)之后和点亮灯一样 0-15位设置BSRR寄存器为1,为1之后ODR就会被设置为1.
4.3 LED翻转函数
/*
*********************************************************************************************************
* 函 数 名: bsp_LedToggle
* 功能说明: 翻转指定的LED指示灯。
* 形 参: _no : 指示灯序号,范围 1 - 4
* 返 回 值: 按键代码
*********************************************************************************************************
*/
void bsp_LedToggle(uint8_t _no)
{
if (_no == 1)
{
GPIO_PORT_LED1->ODR ^= GPIO_PIN_LED1;
}
else if (_no == 2)
{
GPIO_PORT_LED2->ODR ^= GPIO_PIN_LED2;
}
else if (_no == 3)
{
GPIO_PORT_LED3->ODR ^= GPIO_PIN_LED3;
}
else if (_no == 4)
{
GPIO_PORT_LED4->ODR ^= GPIO_PIN_LED4;
}
}
这里就没有no--的操作了,写几进来就是几
还是举例;假设写入1
^= :异或等于 不同为1 相同为0 :比如0010(2)0001(1) 异或就是0011(3)
然后:ODR假设为第5位为0 右侧第5位为1 异或之后就是1
ODR为1,我们异或1 这时候ODR就会变为0,ODR就会不停翻转,到led上就是LED一直亮灭。
4.4 判断是否有输出
/*
*********************************************************************************************************
* 函 数 名: bsp_IsLedOn
* 功能说明: 判断LED指示灯是否已经点亮。
* 形 参: _no : 指示灯序号,范围 1 - 4
* 返 回 值: 1表示已经点亮,0表示未点亮
*********************************************************************************************************
*/
uint8_t bsp_IsLedOn(uint8_t _no)
{
if (_no == 1)
{
if ((GPIO_PORT_LED1->ODR & GPIO_PIN_LED1) == 0)
{
return 1;
}
return 0;
}
else if (_no == 2)
{
if ((GPIO_PORT_LED2->ODR & GPIO_PIN_LED2) == 0)
{
return 1;
}
return 0;
}
else if (_no == 3)
{
if ((GPIO_PORT_LED3->ODR & GPIO_PIN_LED3) == 0)
{
return 1;
}
return 0;
}
else if (_no == 4)
{
if ((GPIO_PORT_LED4->ODR & GPIO_PIN_LED4) == 0)
{
return 1;
}
return 0;
}
return 0;
}
假设写入为1:进入if这里通过ODR内存的值与库函数的值相与:按位与:相同为1不同为0 如果输出不为0 则代表ODR内的值为1 LED灯灭
所以为0时则返回1 代表已经点了