STM32F103寄存器方式点亮LED流水灯

目录

 一、STM32寄存器规则及IO口介绍

二、硬件连接设计

三、软件设计

3.1配置IO口 

 3.2编写主函数

 3.3烧录:STM32与PC端连接

3.4效果演示

四、汇编语言实现 

五、参考文献  


 

 一、STM32寄存器规则及IO口介绍

本博客将要实现的是控制 STM32 开发板上的三个IO口实现一个类似流水灯的效果,该实验的关键在于如何控制 STM32 的IO口输出。了解了 STM32的IO口如何输出的,就可以实现流水灯了。通过这次的学习,你将初步掌握 STM32 基本 IO 口的使用,而这是迈向STM32的第一步。 

STM32 的 IO 口可以由软件配置成如下 8 种模式:

1、输入浮空

2、输入上拉

3、输入下拉

4、模拟输入

5、开漏输出

6、推挽输出

7、推挽式复用功能

8、开漏复用功能 

每个 IO 口可以自由编程,但 IO 口寄存器必须要按 32 位字被访问。STM32 的每个 IO 端口都有 7 个寄存器来控制。他们分别是:配置模式的 2 个 32 位的端口 配置寄存器 CRL 和 CRH;2 个 32 位的数据寄存器 IDR 和 ODR;1 个 32 位的置位/复位寄存器 BSRR;一个 16 位的复位寄存器 BRR;1 个 32 位的锁存寄存器 LCKR;这里我们仅介绍常用 的 几个寄存器,我们常用的 IO 端口寄存器只有 4 个:CRL、CRH、IDR、ODR。

CRL 和 CRH 控制着每个 IO 口的模式及输出速率。 STM32 的 IO 口位配置表如表所示:

STM32F103寄存器方式点亮LED流水灯_第1张图片

 STM32 输出模式配置如表所示:

STM32F103寄存器方式点亮LED流水灯_第2张图片

接下来我们看看端口低配置寄存器 CRL 的描述,如图所示: 

STM32F103寄存器方式点亮LED流水灯_第3张图片 该寄存器的复位值为 0X4444 4444,从图 6.1.1 可以看到,复位值其实就是配置端口为浮空 输入模式。从上图还可以得出:STM32 的 CRL 控制着每组 IO 端口(A~G)的低 8 位的模式。 每个 IO 端口的位占用 CRL 的 4 个位,高两位为 CNF,低两位为 MODE。这里我们可以记住几 个常用的配置,比如 0X0 表示模拟输入模式(ADC 用)、0X3 表示推挽输出模式(做输出口用, 50M 速率)、0X8 表示上/下拉输入模式(做输入口用)、0XB 表示复用输出(使用 IO 口的第二 功能,50M 速率)。

CRH 的作用和 CRL 完全一样,只是 CRL 控制的是低 8 位输出口,而 CRH 控制的是高 8 位输出口。这里我们对 CRH 就不做详细介绍了。 给个实例,比如我们要设置 PORTC 的 11 位为上拉输入,12 位为推挽输出。代码如下:

GPIOC->CRH&=0XFFF00FFF;//清掉这 2 个位原来的设置,同时也不影响其他位的设置

GPIOC->CRH|=0X00038000; //PC11 输入,PC12 输出

GPIOC->ODR=1<<11; //PC11 上拉

通过这 3 句话的配置,我们就设置了 PC11 为上拉输入,PC12 为推挽输出。

IDR 是一个端口输入数据寄存器,只用了低 16 位。该寄存器为只读寄存器,并且只能以 16 位的形式读出。该寄存器各位的描述如图所示:

STM32F103寄存器方式点亮LED流水灯_第4张图片

要想知道某个 IO 口的状态,你只要读这个寄存器,再看某个位的状态就可以了。使用起 来是比较简单的。

ODR 是一个端口输出数据寄存器,也只用了低 16 位。该寄存器为可读写,从该寄存器读 出来的数据可以用于判断当前 IO 口的输出状态。而向该寄存器写数据,则可以控制某个 IO 口 的输出电平。该寄存器的各位描述如图所示: 

 STM32F103寄存器方式点亮LED流水灯_第5张图片

了解了这几个寄存器,我们就可以开始流水灯实验的真正设计了。 

二、硬件连接设计

根据题目要求,使用GPIOB,GPIOC,GPIOD端口来控制LED灯,在查询STM32数据手册后,我选用了PB6,PC6,PD2管脚分别连接红绿蓝三种颜色的灯(由于我只有红绿黄三个小灯,在之后实际硬件中,会用黄灯代替蓝灯)

三、软件设计

3.1配置IO口 

 我们首先需要配置IO口,这里我们采用的配置方法是使用寄存器。通过配置寄存器的值,来改变IO口的值进行变化。

 led.h

#ifndef __LED_H
#define __LED_H	 
#include "sys.h"
   
