ARM架构与编程

本文详细解释了CPU内部寄存器与外部设备寄存器的区别,以及在STM32F103中段的概念,包括代码段、只读数据段、可读可写数据段和BSS段。着重介绍了重定位在处理全局变量和代码移动中的作用,如数据段重定位和代码重定位的过程。

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

1. 寄存器的含义:寄存器分CPU内部寄存器,和设备上的寄存器,它们完全不同

我们说"寄存器"时,需要分辨是哪种:

        CPU内部的寄存器

        CPU之外设备自己的寄存器

这是不一样的:

        CPU通过各类汇编指令访问内部寄存器

        CPU要访问设备的寄存器时,就像访问内存一样,要先知道地址

举例:

外部寄存器的地址是0x1000,要读它,使用如下汇编指令:

mov r0, #0x1000  //把外部寄存器地址赋给CPU内部寄存器r0

ldr r0, [r0]  //使用r0所表示的地址来读数据,存入CPU内部寄存器r1

2.段的概念_重定位的引入

从源头理解代码重定位,现在从*.s开始写程序,使用串口打印全局变量,看看会发生什么事情。

start.s

                PRESERVE8
                THUMB


; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
				EXPORT  __Vectors
					
__Vectors       DCD     0                  
                DCD     Reset_Handler              ; Reset Handler

				AREA    |.text|, CODE, READONLY

; Reset handler
Reset_Handler   PROC
				EXPORT  Reset_Handler             [WEAK]
                IMPORT  main

				LDR SP, =(0x20000000+0x10000)
				BL main

                ENDP
                
                 END

string.c

#include "uart.h"
int puts(const char *s)
{
	while (*s)
	{
		putchar(*s);
		s++;
	}
	return 0;
}

void puthex(unsigned int val)
{
	/* 0x76543210 */
	int i, j;

	puts("0x");
	for (i = 7; i >= 0; i--)
	{
		j = (val >> (i*4)) & 0xf;
		if ((j >= 0) && (j <= 9))
			putchar('0' + j);
		else
			putchar('A' + j - 0xA);
	}	
}

void put_s_hex(const char *s, unsigned int val)
{
	puts(s);
	puthex(val);
	puts("\r\n");
}

main.c

#include "string.h"

char g_Char = 'A';
const char g_Char2 = 'B';
int g_A = 0;
int g_B;

int main()
{
	
	put_s_hex("g_Char's addr  = ", &g_Char);  //g_Char's addr  = 0x20000000
	put_s_hex("g_Char2's addr = ", &g_Char2); //g_Char2's addr  = 0x080002EC
	
	putchar(g_Char); //打印出乱码
	putchar(g_Char2); //打印出B
	
	while (1)
	{
	}
	
	return 0;
}

运行结果:

    put_s_hex("g_Char's addr  = ", &g_Char);  //g_Char's addr  = 0x20000000
    put_s_hex("g_Char2's addr = ", &g_Char2); //g_Char2's addr  = 0x080002EC
    
    putchar(g_Char); //打印出乱码
    putchar(g_Char2); //打印出B

全局变量char g_Char = 'A'; g_Char是可读可写的,数值会变,所以放在RAM。乱码的原因是因为g_Char最开始数据是放在RAM上的,在main()中无人初始化它,所以打印出来是乱码。

程序使用该地址(g_Char's addr  = 0x20000000)访问时,就可以访问到RAM,无人赋值,打印出来的时随机值,所以是乱码。


全局变量const char g_Char2 = 'B'; g_Char2是常量,数值不会变,所以放在ROM。成功打印的原因是:*.bin文件被烧写在ROM中时,ROM里面已经有了g_Char2的数值,虽然在main()中无人初始化它,但是g_Char2的数值已经烧写在ROM中了,所以打印出来是B。

程序使用该地址(g_Char2's addr  = 0x080002EC)访问时,就可以访问里面的数据B。


基于上面的问题,g_Char应该有人初始化,谁来做?应该由*.s来做。

设置好栈之后,在调用main()函数之前,*.s还应该做一些事情,比如ROM上有数据、有代码,所以需要把ROM上的数据、代码重定位到RAM上去。

因为代码可以放在ROM中,对于可读可写的数据,需要重定位到RAM中去。

例如g_Char这一类,统一放在一个统计,为可读可写的数据段。

例如g_Char2这一类,统一放在一个统计,为只读的数据段。


段的概念_重定位的引入(STM32F103)

1. 问题的引入

在串口程序中添加全局变量,把它打印出来,看看会发生什么事。

2. 段的概念

代码段、只读数据段、可读可写的数据段、BSS段。

char g_Char = 'A';           // 可读可写,不能放在ROM上,应该放在RAM里
const char g_Char2 = 'B';    // 只读变量 ,可以放在ROM上
int g_A = 0;   // 初始值为0,干嘛浪费空间保存在ROM上?没必要
int g_B;       // 没有初始化,干嘛浪费空间保存在ROM上?没必要

所以,程序分为这几个段:

  • 代码段(RO-CODE):就是程序本身,不会被修改

  • 可读可写的数据段(RW-DATA):有初始值的全局变量、静态变量,需要从ROM上复制到内存

  • 只读的数据段(RO-DATA):可以放在ROM上,不需要复制到内存

  • BSS段或ZI段:

    • 初始值为0的全局变量或静态变量,没必要放在ROM上,使用之前清零就可以

    • 未初始化的全局变量或静态变量,没必要放在ROM上,使用之前清零就可以

  • 局部变量:保存在栈中,运行时生成

  • 堆:一块空闲空间,使用malloc函数来管理它,malloc函数可以自己写

3. 重定位

保存在ROM上的全局变量的值,在使用前要复制到内存,这就是数据段重定位。

想把代码移动到其他位置,这就是代码重定位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值