用STM32F103C8T6点亮一组由3个不同色的LED组成的流水灯(寄存器地址方式)【Proteus】【STM32开发板】
实验要求
-
用Proteus设计一个STM32最小系统板+LED流水灯实验原理图,仿真运行。
-
以STM32最小系统核心板(STM32F103C8T6)+面包板+3只(或更多)红绿蓝LED搭建电路,使用GPIOA、GPIOB、GPIOC这3个端口控制LED灯,轮流闪烁,间隔时长1秒。
-
写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;
-
用C语言寄存器方式编程实现,代码须有详细注解。
-
STM32最小系统核心板子出厂时已经焊接好了1个led灯(标注了PC13处),一般可通过此灯的点亮让编程者验证自己烧录的代码是否正常运行了。请查阅最小版电路原理图和相关资料,将这个灯也用在流水灯中,重编新程序。
-
项目编写
GPIO端口的初始化设置
GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32 芯片的 GPIO 被分成很多组,每组有 16 个引脚,如型号为 STM32F103VET6 型号芯片有 GPIOA、GPIOB、GPIOC至 GPIOE共 5组 GPIO,芯片一共 100个引脚,其中 GPIO就占了一大部分,所有的 GPIO 引脚都有基本的输入输出功能。
点亮LED灯,实现流水灯效果需要用到GPIO端口。为了点亮LED灯,进行以下三个步骤:
1. 打开GPIO口的时钟
GPIO的地址:
时钟的地址:
即时钟的地址为0x40021018,打开三个I/O口的时钟需要将三个位都置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口有输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽式输出、推挽式复用、开漏服用八种模式。本次实验使用推挽输出。
端口1-7为低,端口8-15为高。每个引脚由四个位控制。
对于本次实验用到的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);
完整代码
在Keil中编写以下代码,保存并编译。编译时选择生成hex文件,用于Proteus仿真。
#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(1000);
C_LED_LIGHT();
Delay_ms(1000);
A_LED_LIGHT();
Delay_ms(1000);
}
}
void SystemInit(){
}
对于第二问的第三小问,只需将以上代码中的所有PC15改为PC13,将PC15推挽输出的地址0x30000000改为PC13推挽输出的地址0x00300000即可。
Proteus仿真
Proteus电路图如下:
(建立工程时,固件系列为Cortex-M3,控制器为STM32F103C8。若没有STM32F103C8固件,可用STM32F103C6代替,如图)
将Keil编译生成的hex文件导入单片机,运行,结果如下。
STM32开发板实现
如下图所示,将STM32开发板插入面包板并与ST-link连接(相接的两接口应同名),分别用导线将开发板两侧的3.3V输出接至面包板两侧的正极槽、接地输入接至面包板两侧的负极槽。将一个红色LED接在开发板的B9接口上,具体做法是将正极(长管脚)接至正极槽,负极(短管脚)接至开发板B9接口所接在的一槽。用同样的方法将一个绿色LED接在开发板的C15接口上,将一个蓝色LED接在开发板的A4接口上。
代码在Keil中编译完成后,点击“下载”,即可将代码载入STM32单片机中并开始运行。运行结果如下。
第二问的第三小问的运行结果如下。
总结
本次实验在用STM32点亮一个LED灯的基础上拓展,了解了STM32时钟的控制方式,提高了我利用寄存器编写程序的能力。