#实验准备
硬件 STM32F103C8T6最小系统版 面包板 led*3 stlink 跳线若干
#实验目的
了解寄存器的配置和输出流程,完成实际的硬件操作
#实验任务
1.以 STM32最小系统核心板(STM32F103C8T6)+面板板+3只_(或更多)红绿蓝LED 搭建电路,使用GPIOA、GPIOB、GPIOC这3个端口控制LED灯,轮流闪烁,间隔时长1秒。
1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;
2)用C语言寄存器方式编程实现,代码须有详细注解。
3)STM32最小系统核心板子出厂时已经焊接好了1个led灯(标注了PC13处),一般可通过此灯的点亮让编程者验证自己烧录的代码是否正常运行了。请查阅最小版电路原理图和相关资料,将这个灯也用在流水灯中,重编新程序。
2.在实验1的基础上,改用标准外设库方式使用某个端口GPIOx端口管脚控制几个LED灯,轮流闪烁,间隔时长1秒。
1)写出工程项目创建文件夹、添加STM32标准外设库文件(.c,.h)的详细过程;
2)LED灯的亮/灭周期是通过软件循环延时完成的,其准确周期大致是多少呢?
在没有示波器条件下,可以使用Keil的软件仿真逻辑分析仪功能观察管脚的时序波形,更方便动态跟踪调试和定位代码故障点。 请用此功能观察GPIO端口的输出波形,并分析时序状态正确与否、高低电平转换周期(LED闪烁周期)实际为多少。
#实验内容
1.以 STM32最小系统核心板(STM32F103C8T6)+面板板+3只_(或更多)红绿蓝LED 搭建电路,使用GPIOA、GPIOB、GPIOC这3个端口控制LED灯,轮流闪烁,间隔时长1秒。
1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;
2)用C语言寄存器方式编程实现,代码须有详细注解。
3)STM32最小系统核心板子出厂时已经焊接好了1个led灯(标注了PC13处),一般可通过此灯的点亮让编程者验证自己烧录的代码是否正常运行了。请查阅最小版电路原理图和相关资料,将这个灯也用在流水灯中,重编新程序。
步骤1
写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;
关闭GPIOB的Pin9和GPIOC的Pin15灯
延时1秒
点亮GPIOB的Pin9灯
关闭GPIOA的Pin7和GPIOC的Pin15灯
延时1秒
点亮GPIOC的Pin15灯
关闭GPIOA的Pin7和GPIOB的Pin9灯
延时1秒
GPIOx端口的各寄存器地址和详细参数:
GPIOA_BASE:0x40010800
GPIOB_BASE:0x40010C00
GPIOC_BASE:0x40011000
GPIOx_CRL/CRH:GPIO配置寄存器,用于配置各引脚的模式、速度和推挽/开漏等属性。
步骤2
用C语言寄存器方式编程实现,代码须有详细注解
代码如下
#include <stdint.h>
#include <stm32f10x.h>
void delay(uint32_t count) {
for (volatile uint32_t i = 0; i < count; ++i) {
for (volatile uint32_t j = 0; j < 7200; ++j) {}
}
}
int main(void) {
// 1. 初始化GPIOA、GPIOB和GPIOC的相关寄存器,配置为输出模式
// 使能GPIOA、GPIOB和GPIOC的时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN;
// 配置GPIOA的Pin7为推挽输出模式
GPIOA->CRL &= ~(GPIO_CRL_CNF7 | GPIO_CRL_MODE7);
GPIOA->CRL |= GPIO_CRL_MODE7;
// 配置GPIOB的Pin9为推挽输出模式
GPIOB->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9);
GPIOB->CRH |= GPIO_CRH_MODE9;
// 配置GPIOC的Pin15为推挽输出模式
GPIOC->CRH &= ~(GPIO_CRH_CNF13 | GPIO_CRH_MODE13);
GPIOC->CRH |= GPIO_CRH_MODE13;
// 2. 循环中轮流点亮其中一个LED灯,并延时1秒
while (1) {
// 点亮GPIOA的Pin7灯
GPIOA->BSRR = GPIO_BSRR_BS7;
// 关闭GPIOB的Pin9和GPIOC的Pin15灯
GPIOB->BRR = GPIO_BRR_BR9;
GPIOC->BRR = GPIO_BRR_BR13;
delay(1000); // 延时1秒
// 点亮GPIOB的Pin9灯
GPIOB->BSRR = GPIO_BSRR_BS9;
// 关闭GPIOA的Pin7和GPIOC的Pin15灯
GPIOA->BRR = GPIO_BRR_BR7;
GPIOC->BRR = GPIO_BRR_BR13;
delay(1000); // 延时1秒
// 点亮GPIOC的Pin15灯
GPIOC->BSRR = GPIO_BSRR_BS13;
// 关闭GPIOA的Pin7和GPIOB的Pin9灯
GPIOA->BRR = GPIO_BRR_BR7;
GPIOB->BRR = GPIO_BRR_BR9;
delay(1000); // 延时1秒
}
return 0;
}
步骤3
3)STM32最小系统核心板子出厂时已经焊接好了1个led灯(标注了PC13处),一般可通过此灯的点亮让编程者验证自己烧录的代码是否正常运行了。请查阅最小版电路原理图和相关资料,将这个灯也用在流水灯中,重编新程序。
代码如下
#include <stdint.h>
#include <stm32f10x.h>
#include <Delay.h>
int main(void) {
// 1. 初始化GPIOA、GPIOB和GPIOC的相关寄存器,配置为输出模式
// 使能GPIOA、GPIOB和GPIOC的时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN;
// 配置GPIOA的Pin7为推挽输出模式
GPIOA->CRL &= ~(GPIO_CRL_CNF7 | GPIO_CRL_MODE7);
GPIOA->CRL |= GPIO_CRL_MODE7;
// 配置GPIOB的Pin9为推挽输出模式
GPIOB->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9);
GPIOB->CRH |= GPIO_CRH_MODE9;
// 配置GPIOC的Pin13为推挽输出模式
GPIOC->CRH &= ~(GPIO_CRH_CNF13 | GPIO_CRH_MODE13);
GPIOC->CRH |= GPIO_CRH_MODE13;
// 2. 循环中轮流点亮其中一个LED灯,并延时1秒
while (1) {
// 点亮GPIOA的Pin7灯
GPIOA->BSRR = GPIO_BSRR_BS7;
// 关闭GPIOB的Pin9和GPIOC的Pin13灯
GPIOB->BRR = GPIO_BRR_BR9;
GPIOC->BRR = GPIO_BRR_BR13;
Delay_ms(1000); // 延时1秒
// 点亮GPIOB的Pin9灯
GPIOB->BSRR = GPIO_BSRR_BS9;
// 关闭GPIOA的Pin7和GPIOC的Pin13灯
GPIOA->BRR = GPIO_BRR_BR7;
GPIOC->BRR = GPIO_BRR_BR13;
Delay_ms(1000); // 延时1秒
// 点亮GPIOC的Pin13灯
GPIOC->BSRR = GPIO_BSRR_BS13;
// 关闭GPIOA的Pin7和GPIOB的Pin9灯
GPIOA->BRR = GPIO_BRR_BR7;
GPIOB->BRR = GPIO_BRR_BR9;
Delay_ms(1000); // 延时1秒
}
return 0;
}
2.STM32F103寄存器方式点亮LED流水灯
一 寄存器
1.1 地址映射和寄存器映射原理
地址映射:
为了使CPU执行指令能够准确无误的访问正确的存储单元,需将用户程序中的逻辑地址转换为由机器直接寻址的物理地址,这一过程称为地址映射
寄存器映射:
寄存器映射是在地址映射的基础上运行的。以STM32为例,在存储器片上外设区域,四字节为一个单元,每个单元对应不同的功能。我们可以通过控制这些单元时来完成相应的功能。我们可以找到每个单元的起始地址,然后通过C 语言指针的操作方式来访问这些单元。但这些单元的地址繁琐冗长容易出错,因此就以功能为名给这个内存单元取一个代号,这个别代号实质上就是寄存器名字。综上,给已分配好地址的能完成相应功能的内存单元取代号的过程就叫寄存器映射。
1.2 寄存器
寄存器是CPU内部用来存放数据的一些小型存储区域,用来存放二进制数据或代码的信息
二 GPIO
2.1 GPIO端口的初始化设置
GPIO端口的初始化设置一共分为三个步骤:
1.打开GPIO口的时钟
GPIO地址:
时钟地址:
2.设置低电平
三.流水灯的仿真与实现
3.1 对GPIOA的A7、GPIOB的B9、GPIOC的C15设置:
//--------------APB2使能时钟寄存器------------------------
#define RCC_AP2ENR *((unsigned volatile int*)0x40021018)
//----------------GPIOA配置寄存器 ------------------------
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
#define GPIOA_ORD *((unsigned volatile int*)0x4001080C)
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRH *((unsigned volatile int*)0x40010C04)
#define GPIOB_ORD *((unsigned volatile int*)0x40010C0C)
//----------------GPIOC配置寄存器 ------------------------
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
#define GPIOC_ORD *((unsigned volatile int*)0x4001100C)
//-------------------简单的延时函数-----------------------
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
void A_LED_LIGHT(){
GPIOA_ORD=0x0<<7; //PA7低电平
GPIOB_ORD=0x1<<9; //PB9高电平
GPIOC_ORD=0x1<<15; //PC15高电平
}
void B_LED_LIGHT(){
GPIOA_ORD=0x1<<7; //PA7高电平
GPIOB_ORD=0x0<<9; //PB9低电平
GPIOC_ORD=0x1<<15; //PC15高电平
}
void C_LED_LIGHT(){
GPIOA_ORD=0x1<<7; //PA7高电平
GPIOB_ORD=0x1<<9; //PB9高电平
GPIOC_ORD=0x0<<15; //PC15低电平
}
//------------------------主函数--------------------------
int main()
{
int j=100;
RCC_AP2ENR|=1<<2; //APB2-GPIOA外设时钟使能
RCC_AP2ENR|=1<<3; //APB2-GPIOB外设时钟使能
RCC_AP2ENR|=1<<4; //APB2-GPIOC外设时钟使能
//这两行代码可以合为 RCC_APB2ENR|=1<<3|1<<4;
GPIOA_CRL&=0x0FFFFFFF; //设置位 清零
GPIOA_CRL|=0x20000000; //PA7推挽输出
GPIOA_ORD|=1<<7; //设置PA7初始灯为灭
GPIOB_CRH&=0xFFFFFF0F; //设置位 清零
GPIOB_CRH|=0x00000020; //PB9推挽输出
GPIOB_ORD|=1<<9; //设置初始灯为灭
GPIOC_CRH&=0x0FFFFFFF; //设置位 清零
GPIOC_CRH|=0x30000000; //PC15推挽输出
GPIOC_ORD|=0x1<<15; //设置初始灯为灭
while(j)
{
A_LED_LIGHT();
Delay_ms(10000000);
B_LED_LIGHT();
Delay_ms(10000000);
C_LED_LIGHT();
Delay_ms(10000000);
}
}
3.2 Protues仿真图
3.3建立工程文件
四.最终效果
#实验心得
本实验基于STM32F103的GPIO端口控制。通过操作相应的寄存器地址,我们可以控制每个GPIO引脚的电平,从而实现流水灯的点亮和熄灭。通过本次实验,我成功掌握了STM32F103的GPIO控制方法,理解了通过操作寄存器地址来控制硬件的方式。同时,我也学习了如何编写程序实现特定的硬件控制效果。
在编写代码以及仿真过程中高低电平没有分清,导致仿真很多次连接错误,很多次没有做出来。以及寄存器的使用方式开始并不熟悉,还有GPIO端口的初始化设置和工作模式开始并没有掌握太多,最后通过查询优快云解决掉了部分错误。