前言
一、开发环境与硬件准备
1、开发环境
操作系统:win10
集成开发环境(IDE):keil5
烧录工具:mcuisp
串口驱动:CH340驱动
2、硬件准备
开发板:stm32F1038C6
下载器:USB转串口(CH340)
其它:TTL连接线、面包板、LED灯
二、基于寄存器的stm32流水灯
1、寄存器工作原理
在STM32微控制器中,寄存器用于访问和控制芯片的各种功能和外设。这些寄存器直接映射到芯片的内部寄存器,通过读取和编写这些寄存器,可以对芯片进行配置和控制。
1.1 GPIO寄存器
GPIO寄存器(General-Purpose Input/Output Registers)用于配置和控制STM32微控制器上的GPIO引脚。每个GPIO引脚都对应着一组相应的寄存器,用于控制引脚的输入和输出状态
下面是一些GPIO常用工作模式:
1.1.1 GPIOx_MODER
GPIO模式寄存器,用于配置GPIO引脚的工作模式。每个引脚占用2位,共4种模式:
输入模式(IN):引脚被配置为输入,例如读取外部信号。
输出模式(OUT):引脚被配置为推挽输出、开漏输出等。
复用功能模式(AF):引脚被配置为复用功能,例如作为USART或SPI的引脚。
模拟模式(AN):引脚被配置为模拟输入或模拟输出。
1.1.2 GPIOx_OTYPER
GPIO输出类型寄存器,用于配置GPIO引脚的输出类型。每个引脚占用1位,有两种类型:
推挽输出:引脚可以输出高电平或低电平。
开漏输出:引脚可以输出低电平或悬空状态,需要外部上拉电阻。
1.1.3 GPIOx_OSPEEDR
GPIO输出速度寄存器,用于配置GPIO引脚的输出速度。每个引脚占用2位,共4种速度级别:
低速输出:适用于低功耗应用。
中速输出:适用于一般应用。
高速输出:适用于需要更高切换速度的应用。
非特定输出速度:可以根据具体需求选择。
1.2 RCC寄存器
RCC寄存器(Reset and Clock Control Registers)用于配置和控制STM32微控制器的复位和时钟控制功能。RCC寄存器允许你设置系统时钟和各个外设的时钟源以及时钟分频等参数。
以下是一些常见的RCC寄存器及其功能:
1.2.1RCC_CR
时钟控制寄存器,用于控制系统时钟和时钟源。
HSEON:使能/禁用外部高速振荡器(High-Speed External Oscillator)。
HSERDY:外部高速振荡器稳定就绪标志。
HSION:使能/禁用内部高速振荡器(High-Speed Internal Oscillator)。
HSIRDY:内部高速振荡器稳定就绪标志。
CSSON:使能/禁用时钟安全系统(Clock Security System)。
PLLON:使能/禁用锁相环(PLL)。
PLLRDY:锁相环稳定就绪标志。
RCC_CFGR:时钟配置寄存器,用于配置系统时钟和外设时钟。
1.2.2 SW
系统时钟选择位,确定系统时钟源(HSI、HSE或PLL)。
HPRE:AHB时钟分频器,用于配置AHB总线的时钟频率。
PPRE1和PPRE2:APB1和APB2时钟分频器,用于配置APB1总线和APB2总线的时钟频率。
ADCPRE:ADC时钟分频器,用于配置ADC的时钟频率。
PLLSRC:PLL输入时钟源选择位(HSI、HSE)。
PLLM和PLLN:PLL的输入和反馈分频器,并决定PLL的输出频率。
1.2.3RCC_AHB1ENR/RCC_APB1ENR/RCC_APB2ENR
时钟使能寄存器,用于使能或禁用各个外设的时钟。
RCC_AHB1ENR:使能/禁用AHB1总线上的外设时钟。
RCC_APB1ENR:使能/禁用APB1总线上的外设时钟。
RCC_APB2ENR:使能/禁用APB2总线上的外设时钟。
每个外设对应一个位域,可以设置对应外设的时钟使能状态
2、创建keil工程(寄存器版)
2.1 配置启动文件
1、创建好工程后,在工程目录下新建Start文件夹存放启动文件
2、打开固件库->libraries->CMSIS->CM3->DeviceSupport->ST->STM32F10x->startup->arm 找到如下文件
这些文件是STM32的启动文件,STM32的程序就是从启动文件开始执行的
3、将所有文件复制,粘贴到Start文件夹中
4、接着回到固件库的STM32F10x的文件夹,可以看到stm32f10x.h和system_stm32f10x.h开头的文件,这些是STM32的外设寄存器描述文件用来描述STM32有哪些对应的寄存器和地址
5、把这三个文件都复制下来,粘贴到Start文件夹下
6、打开CM3->CoreSupport找到两个描述寄存器内核的文件,将其一并复制,粘贴到Start文件夹下
7、在keil软件中打开工程,新建Start组群,将工程目录Start文件下后缀为md.s的启动文件添加进组群中,并将所有的.c .h文件一并添加
2.2 配置项目环境
1、点击魔术棒按->c/c++,找到Include Paths栏,点击旁边三个按钮,再点右上的新建路径按钮,将工程目录下的Start的路径添加进来
2、在工程目录下新建User文件夹用来存放main函数及后续的其它相关配置文件
3、在keil中新建User组,在User组下创建main.c文件(main.c的保存位置要更改到工程文件的User目录下,不然就会存放到文件外面)
3、配置寄存器(寄存器版)
3.1 延时函数
定义了延时函数delay(),用于在循环中产生延时效果
//延时
void delay()
{
int i, j;
for (i = 0; i < 100000; i++)
{
for (j = 0; j < 100; j++)
{
}
}
}
3.2 寄存器的配置
对寄存器进行配置,以使能GPIO口的控制。通过设置RCC寄存器来使能APB2总线上的GPIOA、GPIOB和GPIOC。
然后通过对GPIO寄存器的设置,设置三个引脚(PA0、PB0、PC13)为推挽输出模式。
//初始地址GPIOA:0x40010800、GPIOB:0x40010C00、GPIOC:0x40011000
//配置复位和时钟控制寄存器(RCC)-使能寄存器APB2
*(volatile unsigned long*)0x40021018 |= 0x7 << 2;
//配置为推挽输出
*(volatile unsigned long*)0x40010800 |= 0x3; // PA0
*(volatile unsigned long*)0x40010C00 |= 0x3; // PB0
*(volatile unsigned long*)0x40011008 |= 0x30 << 16; // PC13
3.3 配置LED外设
在一个无限循环中,通过对寄存器的设置,依次点亮和灭掉LED灯。具体的控制逻辑是:
- 先将PA0、PB0和PC13引脚输出低电平,使对应的LED灯点亮。
- 延时一段时间(通过调用delay()函数)。
- 将PA0、PB0和PC13引脚输出高电平,使对应的LED灯熄灭。
- 延时一段时间。
- 重复以上步骤,实现LED灯的循环点亮效果
//循环点亮
while (1)
{
//全亮
*(volatile unsigned long*)0x4001080C &= ~(0x1 << 0); // PA0
*(volatile unsigned long*)0x40010C0C &= ~(0x1 << 0); // PB0
*(volatile unsigned long*)0x4001100C &= ~(0x1 << 13); // PC13
delay();
//AB亮,C灭
*(volatile unsigned long*)0x4001080C &= ~(0x1 << 0); // PA0
*(volatile unsigned long*)0x40010C0C &= ~(0x1 << 0); // PB0
*(volatile unsigned long*)0x4001100C |= 0x1 << 13; // PC13
delay();
//A亮,BC灭
*(volatile unsigned long*)0x4001080C &= ~(0x1 << 0); // PA0
*(volatile unsigned long*)0x40010C0C |= 0x1 << 0; // PB0
*(volatile unsigned long*)0x4001100C |= 0x1 << 13; // PC13
delay();
//ABC全灭
*(volatile unsigned long*)0x4001080C |= 0x1 << 0; // PA0
*(volatile unsigned long*)0x40010C0C |= 0x1 << 0; // PB0
*(volatile unsigned long*)0x4001100C |= 0x1 << 13; // PC13
delay();
}
3.4 完全代码
#include "stm32f10x.h" // Device header
//延时
void delay()
{
int i, j;
for (i = 0; i < 100000; i++)
{
for (j = 0; j < 100; j++)
{
}
}
}
int main(void)
{
//初始地址GPIOA:0x40010800、GPIOB:0x40010C00、GPIOC:0x40011000
//配置复位和时钟控制寄存器(RCC)-使能寄存器APB2
*(volatile unsigned long*)0x40021018 |= 0x7 << 2;
//配置为推挽输出
*(volatile unsigned long*)0x40010800 |= 0x3; // PA0
*(volatile unsigned long*)0x40010C00 |= 0x3; // PB0
*(volatile unsigned long*)0x40011008 |= 0x30 << 16; // PC13
//循环点亮
while (1)
{
//全亮
*(volatile unsigned long*)0x4001080C &= ~(0x1 << 0); // PA0
*(volatile unsigned long*)0x40010C0C &= ~(0x1 << 0); // PB0
*(volatile unsigned long*)0x4001100C &= ~(0x1 << 13); // PC13
delay();
//AB亮,C灭
*(volatile unsigned long*)0x4001080C &= ~(0x1 << 0); // PA0
*(volatile unsigned long*)0x40010C0C &= ~(0x1 << 0); // PB0
*(volatile unsigned long*)0x4001100C |= 0x1 << 13; // PC13
delay();
//A亮,BC灭
*(volatile unsigned long*)0x4001080C &= ~(0x1 << 0); // PA0
*(volatile unsigned long*)0x40010C0C |= 0x1 << 0; // PB0
*(volatile unsigned long*)0x4001100C |= 0x1 << 13; // PC13
delay();
//ABC全灭
*(volatile unsigned long*)0x4001080C |= 0x1 << 0; // PA0
*(volatile unsigned long*)0x40010C0C |= 0x1 << 0; // PB0
*(volatile unsigned long*)0x4001100C |= 0x1 << 13; // PC13
delay();
}
}
4、实现效果
1
三、基于固件库的stm32流水灯
1、创建keil工程(固件库版)
1.1 添加库函数文件
1、在工程文件下新建library文件夹,用来存放库函数
2、打开固件库文件夹->libraries->STM32标准驱动外设->src,找到库函数源文件,将所有文件复制,粘贴到library文件夹下
3、打开固件库的inc文件夹,找到库函数的头文件,同样复制粘贴到library 文件夹下
1.2 配置项目环境
1、在keil项目中新建组library,将工程项目文件library的库函数文件全部添加进去
2、打开固件库文件夹->project->STM32Template,可以看到stm32f10x_conf.h和两个it结尾的文件。其中configuration文件是用来配置库函数头文件包含关系的,两个it文件是用来存放中断函数的,将三个文件复制粘贴到工程文件的User文件目录下
3、在keil的User组群中添加这三个文件
4、在keil项目的Start文件中打开stm32f10c.h文件,滑倒最下层,找到字符串USE_STDPERIPH_DRIVER,复制该字符串
5、在魔术棒->c/c+±>Define栏中粘贴该字符串
6、依据前文方法,添加library、User文件夹的路径到Include Path中
2、配置寄存器(固件库版)
2.1 LED灯的初始化
初始化LED灯的函数。它通过对寄存器的设置,使LED引脚成为推挽输出模式,并配置输出速度为50MHz。
具体的代码逻辑如下:
-
1、定义了一个GPIO初始化结构体GPIO_InitStr,用于存储GPIO初始化参数。
-
2、使用RCC_APB2PeriphClockCmd函数分别开启了GPIOA、GPIOB和GPIOC的时钟配置,以使能这三个GPIO口的控制。
-
3、设置结构体参数:
GPIO_Mode成员设置为GPIO_Mode_Out_PP,表示将引脚设置为推挽输出模式。
GPIO_Pin成员设置为具体的引脚序号,分别为0、0和13,对应PA0、PB0和PC13引脚。
GPIO_Speed成员设置为GPIO_Speed_50MHz,表示将引脚的输出速度设置为50MHz。
分别使用GPIO_Init函数将结构体参数配置到对应的GPIO口上。具体是调用了三次GPIO_Init函数,分别对应GPIOA、GPIOB和GPIOC的引脚初始化。
使用GPIO_SetBits函数将初始化后的引脚设置为高电平。这里分别对应PA0、PB0和PC13引脚设置为高电平,表示LED灯初始状态为熄灭。
代码如下:
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStr;//GPIO定义结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //开启GPIOA时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启GPIOB时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); //开启GPIOC时钟配置
/* 设置结构体参数*/
GPIO_InitStr.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出模式
GPIO_InitStr.GPIO_Pin = GPIO_Pin_0; //操作第0个端口
GPIO_InitStr.GPIO_Speed = GPIO_Speed_50MHz; //输出速度为50兆
GPIO_Init(GPIOA,&GPIO_InitStr); //用结构体参数配置GPIO口
GPIO_SetBits(GPIOA,GPIO_Pin_0); //初始输出为高电平
GPIO_InitStr.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStr.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStr.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStr);
GPIO_SetBits(GPIOB,GPIO_Pin_0);
GPIO_InitStr.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStr.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStr.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStr);
GPIO_SetBits(GPIOC,GPIO_Pin_0);
}
2.2 流水灯点亮
-
主函数main中首先调用LED_Init函数对LED灯进行初始化。
-
进入一个无限循环while(1),在循环中依次对GPIOA、GPIOB和GPIOC的引脚进行控制。
-
通过GPIO_ResetBits函数将对应引脚设置为低电平,从而使LED灯亮起。具体是先将GPIOA的引脚设置为低电平,延时一段时间后再将其设置为高电平使其熄灭;然后控制GPIOB和GPIOC的引脚依次点亮和熄灭。
-
使用延时函数delay进行延时,控制LED灯亮灭的时间间隔。
-
重复以上步骤,实现LED灯的循环点亮和熄灭效果。整个循环不会退出,所以LED灯会一直循环点亮和熄灭。
代码如下:
void delay()
{
int i,j;
for (i=0;i<10000;i++)
for (j=0;j<100;j++);
}
int main(void)
{
LED_Init();
while(1){
GPIO_ResetBits(GPIOA,GPIO_Pin_0); // A亮
delay();
GPIO_SetBits(GPIOA,GPIO_Pin_0); // A灭
delay();
GPIO_ResetBits(GPIOB,GPIO_Pin_0);// B亮
delay();
GPIO_SetBits(GPIOB,GPIO_Pin_0); // B灭
delay();
GPIO_ResetBits(GPIOC,GPIO_Pin_13);// C亮
delay();
GPIO_SetBits(GPIOC,GPIO_Pin_13); // C灭
delay();
}
}
2.3 完全代码
led.h
#ifndef __LED_H
#define __LED_H
void LED_Init(void); //LED初始化设置
#endif
led.c
#include "led.h"
#include "stm32f10x.h" // Device header
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStr;//GPIO定义结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //开启GPIOA时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启GPIOB时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); //开启GPIOC时钟配置
/* 设置结构体参数*/
GPIO_InitStr.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出模式
GPIO_InitStr.GPIO_Pin = GPIO_Pin_0; //操作第0个端口
GPIO_InitStr.GPIO_Speed = GPIO_Speed_50MHz; //输出速度为50兆
GPIO_Init(GPIOA,&GPIO_InitStr); //用结构体参数配置GPIO口
GPIO_SetBits(GPIOA,GPIO_Pin_0); //初始输出为高电平
GPIO_InitStr.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStr.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStr.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStr);
GPIO_SetBits(GPIOB,GPIO_Pin_0);
GPIO_InitStr.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStr.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStr.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStr);
GPIO_SetBits(GPIOC,GPIO_Pin_0);
}
main.c
#include "stm32f10x.h" // Device header
#include "led.h"
void delay()
{
int i,j;
for (i=0;i<10000;i++)
for (j=0;j<100;j++);
}
int main(void)
{
LED_Init();
while(1){
GPIO_ResetBits(GPIOA,GPIO_Pin_0); // A亮
delay();
GPIO_SetBits(GPIOA,GPIO_Pin_0); // A灭
delay();
GPIO_ResetBits(GPIOB,GPIO_Pin_0);// B亮
delay();
GPIO_SetBits(GPIOB,GPIO_Pin_0); // B灭
delay();
GPIO_ResetBits(GPIOC,GPIO_Pin_13);// C亮
delay();
GPIO_SetBits(GPIOC,GPIO_Pin_13); // C灭
delay();
}
}
3、实现效果
[video(video-Ov0nDEzj-1697539561455)(type-csdn)(url-https://live.csdn.net/v/embed/335830)(image-https://video-community.csdnimg.cn/vod-84deb4/0a2131606cd771ee913f5420848d0102/snapshots/b59d21b38fa04cfea9e4613c58b2c01f-00001.jpg?auth_key=4851138139-0-0-5d975aa7bfdca933bda82f20585f8db4)(title)]
四、两种点灯方式的对比
寄存器点灯方式和库函数点灯方式是两种不同的方法来控制微控制器上的外设(如LED灯)。
寄存器点灯方式
寄存器点灯方式优点
- 直接通过操作寄存器进行控制,效率较高。
- 可以更加灵活地控制每个引脚的功能和状态。
- 适用于对微控制器的底层硬件有较深了解的开发者。
寄存器点灯方式缺点
- 编码复杂度高,需要了解硬件寄存器的映射和位掩码等细节。
- 开发过程相对繁琐,需要编写大量的底层代码。
- 对于复杂的应用,可能需要编写大量的寄存器操作代码,增加开发难度。
库函数点灯方式
库函数点灯方式优点
- 使用库函数可以屏蔽底层硬件细节,简化开发过程。
- 提供了高级的函数和接口,使开发者能够更容易地控制和操作外设。
- 适用于对寄存器操作不够熟悉或者对开发速度要求较高的情况。
库函数点灯方式缺点
- 由于库函数抽象了底层硬件,可能引入一定的性能损失。
- 在某些特定情况下,库函数提供的接口可能不够灵活,无法满足特定需求。
- 对于底层硬件细节的学习和理解程度较低。