stm32F103的LED亮灭之版本1(寄存器地址版)

实验目的:

1、了解stm32f103c8t6最小核心板的主要引脚接口;

2、掌握Keil 开发stm32程序的环境搭建和设置;

3、使用GPIO引脚,外接LED灯,编写程序让LED灯周期性亮灭。

STM32F10xxx参考手册链接:https://pan.baidu.com/s/10gACCMp8zm7OMX7J_mC4SQ
提取码:9962

1.了解STM32最小系统核心板的电路原理图,用Proteus 设计一个STM32最小系统板+LED流水灯实验原理图,仿真运行。

最小系统板的引脚原理图如下:

stm32最小系统板的电路原理图:

image-20240501221343546

Proteus仿真(利用PA4,PB10,PC15分别点亮绿灯、蓝灯、红灯)

keil新建工程

选择stm32f103c8系列芯片

image-20240502133030381

勾选CORE和Startup

image-20240502133241944

选择ST-Link Debugger并点击setting进入设置

image-20240502133750093

勾选Reset and Run

image-20240502133940312

Proteus仿真如下:

请添加图片描述

2.STM32最小系统核心板(STM32F103C8T6)+面板板+3只_(或更多)红绿蓝LED 搭建电路,使用GPIOA、GPIOB、GPIOC这3个端口控制LED灯,轮流闪烁,间隔时长1秒。

2.1程序设计思路以及GPIOx端口的各寄存器地址和详细参数

程序设计思路:

1.开启GPIO的时钟

2.初始化GPIO口

3.利用高低电平和延时函数实现流水灯

2.1.1开启GPIO时钟

GPIO端口地址

RCC寄存器地址

image-20240503005656079

image-20240503005758224

因为RCC起始地址为:0x40021000

APB2外设时钟使能寄存器的偏移地址是0x18

所以实际地址为:0x40021000+0x18= 0x40021018

使能GPIOA、GPIOB、GPIOC时钟代码如下:

#define RCC_APB2ENR (*(unsigned int *)0x40021018)
// 打开时钟
RCC_APB2ENR |= (1<<3);  // 打开 GPIOB 时钟
RCC_APB2ENR |= (1<<4);  // 打开 GPIOC 时钟
RCC_APB2ENR |= (1<<2);  // 打开 GPIOA 时钟
 

2.1.2GPIO端口配置

GPIO的配置寄存器CRL和CRH
STM32的一组GPIO有16个IO口,比如GPIOA这一组,有GPIOA0~GPIOA15一共16个IO口。每一个IO口需要寄存器的4位用来配置工作模式。

那么一组GPIO就需要16x4=64位的寄存器来存放这一组GPIO的工作模式的配置,但STM32的寄存器都是32位的,所以只能使用2个32位的寄存器来存放了。CRL用来存放低八位的IO口(GPIOx0—GPIOx7)的配置,CRH用来存放高八位的IO口(GPIOx8—GPIOx15)的配置。

这两个寄存器的全称是:端口配置低寄存器(GPIOx_CRL) (x=A…E) 和 端口配置高寄存器(GPIOx_CRH) (x=A…E)

也就是每一组GPIO都有两个32位的寄存器是用来配置IO口的工作模式的。

我们都清楚STM32的GPIO有八种工作模式,4个二进制数可以组合出16种情况,而我们只需要8种就行了。至于4位数怎么组合是什么工作模式,我们看STM32的手册。

工作模式的配置

image-20240502194822611

image-20240502194842983

可以看出,4位中又分为了CNFy和MODEy(y表示这组GPIO的第几个IO口),现在我们分析这两个的作用:

MODEy:

00:输入模式(复位后的状态) 
01:输出模式,最大速度10MHz 
10:输出模式,最大速度2MHz 
11:输出模式,最大速度50MHz

可以看出MODEy是用来配置是输出还是输入模式的一般是使用00和11这两种情况。00是输入模式,11是输出模式。

CNFy:

在输入模式(MODE[1:0]=00)00:模拟输入模式
01:浮空输入模式(复位后的状态) 
10:上拉/下拉输入模式
11:保留 
在输出模式(MODE[1:0]>00)00:通用推挽输出模式
01:通用开漏输出模式
10:复用功能推挽输出模式
11:复用功能开漏输出模式

这些就是CNFy的配置,配置具体的工作模式。配合MODEy就可以配置出所有的工作模式了。

比如我需要配置上拉输入模式,那么4位寄存器的配置就是CNFy【10】MODEy【00】

具体的假如我要将PB1端口设置为推挽输出模式,就应该用到GPIOx_CRL,将除了PB1端口的其它位的CNFy[1:0]填入11(保留)MODEy[1:0]位也填入11,两个连起来就是1111(对应十六进制的F),而CNF1[1:0]填入00(通用推挽) MODE1[1:0]填入11(输出模式50Mhz)即是0011,对应的十六进制为3

GPIOB_CRL&=0xffffff0f;//设置位清零	
GPIOB_CRL|=0x00000030;//通用推挽输出,频率50Mhz

