STM32F103C8T使用寄存器方式点亮流水灯

本文详细介绍了STM32 GPIO端口的初始化步骤,包括时钟配置、输入输出模式设置和最大速率设置,并分别用C语言和汇编语言实现了流水灯功能。通过配置RCC和GPIO寄存器,控制LED灯的亮灭,展示了C语言和汇编语言在微控制器编程中的应用。
摘要由CSDN通过智能技术生成

一、GPIO端口的初始化设置:时钟配置、输入输出模式设置、最大速率设置

    1、因为流水灯要操作的引脚都是在GPIO端口的,所以根据系统结构图,属于AHB总线,所以所要用的端口的复位和时间控制都受RCC控制。

在这里插入图片描述

    2、跳到这里,就是外设时钟使能寄存器,,偏移量为0x18,而在前面一个表可以看到起始地址为0x4002 1000,偏移量为0x18,所以该寄存器的地址为0x4002 1018

在这里插入图片描述
    3、然后就是配置端口配置寄存器了,这个是非常关键的,可以发现上面的时钟使能寄存器开启时钟是针对一个区域的,并不能确定引脚,而这个寄存器就是确定引脚的,端口配置寄存器有两个,分别为端口配置低寄存器(CRL)和端口配置高寄存器(CRH),每四位配置一个端口,如11 01,11就是选择开启功能,01就是选择模式和确定最大速度,但有一点不一样,低寄存器的偏移地址为0x00,高寄存器的偏移地址为0x04
在这里插入图片描述
在这里插入图片描述
    4、以PC15为示例,相应端口配置器GPIOA_CRL地址为GPIOA的基址+上偏移量,为0x40011004``,而这个端口要开启,所以要使对应位为相应的值,我这里是0x30000000,设置推挽输出并设置最大速度为2Mhz,下面为相应代码

#define GPIOC_CRL	*((unsigned volatile int*)0x40011004)
GPIOC_CRL=0x30000000;		//PC15推挽输出,2Mhz

    5、接下来就是配置端口输出寄存器(ORD),可以看到偏移量为0xc,所以该寄存器的地址等于端口的基址加上偏移量,在相应的位赋值可以控制输出电压,0为低电压,1为高电压,以PA7引脚为例子,想要输出高电压,就需要在第八位赋1

#define	GPIOA_ORD	*((unsigned volatile int*)0x4001080C)
GPIOA_ORD|=1<<7;			//设置初始灯为亮

二、C语言实现流水灯

    1、使用keil创建一个新工程,不会可参考:基于MDK的stm32的C语言程序汇编
    2、build编译程序在这里插入图片描述
    3、使用mcuisp软件将程序烧录到最小版上面,先选择编译生成的.hex文件,然后点击开始编译,也可以先读取器件信息再编译在这里插入图片描述

    4、烧录成功之后如图接好线就可以点亮LED灯了在这里插入图片描述
    5、运行结果
在这里插入图片描述

三、汇编语言实现LED灯点亮

    1、新建一个工程,不要勾选Startup,否则会程序报错
    2、编入汇编代码

RCC_APB2ENR EQU 0x40021018;配置RCC寄存器,时钟,0x40021018为时钟地址
GPIOC_CRH EQU 0x40011004;配置GPIOC_CRH寄存器,是端口配置高寄存器,高位的偏移地址为0x04 
GPIOC_ORD EQU 0x4001100c;配置GPIOC_ORD寄存器,是端口输出寄存器,输出由这里控制
GPIOA_CRL EQU 0x40010800;配置GPIOC_CRH寄存器,是端口配置高寄存器,高位的偏移地址为0x04 
GPIOA_ORD EQU 0x4001080C;配置GPIOC_ORD寄存器,是端口输出寄存器,输出由这里控制
GPIOB_CRH EQU 0x40010C04;配置GPIOC_CRH寄存器,是端口配置高寄存器,高位的偏移地址为0x04 
GPIOB_ORD EQU 0x40010C0C;配置GPIOC_ORD寄存器,是端口输出寄存器,输出由这里控制
Stack_Size EQU  0x00000400;栈的大小
;分配一个stack段,该段不初始化,可读写,按8字节对齐。分配一个大小为Stack_Size的存储空间,并使栈顶的地址为__initial_sp。
                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               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                    
                    
                AREA    |.text|, CODE, READONLY
                    
                THUMB
                REQUIRE8
                PRESERVE8
                    
                ENTRY
Reset_Handler 
				bl LED_Init;bl:带链接的跳转指令。当使用该指令跳转时,当前地址(PC)会自动送入LR寄存器
MainLoop        BL LED_ON_C
                BL Delay
                BL LED_OFF_C
                BL Delay
				BL LED_ON_A
                BL Delay
                BL LED_OFF_A
                BL Delay
				BL LED_ON_B
                BL Delay
                BL LED_OFF_B
                BL Delay
                
                B MainLoop;B:无条件跳转。
