arm汇编和c混合编程

来源:互联网 发布:left软件安卓版 编辑:程序博客网 时间:2024/05/02 04:29
一、ARM在复位启动的时候,执行的是汇编指令
ARM复位启动,首先执行汇编指令,完成一些硬件的基本初始化(stack、clock、DDR2 SDRAM、uart、cache、wdt、int、.....)。
    然后在使用C来初始化:LCD、网卡、USB、加载操作系统并启动操作系统。


ARM代码的编写过程:
ARM汇编 ----> C语言




不能C和汇编反复调用
ARM汇编   -->    C语言             -->ARM汇编
使用了R2     局部变量a-->R2          再使用R2,产生问题
注意入栈和出栈。




====================================================================================
二、汇编调用C


汇编程序:


.global _start
_start:
@将GPJ2_3 --->output
LDR R0, =0xe0200280 //R0=0xe0200280
LDR R1, [R0] //R1=0xe0200280地址下的内容
BIC R1,R1,#(0xf<<12) //R1 &= ~(0xf<<12)
ORR R1, R1, #(1<<12) //R1 |= (1<<12)
STR R1,[R0] //将R1 保存到 0xe0200280


loop:
@将GPJ2_3  output 0,LED4-->on
LDR R0, =0xe0200284 //R0=0xe0200284
LDR R1, [R0] //R1=0xe0200284地址下的内容
BIC R1,R1,#(1<<3) //R1 &= ~(1<<3)
STR R1,[R0] //将R1 保存到 0xe0200284

mov R0, #0x300000 //R0作为参数,传递给delay()
BL delay //BL -- 在跳转之前,保存了返回地址-->R14

@将GPJ2_3  output 1,LED4-->off
LDR R0, =0xe0200284 //R0=0xe0200284
LDR R1, [R0] //R1=0xe0200284地址下的内容
ORR R1, R1, #(1<<3) //R1 |= (1<<3)
STR R1,[R0] //将R1 保存到 0xe0200284


mov R0, #0x300000 //R0作为参数,传递给delay()
BL delay

b loop //while(1) b--branch






C程序:
void delay(int time) //time <---R0
{
int i;
for(i=0;i<time;i++);
}


===========================================================================================
三、编译器的优化级别


gcc有几个优化等级:
  O0,O1,O2,O3
       -O0表示没有优化,-O1为缺省值,-O3优化级别最高。


默认的优化级别-O1
gec@ubuntu:/mnt/hgfs/08arm/04ASM_C/demo1$ size led.elf
   text   data   bss    dec   hex filename
    148      0     0    148    94 led.elf


使用-O2优化级别
gec@ubuntu:/mnt/hgfs/08arm/04ASM_C/demo2-O2$ size led.elf
   text   data   bss    dec   hex filename
     84      0     0     84    54 led.elf




编译器将delay()优化成:
40000050 <delay>:
40000050: e12fff1e bx lr  //mov PC,lr




如何避免delay被优化:
void delay(int time) //time <---R0
{
volatile int i;  //int i;
for(i=0;i<time;i++);
}




另一个volatile例子:


int key_cnt=0; //虽然key_cnt赋值为0,但是key_cnt是存放在BSS段,BSS的内容是内存中默认值。
               //所以key_cnt的值是未知的。


void key_irq(void) //按键的中断服务程序
{
key_cnt ++;
}


int a, b


int main(void)
{
   
   key_cnt = 0;
   .........
   a = key_cnt;
   sleep(10);
   b = key_cnt;
   if(a==b)
      printf("no key is pressing in 10s\n");
    else
      printf("key is pressing in 10s\n");

}


这样的程序在编译过程中会被优化(-O2)。编译器会认为a和b是相等的。编译器没有考虑key_cnt是变化的。在这里面key_cnt应该是一个“易变的”
变量,这个变量时时刻刻都可能都会改变,所以每次使用这个变量的时候,都要重新从内存中读取该变量的值,不能优化这个变量。
所以:
volatile int key_cnt=0;


volatile用来修饰一个“易变的”变量,每次使用这个变量的时候都重新从内存中读取它的值,防止被编译器优化。


voltale的应用场合:
1)中断服务程序和应用程序之间共享的全局变量
2)两个线程之间共享的全局变量
3)定义硬件寄存器的内存
#define rGPJ2CON (*( volatile unsigned int *)0xe0200280)
#define rGPJ2DAT (*( volatile unsigned int *)0xe0200284)




===========================================================================================
五、C调用汇编


1、C语言程序
#define rGPJ2CON (*( volatile unsigned int *)0xe0200280)
#define rGPJ2DAT (*( volatile unsigned int *)0xe0200284)
//GPJ2_3 ----> LED4


extern void delay(int dat);


void _start(void) //相当于main()
{
//将GPJ2_3 --->output
rGPJ2CON &= ~(0xf<<12);
rGPJ2CON |= (1<<12);

while(1)
{
//将GPJ2_3  output 0,LED4-->on
rGPJ2DAT &= ~(1<<3);
delay(0x3000000); //

//将GPJ2_3  output 1,LED4-->off
rGPJ2DAT |= (1<<3); //1000b
delay(0x3000000);
}
}




2、汇编程序
.global delay
delay:   
SUB R0,R0,#1 // R2=R2-1
TEQ R0, #0 //compare  if(R2 == 0)
BNE delay //B--branch,NE -- Not Equal 
 
bx lr // R15=R14, delay返回
@传递参数的规则--->ATPCS规则




===========================================================================================
六、C内嵌汇编
arm-linux-gcc支持C内嵌汇编。
0 0