这里假设需求为A7和M4直接发送大量数据。
前面将A7和M4通信,封装成了虚拟串口。如果需要传输大量数据,且对性能有一定要求,可尝试本节示例,在Linux驱动层和M4数据传输。
和上一小节一致。
参考前面的示例,依次在STM32CubeMX配置UART8、IPCC和OPENAMP,然后生成初始化代码。
Linux内核提供了一个驱动示例“linux-origin_master/samples/rpmsg/rpmsg_client_sample.c”,在此驱动上简单修改了打印内容,方便查看。
/*
* Remote processor messaging - sample client driver
*
* Copyright (C) 2011 Texas Instruments, Inc.
* Copyright (C) 2011 Google, Inc.
*
* Ohad Ben-Cohen <ohad@wizery.com>
* Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rpmsg.h>
#define MSG "hello world! A7->M4"
#define MSG_LIMIT 100
struct instance_data {
int rx_count;
};
static int rpmsg_sample_cb(struct rpmsg_device *rpdev, void *data, int len,
void *priv, u32 src)
{
int ret;
struct instance_data *idata = dev_get_drvdata(&rpdev->dev);
//dev_info(&rpdev->dev, "incoming msg %d (src: 0x%x)\n", ++idata->rx_count, src);
//print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1, data, len, true);
printk(KERN_DEBUG "received_rpmsg: %p %d \n", data, ++idata->rx_count);
/* samples should not live forever */
if (idata->rx_count >= MSG_LIMIT) {
//dev_info(&rpdev->dev, "goodbye!\n");
return 0;
}
/* send a new message now */
ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG));
if (ret)
dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
else
printk(KERN_DEBUG "send_rpmsg: %s \n", MSG);
return 0;
}
static int rpmsg_sample_probe(struct rpmsg_device *rpdev)
{
int ret;
struct instance_data *idata;
dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
rpdev->src, rpdev->dst);
idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL);
if (!idata)
return -ENOMEM;
dev_set_drvdata(&rpdev->dev, idata);
/* send a message to our remote processor */
ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG));
if (ret) {
dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
return ret;
}
else
printk(KERN_DEBUG "send_rpmsg: %s \n", MSG);
return 0;
}
static void rpmsg_sample_remove(struct rpmsg_device *rpdev)
{
dev_info(&rpdev->dev, "rpmsg sample client driver is removed\n");
}
static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = {
{ .name = "rpmsg-client-sample" },
{ },
};
MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table);
static struct rpmsg_driver rpmsg_sample_client = {
.drv.name = KBUILD_MODNAME,
.id_table = rpmsg_driver_sample_id_table,
.probe = rpmsg_sample_probe,
.callback = rpmsg_sample_cb,
.remove = rpmsg_sample_remove,
};
module_rpmsg_driver(rpmsg_sample_client);
MODULE_DESCRIPTION("Remote processor messaging sample client driver");
MODULE_LICENSE("GPL v2");
驱动比较简单,核心是“rpmsg_sample_cb()“,rpmsg收到数据后将回调该函数。从“data”获得M4发来的数据,通过“rpmsg_send()”将数据发给M4。
将该驱动和Makefile上传到Linux主机,根据内核源码路径,修改Makefile,编译即可得到“rpmsg_client_sample.ko”,驱动的开发流程参考Linux开发手册,这里不再赘述。同时,本示例也提供“rpmsg_client_sample.ko”文件,用户可以直接使用。
使用STM32CubeMX生成初始化代码后,下面将编写程序从A7接收数据,并发送数据。
/* Create an rpmsg channel to communicate with the Master processor CPU1(CA7) */
OPENAMP_create_endpoint(&resmgr_ept, RPMSG_SERVICE_NAME, RPMSG_ADDR_ANY, rx_callback, NULL);
注意这里的“RPMSG_SERVICE_NAME”和驱动里“rpmsg_device_id”结构体的“.name”的名字一样。之后,一旦rpmsg通道数据,将回调rx_callback()函数。
此函数里,需要将接收的数据复制到用户内存,并修改接收标志位。
static int rx_callback(struct rpmsg_endpoint *rp_chnl, void *data, size_t len, uint32_t src, void *priv)
{
/* copy received msg, and raise a flag */
memcpy(received_rpmsg, data, len > sizeof(received_rpmsg) ? sizeof(received_rpmsg) : len);
printf("received_rpmsg=%s\r\n", received_rpmsg);
rx_status = SET;
return 0;
}
“OPENAMP_check_for_message()”查询MailBox状态。当收到数据时,“rx_callback()”会保存好收到数据,然后修改“rx_status”标志位。主函数里发现“rx_status”标志位发生变化时,即可获取接收的数据。
while (1)
{
OPENAMP_check_for_message();
/* USER CODE END WHILE */
if (rx_status == SET)
{
/* Message received: send back a message anwser */
rx_status = RESET;
/*rpmsg收到数据*/
}
/*
使用“OPENAMP_send()”向A7发送数据。
if (rx_status == SET)
{
/* Message received: send back a message anwser */
rx_status = RESET;
if (++count < 100)
sprintf((char *)msg, "hello world! M4->A7 %02ld", count);
else
strcpy((char *)msg, "goodbye!");
if (OPENAMP_send(&resmgr_ept, msg, strlen((char *)msg) + 1) < 0)
{
printf("Failed to send message\r\n");
Error_Handler();
}
else
printf("send_rpmsg=%s\r\n", msg);
}
硬件连接和上一实验保持不变。编译得到“rpmsg_kernel_CM4.elf”,将它和驱动“rpmsg_client_sample.ko”一起上传到开发板Linux。然后加载驱动,将“rpmsg_kernel_CM4.elf”复制到“/lib/firmware/”目录,为了查看驱动的打印信息,这里还要修改内核的打印等级,设置如所示。
[root@100ask:~]$ insmod rpmsg_client_sample.ko
[root@100ask:~]$ cp rpmsg_kernel_CM4.elf /lib/firmware/
[root@100ask:~]$ echo 8 > /proc/sys/kernel/printk

接着启动固件,可以看到如图 3.3.2所示打印。可以看到启动M4后,A7向M4发送“helloworld! A7->M4”,同时收到M4发过来的“hello world!
M4->A7”。M4收到A7发过来的“hello world! A7->M4”,同时向A7发送“hello world!
M4->A7”,在极短时间里相互之间发送了100次。
