ARM半主机简介

2021-02-17 19:42:08

固件开发人员通常习惯于使用日志记录执行信息。在微控制器上,这通常是通过连接到主机终端的串行接口来完成的。

但是主机可能需要更高级的功能! not不久前我就担任这个职位。

在使用相机固件,尤其是图像处理算法时,我一直需要将图像从相机传输到主机,以确保一切正常。因此,我开始使用半主机!它不仅极大地促进了我的开发,而且极大地方便了嵌入式软件的调试。

半主机是ARM Cortex微控制器上可用的许多有趣功能之一。当调试器运行时,它允许嵌入式程序利用连接的计算机的功能。

这篇文章介绍了半主机,并展示了如何使用它并将其集成到您自己的嵌入式项目中。

根据ARM文档1,半主机是一种机制,它使在ARM目标上运行的代码能够在运行调试器的主机上进行通信和使用输入/输出功能。

换句话说,基于ARM的MCU可以运行C库函数,例如printf(),scanf甚至fopen,并使它们直接与连接到设备的主机交互。这样,它可以从屏幕,键盘或主机磁盘中受益。

这是通过调试程序代理暂停CPU目标来完成的,可以运行到断点指令(对于ARMv6-M或ARMv7-M,是BKPT 0xAB),也可以根据目标体系结构或处理器发送主管调用指令(SVC 0xAB或SVC 0x123456)。 。

然后,调试器代理通过读取r0的内容找出目标请求的操作,并在必要时访问r1指向的所有其他功能参数。

当CPU仍处于暂停状态时,主机将执行所请求的操作,并在允许处理器继续运行其程序之前将结果返回到r0。

/ *文件操作* / SYS_OPEN EQU 0x01 //在主机系统上打开文件或流。 SYS_ISTTY EQU 0x09 //检查文件句柄是否与文件或流/终端(例如stdout)相关联。 SYS_WRITE EQU 0x05 //写入文件或流。 SYS_READ EQU 0x06 //从当前光标位置的文件读取。 SYS_CLOSE EQU 0x02 //关闭已由SYS_OPEN打开的主机上的文件。 SYS_FLEN EQU 0x0C //获取文件的长度。 SYS_SEEK EQU 0x0A //将文件光标设置到文件中的给定位置。 SYS_TMPNAM EQU 0x0D //获取一个临时的绝对文件路径以创建一个临时文件。 SYS_REMOVE EQU 0x0E //删除主机系统上的文件。可能不安全! SYS_RENAME EQU 0x0F //重命名主机系统上的文件。可能不安全! / *终端I / O操作* / SYS_WRITEC EQU 0x03 //将一个字符写入调试终端。 SYS_WRITE0 EQU 0x04 //将以0结尾的字符串写入调试终端。 SYS_READC EQU 0x07 //从调试终端读取一个字符。 / *时间操作* / SYS_CLOCK EQU 0x10 SYS_ELAPSED EQU 0x30 SYS_TICKFREQ EQU 0x31 SYS_TIME EQU 0x11 / *系统/杂项。操作* / SYS_ERRNO EQU 0x13 //返回与半主机实现相关联的C库errno变量的值。 SYS_GET_CMDLINE EQU 0x15 //获取要用于运行的应用程序的命令行参数(对于main()为argc和argv)SYS_HEAPINFO EQU 0x16 SYS_ISERROR EQU 0x08 SYS_SYSTEM EQU 0x12

在此示例中,我在STM32上运行该程序,并将arm-none-eabitoolchain与openOCD gdbserver一起使用。取决于您的硬件,可以改用其他工具,有关使用哪个调试接口的更多详细信息,阅读本文是明智的。

并将规范字符串文件5更改为--specs = rdimon.specs以使用这些托管版本的syscalls。

最后一点实质上意味着在附加adebugger时可以使用系统调用(请注意,如果不存在调试器,则CPU可能会崩溃)。

现在在C代码端,您需要在启动半主机操作之前调用initialise_monitor_handles()。

#ifdef SEMIHOSTING extern void initialise_monitor_handles(void); #endif int main(void){#ifdef SEMIHOSTING initialise_monitor_handles(); #endif //其他任务... #ifdef SEMIHOSTING printf(" hello world!\ n"); printf(" hello world!\ n"); #endif //其他任务...返回0; }

如果您使用的是crt0初始化函数,则在main()之前已经为您调用了initialise_monitor_handles()。

完成此操作后,您可以照常编译和编程微控制器,但是,一旦微控制器闪烁,则应停止运行而不是运行。

就我而言,我在一个终端中启动OpenOCD gdbserver,以刷新,重置和停止CPU。然后从另一个终端运行gdb以连接到服务器,并在服务器端6启用半主机。

$ arm-none-eabi-gdb -ex"目标扩展远程本地主机:3333" -ex"显示器重置暂停" -ex" monitor arm半主机启用"

如果在启用服务器中的半主机之前调用了initialise_monitor_handles(),则可能由于意外的调试事件而发生HardFault。

启用半主机后,您可以从gdb运行程序,并且这些mihosting操作将由gdbserver处理。 我希望阅读这篇文章会很有趣,因为半主机不是嵌入式开发中常用的方法,因为它会降低执行速度,但是在某些情况下它会非常方便。 看到您想更改的内容吗? 提交拉取请求或在GitHub上发布问题