实验任务1:寄存器方式控制LED流水灯
实验任务1:寄存器方式控制LED流水灯
1. 程序设计思路
GPIO寄存器地址和参数
STM32F103C8T6 GPIO寄存器基地址:
- GPIOA: 0x4001 0800
- GPIOB: 0x4001 0C00
- GPIOC: 0x4001 1000
关键寄存器偏移地址:
- CRL (端口配置低寄存器): +0x00
- CRH (端口配置高寄存器): +0x04
- IDR (输入数据寄存器): +0x08
- ODR (输出数据寄存器): +0x0C
- BSRR (位设置/清除寄存器): +0x10
- BRR (位清除寄存器): +0x14
端口配置参数:
- 推挽输出模式: 0x01 (CNF=00, MODE=01 → 2MHz输出)
- 上拉输入模式: 0x08 (CNF=10, MODE=00)
电路连接
- LED1 (红): PA0
- LED2 (绿): PA1
- LED3 (蓝): PA2
- 板载LED: PC13
2. C语言寄存器方式程序
功能 1:硬件寄存器映射与定义
功能说明:定义 STM32 芯片的寄存器地址和结构体,建立软件操作与硬件寄存器的映射关系,为后续配置提供基础。
代码模块:
// 寄存器地址定义
#define GPIOA_BASE 0x40010800
#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define RCC_BASE 0x40021000
// GPIO寄存器结构体指针
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define RCC ((RCC_TypeDef *) RCC_BASE)
// 寄存器结构体定义
typedef struct {
volatile uint32_t CRL;
volatile uint32_t CRH;
volatile uint32_t IDR;
volatile uint32_t ODR;
volatile uint32_t BSRR;
volatile uint32_t BRR;
volatile uint32_t LCKR;
} GPIO_TypeDef;
typedef struct {
volatile uint32_t CR;
volatile uint32_t CFGR;
volatile uint32_t CIR;
volatile uint32_t APB2RSTR;
volatile uint32_t APB1RSTR;
volatile uint32_t AHBENR;
volatile uint32_t APB2ENR;
volatile uint32_t APB1ENR;
volatile uint32_t BDCR;
volatile uint32_t CSR;
} RCC_TypeDef;
功能 2:延时功能实现
功能说明:提供毫秒级延时函数,用于控制 LED 点亮的持续时间,实现闪烁效果的时间间隔控制。
代码模块:
// 简单延时函数
void delay_ms(uint32_t ms) {
for(uint32_t i = 0; i < ms * 1000; i++) {
for(uint32_t j = 0; j < 72; j++) {
__NOP();
}
}
}
功能 3:硬件初始化配置
功能说明:对 GPIO 端口进行时钟使能和工作模式配置,设置 LED 初始状态,为 LED 控制做好硬件准备。
代码模块:
// 1. 使能GPIOA和GPIOC时钟
RCC->APB2ENR |= (1 << 2) | (1 << 4); // 使能GPIOA和GPIOC时钟
// 2. 配置PA0,PA1,PA2为推挽输出(2MHz)
GPIOA->CRL &= ~(0xFFF << 0); // 清空PA0-PA2配置位
GPIOA->CRL |= (0x01 << 0) | (0x01 << 4) | (0x01 << 8); // PA0,PA1,PA2推挽输出
// 3. 配置PC13为推挽输出
GPIOC->CRH &= ~(0xF << 20); // 清空PC13配置位
GPIOC->CRH |= (0x01 << 20); // PC13推挽输出
// 4. 初始状态:所有LED熄灭
GPIOA->ODR |= (1 << 0) | (1 << 1) | (1 << 2); // PA0,PA1,PA2高电平
GPIOC->ODR |= (1 << 13); // PC13高电平
功能 4:LED 循环闪烁控制
功能说明:通过循环控制不同 GPIO 引脚的电平状态,实现四个 LED(PA0、PA1、PA2、PC13)按顺序轮流点亮的流水灯效果。
代码模块:
while(1) {
// LED1亮(PA0低电平),其他灭
GPIOA->ODR |= (1 << 1) | (1 << 2); // PA1,PA2高
GPIOC->ODR |= (1 << 13); // PC13高
GPIOA->ODR &= ~(1 << 0); // PA0低
delay_ms(1000);
// LED2亮(PA1低电平),其他灭
GPIOA->ODR |= (1 << 0) | (1 << 2); // PA0,PA2高
GPIOC->ODR |= (1 << 13); // PC13高
GPIOA->ODR &= ~(1 << 1); // PA1低
delay_ms(1000);
// LED3亮(PA2低电平),其他灭
GPIOA->ODR |= (1 << 0) | (1 << 1); // PA0,PA1高
GPIOC->ODR |= (1 << 13); // PC13高
GPIOA->ODR &= ~(1 << 2); // PA2低
delay_ms(1000);
// 板载LED亮(PC13低电平),其他灭
GPIOA->ODR |= (1 << 0) | (1 << 1) | (1 << 2); // PA0,PA1,PA2高
GPIOC->ODR &= ~(1 << 13); // PC13低
delay_ms(1000);
}
3. 包含板载LED的新程序
上述程序已经包含了板载PC13 LED的控制。在STM32F103C8T6最小系统板上,PC13连接了一个LED,当PC13输出低电平时LED亮,高电平时LED灭。
4.实验结果