所以设置PA4、PB1、PC15的代码为:

GPIOA_CRL &= 0xfff0ffff; //设置位 清零		
GPIOA_CRL|=0x00030000; //PA4推挽输出 频率50Mhz

GPIOB_CRL&=0xffffff0f;//设置位清零	
GPIOB_CRL|=0x00000030;//PB1通用推挽输出,频率50Mhz

GPIOC_CRH &= 0x0fffffff; //设置位 清零		
GPIOC_CRH|=0x30000000;  //PC15推挽输出 频率50Mhz

2.1.3流水灯实现(C语言寄存器)

#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define GPIOA_BASE 0x40010800

#define RCC_APB2ENR (*(unsigned int *)0x40021018)
 
#define GPIOB_CRL (*(unsigned int *)0x40010C00)
#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 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=0x1<<4;		//PA4低电平
	GPIOB_ODR=0x0<<1;		//PB1高电平
	GPIOC_ODR=0x0<<15;		//PC15高电平
}
void B_LED_LIGHT(){
	GPIOA_ODR=0x0<<4;		//PA4高电平
	GPIOB_ODR=0x1<<1;		//PB1低电平
	GPIOC_ODR=0x0<<15;		//PC15高电平
}
void C_LED_LIGHT(){
	GPIOA_ODR=0x0<<4;		//PA4高电平
	GPIOB_ODR=0x0<<1;		//PB1高电平
	GPIOC_ODR=0x1<<15;		//PC15低电平	
}
 
int main(){
	// 开启时钟
	RCC_APB2ENR |= (1<<3); // 开启 GPIOB 时钟
	RCC_APB2ENR |= (1<<4); // 开启 GPIOC 时钟
	RCC_APB2ENR |= (1<<2); // 开启 GPIOA 时钟
	
	
//初始化GPIO	
GPIOA_CRL &= 0xfff0ffff; //设置位 清零		
GPIOA_CRL|=0x00030000; //PA4推挽输出 频率50Mhz

GPIOB_CRL&=0xffffff0f;//设置位清零	
GPIOB_CRL|=0x00000030;//PB1通用推挽输出,频率50Mhz

GPIOC_CRH &= 0x0fffffff; //设置位 清零		
GPIOC_CRH|=0x30000000;  //PC15推挽输出 频率50Mhz
 
	// 3个LED初始化为不亮(即高点位)
	GPIOA_ODR |= (1<<4);  
    GPIOB_ODR |= (1<<1); 
    GPIOC_ODR |= (1<<15); 

	while(1){
        B_LED_LIGHT();
		Delay_ms(10000);//延时1s
        
        C_LED_LIGHT();
		Delay_ms(10000);
 
		A_LED_LIGHT();
		Delay_ms(10000);
	}
}

现象如下:
请添加图片描述

2.3修改码让PC13控制的LED也加入流水灯

接线图不变,配置GPIOC的代码如下:

#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define GPIOA_BASE 0x40010800

#define RCC_APB2ENR (*(unsigned int *)0x40021018)
 
#define GPIOB_CRL (*(unsigned int *)0x40010C00)
#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 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=0x1<<4;		
	GPIOB_ODR=0x0<<1;		
	GPIOC_ODR=0x1<<13;		
}
void B_LED_LIGHT(){
	GPIOA_ODR=0x0<<4;		
	GPIOB_ODR=0x1<<1;		
	GPIOC_ODR=0x1<<13;		
}
void C_LED_LIGHT(){
	GPIOA_ODR=0x0<<4;		
	GPIOB_ODR=0x0<<1;		
	GPIOC_ODR=0x0<<13;		
}
 
int main(){

RCC_APB2ENR |= (1<<3); 
RCC_APB2ENR |= (1<<4); 
RCC_APB2ENR |= (1<<2); 
	
GPIOA_CRL &= 0xfff0ffff; 	
GPIOA_CRL|=0x00030000;

GPIOB_CRL&=0xffffff0f;	
GPIOB_CRL|=0x00000030;

GPIOC_CRH &= 0xff0fffff;		
GPIOC_CRH|=0x00300000;  
 
	GPIOA_ODR |= (1<<4);  
    GPIOB_ODR |= (1<<1); 
    GPIOC_ODR |= (1<<13); 

	while(1){
        B_LED_LIGHT();
		Delay_ms(10000);
        
        C_LED_LIGHT();
		Delay_ms(10000);
 
		A_LED_LIGHT();
		Delay_ms(10000);
	}
}

现象如下:

请添加图片描述

总结

用寄存器进行编程需要依靠STM32F103参考手册去查找地址,编程方式比较原始,麻烦,但是能让我们懂得一些底层原理,帮助我们更好地理解和学习stm32

参考资料:

1.STM32 GPIO的配置寄存器(CRL、CRH)快速学习_gpioa->crh-CSDN博客

2.STM32F103的流水灯点亮版本1(寄存器地址操作)_pc13推挽输出-CSDN博客

3.【寻址方式】基地址与偏移地址的详细解释-CSDN博客

  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值