接上文,文中的图片,大多数来自视频的截图(来自洋桃电子)。
欢迎大家批评指正!
文章目录
一、核心板电路分析
洋桃核心板
![](https://img-blog.csdnimg.cn/022e12392ba8494b817da7603bcd416a.png)
二、点灯 LED
1、LED电路
![](https://img-blog.csdnimg.cn/f735fc5f932741ae88d4418c8e271bca.png)
2、LED功能相关初始化配置
- GPIO 初始化配置
- 结构体声明—— IO端口号、IO端口速度、IO端口模式
- 时钟启动 IO端口——APB2 高速总线启动 GPIOA、GPIOB、GPIOC
- 设置 IO端口号(宏定义)
- 配置 IO工作模式——开漏输出、推挽输出
- 配置 IO端口速度(仅输出模式下需要配置)
- 运行 GPIO 的初始化库函数,将上述配置写入 IO端口组的寄存器
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、LED功能封装函数介绍
视频中,将LED功能封装成了特定函数。(即固件库GPIO功能函数)
3.1 方法1
![](https://img-blog.csdnimg.cn/a9c28c8af7d146e4b71208ac7e665419.png)
- GPIO_WriteBit —— 对端口进行写操作,函数参数为 端口组、端口名、位控制。
![](https://img-blog.csdnimg.cn/cf92064cdbb24673b788c0c4f30aacdd.png)
3.2 方法2
![](https://img-blog.csdnimg.cn/bc2ce414e84a49139c89cac1556638fe.png)
- GPIO_ReadOutputDataBit —— 读取端口输出值
![](https://img-blog.csdnimg.cn/29cca8809737449291a5f75fc78e4a0b.png)
3.3 方法3
![](https://img-blog.csdnimg.cn/540de7a11e2349c58b1a6d5ac7440326.png)
两函数的参数均只有两个,端口组、端口名
- GPIO_SetBits —— 端口置1(拉高)
![](https://img-blog.csdnimg.cn/cdad12bb2f534fe0959243b4fbce064a.png)
- GPIO_ResetBits —— 端口清零(拉低)
3.4 方法4
![](https://img-blog.csdnimg.cn/26d8f72382874a9f93a7eb5adb5075d4.png)
对端口组赋值的函数,参数为端口组、4位十六进制数
GPIO_Write —— 对端口组赋十六进制数,给16个端口各自赋值
![](https://img-blog.csdnimg.cn/471b59a5759047ee89dca206248a6f32.png)
3.5 方法5
以上是以固件库函数控制。
这里使用多层宏定义,直接控制端口。
如 : PB(1)=1;
PB1端口直接赋值,很方便易懂。
4、闪灯和呼吸灯
包含 delay.c delay.h ,调用延时函数(滴答定时器实现,精准延时)
4.1 延时函数
#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; //关闭定时器
}
4.2 呼吸灯
延时控制亮度——占空比思想(视觉暂留)
高频切换亮灭,可以发现灯不闪,而是变暗。亮度则由亮灭延时的比例决定。
呼吸灯逐渐变亮:
for(i = 0; i < 10; i++)
{
GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //LED1接口输出高电平1
delay_us(t); //延时
GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); //LED1接口输出低电平0
delay_us(501-t); //延时
}
t++;
5、按键控制灯
还是GPIO相关。
5.1 初始化
配置模式为读取IO状态
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_Init(KEYPORT,&GPIO_InitStructure);
}
5.2 例子1 无锁存
if(GPIO_ReadInputDataBit(KEYPORT,KEY1)) //读按键接口的电平
{
GPIO_ResetBits(LEDPORT,LED1); //LED灯都为低电平(0)
}
else
{
GPIO_SetBits(LEDPORT,LED1); //LED灯都为高电平(1)
}
- GPIO_ReadInputDataBit —— 读取端口输入
![](https://img-blog.csdnimg.cn/0bf4580c6e074f2b8e56981738945ae6.png)
5.3 例子2 无锁存
GPIO_WriteBit(LEDPORT,LED1,(BitAction)(!GPIO_ReadInputDataBit(KEYPORT,KEY1)));
效果同上
5.4 例子3 有锁存
if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)) //读按键接口的电平
{
delay_ms(20); //延时去抖动
if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)) //读按键接口的电平
{
GPIO_WriteBit(LEDPORT,LED1,(BitActio(1-GPIO_ReadOutputDataBit(LEDPORT,LED1))); //LED取反
while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按键松开
}
}
- 按下按键时,GPIO_ReadInputDataBit(KEYPORT,KEY1) 为 0。
- 延时消除按下的机械抖动,通常为20ms左右。
- 通过 while循环 锁存按键状态,下次按下按键才会跳出该循环,再次进入 if,从而转换灯的状态。
5.5 例子4 有锁存
u8 a = 0;
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)); //等待按键松开
}
}
- 以 a 的值赋给端口组,实现 “仅D1亮、仅D2亮、都亮、都灭” 的循环。
三、FLASH 读写程序
FLASH 可以实现掉电不丢失。
1、 FLASH.c 文件:
#include "flash.h"
//FLASH写入数据
void FLASH_W(u32 add,u16 dat)
{ //参数1:32位FLASH地址。参数2:16位数据
RCC_HSICmd(ENABLE); //打开HSI时钟
FLASH_Unlock(); //解锁FLASH编程擦除控制器
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR); //清除标志位
FLASH_ErasePage(add); //擦除指定地址页
FLASH_ProgramHalfWord(add,dat); //从指定页的addr地址开始写
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR); //清除标志位
FLASH_Lock(); //锁定FLASH编程擦除控制器
}
//FLASH读出数据
u16 FLASH_R(u32 add)
{ //参数1:32位读出FLASH地址。返回值:16位数据
u16 a;
a = *(u16*)(add); //从指定页的addr地址开始读
return a;
}
RCC_HSICmd(ENABLE); //打开HSI时钟
例子中注释掉了这句,因为前面已经打开了HSI。- 擦除只能以页擦除。
F103对应:
2、固件库中相关函数
![](https://img-blog.csdnimg.cn/9ec2df90b1854109ae157fe89ba865c5.png)
3、前操作及初始化
- 定义写入地址
#define FLASH_START_ADDR 0x0801f000 //写入的起始地址
- 时钟等初始化
- 在while循环前,指定页地址读FLASH 。
a = FLASH_R(FLASH_START_ADDR);//从指定页的地址读FLASH
- 注意写入数据大小,留足空白位置
4、main 函数
在需要的位置写入
5、注意
- 操作一定要先擦后写。
- 每页是1024个地址,起始地址Ox08000000
- 擦除操作以页为单位,写操作则必须以16位宽度为单位,允许跨页写入。
- STM32内置FLASH擦或写时,必须打开外部/内部高速振荡器。
- FLASH可多次擦写10万次,不可死循环擦写。
- 擦写时要避开用户程序存储区的区域,否则会擦掉用户程序导致错误。
- 擦除一页要10ms(对于1k大小的一页),比较慢。而且不能单个字节的擦写。