C/C++教程

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

本文主要是介绍Keil下STM32的C与汇编语言混合编程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

一、添加源代码与编写程序

1.main.c代码

2.func.s代码

 二.编译并调试

1.编译并调试:

2.调试结果:

2.1在比较R1与10的大小处设置断点,观察每次比较时的R1和R2大小 ,初始R1和R2为0

 2.2由于目前R1是小于10的会不断进入i++和j++循环

10时,跳出循环到LOOP_END ">2.3 当R1>10时,跳出循环到LOOP_END 

 三.修改参考代码

1. 函数的参数传递机制

2.寄存器与堆栈使用规则

3. 要求将原汇编语言 Init_1函数的类型改为 int Init_1(init) ,此函数功能修改为 传入一个整型数x,函数运行后返回整型数 x+100。

3.1main.c

3.2Func.s

3.3添加文件并开始编译

 3.4调试结果

四.总结

参考文献:



一、添加源代码与编写程序

创建新工程步骤跟上一篇博客相同,在这就不再赘述了。搭建并配置Keil嵌入式开发环境,完成一个基于STM32汇编程序的编写_SS_SS_SSS_SSS的博客-CSDN博客 

1.main.c代码

#include<stdio.h>
extern void Init_1(void);
int main(){
    Init_1();
    return 0;
	}

2.func.s代码

	AREA	My_Function,CODE,READONLY	;这一行必有的除了My_ Function可以自己取名意以外,其它的都是模版啦
	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段
LOOP_END	;写在最左边的是程序段的段名,执行跳转程序时要用到
	NOP
	END	;必须空格后再写END,不然会被认为是段名,表示程序结束

 提示:要空格后再写END,不然会被认为是段名,表示程序结束。

第一排代码AREA***要空格,不能顶格。

LOOP_END 注意不要“LOOP”和“_”和“END”之间不能有空格 比如“LOOP_ END”不然就会报错:

这个错误找了很久,最后才发现是因为“LOOP”和“_”和“END”之间的空格。

原理:首先在C里面用 extern 声明 Init_1这个函数,再在main里面调用好了。 然后在汇编里面用EXPORT Init_1与C联系起来就可以了。

 二.编译并调试

1.编译并调试:

2.调试结果:

2.1在比较R1与10的大小处设置断点,观察每次比较时的R1和R2大小 ,初始R1和R2为0

 2.2由于目前R1是小于10的会不断进入i++和j++循环

2.3 当R1>10时,跳出循环到LOOP_END 

 三.修改参考代码

1. 函数的参数传递机制


 对于x86平台,主要分为32位和64位程序。对于32位程序采用的是栈传递。对于64位程序,根据参数的不同当参数为1~6个时,采用寄存器传参,但当参数大于六个时,多余的参数部分由栈传递。

 对于ARM平台
参数值传递按顺序存放在寄存器r0,r1,r2,r3里,超过4个参数值传递则放栈里。

r0-r3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。被调用函数在返回之前不必恢复 r0-r3。---如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。

r4-r11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。r11 是栈帧指针 fp。

 r12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。

寄存器 r13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。

寄存器 r14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复 6. 寄存器 r15 是程序计数器 pc。它不能用于任何其它用途。

2.寄存器与堆栈使用规则


(1)寄存器的使用规则

子程序之间通过寄存器r0~r3来传递参数,当参数个数多于4个时,使用堆栈来传递参数。此时r0~r3可记作A1~A4。 在子程序中,使用寄存器r4~r11保存局部变量。因此当进行子程序调用时要注意对这些寄存器的保存和恢复。此时r4~r11可记作V1~V8。 寄存器r12用于保存堆栈指针SP,当子程序返回时使用该寄存器出栈,记作IP。 寄存器r13用作堆栈指针,记作SP。寄存器r14称为链接寄存器,记作LR。该寄存器用于保存子程序的返回地址。 寄存器r15称为程序计数器,记作PC。

(2)堆栈的使用规则

ATPCS规定堆栈采用满递减类型(FD,Full Descending),即堆栈通过减小存储器地址而向下增长,堆栈指针指向内含有效数据项的最低地址。 3)参数的传递规则 整数参数的前4个使用r0~r3传递,其他参数使用堆栈传递;浮点参数使用编号最小且能够满足需要的一组连续的FP寄存器传递参数。 子程序的返回结果为一个32位整数时,通过r0返回;返回结果为一个64位整数时,通过r0和r1返回;依此类推。结果为浮点数时,通过浮点运算部件的寄存器F0、D0或者S0返回。

3. 要求将原汇编语言 Init_1函数的类型改为 int Init_1(init) ,此函数功能修改为 传入一个整型数x,函数运行后返回整型数 x+100。

3.1main.c

定义一个返回值为整型的Init_1的函数

#include<stdio.h>
 
extern int Init_1(int x);
 
int main()
	{
	 int x,y;
		x=10;
	y=Init_1(x);
	
return 0;
	}

3.2Func.s

	AREA	My_Function,CODE,READONLY	;这一行必有的除了My_ Function可以自己取名意以外,其它的都是模版啦
	EXPORT Init_1	;与在c文件中定义的Init_ 1函数关联起来
	;高级语言中的声明和使用变量其实是对板子寄存器的使用,所以我们只需要直接使用寄存器即可
Init_1
loop ;写在最左边的是程序段的段名,执行跳转程序时要用到
	MOV R2,100	;设R2寄存器的值为100
	ADD R1,R0,R2;计算R0与R2的和,并将值存储到R1
LOOP_END ;写在最左边的是程序段的段名,执行跳转程序时要用到
	MOV  PC,LR; 返回值,这是必要的格式
	END ;必须空格后再写END,不然会被认为是段名,表示程序结束

3.3添加文件并开始编译

添加文件和编译步骤与上面相同

 3.4调试结果

 R0十六进制对应的是传入的参数值,R1为x+100后的值

四.总结

C语言与汇编语言混合编程,比较关键的点就在于在C中用 extern 声明一个函数,在main调用后再在汇编语言里用EXPORT+函数名与C联系起来。通着这个编程了解了一些汇编语言的指令,也了解了寄存器与堆栈使用规则。在第一个实验中由于空格问题导致编译出错,找了很久的错误。对于C与汇编语言混合编程的用法还没完全掌握,还得逐渐摸索学习。

参考文献:

C语言在ARM中函数调用时,栈是如何变化的? - 云+社区 - 腾讯云

C语言嵌套汇编语言_Laul Ken-Yi的博客-CSDN博客

这篇关于Keil下STM32的C与汇编语言混合编程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!