您现在的位置是:首页 > 数码 > 

流水灯实验linux,Exynos4412之C语言实现流水灯

2025-07-27 13:22:56
流水灯实验linux,Exynos4412之C语言实现流水灯 但是,汇编语言可读性太差,在这一节我们用 C语言来实现了同样的功能,而以后的试验也尽量用 C语言实现。我们在编写上位机程序时,C语言程序执行的第一条指令,并不在main函数中。生成一个 C程序的可执行文件时,编译器通常会在我们的代码中加上几个被称为启动

流水灯实验linux,Exynos4412之C语言实现流水灯

但是,汇编语言可读性太差,在这一节我们用 C语言来实现了同样的功能,而以后的试验也尽量用 C语言实现。

我们在编写上位机程序时,C语言程序执行的第一条指令,并不在main函数中。生成一个 C程序的可执行文件时,编译器通常会在我们的代码中加上几个被称为启动文件的代码—— 、、 、 等,它们是标准库文件。这些代码设置C程序的堆栈等,然后调用 main 函数。它们依赖于操作系统,在裸板上这些代码无法执行,所以需要自己写一个。

这段代码很简单, 关键指令只有2条。自己编写的 start .S启动文件内容如下:

====================================================================

.text

.globl _start

_start:

ldr sp, =0x02027800

// 调用C函数之前必须设置栈,栈用于保存运行环境,给局部变量分配空间

// 参考ROM手册P14, 我们把栈指向BL2上方1K处(1K已经够用),

//0x02020000 (iRAM基地址)  5K(iROM代码用)  8K(BL1用)  16K(BL2用)  1K(用作栈)

bl main  // 调用main函数(main这个名称不是固定的,可以随意改)

halt_loop:

b halt_loop

====================================================================

它在第 4行设置好栈指针后,就可以通过第8行调用C函数 main了--------------- C函数执行前,必须设置栈。

问:CPU不是有看门狗嘛?为什么没有看到关看门狗的代码?这样程序能正常运行吗?

答:在上一篇文章《Exynos 4412的启动过程分析》中,我们已经介绍过了,在执行我们的程序前,CPU会首先执行iROM中的代码和BL1的代码,在这两部分程序中会关闭看门狗。

其实我们自己关闭看门狗也很简单,只需往寄存器WTCO写入0即可;

问:为什么调用C函数要设置栈?

答:1.  栈的整体作用

1)  保存现场;

2)  传递参数:汇编代码调用C函数时,需传递参数;

)  保存临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量;

2.  详细解释

1)保存现场

现场,意思就相当于案发现场,总有一些现场的情况,要记录下来的,否则被别人破坏掉之后,你就无法恢复现场了。而此处说的现场,就是指CPU运行的时候,用到了一些寄存器,比如r0,r1等等,对于这些寄存器的值,如果你不保存而直接跳转到子函数中去执行,那么很可能就被其破坏了,因为其函数执行也要用到这些寄存器。因此,在函数调用之前,应该将这些寄存器等现场,暂时保持起来(入栈push),等调用函数执行完毕返回后(出栈pop),再恢复现场。这样CPU就可以正确的继续执行了。保存寄存器的值,一般用的是push指令,将对应的某些寄存器的值,一个个放到栈中,把对应的值压入到栈里面,即所谓的压栈。然后待被调用的子函数执行完毕的时候,再调用pop,把栈中的一个个的值,赋值给对应的那些你刚开始压栈时用到的寄存器,把对应的值从栈中弹出去,即所谓的出栈。其中保存的寄存器中,也包括lr的值(因为用bl指令进行跳转的话,那么之前的PC的值是存在lr中的),然后在子程序执行完毕的时候,再把栈中的lr的值pop出来,赋值给PC,这样就实现了子函数的正确的返回。

2)传递参数

