stm32寄存器操作点亮流水灯


用Proteus 设计一个STM32最小系统板+LED流水灯实验原理图,仿真运行。以 STM32最小系统核心板(STM32F103C8T6)+面板板+3只_(或更多)红绿蓝LED 搭建电路,使用GPIOA、GPIOB、GPIOC这3个端口控制LED灯,轮流闪烁,间隔时长1秒。

1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;

2)用C语言寄存器方式编程实现,代码须有详细注解。

3)STM32最小系统核心板子出厂时已经焊接好了1个led灯(标注了PC13处),一般可通过此灯的点亮让编程者验证自己烧录的代码是否正常运行了。请查阅最小版电路原理图和相关资料,将这个灯也用在流水灯中,重编新程序。

一、stm32C8T6介绍

STM32F103C8T6是一款由意法半导体公司(ST)推出的基于Cortex-M3内核的32位微控制器,硬件采用LQFP48封装,属于ST公司微控制器中的STM32系列。除了被我们熟知的STM32,ST公司还有SPC5X系列、STM8系列等,具体参数如下:
在这里插入图片描述

二、GPIOx端口的各寄存器地址和详细参数

1.地址查找

·第一步,找到GPIOB的基地址
也就是找到GPIOB的小区。结论是,所有GPIOB相关的寄存器,都住在0x4001 0C00到0x4001 0FFF范围内。
在这里插入图片描述
·第二步,找到端口输入寄存器的地址偏移
找到存储数据的那个屋子,结论是0x4001 0C00+8 = 0x4001 0C08
在这里插入图片描述
·第三步,找到知道数据的那个人
PB3的数据位于从右往左数第4个。
在这里插入图片描述
而这个寄存器的位数是32位(虽然高16位没有用到),这就是32位的单片机的意思。每个寄存器都占据4字节,32位。
PB3的输入数据位于0x4001 0C08这个地址上,这个地址上存放数据的右起第4个位就是PB3引脚对应的高低电平。
直接访问这个地址:

 unsigned int *pGPIOB_IDR = (unsigned int *)0x40010C08;
 unsigned char PB3 = *pGPIOB_IDR & 0x8;//取出从右往左数的第4位

2.直接操作寄存器来点亮LED

使用LED在PB8。

