STM32实现LED流水灯闪烁
本文通过对STM32最小系统核心板及其寄存器进行了简要描述,并利用GPIO口推挽输出进行了LED流水灯闪烁的实验。
一、STM32最小系统核心板(STM32F103C8T6)
简介:STM32最小系统核心板(STM32F103C8T6)由单片机芯片、供电电路、时钟电路、复位电路、程序下载电路、启动配置电路组成。
其引脚原理图如下所示。
其电路原理图如下所示。
二、GPIO端口
1.概述
GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。
每个 GPIO 端口有两个 32 位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个 32位数据寄存器(GPIOx_IDR,GPIOx_ODR),一个 32 位置位/复位寄存器(GPIOx_BSRR),一个 16 位复位寄存器(GPIOx_BRR)和一个 32 位锁定寄存器(GPIOx_LCKR)。
2.GPIO寄存器
2.1-低寄存器(0-7引脚)
1.CNF:configure。配置对应端口的输入输出模式。
2.MODE:配置对应端口的输出速度。
2.2-高寄存器(8-15引脚)
2.3-APB2 外设时钟使能寄存器(RCC_APB2ENR)
三、LED流水灯闪烁
要求:以 STM32最小系统核心板(STM32F103C8T6)+面板板+3只_(或更多)红绿蓝LED 搭建电路,使用GPIOA、GPIOB、GPIOC这3个端口控制LED灯,轮流闪烁,间隔时长1秒。
点亮LED灯,实现流水灯效果需要用到GPIO端口。为了点亮LED灯,进行以下三个步骤:
1.打开GPIO口时钟
GPIO地址:
时钟地址:
时钟设置:通过查询STM32参考手册,可以找到APB2的外设时钟使能寄存器进行设置。RCC时钟控制的起始地址为0x4002 1000,APB2外设时钟使能寄存器(RCC_APB2ENR)的偏移地址为0x18,所以打开三个IO口的时钟需要将三个位都需要置1。
#define RCC_APB2ENR (*(unsigned int *)0x40021018)
// 打开时钟
RCC_APB2ENR |= (1<<3); // 打开 GPIOB 时钟
RCC_APB2ENR |= (1<<4); // 打开 GPIOC 时钟
RCC_APB2ENR |= (1<<2); // 打开 GPIOA 时钟
2.初始化GPIO口(选择推挽输出)
本次实验通过推挽输出通过先前对GPIO寄存器的描述,选择相对应的编码,以实现推挽输出。
#define GPIOA_CRL (*(unsigned int *)0x40010800)
// 最后四位变为0001
GPIOA_CRL |= (1<<0); // 最后一位变1
GPIOA_CRL &= ~(0xE<<0); // 倒数2、3、4位变0
对于GPIOB的B9、GPIOC的C15、GPIOA的A4,设置如下:
#define GPIOB_CRH (*(unsigned int *)0x40010C04)
#define GPIOC_CRH (*(unsigned int *)0x40011004)
#define GPIOA_CRL (*(unsigned int *)0x40010800)
GPIOB_CRH&= 0xffffff0f;
GPIOB_CRH|=0x00000020;
GPIOC_CRH &= 0x0fffffff;
GPIOC_CRH|=0x30000000;
GPIOA_CRL &= 0xfff0ffff;
GPIOA_CRL|=0x00010000;
3.设置高电平
输出高电平则为1,低电平则为0。 对于GPIOB的B9、GPIOC的C15、GPIOA的A4,设置如下:
#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
#define GPIOC_ODR (*(unsigned int *)0x4001100C)
#define GPIOA_ODR (*(unsigned int *)0x4001080C)
GPIOB_ODR &= ~(1<<9);
GPIOC_ODR &= ~(1<<15);
GPIOA_ODR &= ~(1<<4);
4.代码实现
在Keil中新建一个工程,选择STM32F103C8,以便完成STM32的操作。
代码如下。
#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define GPIOA_BASE 0x40010800
#define RCC_APB2ENR (*(unsigned int *)0x40021018)
#define GPIOB_CRH (*(unsigned int *)0x40010C04)
#define GPIOC_CRH (*(unsigned int *)0x40011004)
#define GPIOA_CRL (*(unsigned int *)0x40010800)
#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
#define GPIOC_ODR (*(unsigned int *)0x4001100C)
#define GPIOA_ODR (*(unsigned int *)0x4001080C)
void SystemInit(void);
void Delay_ms(volatile unsigned int);
void A_LED_LIGHT(void);
void B_LED_LIGHT(void);
void C_LED_LIGHT(void);
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
void A_LED_LIGHT(){
GPIOA_ODR=0x0<<4; //PA4低电平
GPIOB_ODR=0x1<<9; //PB9高电平
GPIOC_ODR=0x1<<15; //PC15高电平
}
void B_LED_LIGHT(){
GPIOA_ODR=0x1<<4; //PA4高电平
GPIOB_ODR=0x0<<9; //PB9低电平
GPIOC_ODR=0x1<<15; //PC15高电平
}
void C_LED_LIGHT(){
GPIOA_ODR=0x1<<4; //PA4高电平
GPIOB_ODR=0x1<<9; //PB9高电平
GPIOC_ODR=0x0<<15; //PC15低电平
}
int main(){
int j=100;
// 开启时钟
RCC_APB2ENR |= (1<<3); // 开启 GPIOB 时钟
RCC_APB2ENR |= (1<<4); // 开启 GPIOC 时钟
RCC_APB2ENR |= (1<<2); // 开启 GPIOA 时钟
// 设置 GPIO 为推挽输出
GPIOB_CRH&= 0xffffff0f; //设置位 清零
GPIOB_CRH|=0x00000020; //PB9推挽输出
GPIOC_CRH &= 0x0fffffff; //设置位 清零
GPIOC_CRH|=0x30000000; //PC15推挽输出
GPIOA_CRL &= 0xfff0ffff; //设置位 清零
GPIOA_CRL|=0x00010000; //PA4推挽输出
// 3个LED初始化为不亮(即高点位)
GPIOB_ODR |= (1<<9);
GPIOC_ODR |= (1<<15);
GPIOA_ODR |= (1<<4);
while(j){
B_LED_LIGHT();
Delay_ms(1000000);
C_LED_LIGHT();
Delay_ms(1000000);
A_LED_LIGHT();
Delay_ms(1000000);
}
}
void SystemInit(){
}
5.Proteus仿真
在Proteus中绘制如下图所示电路图,并将上述代码生成hex文件,录入Proteus中,便能看到LED灯流水灯闪烁。
6.开发板展示
首先在开发板上进行电路连接,ST-LINK与开发板的连接如下所示。
再将三个灯分别连接在对应位置,本例中将蓝灯连在B9端口,黄灯连在C15端口,红灯连在A4端口
通过ST-LINK将开发板与电脑连接,并在Debug中做如下设置,以便进行连接。
运行后便可以在开发板上看到LED灯流水灯闪烁。
7.附加
STM32最小系统核心板子出厂时已经焊接好了1个led灯(标注了PC13处),一般可通过此灯的点亮让编程者验证自己烧录的代码是否正常运行了。现需将这个灯也用在流水灯中。 代码如下所示。
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main()
{
//配置三个口的时钟
RCC->APB2ENR=0x0000001C; //给GPOIA、B、C口配置时钟
//配置GPIOA2口的输出模式及输出电平
GPIOA->CRL=0x00000300; //使用GPIOA2引脚
//GPIOA->ODR=0x00000000; //GPIOA2引脚低电平点亮
GPIOA->ODR=0x00000004; //GPIOA2引脚高电平熄灭
//配置GPIOB12口的输出模式及输出电平
GPIOB->CRH=0x00030000; //使用GPIOB12引脚
//GPIOB->ODR=0x00000000; //GPIOB12引脚低电平点亮
GPIOB->ODR=0x00001000; //GPIOB12引脚高电平熄灭
//配置GPIOC13口和GPIOC15口的输出模式及输出电平
GPIOC->CRH=0x30300000; //使用GPIOC13和15引脚
//GPIOC->ODR=0x00000000; //GPIOC13和15引脚低电平点亮
GPIOC->ODR=0x0000A000; //GPIOC13和15仍然保持高电平
while(1)
{
GPIOA->ODR=0x00000000; //GPIOA2引脚低电平点亮
Delay_s(1);
GPIOA->ODR=0x00000004; //GPIOA2引脚高电平熄灭
GPIOB->ODR=0x00000000; //GPIOB12引脚低电平点亮
Delay_s(1);
GPIOB->ODR=0x00001000; //GPIOB12引脚高电平熄灭
GPIOC->ODR=0x00008000; //GPIOC13引脚低电平点亮,GPIOC15仍然保持高电平
Delay_s(1);
GPIOC->ODR=0x0000A000; //GPIOC13和GPIOC15引脚高电平熄灭
GPIOC->ODR=0x00002000; //GPIOC15引脚低电平点亮,GPIOC13仍然保持高电平
Delay_s(1);
GPIOC->ODR=0x0000A000; //GPIOC13和GPIOC15引脚高电平熄灭
}
}
开发板展示如下所示。
四、总结
本文通过使用寄存器进行单片机编程,需要依靠STM32F103参考手册去查找地址,编程方式比较原始,但是能让我们懂得一些底层原理,帮助我们更好地理解和学习STM32。
五、参考资料
STM32F103的流水灯点亮版本1(寄存器地址操作)
作者:不可思议迷宫