1)实验平台:正点原子STM32MP157开发板
2)购买链接:https://item.taobao.com/item.htm?&id=629270721801
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-318813-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子STM32MP157技术交流群:691905614
我们在前面的第六章节分析STM32CubeMP1固件包的时候,固件包下有一个OpenAMP文件夹,AMP是指非对称多处理(Asymmetric Multiprocessing),非对称多处理是指各核的结构并非对称,例如STM32MP1是两个Cortex-A7内核加一个Cortex-M4内核的组合,各个核结构并非对称。OpenAMP常用于处理器间通信,OpenAMP软件框架为开发AMP系统提供了必要的API函数,可以基于这些API实现核间通信。
OpenAMP下的Remoteproc允许本地处理器与系统上可用的远程处理器进行通信,对于STM32MP157,本地处理器就是指Cortex-A7,远程处理器指Cortex-M4,本节我们就利用Remoteproc来让Cortex-A7远程控制Cortex-M4的生命周期,即加载、启动和停止Cortex-M4上的固件(.elf或者.axf文件)。
前面的Cortex-M4开发实验部分我们是在线仿真的,即通过ST-Link将程序下载到SRAM中运行,开发板掉电后程序就会丢失,这种方式适合在Cortex-M4开发调试阶段,也叫工程模式(Engineering Mode)。如果在核间通信的时候,我们就需要使用量产模式(Production Mode)来进行联合调试,下面我们讲解如何实现Cortex-A7和Cortex-M4联合调试。
关于Cortex-A7和Cortex-M4双核通信的相关实验,正点原子会有独立的文档章节进行讲解。本节我们只是讲解如何在Linux操作系统中使用Cortex-A7去加载、启动和停止运行Cortex-M4的固件。
本章将分为如下几个小节:
28.1、Remoteproc框架简介;
28.2、使用STM32CubeIDE进行调试;
28.3、拷贝文件后手动调试;
28.1 Remoteproc框架简介
28.1.1 Remoteproc框架
对于具有非对称多处理的SOC,不同的核心可能跑不同的操作系统,例如STM32MP157的Cortex-A7运行Linux操作系统,Cortex-M4可以运行RTOS操作系统。为了使运行Linux的主处理器与协处理器之间能够轻松通信,Linux引入了Remoteproc框架,该框架允许不同的平台/体系结构在抽象硬件差异的同时控制那些远程协处理器,如主处理器加载协处理器固件,打开、关闭或配置协处理器等。此外,Remoteproc框架还添加了RPMsg和Virtio,所以Remoteproc驱动只需要提供一些低级处理程序,其它RPMsg驱动程序就可以正常工作。
Remoteproc驱动提供可直接调用的API,方便用户层去调用,在Linux操作系统下已经有remoteproc 的相关驱动和API,这里我们就不需要过多地去关注remoteproc 了,我们直接使用它来控制M4内核即可(Remoteproc、RPMsg和Virtio是OpenAMP下重要的框架,在后面的章节我们会对它们进行讲解)。STM32MP157的Linux下Remoteproc框架如下:
图28.1.1. 1 Remoteproc框架
在Remoteproc框架框图中,我们查看两个重要的组成部分:
remoteproc是通用远程处理框架部分,其作用是:
1)将.elf文件加载到Cortex-M4内核中(在MDK下是.axf文件);
2)解释.elf文件资源表以设置关联的资源(例如IPC和内存分割等);
3)控制Cortex-M4内核启动、关闭;
4)提供监视和调试远程服务;
stm32_rproc是远程处理器平台(即M4)驱动程序,其作用是:
1)向Remoteproc框架注册供应商特定的功能(如回调部分);
2)处理Cortex-A7和Cortex-M4关联的平台资源(例如寄存器,看门狗,复位,时钟和存储器);
3)通过邮箱框架将通知转发到M4;
以上Cortex-A7称为主处理器,Cortex-M4称为协处理器,主处理器先启动,然后引导协处理器启动,主处理器再加载协处理器固件。总之,Remoteproc框架实现了对远程协处理器生命周期的控制,它允许Cortex-A7主处理器将.elf固件加载到Cortex-M4内核中,然后解析固件资源列表后再启动/关闭/配置Cortex-M4内核。而RPMsg(远程处理器消息传递,英文:Remote Processor Messaging)通过共享内存向远程CPU发送消息或从远程CPU接收消息。
下面,我们利用Remoteproc来实现Cortex-A7加载并运行Cortex-M4固件(.elf文件)。
28.1.2 实验准备
实验前,我们需要以下硬件资源:
1)正点原子STM32MP157开发板(配套电源线);
2)2根Type-C线;
3)ST-Link下载器(加转接板和T口USB线);
4)一张TF卡或U盘;
5)一根网线;
以上的第3和第4项根据选择的环境不同而需求不同,例如:如果使用STM32CubeIDE将固件通过Remoteproc下载到开发板的Linux操作系统中的话,那么ST-Link下载器就必须要用到;如果选择通过将TF卡或者U盘拷贝固件到开发板的话,则不需要ST-Link下载器;如果使用网络(如FTP或SSH)的方式传输固件的话,则第3和第4项都不需要,但是要一根网线。
注意事项:
一开始ST推出了STM32CubeIDE1.4.0版本,本教程一直使用的也是STM32CubeIDE1.4.0版本,使用正常,没发现什么问题。后来ST又推出了STM32CubeIDE1.5以及STM32CubeIDE1.6.1,测试发现STM32CubeIDE1.6.1无法识别MPU Serial(详情请见第28.2小节),可能是新版本软件的Bug或者其它原因,目前遇见这种情况的小伙伴,他们将STM32CubeIDE1.6.1卸载,再安装STM32CubeIDE1.4.0,然后再安装STM32CubeIDE1.6.1就可以了,这也是奇怪,笔者还未发现具体是什么原因,如果找到原因的朋友可以在正点原子论坛http://www.openedv.com/forum.php发表你的解决办法,大家一起学习,共同进步!
1.硬件连接
1)使用STM32CubeIDE进行调试
下面,我们采用STM32CubeIDE将固件通过Remoteproc下载到开发板的Linux操作系统中,我们先进行连线操作。首先,需要使用一根Type-C线将开发板的USB_OTG接口接到PC的USB接口,用于模拟出一个USB网卡,然后再用一根Type-C线将开发板的USB_TTL接口接到PC的USB接口,用于模拟通过串口获取USB网卡的IP,以实现开发板和电脑通信,如下图:
图28.1.2.1.1硬件连接图1
2)网络传输文件
如果不是使用STM32CubeIDE进行调试,而是使用网络的方式传输固件的话,硬件连接方式如下,只需要接电源线以及在USB_TTL口接Type-C线,外加网口接一根网线(网线另一端可以接路由器或者和电脑直连,根据个人情况来选择):
在这里插入图片描述
图28.1.2.1.2硬件连接图1
4)采用TF或者U盘卡拷贝固件
如果使用TF卡或者U盘等存储设备来拷贝的话,开发板只需要接电源线以及在USB_TTL接口接Type-C线即可。
2.启动Linux操作系统
先接好线,开发板拨码开关拨到010,即eMMC启动方式,因为开发板出厂Linux系统已经烧录在eMMC里了,接好电源线启动开发板,等待A7启动完成。判断A7是否启动完成,可以通过观察底板的LED0是否在闪烁(出厂的Linux系统在设备树里已经默认配置LED0为心跳灯,启动进入Linux系统后,LED0会闪烁),或者在串口终端查看串口的打印信息,如果开发板底板的LED0在闪烁或者串口终端已经打印“root@ATK-stm32mp1”,则说明Linux 系统已经完全启动。
图28.1.2.2.1串口打印信息
此时,查看设备管理器,网络适配器下多出了“Remote NDIS Compatible Device #2”,该设备是USB_OTG模拟的USB网卡;端口下多出了一个CH340设备口,是USB_TTL的COM口(这里显示COM65),如下图所示:
图28.1.2.2.2设备管理器情况
如下,在网络连接处也可以看到模拟的USB网卡“Remote NDIS Compatible Device #2”:
图28.1.2.2.3网络连接情况
如果没有看到模拟的USB网卡,可以检查开发板Linux系统是否正常运行、连接在USB_OTG口的Type-C线是否接好以及线是否有问题等,可以尝试重启开发板或者重新接好Type-C线。下面,我们利用此USB网卡并借助Remoteproc将固件(.elf文件)传输到Linux操作系统中。
28.2 使用STM32CubeIDE进行调试
28.2.1 操作步骤
如果使用STM32CubeIDE来开发M4,最终生成.elf文件,可通过STM32CubeIDE将.elf文件上传到开发板的“/lib/firmware”目录下并自动运行。
我们本小节实验以第十一章的实验来进行测试,首先,按照28.1.2小节讲解的步骤接好线,再将ST-Link接在开发板JTAG口,然后开发板从eMMC启动,等待进入Linux操作系统,注意观察是否有USB网卡:
28.2. 1 硬件连接图
然后打开STM32CubeIDE,我们需要设置串行过滤器,因为默认情况下,STM32CubelDE是只将STMicroelectronics和FTDI设备作为首选的串行设备,其它设备无法识别,所以我们要关闭默认选项而选择我们需要的设备,点击“Windows PreferencesSTM32CubeMPU Serial”,设置如下:
28.2. 2 设置属性
可能会出现多个COM口,根据设备管理器显示的设备来选中对应的COM口:
28.2. 3 勾选COM口
点击Debug配置选项,进入Debug Configurations配置界面:
28.2. 4 Debug配置
如下,选中默认已有的BEEP_CM4 Debug配置项,如果没有此配置项,可以双击“STM32 Cortex-M C/C++ Applications”来新建一个配置项,关于这些操作,相信大家都已经很熟悉了,如果不清除的,可以看前面第四章的实验部分。
28.2. 5 Debug工程配置
进入调试器配置部分,按照如下来配置,在Load Mode配置项选中“thru JTAG/SWD link(Production mode),Serial Port”处根据设备管理器显示部分选择COM口,这里选择“MPU Serial (COM65)”,“Inet Address”处会自动显示192.168.7.1,此IP地址就是USB网卡的IP地址:
28.2. 6 调试器配置
如果此时在Linux操作系统下输入ifconfig,可以看到USB网卡的IP为192.168.7.1,STM32CubeIDE就是通过此IP地址将.elf文件传输到Linux操作系统的。
28.2. 7 查看USB网卡
点击Debug后,STM32CubeIDE开始将BEEP_CM4.elf文件下载到Linux文件系统的“/lib/firmware”下,下载好后,系统会自动解析BEEP_CM4.elf文件资源,然后设置相关的资源,并运行程序:
28.2. 8 使用STM32CubeIDE进行调试
此时,开发板的蜂鸣器开始嘀嗒嘀嗒发出声响,在串口终端的“/lib/firmware”下也可以找到BEEP_CM4.elf文件,同时串口终端打印一些调试信息:
1 remoteproc remoteproc0: powering up m4
2 remoteproc remoteproc0: Booting fw image BEEP_CM4.elf, size 1920276
3 remoteproc remoteproc0: header-less resource table
4 remoteproc remoteproc0: no resource table found for this firmware
5 remoteproc remoteproc0: header-less resource table
6 remoteproc remoteproc0: remote processor m4 is now up
28.2. 9 串口打印信息
以上的打印信息中:
第1行:启动M4;
第2行,启动固件BEEP_CM4.elf,并提示该固件文件大小为1920276(指文件大小,不是程序大小);
第3和第5行,A7打印“header-less resource table”表示资源表大小为0,资源表是描述固件资源的一个条目(或者说数据结构),在加载M4固件的时候,A7会去解析固件,并检查资源表的信息,如果资源表大小为0,就会打印“header-less resource table”;
第4行,打印提示没有这个固件的资源表;
第6行,远程处理器M4已经启动,这里主处理器是A7。
可以点击STM32CubeIDE的暂停键暂停调试,蜂鸣器就停止发声,或者点击停止键退出调试:
28.2. 10 使用STM32CubeIDE调试功能
28.2.2 fw_cortex_m4.sh脚本分析
本小节可以作为对前面STM32CubeIDE操作过程的理解,可以不必深究。
1、fw_cortex_m4.sh脚本分析
前面操作完成后,跑Linux操作系统的A7成功运行了跑裸机的M4的固件,这个过程其实是由一个fw_cortex_m4.sh脚本参与完成的,该脚本我们在前面的8.3小节里有简单提到过,因为还没有实验用到该脚本,所以我们前面未对该脚本进行解释。下面我们来看看这个脚本。
在STM32CubeIDE的RemoteProc目录下可以看到该脚本:
图28.2.2.1 IDE中的fw_cortex_m4.sh脚本
不能直接在STM32CubeIDE上双击查看此脚本,可以使用代码查看软件来查看此脚本的内容,内容如下:
1 #!/bin/sh
2
3 rproc_class_dir="/sys/class/remoteproc/remoteproc0"
4 fmw_dir="/lib/firmware"
5 fmw_name= "BEEP_CM4.elf"
6
7 cd $(/usr/bin/dirname $(/usr/bin/readlink -f $0))
8
9 if [ $1 == "start" ]
10 then
11 # Start the firmware
12 /bin/rm -f /lib/firmware/$fmw_name
13 /bin/ln -s $PWD/lib/firmware/$fmw_name $fmw_dir
14 /bin/echo -n "$fmw_name" > $rproc_class_dir/firmware
15 /bin/echo -n start > $rproc_class_dir/state
16 fi
17
18 if [ $1 == "stop" ]
19 then
20 # Stop the firmware
21 /bin/echo -n stop > $rproc_class_dir/state
22 fi
如果学过shell的基本语法,这段脚本很容易可以看懂,我们简单了解一下该脚本做了什么:
第3行,/sys/class/remoteproc/remoteproc0是加载、启动和停止固件的文件操作路径,Linux下一切皆文件,我们实际上是通过操作文件来实现对应的功能。我们查看此路径下有什么:
图28.2.2.2加载、启动、停止固件相关文件
可以看到/sys/class/remoteproc/remoteproc0/firmware文件的内容是BEEP_CM4.elf,这个就是本实验M4固件的名字,默认情况下该文件的内容是rproc-m4-fw,在A7加载M4固件后,此文件内容就会被修改。
第4行,/lib/firmware是固件(或者固件的软链接文件)所在的路径,我们看看该路径下有什么:
图28.2.2.3链接文件
可以看到,/lib/firmware下的BEEP_CM4.elf文件指向了/usr/local/projects/BEEP_CM4/lib/firmware/BEEP_CM4.elf,也就是/lib/firmware/BEEP_CM4.elf文件是软链接文件,实际的文件是/usr/local/projects/BEEP_CM4/lib/firmware/BEEP_CM4.elf,也就是运行固件时,实际上是运行/usr/local/projects/BEEP_CM4/lib/firmware/下的固件,如下图,可以看到BEEP_CM4.elf文件大小是1.9MB,同时我们注意到,在/usr/local/projects/BEEP_CM4下有一个fw_cortex_m4.sh文件,此文件的内容和STM32CubeIDE上的fw_cortex_m4.sh文件内容一样:
图28.2.2.4 文件系统中的fw_cortex_m4.sh文件
第5行,固件的名字是BEEP_CM4.elf;
第7行,熟悉shell基本指令的话,就知道/usr/bin/dirname表示获取当前脚本的路径,$(/usr/bin/readlink -f 0 ) 表 示 找 出 软 链 接 所 指 向 的 位 置 , 也 就 是 f w c o r t e x m 4. s h 文 件 的 位 置 , 整 个 语 句 0)表示找出软链接所指向的位置,也就是fw_cortex_m4.sh文件的位置,整个语句 0)表示找出软链接所指向的位置,也就是fwcortexm4.sh文件的位置,整个语句(/usr/bin/dirname $(/usr/bin/readlink -f $0))就是指获取当前脚本文件fw_cortex_m4.sh所在的目录。上图中我们已经知道脚本位于/usr/local/projects/BEEP_CM4下了,cd $(/usr/bin/dirname $(/usr/bin/readlink -f $0))就表示进入了/usr/local/projects/BEEP_CM4目录,该目录就是当前脚本所在的位置,如果执行pwd指令的话,就显示当前目录为:
/usr/local/projects/BEEP_CM4
图28.2.2.5 fw_cortex_m4.sh文件所在路径
第9行,如果指令的第二个字符是"start",则执行第10~16行的shell语句。在shell的基本语法中,如果执行如下指令:
./test.sh start BEEP_CM4.elf
$0就是./test.sh,$1就是start, 2 就 是 B E E P C M 4. e l f , 大 家 理 解 其 操 作 含 义 即 可 。 我 们 看 看 10 16 行 做 了 什 么 : 第 12 行 , 先 删 除 / l i b / f i r m w a r e / B E E P C M 4. e l f 文 件 ; 第 13 行 , 执 行 / b i n / l n 下 的 l n 指 令 进 行 软 链 接 到 2就是BEEP_CM4.elf,大家理解其操作含义即可。我们看看10~16行做了什么: 第12行,先删除/lib/firmware/ BEEP_CM4.elf 文件; 第13行,执行/bin/ln下的ln指令进行软链接到 2就是BEEPCM4.elf,大家理解其操作含义即可。我们看看10 16行做了什么:第12行,先删除/lib/firmware/BEEPCM4.elf文件;第13行,执行/bin/ln下的ln指令进行软链接到PWD/lib/firmware/ f m w n a m e , 这 里 fmw_name,这里 fmwname,这里PWD=/usr/local/projects/BEEP_CM4, f m w n a m e = B E E P C M 4. e l f , 所 以 / l i b / f i r m w a r e / 下 的 B E E P C M 4. e l f 文 件 实 际 上 是 链 接 到 了 / u s r / l o c a l / p r o j e c t s / B E E P C M 4 / / l i b / f i r m w a r e / fmw_name=BEEP_CM4.elf,所以/lib/firmware/下的BEEP_CM4.elf文件实际上是链接到了/usr/local/projects/BEEP_CM4//lib/firmware/ fmwname=BEEPCM4.elf,所以/lib/firmware/下的BEEPCM4.elf文件实际上是链接到了/usr/local/projects/BEEPCM4//lib/firmware/fmw_name/ BEEP_CM4.elf:
图28.2.2.6固件链接的位置
第14行," f m w n a m e " = B E E P C M 4. e l f , fmw_name"= BEEP_CM4.elf, fmwname"=BEEPCM4.elf,rproc_class_dir/firmware= /sys/class/remoteproc/remoteproc0 /firmware,表示将字符串BEEP_CM4.elf写入到文件/sys/class/remoteproc/remoteproc0 /firmware中;
第15行,表示运行固件,当固件运行后,/sys/class/remoteproc/remoteproc0/state文件的内容是running,表示固件正在运行,如果显示的是offline,则表示固件处于停止状态:
图28.2.2.7固件在运行
第18行,如果指令的第二个字符是"start",则执行第19~22行的shell语句,第21行就是停止固件的作用。
2、fw_cortex_m4.sh的使用
经过前面分析,我们大体知道STM32CubeIDE做了如下工作:
1)STM32CubeIDE将fw_cortex_m4.sh文件传输到Linux文件系统的/usr/local/projects/BEEP_CM4目录下,将BEEP_CM4.elf文件传输到/usr/local/projects/BEEP_CM4/lib/firmware目录下;
2)执行fw_cortex_m4.sh脚本文件,将/lib/firmware/BEEP_CM4.elf链接到/usr/local/projects/BEEP_CM4/lib/firmware/BEEP_CM4.elf,然后再将字符串BEEP_CM4.elf写入到文件/sys/class/remoteproc/remoteproc0 /firmware中,再去运行或者停止BEEP_CM4.elf固件。
我们可以模仿fw_cortex_m4.sh脚本,自己去编写一个类似的脚本来实现加载、启动、停止固件。如下,我们在/lib/firmware目录下新建一个test.sh脚本,脚本内容如下:
#!/bin/sh
rproc_class_dir="/sys/class/remoteproc/remoteproc0"
fmw_dir="/lib/firmware"
cd /sys/class/remoteproc/remoteproc0
if [ $1 == "start" ]
then
/bin/echo -n $2 > $rproc_class_dir/firmware
/bin/echo -n start > $rproc_class_dir/state
fi
if [ $1 == "stop" ]
then
/bin/echo -n stop > $rproc_class_dir/state
fi
编写好脚本文件后,执行如下指令给脚本文件可读可写可执行权限:
图28.2.2. 8 给脚本文件可读可写可执行权限
因为/lib/firmware目录下已经有了BEEP_CM4.elf,我们可以执行如下指令来加载、运行以及停止BEEP_CM4.elf固件:
/* 加载、运行固件 /
./test.sh start BEEP_CM4.elf
/ 停止运行固件 */
./test.sh stop BEEP_CM4.elf
图28.2.2.9编写脚本实现加载、运行、停止固件
所以,我们可以不使用STM32CubeIDE来运行M4固件,使用该脚本就可以了。
28.3 传输文件后手动运行调试
如果不使用STM32CubeIDE来进行调试,可以使用TF卡或者U盘拷贝文件的方式将BEEP_CM4.elf文件拷贝到开发板Linux文件系统的/lib/firmware目录下,然后执行相关指令对固件BEEP_CM4.elf进行加载、运行和关闭操作(如果操作了前面28.2小节实验,实际上/lib/firmware目录下已经有该文件了)。如果使用的是网络的方式将固件BEEP_CM4.elf传输到Linux文件系统的/lib/firmware目录下,硬件接线方式请参考第28.1.2小节,按照如下操作进行传输文件。
28.3.1 通过网络传输文件
成功编译工程后,进入工程CM4\Debug下可以看到BEEP_CM4.elf文件,同时按下Shift键并按下鼠标右键,可以打开选项界面,如下,可以看到Powershell选项或者如果电脑安装了Git的话,可以看到Git Bash Here,这两个也就是shell终端,可以打开shell后操作Linux指令,两者选其中一个就可以,下面我们就选择使用Powershell吧。
图28.3.1. 1 打开Powershell
打开Powershell后,输入ls后按下回车,可以看见当前目录下有什么文件:
图28.3.1. 2 查看文件
开发板启动进入Linux操作系统,输入ifconfig可以查看开发板的IP地址,如下IP地址是192.168.1.26:
图28.3.1. 3 查看开发板的IP地址
我们就是通过此IP将文件传输到开发板的,先检查开发板和Windows是否能正常通信,在Powershell中ping开发板IP地址可以ping通,说明两者通信正常,可以互传文件:
图28.3.1. 4 测试Windows和Linux通信
执行如下指令将BEEP_CM4.elf文件传输到开发板的/lib/firmware目录下:
scp BEEP_CM4.elf [email protected]:/lib/firmware
图28.3.1. 5 将文件传输到Linux
在开发板的/lib/firmware目录下可以看到已经有该文件了:
图28.3.1. 6 文件传输成功
28.3.2 加载固件和测试
传输BEEP_CM4.elf文件成功后,通过执行以下指令来手动加载、运行或者关闭固件:
/* 加载固件 /
echo BEEP_CM4.elf > /sys/class/remoteproc/remoteproc0/firmware
/ 开启,此时程序就会运行,蜂鸣器就嘀嗒嘀嗒发出响声 /
echo start > /sys/class/remoteproc/remoteproc0/state
/ 关闭,停止固件运行 */
echo stop > /sys/class/remoteproc/remoteproc0/state
不过要注意的是,执行最后一条stop指令的时候,如果此时蜂鸣器刚好由不响到响,执行此条指令以后蜂鸣器会一直响,如果此时蜂鸣器刚好由响到不响,执行此指令以后蜂鸣器就不响了,也就是停止在执行指令的那个状态。
图28.3.2.1串口打印信息
如果在执行启动固件的指令,蜂鸣器优惠继续响:
scp BEEP_CM4.elf [email protected]:/lib/firmware
当然,也可以参考前面28.2.2小节讲解的,执行如下指令即可:
/* 加载、运行固件 /
./test.sh start BEEP_CM4.elf
/ 停止运行固件 */
./test.sh stop BEEP_CM4.elf
至此,我们已经掌握了如何在Linux下去运行M4固件,在双核通信实验部分,我们也是基于此方式来实现。关于双核通信的实验,大家可以看《【正点原子】STM32MP1异核通讯》文档教程。