·配置时钟使能。
参考手册,搜索"时钟",在表1里可以看到。
时钟控制名字叫做RCC,属于AHB总线。GPIOB属于APB2。
在这里插入图片描述
下图系统结构可以看到时钟的从属关系,可以看出AHB总线包含RCC时钟控制,GPIO是属于APB2的。
在这里插入图片描述
GPIO端口B的地址从0x4001 0C00开始。接下来只寻找时钟使能寄存器的地址:
复位和时钟控制RCC的地址从0x4002 1000开始;
可以在6.3.7小节找到APB2外设时钟使能寄存器(RCC_APB2ENR),偏移地址是0x18,所以APB2的地址就是0x4002 1018。
看手册RCC_APB2ENR,位3是IOPBEN,名字是IO端口B时钟使能,就是我们想要的。把RCC_APB2ENR的位3赋值为1,就是开启GPIOB时钟。
在这里插入图片描述
·配置为通用输出
既然叫做IO,那么肯定就是可以输入,可以输出。
控制LED需要输出高电平或是低电平,所以需要配置为输出。
由于STM32的每个IO都需要4个位来配置,所以一个32位的寄存器最大只能配置8个IO(32位的单片机的寄存器就是32位的)。STM32中,用端口配置低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用端口配置高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。
配置引脚PB8,使用的寄存器是GPIOB_CRH。下面我们来寻找这个寄存器的地址。
在这里插入图片描述
关于此寄存器的说明位于8.2.2小节。先看标题GPIOx,表示不管是PA,PB还是PE,都能用。
偏移地址是0x04,意思是在基地址的基础上再加0x04,所以,对于GPIOB来说就是0x4001 0c04。如果配置PB0-PB7,那么需要的寄存器是低位的寄存器GPIOB_CRL,它的地址是0x4001 0c00。我们需要配置的寄存器是GPIOB_CRH。
找到需要操作的寄存器后,把它配置为通用输出。
复位值是0x4444 4444,并不是0x0000 0000。所谓的复位值,就是指如果没有操作这个寄存器时,寄存器存放的默认值。复位值按位拆分0x4 = 0b0100,0x表示16进制,0b表示二进制,也就是默认CNF 01,MODE 00,是浮空输入。
我们需要的是输出高低电平,所以要设置为输出。输出模式有:
在这里插入图片描述
推挽输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。
开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。
开漏是需要外接上拉电阻才可以输出高电平的,这里并不适合。所以需要设置为推挽输出。
所以配置为输出模式,通用推挽输出。所以设置GPIOB_CRH的MODE8与CNF8为0b0011,即0x3。此寄存器中其它的位暂时不做修改,使用默认值,也就是GPIOB_CRH设置为:0x4444 4443。
点亮LED需要输出低电平,我们需要输出0,得知地址的偏移是0x0C,所以这个数据寄存器的地址就是0x4001 0C0C,把第8位写为0。
在这里插入图片描述
在这里插入图片描述
·使用直接赋值的方式写寄存器的地址

    int main(void)
    {
        unsigned int *pRCC_APB2ENR = (unsigned int *)0x40021018;
        unsigned int *pGPIOB_CRH = (unsigned int *)0x40010c04;
        unsigned int *pGPIOB_ODR = (unsigned int *)0x40010c0c;
        *pRCC_APB2ENR = 0x00000008;
        *pGPIOB_CRH = 0x44444443;
        *pGPIOB_ODR = 0x00000000;
         return 0;             
    }

三、设计思路

需要在 STM32F103C8T6 上面通过 初始化GPIO 来实现点亮 LED 灯。
外设实现的功能可能是完全不同的,但是,多数情况下,我们在设计程序的时候不需要考虑外设具体如何怎样实现功能,只需要给外设接在哪里、高电平有效还是低电平有效。因此,完成题目要求的时候,只需要找到 GPIOA-5、GPIOB-9、GPIOC-14 的地址,然后通过 GPIO的初始化,控制寄存器将片外引脚(我们称之为 IO口)拉低拉高, 输出高低电平,以控制LED亮灭。

点灯是所有学单片机的人都应该学会的一项技能。C51单片机和 stm32 点灯类似。

51单片机的点灯是,通过控制寄存器将片外引脚(我们称之为IO口)拉低拉高,输出高低电平,以控制LED亮灭。
其过程:单片机给指令->控制寄存器->给IO口电平->控制LED亮灭
stm32的点灯则是,通过使能外设GPIO时钟,发出指令给外设GPIO,外设GPIO收到指令后,着手配置自己的寄存器,然后给IO口模式,让其实现各种功能。其过程:CPU给指令->GPIO收到指令->配置内部寄存器->配置IO口模式(注意是模式)->控制LED亮灭
STM32开发板中包含较多寄存器,实现流水灯操作,需要对相应的引脚进行操作,对相应的引脚进行时钟配置、输入输出模式设置、最大速率设置。
于是利用 STM32F103C8T6 实现流水灯,要经过以下步骤:

·时钟配置
·输入输出模式设置
·最大速率设置
·烧录程序
·运行

四、实现过程

1、时钟配置

找到时钟使能寄存器映射基地址
在这里插入图片描述

找到端口偏移地址以及对应端口所在位置
在这里插入图片描述

·外设时钟使能寄存器,偏移量为0x18,起始地址0x4002 1000,该寄存器地址为0x4002 1018
·使能对应端口时钟
查询数据手册可发现,外设时钟使能寄存器,设偏移量为0x18,起始地址0x4002 1000,该寄存器地址为0x4002 1018