C语言进行函数调用的时候,常常会传递给被调用的函数一些参数,对于这些C语言级别的参数,被编译器翻译成汇编语言的时候,就要个地方存放一下,并且让被调用的函数能够访问,否则就没发实现传递参数了。对于个地方放一下,分两种情况。一种情况是,本身传递的参数不多于4个,就可以通过寄存器r0~r传送参数。因为在前面的保存现场的动作中,已经保存好了对应的寄存器的值,那么此时,这些寄存器就是空闲的,可以供我们使用的了,那就可以放参数。另一种情况是,参数多于4个时,寄存器不够用,就得用栈了。

)临时变量保存在栈中

包括函数的非静态局部变量以及编译器自动生成的其他临时变量。

现在,我们可以很容易写出控制 LED 的程序了。毕竟是用C语言嘛,相当的灵活。 main 函数在 文件中,代码如下:

#define GPM4CO (*(volatile unsigned int *)0x110002E0)

#define GPM4DAT (*(volatile unsigned int *)0x110002E4)

void delay(volatile int time)

{

for(; time > 0; time-- );

}

int main(void)

{

unsigned long tmp = 0;

int i = 0;

// GPM4_0-GPM4_ 设置为输出功能

tmp = GPM4CO;

tmp &= ~0xffff;

tmp |= 0x1111;

GPM4CO = tmp;

// 实现流水灯

while(1)

{

GPM4DAT = i;

if (i == 16)

i = 0;

delay(9999999);

}

return 0;

}

来看看Makefile:

====================================================================

objs :=

led.bin : $(objs)

arm-linux-ld -Tled.lds - -o $^

arm-linux-objcopy -O binary -S $@

arm-linux-objdump -D -m arm > led.dis

%.o:%.c

arm-linux-gcc -Wall -marm -c -O2 -o $@ $<

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/shuma/857201.html

相关标签:无
上传时间: 2024-02-10 09:32:26
留言与评论(共有 15 条评论)
本站网友 揭阳男科
22分钟前 发表
毕竟是用C语言嘛,相当的灵活
本站网友 弘毅投资
22分钟前 发表
其中保存的寄存器中,也包括lr的值(因为用bl指令进行跳转的话,那么之前的PC的值是存在lr中的),然后在子程序执行完毕的时候,再把栈中的lr的值pop出来,赋值给PC,这样就实现了子函数的正确的返回
本站网友 政务区二手房
12分钟前 发表
本站网友 友谊二手房
6分钟前 发表
另一种情况是,参数多于4个时,寄存器不够用,就得用栈了
本站网友 空空
29分钟前 发表
对于个地方放一下,分两种情况
本站网友 在线调色板
6分钟前 发表
保存寄存器的值,一般用的是push指令,将对应的某些寄存器的值,一个个放到栈中,把对应的值压入到栈里面,即所谓的压栈
本站网友 谜局
19分钟前 发表
汇编代码调用C函数时,需传递参数;)  保存临时变量
本站网友 关晓彤高考成绩
21分钟前 发表
现在,我们可以很容易写出控制 LED 的程序了
本站网友 静海租房
16分钟前 发表
因为在前面的保存现场的动作中,已经保存好了对应的寄存器的值,那么此时,这些寄存器就是空闲的,可以供我们使用的了,那就可以放参数
本站网友 品质改变世界
16分钟前 发表
生成一个 C程序的可执行文件时,编译器通常会在我们的代码中加上几个被称为启动文件的代码——
本站网友 任我遨游
19分钟前 发表
自己编写的 start .S启动文件内容如下:====================================================================.text.globl _start_start
本站网友 中工网
11分钟前 发表
这样CPU就可以正确的继续执行了
本站网友 洛阳牡丹燕菜
9分钟前 发表
等,它们是标准库文件
本站网友 自从你离开以后
11分钟前 发表
2)传递参数C语言进行函数调用的时候,常常会传递给被调用的函数一些参数,对于这些C语言级别的参数,被编译器翻译成汇编语言的时候,就要个地方存放一下,并且让被调用的函数能够访问,否则就没发实现传递参数了