STM32-寄存器点亮LED

文章目录

一、STM32内部地址 

二、注意事项

2.1、启动文件

2.2、debug配置

三、LED点亮的原理

四、相关寄存器以及地址介绍

4.1、STM32系统架构

4.2、RCC_APB2ENR寄存器

4.3、GPIO寄存器

五、详细代码

六、Debug分步调试


一、STM32内部地址 

        这篇博客所用的单片机为STM32F103C8T6,属于Cortex-M3。Cortex-M3有32根地址线,所以它的寻址空间大小为232 bit=4 GB。ARM公司设计时,预先把这4 GB的寻址空间分配好了。它把从0x40000000至0x5FFFFFFFF(512 MB)的地址分配给片上外设。通过把片上外设的寄存器映射到这个地址区,就可以简单地以访问内存的方式,访问这些外设的寄存器,从而控制外设的工作。这样,片上外设可以使用C语言来操作。CM3存储器映射见图(1)。

图(1)

         现对这些地址系统地做一下说明:

        一、Code区,地址在0x0000 0000到0x1FFF FFFF之间。

用途说明地址范围
预留0x1FFEC008 ~ 0x1FFFFFFF
选项字节:用于配置读写保护、BOR级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位。当芯片不小心被锁住之后,可以从RAM里面启动来修改这部分相应的寄存器位0x1FFFF800 ~ 0x1FFFF80F
系统存储器:里面存的是ST出厂时烧写好的ISP自举程序(即Bootloader),用户无法改动。串口下载的时候需要用到这部分的程序。0x1FFFF000 ~ 0x1FFFF7FF
预留0x08080000 ~ 0x1FFFF7FF
FLASH:我们写的程序放在这里0x08000000 ~ 0x0807FFFF(512KB)
预留0x00080000 ~ 0x07FFFFFF
取决于BOOT引脚,为FLASH、系统存储器、SRAM的别名0x00000000 ~ 0x0007FFFF

        二、SRAM区,地址范围在0x20000000到0x3FFFFFFF之间。

用途说明地址范围
预留0x20010000 ~ 0x3FFFFFFF
SRAM(64KB)0x20000000 ~ 0x2000FFFF

        三、片上外设,这里主要介绍APB1、APB2和AHB总线,地址范围在0x40000000到0x5003FFFF之间。

用途说明地址范围
APB1总线外设0x40000000 ~ 0x400077FF
APB2总线外设0x40010000 ~ 0x40013FFF
AHB总线外设0x40018000 ~ 0x5003FFFF

二、注意事项

2.1、启动文件

图(2)

        注意启动文件里,那三行关于系统时钟配置的汇编代码要注释掉,因为在主函数中并没有配置系统时钟。如果没有注释掉,就会报图(3)中的错误注释掉后,STM32会把HSI当做系统时钟,HSI = 8 MHz,由芯片内部的振荡器提供。

图(3)

关于上面提到的启动文件那几行汇编代码的含义如下:

Reset_Handler PROC  ;Reset_Handler子程序开始

EXPORT Reset_Handler[WEAK] ;输出子程序Reset_Handler到外部文件

IMPORT__main ;从外部文件引入__main函数

IMPORT SystemInit ;从外部文件引入SystemInit函数

LDR R0,=SystemInit ;把SystemInit函数调用地址加载到通用寄存器R0

BLX R0 ;跳转到R0中保存的地址执行程序(调用SystemInit函数)

LDR R0,=__main ;把main函数调用地址加载到通用寄存器R0

BX R0 ;跳转到R0中保存的地址执行程序(调用main函数)

ENDP ;Reset_Handler子程序结束

 注:汇编语言中用 ;(分号 )  注释

2.2、debug配置

         在烟花棒->Target里面,有两个地方值得注意,一个地方就是System Viewer File中的路径,要选择STM32F103xx.SFR所在的路径,具体看个人安装的地方,我的是E:\software\Keil5\MDK\ARM\PACK\Keil\STM32F1xx_DFP\2.3.0\SVD\STM32F103xx.SFR;另一个地方就是Use Custom前面的√要打上。如果那两个地方没有配置好,图(6)Debug过程中Peripherals下面的菜单就不会出现,也就不能通过分步调试观察寄存器的变化。

图(4)

   