#define RCC_AP2ENR	*((unsigned volatile int*)0x40021018) #时钟使能寄存器

手册RCC_APB2ENR,位3是IOPBEN,名字是IO端口B时钟使能,就是我们想要的。把RCC_APB2ENR的位3赋值为1,就是开启GPIOB时钟

RCC->APB2ENR|=1<<2;			//APB2-GPIOA外设时钟使能
RCC->APB2ENR|=1<<3;			//APB2-GPIOB外设时钟使能	
RCC->APB2ENR|=1<<4;			//APB2-GPIOC外设时钟使能
//这两行代码可以合为 RCC_APB2ENR|=1<<3|1<<4;

在这里插入图片描述

2、输入输出模式设置、最大速率设置

本次实验采用通用推挽输出模式,最高输出时钟频率2Mhz。分别用到GPIOA-5、GPIOB-9、GPIOC-14 这3个引脚上三个引脚。其中A4属于端口配置低寄存器偏移地址为0x00,B9、C14属于端口配置高寄存器偏移地址为0x04。
在这里插入图片描述
在这里插入图片描述

·找到GPIOx端口基地址
在这里插入图片描述

配置对应引脚寄存器,基地址+偏移量
故而配置如下:

//----------------GPIOA配置寄存器 -----------------------
#define GPIOA_CRL		*((unsigned volatile int*)0x40010800)
//----------------GPIOB配置寄存器 -----------------------
#define GPIOB_CRH		*((unsigned volatile int*)0x40010C04)
//----------------GPIOC配置寄存器 -----------------------
#define GPIOC_CRH		*((unsigned volatile int*)0x40011004)

设置输出模式为推挽输出,输出速度为2Mhz
例:将GPIOB-9配置成推挽输出模式,且最大速度为2MHz
首先,其为GPIOB9端口,其属于端口配置高寄存器模块,则由上图可知,CNF9和MODE9位为0,其余位为F,即:GPIOB_CRH&=0xFFFFFF0F;又因其为推挽输出模式,且最大速度为2MHz,所以4位寄存器的配置就是CNF9【00】MODE9【10】,0010换成十进制数就是2,即:GPIOB_CRH|=0x00000020

GPIOA_CRL&=0xFFF0FFFF;		//设置位清零	
GPIOA_CRL|=0X00200000;		//PA5推挽输出,把第23、22、21、20位变为0010
GPIOA->ODR|=1<<5;			//设置PA5初始灯为灭
	
GPIOB_CRH&=0xFF0FFFFF;		//设置位清零	
GPIOB_CRH|=0x00000020;		//PB9推挽输出,把第7、6、5、4变为0010
GPIOB->ODR|=0x1<<9;			//设置初始灯为灭
	 
GPIOC_CRH&=0xFF0FFFFF;		//设置位清零	
GPIOC_CRH|=0x02000000;		//PC14推挽输出,把第27、26、25、24变为0010
GPIOC->ODR|=0x1<<14;			//设置初始灯为灭	

3、代码实现

1.创建新工程

·新建 test2 文件夹 —> 点击 Project 下的 New uVision Project —> 输入文件名 test2
在这里插入图片描述
·选择 STM32F103C8
在这里插入图片描述
·创建项目出现弹窗,勾选 CORE 项,点击 OK 完成创建
在这里插入图片描述

2.添加启动文件

点击 Target1→Source Group1→双击→设置打开文件类型为 Asm Source file→选择 startup_stm32f10x_md.s→点击 Add,如下图所示:
在这里插入图片描述

3.代码编写并编译

(1)加上PC13口前代码

//--------------APB2使能时钟寄存器------------------------
#define RCC_APB2ENR		*((unsigned volatile int*)0x40021018)

