【嵌入式06】STM32的C与汇编语言混合编程

Keil下STM32的C与汇编语言混合编程

  • 一、题目要求
  • 二、项目创建
  • 三、C语言调用汇编-无参数调用
    • 1、仿真设置
    • 2、设置断点
    • 3、编译并调试
  • 四、C语言调用汇编语言-有参数调用
    • 1、修改 三 中代码
    • 2、设置断点
    • 3、编译并调试
  • 五、汇编语言调用C语言函数
    • 1、修改 四 中代码
    • 2、设置断点
    • 3、编译并调试
  • 六、其他
    • 1、C与汇编之间函数调用ATPCS简介
    • 2、参数如何传递
    • 3、参数的清除
    • 4、C语言与汇编语言混合程序
      • 内联汇编
      • 嵌入式汇编
  • 七、总结


一、题目要求

修改代码,要求将原汇编语言 Init_1函数的类型改为 int Init_1(init) ,此函数功能修改为传入一个整型数x,函数运行后返回整型数 x+100。 请编程实现,并仿真跟踪调试;
如果要求在汇编函数中调用一个 C语言写的函数,应该如何修改汇编代码?

二、项目创建

打开Keil ARM,Project->New μVision Project->选择STM32F103ZE
【嵌入式06】STM32的C与汇编语言混合编程_第1张图片

添加main.cFunc.s文件
【嵌入式06】STM32的C与汇编语言混合编程_第2张图片

【嵌入式06】STM32的C与汇编语言混合编程_第3张图片
Func.s


	AREA	MY_FUNCTION,CODE,READONLY
	EXPORT 	Init_1  ; 与在c文件中定义的Init_1函数关联起来


; 高级语言中的声明和使用变量其实是对板子寄存器的使用,所以我们只需要直接使用寄存器即可

Init_1

	MOV R1,#0     ; 设R1寄存器为i
	MOV R2,#0	  ; 设R2寄存器为j
	
LOOP	; 写在最左边的是程序段的段名,执行跳转程序时用到
	CMP R1,#10	  ; 比较R1和10的大小
	BHS LOOP_END  	  ; 如果R1大于等于10,则跳转到LOOP_END程序段,反之忽略该语句,直接执行下面的语句
	ADD R2,#1	  ; j++
	ADD R1,#1     ; i++
	B LOOP		  ; 循环
	
LOOP_END
	NOP	
	
	END  ; 必须空格后再写END,不然会被认为是段名,表示程序结束

main.c

# include

extern void	Init_1(void);

int main(){
	
	Init_1();
	
	return 0;
}

三、C语言调用汇编-无参数调用

1、仿真设置

打开魔法棒,选择Debug,勾选Use Simulator
将左侧的Dialog DLL中的内容改为DARMSTM.DLL,将Parameter的内容改为-pSTM32F103C8
【嵌入式06】STM32的C与汇编语言混合编程_第4张图片

2、设置断点

在目标行前侧点击,生成小红点,即完成断点设置
【嵌入式06】STM32的C与汇编语言混合编程_第5张图片
【嵌入式06】STM32的C与汇编语言混合编程_第6张图片

3、编译并调试

编译buildrebuild后,进行调试
【嵌入式06】STM32的C与汇编语言混合编程_第7张图片
在左上角点击stepstep overRun To Cursor Line
观察R1、R2的寄存器值的变化
可以发现其由0逐步加到10
【嵌入式06】STM32的C与汇编语言混合编程_第8张图片
【嵌入式06】STM32的C与汇编语言混合编程_第9张图片

四、C语言调用汇编语言-有参数调用

1、修改 三 中代码

Func.s

 
	AREA	MY_Function,CODE,READONLY
	EXPORT 	Init_1  ; 与在c文件中定义的Init_1函数关联起来


; 高级语言中的声明和使用变量其实是对板子寄存器的使用,所以我们只需要直接使用寄存器即可

Init_1
	ADD R0,#100     ; 将传入的值+100
	MOV PC,LR		; 返回R0
	
	
LOOP	; 写在最左边的是程序段的段名,执行跳转程序时用到
	CMP R1,#10	  ; 比较R1和10的大小
	BHS LOOP_END  	  ; 如果R1大于等于10,则跳转到LOOP_END程序段,反之忽略该语句,直接执行下面的语句
	ADD R2,#1	  ; j++
	ADD R1,#1     ; i++
	B LOOP		  ; 循环
	
LOOP_END
	NOP	
	
	
	END  ; 必须空格后再写END,不然会被认为是段名,表示程序结束

main.c

# include

extern int Init_1(int x);

int main(){
	
	int xx = Init_1(10);
	printf("%d", xx);
	
	return 0;
}

在Keil中,子函数的参数值传递按顺序存放到了R0R1R2R3中,超过四个参数值传递放栈帧里。

由此,Init_1(10)传入的10放到了R0中,由MOV PC,LR返回110.

2、设置断点

【嵌入式06】STM32的C与汇编语言混合编程_第10张图片
【嵌入式06】STM32的C与汇编语言混合编程_第11张图片

3、编译并调试

操作同上
【嵌入式06】STM32的C与汇编语言混合编程_第12张图片
【嵌入式06】STM32的C与汇编语言混合编程_第13张图片
此时值为6E110,调用成功!

五、汇编语言调用C语言函数

1、修改 四 中代码

Func.s


	AREA	MY_Function,CODE,READONLY
	EXPORT 	Init_1  ; 与在c文件中定义的Init_1函数关联起来
	IMPORT  get5    ; 声明get5 为外部引用


; 高级语言中的声明和使用变量其实是对板子寄存器的使用,所以我们只需要直接使用寄存器即可