//LED端口定义
#define LED0 BIT_ADDR(GPIOB_ODR_Addr,6)   // PB6输出
#define LED1 BIT_ADDR(GPIOC_ODR_Addr,6)	  // PC6输出
#define LED2 BIT_ADDR(GPIOD_ODR_Addr,2)	  // PD2输出
 
void LED_Init(void);	//初始化		 				    
#endif
 

led.c

#include "sys.h"   
#include "led.h"
 
//初始化PB6、PC6和PD2为输出口,并使能这3个口的时钟
//LEDIO初始化
void LED_Init(void)
{
	RCC->APB2ENR|=1<<3;    //使能PORTB时钟
	RCC->APB2ENR|=1<<4;    //使能PORTC时钟
	RCC->APB2ENR|=1<<5;    //使能PORTD时钟
	
	GPIOB->CRL&=0XF0FFFFFF; //PB6清零
	GPIOB->CRL|=0X03000000; //PB6推挽输出  	 
    GPIOB->ODR|=1<<6;       //PB6输出高
	
	GPIOC->CRL&=0XF0FFFFFF; //PC6清零
	GPIOC->CRL|=0X03000000; //PC6推挽输出
	GPIOC->ODR|=1<<6;      //PC6输出高
	
	GPIOD->CRL&=0XFFFFF0FF; //PD2清零
	GPIOD->CRL|=0X00000300;//PD2推挽输出	 
    GPIOD->ODR|=1<<2;      //PD2输出高
											  
}
 

 3.2编写主函数

 test.c

#include "sys.h"		
#include "led.h" 
 
void delay(unsigned int i)   //简单延时函数
{
	unsigned char j;               
	unsigned char k;
	for(;i>0;i--)
		for(j=500; j>0; j--) 
	       for(k =200; k>0; k--);
}
 
int main(void)
{		 
 
	LED_Init();		  	 	//初始化与LED连接的硬件接口   
	while(1)
	{
		
		LED0=0;  //灯亮
		LED1=1;  //灯灭
		LED2=1;
		delay(20);  //延时
		LED0=1;
		LED1=0;
		LED2=1;
		delay(20);
	    LED0=1;
		LED1=1;
		LED2=0;
		delay(20);
		
	}	 
}
 

 3.3烧录:STM32与PC端连接

借助FLYMCU下载软件,即可将test.hex载入,软件资料同样可在网盘下载。这里我利用FlyMcu进行。

STM32F103寄存器方式点亮LED流水灯_第6张图片

3.4效果演示

 

四、汇编语言实现 

代码:

RCC_APB2ENR EQU 0x40021018
 
GPIOA_CRH EQU   0x40010804
GPIOA_ODR EQU   0x4001080C
                                    
GPIOB_CRL EQU   0x40010C00    ;寄存器映射
GPIOB_ODR EQU   0x40010C0C	
	
GPIOC_CRH EQU   0x40011004
GPIOC_ODR EQU   0x4001100C	
	
	
Stack_Size      EQU     0x00000400
 
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
					;NOINIT: = NO Init,不初始化。READWRITE : 可读,可写。ALIGN =3 : 2^3 对齐,即8字节对齐。
Stack_Mem       SPACE   Stack_Size
__initial_sp
 
                AREA    RESET, DATA, READONLY
 
__Vectors       DCD     __initial_sp               
                DCD     Reset_Handler              
                    
                    
                AREA    |.text|, CODE, READONLY
                    
                THUMB
                REQUIRE8
                PRESERVE8
                    
                ENTRY
Reset_Handler 
		
                
MainLoop		BL LED2_Init
                BL LED2_ON
                BL Delay             ;LED2灯闪烁
                BL LED2_OFF
                BL Delay
				
				BL LED1_Init				
				BL LED1_ON
                BL Delay             ;LED1灯闪烁
                BL LED1_OFF
                BL Delay
				
                BL LED3_Init				
				BL LED3_ON
                BL Delay            ;LED3灯闪烁
                BL LED3_OFF
                BL Delay
				
                B MainLoop
				
             
LED1_Init
                PUSH {R0,R1, LR}        ;R0,R1,LR中的值放入堆栈
                
                LDR R0,=RCC_APB2ENR      ;LDR是把地址装载到寄存器中(比如R0)。
                ORR R0,R0,#0x08         ;开启端口GPIOB的时钟,ORR 按位或操作,01000将R0的第二位置1,其他位不变		
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]             ;STR是把值存储到寄存器所指的地址中,将r0里存储的值给rcc寄存器
				;上面一部分汇编代码是控制时钟的
                
                
                LDR R0,=GPIOB_CRL
                ORR R0,R0,#0X00000020   ;GPIOB_Pin_1配置为通用推挽输出;开启的是pb1,所以是2,为0010,是推挽输出模式,最大速度为2mhz
                LDR R1,=GPIOB_CRL
                STR R0,[R1]
                
                LDR R0,=GPIOB_ODR   
                BIC R0,R0,#0X00000002      ;BIC 先把立即数取反,再按位与
                LDR R1,=GPIOB_ODR          ;GPIOB_Pin_1输出为0;由r1控制ord寄存器
                STR R0,[R1]                 ;将ord寄存器的值变为r0的值
             
                POP {R0,R1,PC}             ;将栈中之前存的R0,R1,LR的值返还给R0,R1,PC
 
 
             