//----------------GPIOA配置寄存器 ------------------------
#define GPIOA_CRL		*((unsigned volatile int*)0x40010800)
#define	GPIOA_ODR		*((unsigned volatile int*)0x4001080C)
	
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRH		*((unsigned volatile int*)0x40010C04)
#define	GPIOB_ODR		*((unsigned volatile int*)0x40010C0C)
	
//----------------GPIOC配置寄存器 ------------------------
#define GPIOC_CRH		*((unsigned volatile int*)0x40011004)
#define	GPIOC_ODR		*((unsigned volatile int*)0x4001100C)

//函数声明
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<10000;i++);
}

//控制灯亮灭
void A_LED_LIGHT()
	{
    GPIOA_ODR=0x0<<5;		//PA5低电平
	GPIOB_ODR=0x1<<9;		//PB9高电平
	GPIOC_ODR=0x1<<14;		//PC14高电平	
}
void B_LED_LIGHT()
	{
	GPIOA_ODR=0x1<<5;		//PA5高电平
	GPIOB_ODR=0x0<<9;		//PB9低电平
	GPIOC_ODR=0x1<<14;		//PC14高电平
}
void C_LED_LIGHT()
	{
	GPIOA_ODR=0x1<<5;		//PA5高电平
	GPIOB_ODR=0x1<<9;		//PB9高电平
	GPIOC_ODR=0x0<<14;		//PC14低电平
}

//------------------------主函数--------------------------
int main(){

	RCC_APB2ENR|=1<<2;			//APB2-GPIOA外设时钟使能
	RCC_APB2ENR|=1<<3;			//APB2-GPIOB外设时钟使能	
	RCC_APB2ENR|=1<<4;			//APB2-GPIOC外设时钟使能
	

  GPIOA_CRL&=0xFF0FFFFF;		//设置位清零	
  GPIOA_CRL|=0X00200000;		//PA5推挽输出,把第23、22、21、20位变为0010
	
  GPIOB_CRH&=0xFFFFFF0F;		//设置位清零	
  GPIOB_CRH|=0x00000020;		//PB9推挽输出,把第7、6、5、4变为0010
	 
  GPIOC_CRH&=0xF0FFFFFF;		//设置位清零	
  GPIOC_CRH|=0x02000000;		//PC14推挽输出,把第27、26、25、24变为0010

	GPIOA_ODR |= (1<<5);  
	GPIOB_ODR |= (1<<9);      //设置灯的初始状态为灭
	GPIOC_ODR |= (1<<14); 
	
	while(1)
		{
		  A_LED_LIGHT();
		  Delay_ms(60);
		  B_LED_LIGHT();
		  Delay_ms(60);
		  C_LED_LIGHT();
		  Delay_ms(60);
	}

}

(2)加上PC13口后代码

·由于GPIOC已配置寄存器,只需要加上PC13推挽输出,再循环时增添一个PC13灯亮循环即可,由于PC14和PC13共用一个GPIOC,所以需要增加相应与活=或运算。

//--------------APB2使能时钟寄存器------------------------
#define RCC_APB2ENR		*((unsigned volatile int*)0x40021018)

//----------------GPIOA配置寄存器 ------------------------
#define GPIOA_CRL		*((unsigned volatile int*)0x40010800)
#define	GPIOA_ODR		*((unsigned volatile int*)0x4001080C)
	
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRH		*((unsigned volatile int*)0x40010C04)
#define	GPIOB_ODR		*((unsigned volatile int*)0x40010C0C)
	
//----------------GPIOC配置寄存器 ------------------------
#define GPIOC_CRH		*((unsigned volatile int*)0x40011004)
#define	GPIOC_ODR		*((unsigned volatile int*)0x4001100C)

//函数声明
void Delay_ms(volatile  unsigned  int);
void A_LED_LIGHT(void);
void B_LED_LIGHT(void);
void C_LED_LIGHT(void);
void C3_LED_LIGHT(void);
//时延函数
void Delay_ms( volatile  unsigned  int  t)
{
     unsigned  int  i;
     while(t--)
         for (i=0;i<10000;i++);
}