Init_1

	MOV R1,#0     ; 设R1寄存器为i
	MOV R2,#0	  ; 设R2寄存器为j
	
LOOP	; 写在最左边的是程序段的段名,执行跳转程序时用到
	CMP R1,#10	  ; 比较R1和10的大小
	BHS LOOP_END  	  ; 如果R1大于等于10,则跳转到LOOP_END程序段,反之忽略该语句,直接执行下面的语句
	ADD R2,#1	  ; j++
	ADD R1,#1     ; i++
	BL get5  	  ; 调用get5,返回的值传入R0
	B LOOP		  ; 循环
	
LOOP_END
	NOP	
	
	
	END  ; 必须空格后再写END,不然会被认为是段名,表示程序结束

main.c

# include

extern void	Init_1(void);

int get5(void);

int main(){
	
	printf("Begin...\n");
	Init_1();

	
	return 0;
}

int get5(){
	return 5;
}


2、设置断点

【嵌入式06】STM32的C与汇编语言混合编程_第14张图片

3、编译并调试

【嵌入式06】STM32的C与汇编语言混合编程_第15张图片

执行get5后,R0变为了5,调用成功!

六、其他

1、C与汇编之间函数调用ATPCS简介

  • ARM-Thumb 过程调用标准 ATPCS(ARM-Thumb Procedure Call Standard)
  • ATPCS 标准既是ARM 编译器使用的函数调用规则,也是设计可被 C 程序调用的汇编函数的编写规则
  • ATPCS 规定,ARM 的数据堆栈为 FD 型堆栈,即满递减堆栈
  • 函数是通过寄存器和堆栈来传递参数和返回函数值的,形参和返回值都应定义在具有暂存性质的寄存器和堆栈中

2、参数如何传递

汇编程序调用C函数时,函数的入口参数使用栈来传送,参数的传递顺序是从右到左。即函数最后(最右边的)一个参数先入栈,而最左边的第一个参数最后入栈,然后执行 CALL 指令去调用C函数。

3、参数的清除

在C函数返回后,汇编程序需要把先前压入栈中的函数参数清除掉,即调用者负责清除参数占用的栈空间。

4、C语言与汇编语言混合程序

在C程序中内联或嵌入式汇编代码,以提高程序的效率

内联汇编

  • 在 C 程序中直接编写汇编程序段而形成一个语句块,这个语句块可以使用除了 BX 和 BLX之外的全部ARM指令来编写
  • 可以使程序实现一些不能从C获得的底层功能
  • 汇编语句块中,如果有两条指令占据了同一行,那么必须用分号“ ;”将它们分隔
  • 如果一条指令需要占用多行,那么必须用反斜线符号“ \ ”作为续行符
  • 可以在内联汇编语言块内的任意位置使用C/C++格式的注释
  • 内联汇编代码中定义的标号可被用作跳转或C/C++ goto 语句的目标,同样,在C/C++代码中定义的标号,也可被用作内联汇编代码跳转指令的目标
  • 在内联汇编语句块中最好使用 C 或 C++ 变量作为操作数
  • 状态寄存器 PSR ,任何对 PSR 的引用总是执行指向物理 PSR

void enable_IRQ(void)
{
int tmp;
_ _asm //声名内联汇编代码
{
MRS tmp, CPSR
BIC tmp, tmp, #0x80
MSR CPSR_c, tmp
}
}

  • 内联汇编的限制
    • 它不支持 Thumb 指令;除了程序状态寄存器 PSR 之外,不能直接访问其他任何物理寄存器
    • 如果在内联汇编程序指令中出现了以某个寄存器名称命名的操作数,那么它被叫做虚拟寄存器,而不是实际的物理寄存器。编译器在生成和优化代码的过程中,会给每个虚拟寄存器分配实际的物理寄存器,但这个物理寄存器可能与在指令中指定的不同。
    • 在内联汇编代码中不能使用寄存器 PC(R15)、LR(R14)和SP(R13)
    • 处理器模式会禁止使用 C 操作数或对已编译 C 代码的调用,直到将处理器模式恢复为原设置之后

嵌入式汇编

  • 嵌入式汇编程序是一个编写在C程序外的单独汇编程序,该程序段可以像函数那样被 C 程序调用
  • 嵌入式汇编具有真实汇编的所有特性,数据交换符合 ATPCS 标准,同时支持 ARM 和Thumb,所以它可以对目标处理器进行不受限制的低级访问
  • 不能直接引用 C/C++ 的变量
  • 参数名只允许使用在参数列表中,不能用在嵌入式汇编函数体内
  • 在 C 程序中调用嵌入式汇编程序的方法与调用 C 函数的方法相同

定义一个嵌入式汇编函数的语法格式为:
_ _asm return–type function–name(parameter-list)
{
汇编程序段
}

return–type:函数返回值类型,C语言中的数据类型
function–name:函数名
parameter-list:函数参数列表

项目 内联汇编 嵌入式汇编
指令集 仅限于ARM ARM和Thumb
ARM汇编程序命令 不支持 支持
C表达式 支持 仅支持常量表达式
优化代码 支持 不支持
内联 可能 从不
寄存器访问 不使用物理寄存器 使用物理寄存器
返回指令 自动生成 显式编写

七、总结

ARM在C语言中调用汇编函数的方法
在C中调用汇编文件中的函数,要做的主要工作有两个:
一是在C中声明函数原型,并加extern关键字;
二是在汇编中用EXPORT导出函数名,并用该函数名作为汇编代码段的标识,最后用mov pc, lr返回。然后,就可以在C中使用该函数了。


参考:
[1] https://blog.csdn.net/longintchar/article/details/79511747
[2] https://blog.csdn.net/sinat_27421407/article/details/78829508

你可能感兴趣的:(stm32,c语言)