LED_Init;LED初始化
                PUSH {R0,R1, LR};R0,R1,LR中的值放入堆栈
                
                LDR R0,=RCC_APB2ENR;LDR是把地址装载到寄存器中(比如R0)。
                ORR R0,R0,#0x1c;ORR 按位或操作,11100将R0的第二位置1,其他位不变
                LDR R1,=RCC_APB2ENR
                STR R0,[R1];STR是把值存储到寄存器所指的地址中,将r0里存储的值给rcc寄存器
				;上面一部分汇编代码是控制时钟的
				
				
                ;初始化GPIOA部分
                LDR R0,=GPIOA_CRL
                BIC R0,R0,#0x0fffffff;BIC 先把立即数取反,再按位与
                LDR R1,=GPIOA_CRL
                STR R0,[R1]
                ;上面的代码是初始化CPIOC_CRH
                LDR R0,=GPIOA_CRL
                ORR R0,#0x20000000;开启的是pc15,所以是2,为0100,是推挽输出模式,最大速度为2mhz
                LDR R1,=GPIOA_CRL
                STR R0,[R1]
				;GPIOC的端口配置高寄存器配置完毕,也就是CPIOA_CRH配置完成,端口的输出模式确定,不使用的都设为复位后的状态,为0100,所以上面处理输出为都是4
                ;将PC15置1
                MOV R0,#0x80; 二进制为0b1000 0000 ,7位就是a7引脚的输出电压
                LDR R1,=GPIOA_ORD ;由r1控制ord寄存器
                STR R0,[R1] ;将ord寄存器的值变为r0的值
				
				 ;初始化GPIOB部分
                LDR R0,=GPIOB_CRH
                BIC R0,R0,#0xffffff0f;BIC 先把立即数取反,再按位与,用的是b9,所以把第二位置零
                LDR R1,=GPIOB_CRH
                STR R0,[R1]
                ;上面的代码是初始化CPIOC_CRH
                LDR R0,=GPIOB_CRH
                ORR R0,#0x00000020;开启的是pc15,所以是2,为0100,是推挽输出模式,最大速度为2mhz
                LDR R1,=GPIOB_CRH
                STR R0,[R1]
				;GPIOC的端口配置高寄存器配置完毕,也就是CPIOA_CRH配置完成,端口的输出模式确定,不使用的都设为复位后的状态,为0100,所以上面处理输出为都是4
                ;将PC15置1
                MOV R0,#0x200; 二进制为0b10 0000 0000,16位就是b9引脚的输出电压
                LDR R1,=GPIOB_ORD ;由r1控制ord寄存器
                STR R0,[R1] ;将ord寄存器的值变为r0的值
				
				 ;初始化GPIOC部分
                LDR R0,=GPIOC_CRH
                BIC R0,R0,#0x0fffffff;BIC 先把立即数取反,再按位与,就是将三十二位全部置零
                LDR R1,=GPIOC_CRH
                STR R0,[R1]
                ;上面的代码是初始化CPIOC_CRH
                LDR R0,=GPIOC_CRH
                ORR R0,#0x20000000;开启的是pc15,所以是2,为0100,是推挽输出模式,最大速度为2mhz
                LDR R1,=GPIOC_CRH
                STR R0,[R1]
				;GPIOC的端口配置高寄存器配置完毕,也就是CPIOA_CRH配置完成,端口的输出模式确定,不使用的都设为复位后的状态,为0100,所以上面处理输出为都是4
                ;将PC15置1
                MOV R0,#0x8000; 二进制为0b1000 0000 0000 0000,16位就是c15引脚的输出电压
                LDR R1,=GPIOC_ORD ;由r1控制ord寄存器
                STR R0,[R1] ;将ord寄存器的值变为r0的值
             
                POP {R0,R1,PC};将栈中之前存的R0,R1,LR的值返还给R0,R1,PC
LED_ON_A;亮灯
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x00 ;二进制为0b0000 0000 0000 0000,第16位为0,后面将作为pc15的输出电压
                LDR R1,=GPIOA_ORD ;将GPIOC的地址赋予r1
                STR R0,[R1];将r0的值赋予在GPIOC_ORD中
             
                POP {R0,R1,PC}
             
LED_OFF_A;熄灯
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x80 ;二进制为0b 1000 0000 0000 0000,第16位为1,后面将作为pc15的输出电压
                LDR R1,=GPIOA_ORD ;将GPIOC的地址赋予r1
                STR R0,[R1] ;[]是指对里面的地址操作,所以是将r0的值赋予GPIOC_ORD
             
                POP {R0,R1,PC}  
LED_ON_B;亮灯
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x000 ;二进制为0b0000 0000 0000 0000,第16位为0,后面将作为pc15的输出电压
                LDR R1,=GPIOB_ORD ;将GPIOC的地址赋予r1
                STR R0,[R1];将r0的值赋予在GPIOC_ORD中
             
                POP {R0,R1,PC}
             
LED_OFF_B;熄灯
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x200 ;二进制为0b 1000 0000 0000 0000,第16位为1,后面将作为pc15的输出电压
                LDR R1,=GPIOB_ORD ;将GPIOC的地址赋予r1
                STR R0,[R1] ;[]是指对里面的地址操作,所以是将r0的值赋予GPIOC_ORD
             
                POP {R0,R1,PC}  
LED_ON_C;亮灯
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x0000 ;二进制为0b0000 0000 0000 0000,第16位为0,后面将作为pc15的输出电压
                LDR R1,=GPIOC_ORD ;将GPIOC的地址赋予r1
                STR R0,[R1];将r0的值赋予在GPIOC_ORD中
             
                POP {R0,R1,PC}
             
LED_OFF_C;熄灯
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x8000 ;二进制为0b 1000 0000 0000 0000,第16位为1,后面将作为pc15的输出电压
                LDR R1,=GPIOC_ORD ;将GPIOC的地址赋予r1
                STR R0,[R1] ;[]是指对里面的地址操作,所以是将r0的值赋予GPIOC_ORD
             
                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,#330
                BCC DelayLoop0
                
                MOVS R0,#0
                ADDS R1,R1,#1
                CMP R1,#330
                BCC DelayLoop0 ;无进位

                MOVS R0,#0
                MOVS R1,#0
                ADDS R2,R2,#1
                CMP R2,#15
                BCC DelayLoop0
                
                
                POP {R0,R1,PC}    
                NOP
				END

    3、编译和烧录:过程C语言的一样
    4、运行效果在这里插入图片描述
参考博客:STM32F103C8T6实现流水灯

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答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、付费专栏及课程。

余额充值