//控制灯亮灭
void A_LED_LIGHT()
	{
    GPIOA_ODR &= (0<<5);  //PA5低电平,红灯亮
	GPIOB_ODR |= (1<<9);   //PB8高电平,黄灯灭  
	GPIOC_ODR |= (1<<14); //PC14高电平,绿灯灭
	GPIOC_ODR |= (1<<13);  //PC13,PC13口灯不亮
    		
}
void B_LED_LIGHT()
	{
	GPIOA_ODR |= (1<<5);  //高电平
	GPIOB_ODR &= (0<<9);   //低电平   
	GPIOC_ODR |= (1<<14); //高电平
	GPIOC_ODR |= (1<<13);//高电平
}
void C_LED_LIGHT()
	{
	GPIOA_ODR |= (1<<5);  //高
	GPIOB_ODR |= (1<<9);  //高    
	GPIOC_ODR &= (0<<14); //低
	GPIOC_ODR |= (1<<13); //高
		
	}
void C3_LED_LIGHT()
	{
	GPIOA_ODR |= (1<<5);  //高
	GPIOB_ODR |= (1<<9);    //高  
	GPIOC_ODR = (0<<13);//高
	GPIOC_ODR |= (1<<14); //低
	}

//------------------------主函数--------------------------
int main(){

	RCC_APB2ENR|=1<<2;			//APB2-GPIOA外设时钟使能
	RCC_APB2ENR|=1<<3;			//APB2-GPIOB外设时钟使能	
	RCC_APB2ENR|=1<<4;			//APB2-GPIOC外设时钟使能
	

  GPIOA_CRL&=0xFF0FFFFF;		//设置位清零	
  GPIOA_CRL|=0X00200000;		//PA5推挽输出,把第23、22、21、20位变为0010
	
  GPIOB_CRH&=0xFFFFFF0F;		//设置位清零	
  GPIOB_CRH|=0x00000020;		//PB9推挽输出,把第7、6、5、4变为0010
	 
  GPIOC_CRH&=0xF0FFFFFF;		//设置位清零	
  GPIOC_CRH|=0x02000000;		//PC14推挽输出,把第27、26、25、24变为0010
	
  GPIOC_CRH&=0xFF0FFFFF;		//设置位清零	
  GPIOC_CRH|=0x00200000;		//PC13推挽输出,把第23、22、21、20变为0010

	GPIOA_ODR |= (1<<5);  
	GPIOB_ODR |= (1<<9);      //设置灯的初始状态为灭
	GPIOC_ODR |= (1<<14); 
	GPIOC_ODR |= (1<<13);
	while(1)
		{
		  
		  A_LED_LIGHT();//红灯亮
		  Delay_ms(60);
		  B_LED_LIGHT();//黄灯亮
		  Delay_ms(60);
		  C_LED_LIGHT();//绿灯亮
		  Delay_ms(60);
		  C3_LED_LIGHT();//PC13口亮
		  Delay_ms(60);
	}

}

编译,生成HEX文件
在这里插入图片描述

4、仿真图设计

在这里插入图片描述

5、硬件连接

用杜邦线分别将A5,B9,C14和LED负极连接起来,再将LED正极接+,板子3.3接+
在这里插入图片描述

五、结果实现

1、仿真

2、硬件

总结

这次算是对stm32使用的入门,是第一次通过编写程序实现stm32硬件的应用。这次实验通过简单的寄存器操作,我深刻理解了stm32的工作原理;了解了寄存器的相关介绍;学习了地址查找,直接操作寄存器;FlyMu的使用及ST-link的配置、硬件的连接,收获很大,受益匪浅。

参考:
1.STM32启动文件:startup_stm32f10x_hd.s等启动文件的简单描述
2.STM32F103C8T6寄存器方式借助面包板点亮LED流水灯详解
3.STM32寄存器的简介、地址查找,与直接操作寄存器