LED1_OFF
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOB_ODR
                BIC R0,R0,#0X00000002    ;因为是PB1所以对应二进制0010;GPIOB_Pin_1输出为0,LED1熄灭
			    LDR R1,=GPIOB_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED1_ON
                PUSH {R0,R1, LR}    
                
                 LDR R0,=GPIOB_ODR
                ORR R0,R0,#0X00000002    ;GPIOB_Pin_1输出为1,LED1亮
                 LDR R1,=GPIOB_ODR
                STR R0,[R1]
                POP {R0,R1,PC}           
 
 
				
 
LED2_Init
                PUSH {R0,R1, LR};R0,R1,LR中的值放入堆栈
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x04		   ;打开GPIOA的时钟
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]                
                
                LDR R0,=GPIOA_CRH
                ORR R0,R0,#0X00020000   ;GPIOA_Pin_12配置为通用推挽输出
                LDR R1,=GPIOA_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOA_ODR
                BIC R0,R0,#0X00001000   
                LDR R1,=GPIOA_ODR            ;GPIOA_Pin_12输出为0
                STR R0,[R1]
             
                POP {R0,R1,PC}
				
LED2_OFF
                PUSH {R0,R1, LR}   
                
               LDR R0,=GPIOA_ODR
               BIC R0,R0,#0X00001000        ;GPIOA_Pin_12输出为0,LED2熄灭
			    LDR R1,=GPIOA_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED2_ON
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOA_ODR
                ORR R0,R0,#0X00001000     ;GPIOA_Pin_12输出为1,LED2亮
				 LDR R1,=GPIOA_ODR
                STR R0,[R1]
				
				 POP {R0,R1,PC}
				 
 
LED3_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x10		    ;打开GPIOC的时钟
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]                
                
                LDR R0,=GPIOC_CRH
                ORR R0,R0,#0X02000000   ;GPIOC_Pin_14配置为通用推挽输出
                LDR R1,=GPIOC_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOC_ODR
                BIC R0,R0,#0X00004000   ;GPIOC_Pin_14输出为0
                LDR R1,=GPIOC_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED3_OFF
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOC_ODR
                BIC R0,R0,#0X00004000  ;GPIOC_Pin_14输出为0,LED3熄灭
			    LDR R1,=GPIOC_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED3_ON
                PUSH {R0,R1, LR}    
                
                 LDR R0,=GPIOC_ODR
                ORR R0,R0,#0X00004000   ;GPIOC_Pin_14输出为1,LED3亮
                 LDR R1,=GPIOC_ODR
                STR R0,[R1]
				
                POP {R0,R1,PC}        
                
Delay
                PUSH {R0,R1, LR}
                
                MOVS R0,#0
                MOVS R1,#0
                MOVS R2,#0
                
DelayLoop0        
                ADDS R0,R0,#1
 
                CMP R0,#300
                BCC DelayLoop0
                
                MOVS R0,#0
                ADDS R1,R1,#1
                CMP R1,#300
                BCC DelayLoop0
 
                MOVS R0,#0
                MOVS R1,#0
                ADDS R2,R2,#1
                CMP R2,#15
                BCC DelayLoop0
                
                POP {R0,R1,PC}    
 
                END

 编译烧录方法同上,效果展示

 

五、参考文献  

【嵌入式08】STM32F103C8T6寄存器方式借助面包板点亮LED流水灯详解_噗噗的罐子博客-CSDN博客https://blog.csdn.net/qq_46467126/article/details/120791793STM32寄存器的简介、地址查找,与直接操作寄存器_geekYatao-CSDN博客_stm32寄存器https://blog.csdn.net/geek_monkey/article/details/86291377从零开始一起学stm32(一)---寄存器操作GPIO_李纳克斯的博客-CSDN博客寄存器操作GPIO口1.ARM 介绍2.开发板的介绍2.1软件安装2.2工程建立总线架构和时钟树GPIO口使用寄存器操作GPIO口作业:点亮LED灯1、ARM的介绍1.咱们今天学的是基于ARM cortex-m3的STM32微控制器这本书;首先,咱们要了解这几个名词的意思:ARM:是一个公司的名字---设计芯片内核架构的---设计CPU的cortex-...https://blog.csdn.net/qq_38639426/article/details/88625547

你可能感兴趣的:(stm32)