图(5)

  

图(6)

三、LED点亮的原理

        我所用的开发板中小灯连接的GPIO为GPIOC的第13引脚,如图(7)所示,所以给这个引脚低电平,LED就能点亮。

图(7)

   

四、相关寄存器以及地址介绍

4.1、STM32系统架构

         STM32的系统架构如图(8)所示,可以看到,GPIOC挂载在APB2总线下,所以要想GPIOC工作,首先要做的就是使能APB2。

图(8)

4.2、RCC_APB2ENR寄存器

        在STM32参考手册中可以看到APB2 外设时钟使能寄存器为RCC_APB2ENR,其偏移地址为0x18,这个偏移是相对于RCC寄存器的偏移,RCC寄存器的基址如图(9)所示,为0x4002 1000 ,所以 RCC_APB2ENR的地址就是0x4002 1018。

图(9)

         RCC_APB2ENR的每个位如图(10)所示,由于我们需要点亮的LED所使用的GPIO口为GPIOC,所以我们重点关注GPIOC,可以看到在第4位。由图(11)可以看到,将该位置1,就可以开启GPIOC的时钟。

图(10)

图(11)

4.3、GPIO寄存器

         与点亮LED相关的GPIO寄存器有两位,端口配置高寄存器(GPIOx_CRH)和端口输出数据寄存器(GPIOx_ODR)。

        因为STM32中一个寄存器只有32位,一个输出引脚占4位,所以一个寄存器中只能放8个引脚的数据。而一个GPIO下有16个引脚,所以就有端口配置低寄存器(GPIOx_CRL)和端口配置高寄存器(GPIOx_CRH)之分。而我所用的LED连接的为PC13,所以需要用端口配置高寄存器(GPIOx_CRH)。

        端口配置高寄存器(GPIOx_CRH)的偏移地址为0x04,而GPIOC的基地址为0x4001 1000,如图(12)所示,所以GPIOC_CRH的地址为0x4001 1004.。

图(12)

图(13)

图(14)

         我们需要将PC13配置成通用推挽输出模式,结合图(13)和图(14)可知,CNF13配置成00,MODE13配置成11即可。

         端口输出数据寄存器(GPIOx_ODR)的偏移地址为0x0C,而GPIOC的基地址为0x4001 1000,所以GPIOC_ODR的地址为0x4001 100C。

图(15)

图(16)

         由图(15)和图(16)可知,当我们把ODR13置0后,PC13就会输出低电平,LED也就会被点亮。

   

五、详细代码

typedef unsigned int u32;

#define PERIPH_BASE 0x40000000	//外设基址
#define GPIOC_BASE 0x40011000	//GPIOC的基址

#define APB2_BASE (PERIPH_BASE + 0x10000)	//APB2的基址
#define AHB_BASE (PERIPH_BASE + 0x20000) //AHB总线外设基地址,不同芯片AHB总线外设基地址可能不同
#define RCC_BASE (AHB_BASE + 0x1000)	//复位和时钟控制(RCC)的基址

#define RCC_APB2ENR (RCC_BASE + 0x18)	//RCC_APB2ENR寄存器的地址,作用是打开APB2外设时钟,0x18是其偏移RCC寄存器的地址

#define GPIOX_CRH_OFFSET 0x0004	//GPIOX_CRH寄存器的偏移地址
#define GPIOX_ORH_OFFSET 0x000C	//GPIOX_ORH寄存器的偏移地址

#define GPIOC_CRH (GPIOC_BASE + GPIOX_CRH_OFFSET)	//GPIOX_CRH寄存器的地址
#define GPIOC_ORH (GPIOC_BASE + GPIOX_ORH_OFFSET)	//GPIOX_CRH寄存器的地址

void delay(u32 x)	//差不多延时5秒
{
	u32 i = 0;
	while(x--)
	{
		i = 10000000;
		while(i--);
	}
}