### 回答1: 要使用STM32F103寄存器方式点亮LED流水灯,需要按照以下步骤进行: 1. 首先,需要配置GPIO引脚为输出模式。可以通过设置GPIOx_CRL或GPIOx_CRH寄存器来实现。例如,如果要使用PA引脚,可以将GPIOA_CRL寄存器的第位和第1位设置为01,表示将PA引脚配置为输出模式。 2. 接下来,需要使用GPIOx_BSRR寄存器来设置或清除引脚的电平。例如,如果要点亮PA引脚上的LED,可以将GPIOA_BSRR寄存器的第位设置为1,表示将PA引脚的电平设置为高电平。 3. 然后,可以使用延时函数来控制LED的亮灭时间。例如,可以使用SysTick定时器来实现延时功能。 4. 最后,可以使用循环语句和位运算符来实现LED流水灯效果。例如,可以使用for循环和左移运算符来实现LED从左到右依次亮起的效果。 需要注意的是,使用寄存器方式编程需要对STM32F103的寄存器结构和寄存器位的含义有一定的了解。同时,需要注意寄存器的读写顺序和操作的正确性,以避免出现意外的错误。 ### 回答2: STM32F103是一款高性能、低功耗、易于开发的微控制器,它能为嵌入式设备提供强大的计算和控制能力。在使用STM32F103进行开发时,头文件和寄存器操作是必不可少的一部分。 很多初学者都想通过点亮LED来入门STM32F103的开发,这里以寄存器方式点亮LED流水灯为例进行讲解: 首先需要初始化GPIO口,确定要控制的IO口和使用的引脚。这里用到了重映射技术,将LED1连接至PD2引脚(具体可以参考datasheet),可以将GPIO口D对应的寄存器地址复制到某个变量用于后续的操作。 代码示例: RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO, ENABLE);//使能GPIO时钟 GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO初始化结构体 GPIO_InitStructure.GPIO_Pin= GPIO_Pin_2;//选择PD2引脚 GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;//推挽输出 GPIO_InitStructure.GPIO_Speed= GPIO_Speed_10MHz;//输出速度10MHz GPIO_Init(GPIOD, &GPIO_InitStructure);//将设置好的GPIO配置应用 接下来,可以编写流水灯的代码,通过设置GPIO口输出高低电平,控制LED灯的亮灭。循环体中,分别点亮/熄灭LED,并加上适当的时间延时,从而实现流水灯的效果。 代码示例: while(1) { GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_SET);//将PD2输出高电平,点亮LED1 delay(50);//延时 GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_RESET);//将PD2输出低电平,熄灭LED1 delay(50);//延时 } 代码执行上述代码后,即可实现STM32F103寄存器方式点亮LED流水灯的效果。需要注意的是,该示例代码中的延时函数需要自行编写,建议使用STM32CubeMX来生成延时函数。此外,还需要注意GPIO口的配置以及时钟使能,以免出现硬件问题。 以上就是关于STM32F103寄存器方式点亮LED流水灯的简单介绍与实现步骤。希望本文对初学者入门STM32F103开发有所帮助。 ### 回答3: 首先,启用STM32F103寄存器进行点亮LED流水灯需要进行以下准备步骤: 1. 确认所需引脚和LED的连接方式。此处假设我们将LED连接到引脚PB12,那么需要将PB12设置为输出模式。 2. 配置系统时钟,以便使用定时器来控制LED的闪烁速度。不同的系统时钟配置方式可能会略有不同,但主要是设置时钟源和最终频率。 3. 配置定时器,以便以适当的频率闪烁LED。这通常涉及到设置定时器的时钟源、预分频和计数器值。 4. 配置NVIC(Nested Vectored Interrupt Controller)中断,以便在定时器计数完成时处理中断。这需要设置中断源和优先级,以便定时器中断可以正确地触发。 了解了以上准备工作之后,下面开始实现点亮LED流水灯寄存器方式程序: 1. 在头文件中加入相关寄存器定义,方便后续程序的操作。 2. 在主函数中进行引脚配置: ``` RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //使能PB引脚时钟 GPIOB->CRH &= ~(0xF << 16); //清零位16~19 GPIOB->CRH |= (0x3 << 16); //设置位16~17为01,即输出模式 ``` 3. 配置定时器,以便生成适当的延迟时间: ``` RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; //使能TIM3时钟 TIM3->PSC = 7200 - 1; //预分频器7200,即频率为8KHz TIM3->ARR = 1000 - 1; //计数器自动重载值999,即1s的闪烁周期 TIM3->CR1 |= TIM_CR1_ARPE; //开启自动重载 TIM3->CR1 &= ~(TIM_CR1_DIR); //向上计数 TIM3->CR1 &= ~(TIM_CR1_CMS); //开启边缘对齐模式 TIM3->DIER |= TIM_DIER_UIE; //开启更新事件中断 TIM3->CR1 |= TIM_CR1_CEN; //启动计数器 ``` 4. 配置NVIC中断,以便在定时器计数完成时更新LED的状态: ``` NVIC_EnableIRQ(TIM3_IRQn); //使能TIM3中断 NVIC_SetPriority(TIM3_IRQn, 0); //设置TIM3中断优先级为最高 ``` 5. 在计时器中断处理中更新LED的状态,以实现流水灯效果: ``` void TIM3_IRQHandler(void){ if(TIM3->SR & TIM_SR_UIF){ //判断是否为更新中断 TIM3->SR &= ~(TIM_SR_UIF); //清除更新中断标志 static int count=0; static int flag=1; if(count==0){ GPIOB->ODR |= GPIO_ODR_ODR12; //点亮PB12,LED1亮 flag=1; } else if(count==1){ GPIOB->ODR &= ~(GPIO_ODR_ODR12); //熄灭PB12,LED1灭 GPIOB->ODR |= GPIO_ODR_ODR13; //点亮PB13,LED2亮 } else if(count==2){ GPIOB->ODR &= ~(GPIO_ODR_ODR13); //熄灭PB13,LED2灭 GPIOB->ODR |= GPIO_ODR_ODR14; //点亮PB14,LED3亮 } else if(count==3){ GPIOB->ODR &= ~(GPIO_ODR_ODR14); //熄灭PB14,LED3灭 GPIOB->ODR |= GPIO_ODR_ODR15; //点亮PB15,LED4亮 } else if(count==4){ GPIOB->ODR &= ~(GPIO_ODR_ODR15); //熄灭PB15,LED4灭 GPIOB->ODR |= GPIO_ODR_ODR14; //点亮PB14,LED3亮 } else if(count==5){ GPIOB->ODR &= ~(GPIO_ODR_ODR14); //熄灭PB14,LED3灭 GPIOB->ODR |= GPIO_ODR_ODR13; //点亮PB13,LED2亮 } else if(count==6){ GPIOB->ODR &= ~(GPIO_ODR_ODR13); //熄灭PB13,LED2灭 GPIOB->ODR |= GPIO_ODR_ODR12; //点亮PB12,LED1亮 flag=0; } if(flag){ count++; } else{ count--; } } } ``` 上述代码中,首先判断是否为计数器更新中断,然后根据计数值的不同更新LED的状态,实现流水灯效果。其中,计数值的变化可以通过flag来判断是递增还是递减,以实现LED灯的正向或反向流动。 总体来说,通过以上代码实现了基于STM32F103寄存器点亮LED流水灯,可以调整定时器的时钟源和计数器值来实现不同的闪烁效果。虽然这种方式比较繁琐,但对于有一定经验的开发者来说,可以更精准地控制硬件,实现更高效的程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值