实验任务2:标准外设库方式
1. 工程项目创建过程
步骤1:创建项目文件夹结构
Project/
├── CMSIS/
│ ├── core_cm3.c
│ ├── core_cm3.h
│ ├── stm32f10x.h
│ └── system_stm32f10x.c
├── Libraries/
│ └── STM32F10x_StdPeriph_Driver/
│ ├── inc/
│ │ ├── stm32f10x_gpio.h
│ │ ├── stm32f10x_rcc.h
│ │ └── ...
│ └── src/
│ ├── stm32f10x_gpio.c
│ ├── stm32f10x_rcc.c
│ └── ...
├── User/
│ ├── main.c
│ ├── stm32f10x_conf.h
│ └── stm32f10x_it.c
└── Project.uvprojx
步骤2:Keil中创建新项目
- Project → New μVision Project
- 选择STM32F103C8器件
- 不复制启动文件(手动添加)
步骤3:添加文件到项目
- 添加CMSIS文件到项目
- 添加标准外设库文件(GPIO、RCC等)
- 创建main.c文件
2. 标准外设库程序
1. 头文件包含
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
2. 硬件引脚定义
// LED引脚定义
#define LED1_PIN GPIO_Pin_0
#define LED2_PIN GPIO_Pin_1
#define LED3_PIN GPIO_Pin_2
#define LED_ONBOARD_PIN GPIO_Pin_13
3. 函数声明
void GPIO_Configuration(void);
void Delay_ms(uint32_t nTime);
4. 主函数(程序入口)
int main(void) {
// GPIO配置
GPIO_Configuration();
while(1) {
// LED1亮,其他灭
GPIO_SetBits(GPIOA, LED2_PIN | LED3_PIN);
GPIO_SetBits(GPIOC, LED_ONBOARD_PIN);
GPIO_ResetBits(GPIOA, LED1_PIN);
Delay_ms(1000);
// LED2亮,其他灭
GPIO_SetBits(GPIOA, LED1_PIN | LED3_PIN);
GPIO_SetBits(GPIOC, LED_ONBOARD_PIN);
GPIO_ResetBits(GPIOA, LED2_PIN);
Delay_ms(1000);
// LED3亮,其他灭
GPIO_SetBits(GPIOA, LED1_PIN | LED2_PIN);
GPIO_SetBits(GPIOC, LED_ONBOARD_PIN);
GPIO_ResetBits(GPIOA, LED3_PIN);
Delay_ms(1000);
// 板载LED亮,其他灭
GPIO_SetBits(GPIOA, LED1_PIN | LED2_PIN | LED3_PIN);
GPIO_ResetBits(GPIOC, LED_ONBOARD_PIN);
Delay_ms(1000);
}
}
5. GPIO 初始化配置
void GPIO_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
// 配置PA0,PA1,PA2为推挽输出
GPIO_InitStructure.GPIO_Pin = LED1_PIN | LED2_PIN | LED3_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置PC13为推挽输出
GPIO_InitStructure.GPIO_Pin = LED_ONBOARD_PIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// 初始状态:所有LED熄灭
GPIO_SetBits(GPIOA, LED1_PIN | LED2_PIN | LED3_PIN);
GPIO_SetBits(GPIOC, LED_ONBOARD_PIN);
}
6. 毫秒级延时函数
void Delay_ms(uint32_t nTime) {
for(; nTime > 0; nTime--) {
for(uint32_t i = 0; i < 1000; i++) {
for(uint32_t j = 0; j < 72; j++) {
__NOP();
}
}
}
}
7.实验结果
mmexport1759126327074
3. 时序分析和Keil仿真
软件延时周期分析:
-
系统时钟:72MHz(默认)
-
每个NOP指令:1个时钟周期
-
延时函数周期:约1ms(实际需要校准)





Keil逻辑分析仪使用步骤:
-
Project → Options for Target → Debug → Use Simulator
-
在Debug窗口中打开Logic Analyzer
-
添加要观察的引脚:GPIOA.0, GPIOA.1, GPIOA.2, GPIOC.13
-
运行仿真,观察波形
按图示勾选DIalong DLL 的值改为图中值


查看波形来计算时间


实际周期测量:
通过逻辑分析仪可观察到:
- 高电平持续时间:约1000ms
- 低电平持续时间:约1000ms
- 总周期:约2000ms(2秒)
实验一与实验二关系分析
相同点:
- 实现相同的功能:LED流水灯控制
- 使用相同的硬件资源
- 达到相同的视觉效果
不同点:
| 特性 | 实验一(寄存器方式) | 实验二(库函数方式) |
|---|---|---|
| 编程复杂度 | 高,需要直接操作寄存器 | 低,使用封装好的函数 |
| 可读性 | 较差,需要了解寄存器细节 | 较好,函数名直观 |
| 可移植性 | 差,与具体芯片绑定 | 较好,跨系列兼容 |
| 代码量 | 小,直接高效 | 较大,包含库开销 |
| 学习价值 | 深入理解硬件工作原理 | 快速开发应用 |
| 维护性 | 差,修改困难 | 好,易于维护 |
关系总结:
实验一是基础,帮助理解STM32的底层硬件工作原理;实验二是应用,展示如何利用官方库提高开发效率。两者相辅相成,寄存器方式的学习为理解库函数实现原理打下基础,而库函数方式则是实际项目开发的首选方法。
更多推荐

所有评论(0)