STM32F103的流水灯点亮版本1(寄存器地址操作)

本文详细介绍了STM32F103微控制器中如何通过寄存器操作控制GPIO,实现流水灯效果,包括时钟配置、GPIO端口配置和输出控制。内容涵盖了C语言和汇编语言的实例,突出了底层寄存器操作的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、STM32简介 STM32,从字面上来理解,ST 是意法半导体,M 是 Microelectronics 的缩写,32 表示32 位,合起来理解,STM32 就是指 ST 公司开发的 32 位微控制器。在如今的 32 位控制器当中,STM32 可以说是最璀璨的新星,它受宠若娇,大受工程师和市场的青睐,无芯能出其右。

STM32 属于一个微控制器,自带了各种常用通信接口,比如 USART、I2C、SPI 等,可接非常多的传感器,可以控制很多的设备。现实生活中,我们接触到的很多电器产品都有 STM32 的身影,比如智能手环,微型四轴飞行器,平衡车、移动 POST 机,智能电饭锅,3D 打印机等等。

STM32 有很多系列,可以满足市场的各种需求,从内核上分有 Cortex-M0、M3、M4和 M7 这几种,每个内核又大概分为主流、高性能和低功耗。

二.使用通过寄存器点灯原理介绍。 (ps : stm32f1中文文档我在网上找到在这里下载的:

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

再看寄存器组起始地址表,可以看到RCC的地址范围,且可以看到要控制的寄存器都是在APB2总。

从上面发现复位和时钟控制的起始地址为0x4002 1000,

再翻到这里发下偏移量为0x18,所以该寄存器的地址为0x4002 1018

上图表格表示的寄存器里各位的含义,比如第三位也就是2那个位置为1时,就是GPIOA的时钟开启了。这时我们就可以用代码表达出来了。

接下来就是配置端口配置寄存器了,

这个就比较关键了,可以发现上面的时钟使能寄存器开启时钟是针对一个区域的,并不能确定引脚,

而这个寄存器就是确定引脚的,端口配置寄存器有两个,分别为端口配置低寄存器(CRL)和端口配置高寄存器(CRH)。

每四位配置一个端口,如11 01,11就是选择开启功能,01就是选择模式和确定最大速度,但有一点不一样,低寄存器的偏移地址为0x00,高寄存器的偏移地址为0x04。

我们翻到这里

以PA7为示例,相应端口配置器GPIOA_CRL地址为GPIOA的基址+上偏移量

在开头第二张图的那个位置可以找到GPIOA的地址为0x40010800, 低寄存器偏移如上图所示为0

以PA7口为输出口为例,就是要改变的是红色方框里的内容,

根据它下方表格里的内容得知要将他设置为推挽输出,输出模式最大速度为2MHz就是将它赋为0010,转换进制就是2(ps:我个人理解,错了的话希望大佬指正)

所以即将地址0x40010800的最前头一位赋为2,代码如下

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

这里就可以控制led亮或者灭了,实现流水灯只需增加灯的数量和增加一些延时就行了。

三.使用C语言实现。 1.创建一个新的项目。选择自己对应的板子,注意后面需要勾选上core 以及startup

添加一个main.c并写入代码,注意项目结构,使用的引脚是PA7,PB9,PC15,同时如果灯不闪烁,程序没有正常运行,可以先试试仿真调试,仿真调试正常了一般在板子上运行就正常了

//--------------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|=0x20000000; //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); } }

将三个led灯接到PC15,PB9,PA7上,通过keil软件以及STlink将程序烧入其中。结果如下所示

四.通过汇编语言实现 1.新建一个工程,步骤和上面一个差不多,不过不选择startup,选了会有错误,烧录过程也是一致

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

烧录过程一致,小灯展示结果也同上方一样

实验总结:

  1. STM32F103的寄存器地址操作指的是直接操作芯片内部的寄存器,通过改变寄存器的值来实现控制器的功能。

  1. 实验中的流水灯是最基本的实验项目之一,通过控制GPIO口的输出状态来控制LED的亮灭,实现流水灯的效果。

  1. STM32F103芯片的GPIO口有多个,每个GPIO口有相应的寄存器用于配置和控制。

  1. 在实验中,首先需要配置GPIO口为输出模式,通过设置GPIO口的寄存器值来实现。

  1. 配置完成后,可以通过设置GPIO口的寄存器值来控制LED的亮灭状态,即点亮或熄灭LED。

  1. 为了实现流水灯的效果,可以使用循环结构控制LED的亮灭状态,从而形成流水灯的效果。

  1. 寄存器地址操作相比其他操作方式更加底层,可以直接控制硬件,效率较高,但需要对硬件的寄存器和功能有较深的理解。

  1. 在进行寄存器地址操作时,需要注意寄存器的地址和寄存器的位域的定义,以及操作寄存器时的读写顺序和操作的正确性。

总之,通过实验可以了解到STM32F103的寄存器地址操作的基本方法和流程,并且可以利用寄存器

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值