1 C语言控制LED
前述汇编中,写地址0x56000050(GPFCON)和0x56000054(GPFDAT),相当于C中的指针操作。
void main(void)
{
unsigned int * pGPFCON = 0x56000050;
unsigned int * pGPFDAT = 0x56000054;
/* 配置GPF4为输出引脚 */
*pGPFCON = 0x100;
/* 设置GPF4输出0 */
*pGPFDAT = 0;
}
问题来了。1 我们写出了main函数,谁来调用它? 2 main函数中的变量,保存在内存中,内存地址是多少?
解决办法:我们还需要写一个汇编代码,给main函数设置内存,调用main函数
代码如下:
led.c
int main(void)
{
unsigned int * pGPFCON = (unsigned int *)(0x56000050);
unsigned int * pGPFDAT = (unsigned int *)(0x56000054);
/* 配置GPF4为输出引脚 */
*pGPFCON = 0x100;
/* 设置GPF4输出0 */
*pGPFDAT = 0;
return 0;
}
start.S
.text
.global _start
_start:
/* 设置内存: sp栈 */
ldr sp, =4096 /* nand启动 */
// ldr sp, =0x40000000 + 4096 /* nor启动 */
/* 调用main */
bl main
halt:
b halt
Makefile
all:
arm-linux-gcc -c -o led.o led.c
arm-linux-gcc -c -o start.o start.S
arm-linux-ld -Ttext 0 start.o led.o -o led.elf
arm-linux-objcopy -O binary -S led.elf led.bin
arm-linux-objdump -D led.elf > led.dis
clean:
rm *.bin *.o *.elf *.dis
汇编指令中,bl跳转和b的区别(bl会在lr寄存器中,保存当前pc - 4,就是当前指令的下一条指令):
2 C语言分析
几条汇编指令:
2.1 add r0, r1, r2 或 add r0, r1, #4
2.2 sub r0, r1, r2 或 sub r0, r1, #4
3 ldm stm 传送多个数据
按照图示,堆栈操作中,常使用的是ldmia(先读取栈数据,再增加栈地址)和stmdb(先减少栈地址,然后保存到栈中)。
如图所示,stmdb sp!, {fp, ip, lr, pc},大括号里面的寄存器顺序可以随便写,无所谓。
假设sp = 4096:(1)sp=sp-4 pc寄存器的值,被保存在4092~4095内存中;(2)sp=sp-4;lr寄存器的值被保存在4088~4091内存中。。。
到这里,说白了,stmdb就是把寄存器的值保存到内存(栈)中。
最后这个感叹号sp!,表示sp为修改后的值4080;如果不加!,则sp为原始值4096。
ldmia指令
C语言内部机制分析
Start.S做了哪些事情?
1 设置栈;2 调用main函数,把返回地址保存在lr中;
led.c main函数做了什么?
1 定义两个局部变量;2 设置变量; 3 return 0;
问题来了。为什么要设置栈?因为C函数要用。怎么使用?
栈的作用:1 保存局部变量;2 保存lr等寄存器(子函数调用);
调用者如何串参数给被调用者?被调用者如何传返回值给调用者?
怎么从栈中恢复寄存器?
1 ATPCS规则
2 汇编代码与bin文件
可以看到,bin文件完全就是汇编代码。
再次说明:关于BL跳转与LR返回地址的解释:
内存布局:
汇编代码操作寄存器如下:
通过r0寄存器传递参数
led.c
void delay(int cnt)
{
while (cnt--);
}
int led_on(int which)
{
unsigned int * pGPFCON = (unsigned int *)(0x56000050);
unsigned int * pGPFDAT = (unsigned int *)(0x56000054);
if (4 == which)
{
/* 配置GPF4为输出引脚 */
*pGPFCON = 0x100;
}
else if (5 == which)
{
/* 配置GPF5为输出引脚 */
*pGPFCON = 0x400;
}
/* 设置GPF4/5输出0 */
*pGPFDAT = 0;
return 0;
}
start.S
.text
.global _start
_start:
/* 设置内存: sp栈 */
ldr sp, =4096 /* nand启动 */
// ldr sp, =0x40000000 + 4096 /* nor启动 */
// 使用r0传递参数
mov r0, #4
bl led_on
ldr r0, =100000
bl delay
mov r0, #5
bl led_on
halt:
b halt
编译得到的汇编文件如下:
上一篇:S3C2440 UART串口驱动(裸机)
下一篇:04 点亮LED 汇编
推荐阅读最新更新时间:2024-11-23 05:00
设计资源 培训 开发板 精华推荐
- PAS08FN82448E: HC08和S08编程适配器
- LT1170HVCT、5V/5A 正向转换器的典型应用
- MIKROE-3400,用于 AL1781 高亮度 LED 驱动器的 LED 驱动器 6 Click 板,设计用于可调谐智能连接照明 (SCL) 应用
- 单18650 IP5306方案小充电宝
- 使用 Analog Devices 的 LT1173CN8 的参考设计
- EVAL-CN0274-SDPZ,基于 ADXL362 3 轴、±2 g、±4 g、±8 g 数字 MEMS 加速度计的超低功耗独立运动开关评估板
- LTM4677IY 的典型应用电路 1 工作产品与 3 个 LTM4630A 并联,1V 输出,高达 144A 电源系统管理功能可通过 LTM4677 通过 2 线 I2C/SMBus/PMBus 串行接口访问
- LT8609IMSE 3.3V/2A 降压型稳压器的典型应用电路
- LM2931AD50R 具有抑制功能的超低压降稳压器的典型应用电路
- 具有 PowerPath 的 LTC4162EUFD-SAD 12V USB 供电充电器的典型应用