/* ========== start.s 启动汇编 ========== */
.global _start //声明_start为全局标号,让链接器找到入口
_start:
ldr sp, =0xFFFF0000 //设置指针,c语言函数调用要栈,开辟空间。
bl main //跳转到C语言的main函数,bl保存返回地址
halt:
b halt //如果main意外返回,就原地死循环。
/* ========== led.c 主C代码 ========== /
目标:控制引脚,驱动LED去闪烁,就是所谓的点灯实验
// 简易输出IO寄存器
#define GPIOEOUTENB ((volatile unsigned int )0xC001E004) //输出使能寄存器(总电闸)(强制转化为无限制的指针,相当于直接读取寄存器的地址)(原意:通常输入输出使能,是一个总开关的意思)
#define GPIOEOUT ((volatile unsigned int )0xC001E000) //输出电平寄存器(点灯开关)(电平相当于电压的平衡状态比较量,这个量高相当于电压均衡,只有较小的电压差,电流就少,灯就暗。反之,电平小,状态不平衡,电压差大,电流大,灯就亮,从而实现点灯这个环节)(英文原意:通常输入输出,是一种相当于动作的东西)
//软件延时
void delay(void) //停几秒,让肉眼看见灯光
{
volatile unsigned int i = 0xf00000;//这里用了16进制,i其实是15728640
for(; i > 0; i–);//它的含义是每次循环都要CPU去计算消耗时间,通过这个间隙实现亮光的延时
}
int main(void)
{
// 开启bit13引脚输出使能(相当于给13号灯泡放点)
GPIOEOUTENB |= (1U << 13);//1U是无符号的整形1,不是地址,把1向左移动13位,对应bit13
//无限循环闪烁(我改了!改成一直闪,方便实验观察,1是高电平,0是底电平)
while (1) //这个就是永远闪烁的原因,永远是对的,永远出不了循环
{
GPIOEOUT &= ~(1U << 13); //输出低,LED亮,把13号该1,其余该0
delay();
GPIOEOUT |= (1U << 13); //输出高,LED灭,把13号该0,其余该1
delay();
}
}
/ ========== led.lds 链接脚本 ========== */
SECTIONS //规定代码布置和地址存放
{
. = 0xC0000000; //运行的初始地址
.text : {//用于存放c和汇编语言
*(.text) //收集全部.test合并在一起
}
.rodata : {//用于存放数据 *(.rodata) //用于储存常量
}
.data : {
*(.data) //用于存已经初始化的变量
}
.bss : {
*( .bss )//让还没初始化的变量不占据bin的文件空间的指令
}
}
========== 交叉编译命令 ==========
arm-linux-gnueabihf-as start.s -o start.o //把启动汇编的代码编译成statr.o这个目标文件
arm-linux-gnueabihf-gcc -c led.c -o led.o -nostdlib//用来编译C文件
arm-linux-gnueabihf-ld start.o led.o -T led.lds -o led.elf //把它变成可编译的文件
arm-linux-gnueabihf-objcopy -O binary led.elf led.bin //格式转化:转成纯bin文件,只有这样开发板才能执行。
对前文的形象类比
global _start @ 声明_start为全局标号,链接器可以找到这个入口
_start:
ldr sp,=0x40000000 @ 设置栈指针sp:拿到门牌号,开辟一块栈的房间。C语言调用函数、局部变量全都要放在这个房间里,没有栈C代码直接崩溃。
bl main @ bl跳转去main。bl会提前把返回地址(房卡)存到LR链接寄存器,意思:等main执行完,如果它return回来,就拿着房卡回到这里。
halt:
b halt @ 原地死循环。
b halt :单纯无条件跳转,不会保存房卡,一直跳回自己,卡在原地不动。
- 裸机设计:main本就不该返回。万一代码异常、main返回了,就跑到halt,原地卡住,防止CPU跑飞乱执行未知内存的垃圾代码。
一句话区分 bl 和 b
- bl 标签 :跳转 + 存好回家的房卡(LR),函数调用专用
- b 标签 :单纯跳转,不留房卡,死循环、单纯跳转用
补充你的小误区
“C语言只是中间过程,最后再转回汇编,相当于退房卡”
修正:不是C主动退房卡。C的 return 指令,硬件自动读取LR里面预先存好的房卡,跳回汇编。C本身不知道汇编的存在
7138



被折叠的 条评论
为什么被折叠?



