文章目录
- 一. C语言调用汇编函数
- 1. 项目创建
- 2. 编写代码
- 3. 配置环境及代码运行
- (1)仿真器设置以及编译
- (2)程序调试
- 二. 修改函数功能
- (1)修改代码
- (2)设置断点
- (3)编译并调试
- 三. 汇编函数调用C语言函数
- (1)新建func_.s、main_.c
- (2)编写代码
- (3)编译并调试
- 四. 总结
- 五. 参考文献
在基于Keil5创建STM32汇编语言工程的基础上,继续学习Keil5下的STM32的C与汇编语言混合编程.
- 本文内容:
1.参考附件资料,完成C语言调用汇编函数;
2.修改代码,要求将原汇编语言 Init_1函数的类型改为 int Init_1(init),此函数功能修改为传入一个整型数x,函数运行后返回整型数 x+100。 通过编程实现,并仿真跟踪调试。
3.如果要求在汇编函数中调用一个C语言写的函数,应该如何修改汇编代码?
一. C语言调用汇编函数
1. 项目创建
- 创建工程
test2
,添加文件func.s
和main.c
,我选择的芯片是STM32F103C8,具体步骤参考基于Keil5创建STM32汇编语言工程
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,不然会被认为是段名,表示程序结束。
- main.c
#include<stdio.h>
extern void Init_1(void);
int main()
{
Init_1();
return 0;
}
3. 配置环境及代码运行
(1)仿真器设置以及编译
- 打开
魔法棒
,在Debug
下选择Use Simulator
,并将左下的Dialog DLL
中的内容改为DARMSTM.DLL
,将Parameter
的内容改为-pSTM32F103C8
- 点击
Rebuild
进行编译
main.c
func.s
(2)程序调试
Ctrl+F5,
开始调试
- 设置断点
- 点击
Step
进行单步调试
观察R1、R2的寄存器值的变化,发现其由0逐步加到10
说明C语言成功调用汇编程序!
二. 修改函数功能
将原汇编语言 Init_1函数的类型改为 int Init_1(init),此函数功能修改为传入一个整型数x,函数运行后返回整型数x+100
(1)修改代码
main.c
#include<stdio.h>
extern int Init_1(int x);
int main(){
int xx = Init_1(10);
printf("%d", xx);
return 0;
}
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,不然会被认为是段名,表示程序结束
在Keil中,子函数的参数值传递按顺序存放到了R0、R1、R2、R3中,超过四个参数值传递放栈帧里。所以Init_1(10)传入的10放到了R0中,由MOV PC,LR返回110.
(2)设置断点
(3)编译并调试
点击Run to Cursor Line
在main函数调用Init_1函数,给的参数10,函数执行完时,10+100的16进制数为6E,寄存器R0的值也是6E,调用成功!
三. 汇编函数调用C语言函数
在汇编中调用C语言函数XXX,则加上IMPORT XXX,注意Keil工具不允许汇编语句顶格写,否则会报错
(1)新建func_.s、main_.c
(2)编写代码
- 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<stdio.h>
extern void Init_1(void);
int get5(void);
int main(){
printf("Begin...\n");
Init_1();
return 0;
}
int get5(){
return 5;
}
(3)编译并调试
- 点击
Rebuild
进行编译 - 设置断点
执行get5后,寄存值R0变为5,调用成功!
四. 总结
本文通过具体实例讲解C与汇编语言混合编程的过程,让我们更加深刻的体会到C调用汇编语言以及汇编调用C的原理,也对调用函数参数的传递方式以及ARM寄存器使用方法有了一定的理解。C语言调用汇编程序要在C程序中用EXTERN声明,在汇编文件里用EXPORT将C函数与汇编函数联系起来;汇编程序调用C语言文件时要在汇编文件里用IMPORT。
文章仅供参考,若有纰漏,欢迎大家一起讨论~
五. 参考文献
https://blog.csdn.net/qq_45659777/article/details/120651310
https://blog.csdn.net/longintchar/article/details/79511747