前言
学习任何一块开发板的入门第一课就是成为一个合格的“点灯大师”,在基于标准库来进行点亮一个LED灯的过程中,碰到了许多与之前学习单片机不一样的地方,STM32的学习更注重于对计算机系统底层逻辑的学习,比如一些寄存器、时钟信号等等,偏向与最本质的学习,同时将理论知识与实际相结合,在实践过程中不断掌握知识。话不多说接下来记录第一次STM32的学习。
一:创建模板文件
在之前的学习中,自己创建了一个类似于模板的项目,其中包含了支持开发板的标准库和该芯片的支持包,本系列开发都是基于STM32F103ZET6系列进行开发。下图是工程模板
在进行每一个小实验的过程中,我们需要添加另外一个文件夹System用来存放我们需要创建的驱动文件,本实验中需要一个驱动LED的C文件和头文件,在C文件中需要写出LED的启动函数以及其他功能函数;头文件中为函数的申明,便于最后在主程序中调用。
二、System中的文件
01、延时函数的封装(包括三个函数:微秒级延迟,毫秒级延迟,妙计延迟)
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus ; //设置定时器重装值
SysTick->VAL = 0X00; //清空当前计数值
SysTick->CTRL = 0X00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0X00010000)); // 等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长, 范围: 0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 妙级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_us(1000);
}
}
延时函数头文件:
#ifndef _DELAY_H
#define _DELAY_H
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
#endif
#ifndef在C语言中表示"if not defined",是预处理功能中的条件编译指令。它的作用是判断某个标识符是否已经被定义,如果没有被定义,则执行#ifndef后面的代码,否则就跳过这部分代码。这样可以防止头文件的重复包含和编译,同时也便于程序的调试和移植。
三、Hardware文件中的内容
02、LED封装(本实验开发板中含有两个LED灯,分别为PB5和PE5两个端口)
#include "stm32f10x.h" // Device header
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE , ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
GPIO_Init(GPIOB , &GPIO_InitStructure);//地址传递,结构体变量名
GPIO_Init(GPIOE , &GPIO_InitStructure);//地址传递,结构体变量名
//GPIO_SetBits(GPIOB , GPIO_Pin_5); //由于初始化后默认是为低电平(推挽输出模式),LED灯会亮,所以SetBits为了让端口置高,设定为了让LED开始是熄灭的状态
//GPIO_SetBits(GPIOE , GPIO_Pin_5); //同理与上
}
void DSO_ON(void) //开启LED_DSO灯
{
GPIO_ResetBits(GPIOB , GPIO_Pin_5);//
}
void DSO_Off(void)
{
GPIO_SetBits(GPIOB,GPIO_Pin_5);
}
void DSO_Turn(void)//输出端口使得电平翻转
{
if(GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_5) == 0)//当前端口值输出为0
{
GPIO_SetBits(GPIOB, GPIO_Pin_5);//将该端口值置为1;
}
else
{
GPIO_ResetBits(GPIOB, GPIO_Pin_5);//将该端口置为0
}
}
void DS1_ON(void)
{
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
}
void DS1_Off(void)
{
GPIO_SetBits(GPIOE,GPIO_Pin_5);
}
void DS1_Turn(void)//输出端口使得电平翻转
{
if(GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_5) == 0)//当前端口值输出为0
{
GPIO_SetBits(GPIOE, GPIO_Pin_5);//将该端口值置为1;
}
else
{
GPIO_ResetBits(GPIOE, GPIO_Pin_5);//将该端口置为0
}
}
LED功能函数头文件(DS0对应PB5口的LED灯,DS1对应PE5口的LED灯)
#ifndef _LED_H
#define _LED_H
void LED_Init(void); // 可以被外部调用
void DSO_ON(void);//DSO打开
void DSO_Off(void);//DSO关闭
void DS1_ON(void);//DS1打开
void DS1_Off(void);//DS1关闭
void DS1_Turn(void);//翻转PE3端口值
void DSO_Turn(void);//翻转PE4端口值
#endif
四、主函数
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
int main(void)
{
LED_Init(); //启动对应的外设接口,以及相应的参数设置
while(1)
{
DSO_ON(); //打开DSO;
Delay_ms(500); //延时500毫秒
DSO_Off(); //关闭DSO
Delay_ms(500); //延时500毫秒
DS1_ON(); //打开DS1;
Delay_ms(500); //延时500毫秒
DS1_Off(); //关闭DS1
Delay_ms(500); //延时500毫秒
}
五、总结
1、LED的启动文件中有几个函数,需要学习记录一下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOX,ENABLE):该函数用于APB2上的外设重置,以便重新启动,通常用于初始化外设时使用,确保外设打开才能进行后续的数据输出。参数RCC_APB2Periph_GPIOX,其中可改变参数X,表示启动的外设端口,从A-G等,第二个参数一般选择ENABLE为使能开启状态。
GPIO_Init(GPIOX , &GPIO_InitStructure):此函数时定义对应的GPIO模块,以及传递InitStructure中的参数值,注意此处为地址传递,故需要取地址符&;
GPIO_InitTypeDef GPIO_InitStructure;//定义一个结构体类型的变量,用于参数的值传递
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//设置此处的端口输出模式为推挽输出模式,在此处的低电平与高电平都具有驱动能力。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 ;//定义具体的引脚号
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;//定义传输速率,一般为50MHz。
GPIO_SetBits(GPIOB , GPIO_Pin_5); //对PB5端口设置为高电平
GPIO_ResetBits(GPIOB , GPIO_Pin_5); //对PB5端口设置为低电平
2、怎么模块化的进行编程
通过把每一个函数具体的功能函数进行封装为C文件和头文件,然后在主程序中进行调用即可,期间需要明确每一个函数的含义,所以需要在定义函数名时给定一个通俗易懂的函数名,确保自己和别人看懂,最起码自己看的懂。
3、本项目框架
需要把以后所有的实验封装都添加到Hardware文件夹中。
六、实验现象
VID_20240525_162914