int main(void)
{
	*((unsigned int *)RCC_APB2ENR) |= 0x00000010;	//打开APB2时钟
	
	//LED为PC13
	*((unsigned int *)GPIOC_CRH) &= 0xFF0FFFFF;	
	*((unsigned int *)GPIOC_CRH) |= 0x00300000;	//000000000 00110000 00000000 00000000,配置PC13为推挽输出,速度为50MHz 
	
	*((unsigned int *)GPIOC_ORH) &= ~(1 << 13);	//只需要清一位,不能直接像上面一样直接与,因为十六进制中一下改变4位
	*((unsigned int *)GPIOC_ORH) |= 0x00002000;	//默认关LED
	
	while(1)
	{
		//LED亮
		*((unsigned int *)GPIOC_ORH) &= ~(1 << 13);
		*((unsigned int *)GPIOC_ORH) |= 0x00000000;
		delay(1);
		
		//LED灭
		*((unsigned int *)GPIOC_ORH) &= ~(1 << 13);
		*((unsigned int *)GPIOC_ORH) |= 0x00002000;		
		delay(1);
	}
	
//	return 0;
}

        详细代码如上面所示,这里将几点注意问题:

        第一,就是AHB的基地址问题,很多地方都写了AHB的基址为0x40018000,其实这个要根据所使用的芯片而定。我所用的是STM32F103C8T6,AHB的基址则为0x40020000。具体的各个芯片AHB基址的对应关系,我也没能找到好的参考资料。其实在上面的程序写的有点复杂了。像APB2的基址为0x4001 0000,RCC的基址为0x4002 1000,这些在STM32参考手册里都能找到,不用自己根据偏移地址来算。

        关于AHB的基址,我也这么尝试过,就当偏移地址为0x18000。虽然定义AHB的基址是为了确定RCC的基址,为了使得RCC的基址和STM32参考手册中的地址一样,我想当然地把偏移地址设置成0x3000,最终RCC的基址也为0x4002 1000。我以为既然地址是对的,LED应该会被点亮,但事实上LED无论如何都没亮。于是我开始Debug,观察寄存器的变化,结果分步调试时跳到一段汇编代码后就卡死在那里,如图(17)所示。

        #define AHB_BASE (PERIPH_BASE + 0x18000) //AHB总线外设基地址,不同芯片AHB总线外设基地址可能不同
        #define RCC_BASE (AHB_BASE + 0x3000)    //复位和时钟控制(RCC)的基址

图(17)

  

        第二,关于*((unsigned int *)RCC_APB2ENR),这里做下解释。虽然 RCC_APB2ENR本身是一个地址,但是我们的编译器却不认识,所以需要(unsigned int *)把RCC_APB2ENR强制转换成 unsigned int * 类型的指针。学过C语言的都知道,在指针变量外面加一个 *,就可以访问里面所存的内容。这样,通过这条语句,我们就可以操作RCC_APB2ENR寄存器,改变里面的内容。

        第三,当我们操作寄存器里面相关的某几位或者某一位时,要善于使用&、|、<< 和 >>操作。例如在上面的程序中,我们操作GPIOC_CRH寄存器时,由于一下改变4位,先&上 0xFF0FFFFF,将里面的4位清除,同时不改变其他位,再|上0x00300000,这样就把值写进去。对于GPIOC_ORH,由于只改变1位,所以不能像前面那样操作,可以&上 ~(1 << 13),这样就清除了第13位里面的内容,同时也不影响其他位,然后|上0x00000000,就把LED点亮了。

六、Debug分步调试

        Debug中的分步调试也属于STM32中比较重要的一个部分,通过观察寄存器的变化,可以帮助我们快速定位问题,这里通过点亮LED操作,简单说一下Dubug的步骤。

        首先我们打两个断点,如图(18)所示。然后在Peripherals->System Viewer里面选择GPIOC和RCC寄存器,如图(19)所示。

图(18)

图(19)

         然后我们点击RST、Run 、step over,开始运行程序。首先打开RCC寄存器,在APB2ENR那里可以看到IOPCEN打了一个√,这表明IOPCEN已经置1,GPIOC的时钟开启,如图(21)所示。继续运行,发现CRH寄存器中的Mode13变成0x03,说明PC13已经被设置成通用推挽输出模式,速率为50MHz,如图(22)所示。继续运行,发现ODR寄存器中的ODR13前面打了一个√,说明ODR13输出高电平,如图(23)所示。由于我所用LED为低电平点亮,所以此时LED不会亮,我们把√去掉后发现LED就亮了!

图(20)

图(21)

  

图(22)

图(23)

  

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清风自在 流水潺潺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值