STM32的C与汇编语言混合编程
一、混合编程C语言还是有其局限性。有些硬件地址是没有地址一说的。比如处理器的寄存器,协处理器和协处理器的寄存器,系统控制器等。这些硬件资源是不可能使用C语言指针来访问的,这时就只好应用汇编指令了。[*]汇编程序中调用C语言函数
[*]汇编程序使用C语言中定义的全局变量、、
[*]C语言中使用汇编程序中定义的全局变量
[*]C语言中使用汇编程序中定义的全局变量
[*]C语言中内嵌汇编指令
二、C语言嵌套汇编调用函数操作1. 无参数① C语言程序调用汇编语言函数
[*]main.c
#include<stdio.h>extern void Init_1();int main(){ Init_1(); return 0;}
[*]fun.c
AREA My_Function,CODE,READONLY ;固定格式 EXPORT Init_1 Init_1 MOV R1,#0 MOV R2,#0 LOOP CMP R1,#10 BHS LOOP_END ADD R2,#1 ADD R1,#1 B LOOPLOOP_END NOP END② 在arm编程里,函数调用过程中,子函数的参数值传递按顺序存放在r0,r1,r2,r3里,超过4个参数值传递放栈帧
[*]在汇编函数那一行前设置断点,即可看到c语言调用汇编函数
https://i-blog.csdnimg.cn/blog_migrate/7d991481c394129e27a26fd0f2cc5f05.png
https://i-blog.csdnimg.cn/blog_migrate/52f98b1469fc63bd82379f6a2f21f1cf.png
https://i-blog.csdnimg.cn/blog_migrate/49849e3ad991373cc8bbca775846ffc8.png
[*]主要操作在子函数的参数值传递按顺序存放在r0,r1,r2,r3里,超过4个参数值传递放栈帧里,在第一阶段中主要对R1 R2 进行基本操作
[*]寄存器 r13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。
[*]寄存器 r14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复
[*]寄存器 r15 是程序计数器 pc。它不能用于任何其它用途。
https://i-blog.csdnimg.cn/blog_migrate/b58c9fbd4ff3fba01ebe4d55d5cb35f9.png
③ 调试
https://i-blog.csdnimg.cn/blog_migrate/9999eb1c71db0122c5e3774b11712201.png
https://i-blog.csdnimg.cn/blog_migrate/b93777ed4564d3d4e716c1e364543540.png
https://i-blog.csdnimg.cn/blog_migrate/2e83bb13ec52b64d8692ddb8777e51a7.pnghttps://i-blog.csdnimg.cn/blog_migrate/37f3e0889c55ebe3dfb49c1a8ad1daa7.png2. 有参数
[*]main2.c
# include<stdio.h>extern int Init_1(int x);int main(){ int xx = Init_1(10); printf("%d", xx); return 0;}
[*]func2.s
AREA MY_Function,CODE,READONLY EXPORT 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,不然会被认为是段名,表示程序结束① 设置断点
https://i-blog.csdnimg.cn/blog_migrate/324b02b9ac71eb89d9841dc49f2d2a79.png
https://i-blog.csdnimg.cn/blog_migrate/b0778efc7822fab53712474d49aba8d2.png
[*]起始设R0为100 进行R1 R2 等操作,
https://i-blog.csdnimg.cn/blog_migrate/b6d6b3cec2b97b0c910f80e5b16bba44.png
② 调试
https://i-blog.csdnimg.cn/blog_migrate/80dcb18132eca44ec3f50ffe0614ba46.png
https://i-blog.csdnimg.cn/blog_migrate/32ed99bde1537120ea48d49f5f6289f4.png
0x6E=110
在ARM中,子函数的参数值传递按顺序存放在r0,r1,r2,r3里,超过4个参数值传递放栈帧里。
因此Init_1(10)传入的10放到了R0,由MOV PC,LR返回110.
三、汇编语言嵌入C语言
[*]main3.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;}
[*]func3.s
AREA MY_Function,CODE,READONLY EXPORT Init_1; 与在c文件中定义的Init_1函数关联起来 IMPORTget5 ; 声明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,不然会被认为是段名,表示程序结束① 设置断点
https://i-blog.csdnimg.cn/blog_migrate/2268d81281a5eb86dc3d99a4c438e570.png
https://i-blog.csdnimg.cn/blog_migrate/1135659ec38cbc35ec3b826a104bb2dd.png
② 调试
[*]调试R0变为5
https://i-blog.csdnimg.cn/blog_migrate/53dee9c2f1d6e915e89b9dfee336d969.png
https://i-blog.csdnimg.cn/blog_migrate/091ac53dab9b9b69cd11920144976073.png
[*]R1 和R2发生累加变化
https://i-blog.csdnimg.cn/blog_migrate/bbf52b7502d4ec9cc9eb76ab68cff62f.png
https://i-blog.csdnimg.cn/blog_migrate/6b98d3d8289f7c89b8f299a13952f9aa.png
https://i-blog.csdnimg.cn/blog_migrate/17080041c8affafc301c5b57342e3ab3.png
四、总结在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 用于其它用途,程序返回时要恢复
[*]寄存器 r15 是程序计数器 pc。它不能用于任何其它用途。
很奇怪,如果这个嵌入的汇编不用汇编,那么用C能达到相同的作用吗 在STM32上进行C与汇编语言混合编程的一般步骤和注意事项:
选择合适的编译器和工具链:
使用支持C和汇编混合编程的编译器,如GCC。对于STM32,通常会使用Keil或STMicroelectronics提供的工具链。
编写C代码:
编写大部分的应用程序代码使用C语言。C语言提供了高级别的抽象和较好的可移植性。
编写汇编代码:
对于需要优化性能或者控制硬件资源的情况,可以使用汇编语言编写关键代码段。例如,中断服务程序、实时任务或底层硬件初始化等。
集成C和汇编代码:
在项目中,你需要指定哪些函数是用C语言编写的,哪些是汇编语言编写的。编译器需要知道这些信息的接口,以便正确地处理两种语言的交互。在GCC中,通常使用.S扩展名来标识包含汇编代码的文件。在文件中可以嵌入C代码段和汇编代码段。
接口管理:
当在C和汇编之间切换时,要确保正确地管理接口。使用特定的编译器语法来声明C函数可以被汇编代码调用,并确保参数的传递和返回值的处理是正确的。此外,还需要考虑内存管理、栈的使用和寄存器保存等问题。
调试和测试:
混合编程增加了调试的复杂性。确保使用适当的调试工具来跟踪C和汇编代码之间的交互,并测试系统的稳定性和性能。
遵循STM32的编程规范:
根据STM32的具体型号和硬件平台,可能需要遵循特定的编程规范来确保代码的兼容性和稳定性。查阅相关的技术文档和参考手册是很重要的。
考虑可维护性和移植性:
混合编程可能会使代码更难以维护和理解。确保编写清晰的注释和文档,以便于其他开发者理解你的代码逻辑和工作方式。如果可能的话,尽量保持C代码的纯净性,只在必要时使用汇编代码。 我觉得会C基本就差不多了,汇编能看懂就行,用汇编写代码还是难得。
要求太高了¥
页:
[1]