ZYNQ MPSOC 软件无线电开发[软件部分(2)] -在Petalinux工程中添加驱动与自启动脚本
ZYNQ MPSOC 软件无线电开发[软件部分(1)] - 导出Vivado硬件设计并构建基本的Petalinux工程
接上一章,现在已经完成了基本工程的创建,硬件上是Alinx AXU3EGB开发板,PL端连接了AD9238和AD9767。
从该部分往下,大部分内容都可以在Xilinx的官方Petalinux Userguide里找到,而且恰好有官方简中
PetaLinux 工具文档参考指南 (ug1144)
1. 添加自启动脚本
进入Petalinux工程目录,创建一个apps,命名autostart,–enable是启用该apps
petalinux-create -t apps --template install --name autostart --enable
INFO: Create apps: autostart
INFO: New apps successfully created in /home/xlnxdev-2020-1/peta_prj/adda_probtest/petalinux/project-spec/meta-user/recipes-apps/autostart
INFO: Enabling created component...
INFO: sourcing build environment
INFO: silentconfig rootfs
INFO: autostart has been enabled
上面的输出告诉了我们apps的路径,打开文件
~/peta_prj/adda_probtest/petalinux/project-spec/meta-user/recipes-apps/autostart/files/autostart
修改为
#!/bin/sh
echo "Simple applecations autostart"
USERHOOK_SD0=/media/sd-mmcblk0p1/autostart.sh
USERHOOK_SD1=/media/sd-mmcblk1p1/autostart.sh
if [ -f $USERHOOK_SD0 ]; then
sh $USERHOOK_SD0 &
fi
if [ -f $USERHOOK_SD1 ]; then
sh $USERHOOK_SD1 &
fi
该脚本会被编译到Petalinux中,自动查找SD卡(sd-mmcblk1)或者板载emmc(sd-mmcblk0)第一分区的autostart.sh并执行。
现在打开
~/peta_prj/adda_probtest/petalinux/project-spec/meta-user/recipes-apps/autostart/autostart.bb
修改为
#
# This file is the autostart recipe.
#
SUMMARY = "Simple autostart application"
SECTION = "PETALINUX/apps"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://autostart \
"
S = "${WORKDIR}"
inherit update-rc.d
INITSCRIPT_NAME = "autostart"
INITSCRIPT_PARAMS = "start 99 5 ."
do_install() {
install -d ${
D}/${
sysconfdir}/init.d
install -m 0755 ${
S}/autostart ${
D}/${
sysconfdir}/init.d/autostart
}
RDEPENDS_${
PN}_append += "bash"
功能是复制autostart到根文件系统 init.d,然后添加 0755 权限。 至于INITSCRIPT_PARAMS
的内容可以参考其他博客,主要是控制启动顺序和权限。RDEPENDS_${PN}_append += "bash"
是指该脚本依赖bash。
特别指出RDEPENDS_${PN}_append
,如果你用的是新版的Petalinux,这部分需要修改成RDEPENDS:${PN}:append = "bash"
,新的yocto bitbake对override语法有改动。参考https://docs.yoctoproject.org/migration-guides/migration-3.4.html或者ug1144
2. 添加驱动程序
执行命令
petalinux-create -t modules -n ad9767 --enable
刚才的自启动脚本是apps,对于驱动程序一类使用modules。
INFO: Create modules: ad9767
INFO: New modules successfully created in /home/xlnxdev-2020-1/peta_prj/adda_probtest/petalinux/project-spec/meta-user/recipes-modules/ad9767
INFO: Enabling created component...
INFO: sourcing build environment
INFO: silentconfig rootfs
INFO: ad9767 has been enabled
打开生成的.c文件
~/peta_prj/adda_probtest/petalinux/project-spec/meta-user/recipes-modules/ad9767/files/ad9767.c
写入以下驱动程序源码
#include <asm/uaccess.h>
#include <linux/dma/xilinx_dma.h>
#include <linux/cdev.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/delay.h>
#define MODULE_NAME "axi_dac9767_dma"
#define AXI_ADC_MINOR_START 0
#define AXI_ADC_MINOR_COUNT 16
#define AXI_ADC_CALLBACK_TIMEOUTMSEC 200
#define SUCCESS 0
#define FAILURE -1
#define MAX_BUF_COUNT 8
/* IOCTL defines */
#define AXI_ADC_IOCTL_BASE 'W'
#define AXI_ADC_SET_SAMPLE_NUM _IO(AXI_ADC_IOCTL_BASE, 0)
#define AXI_ADC_SET_DMA_LEN_BYTES _IO(AXI_ADC_IOCTL_BASE, 1)
#define AXI_ADC_DMA_INIT _IO(AXI_ADC_IOCTL_BASE, 2)
#define AXI_ADC_DMA_START _IO(AXI_ADC_IOCTL_BASE, 3)
#define AXI_ADC_DMA_DEINIT _IO(AXI_ADC_IOCTL_BASE, 4)
#define AXI_ADC_DMA_CYCLIC_SEND _IO(AXI_ADC_IOCTL_BASE, 5)
#define AXI_ADC_DMA_CYCLIC_STOP _IO(AXI_ADC_IOCTL_BASE, 6)
struct axi_adc_dev
{
struct mutex mutex;
struct platform_device *pdev;
/* ADC Hardware device constants */
void *adc_virtaddr;
/* DMA stuff */
struct dma_chan *tx_chan;
dma_cookie_t tx_cookie;
struct completion tx_cmp;
unsigned long tx_tmo;
int bd_cnt;
struct scatterlist *tx_sg;
/*DMA address of buffer */
dma_addr_t *dma_dsts;
u8 **dsts;
int adc_sample_num;
int dma_len_bytes;
size_t cyclic_len;
};
/*ADC channel name */
static const char adc_channels[][20] =
{
{
"dac0"},
{
"dac1"},
{
"dac2"},
{
"dac3"},
{
"dac4"},
{
"dac5"},
{
"dac6"},
{
"dac7"},
{
"dac8"},
{
"dac9"},
{
"dac10"},
{
"dac11"},
{
"dac12"},
{
"dac13"},
{
"dac14"},
{
"dac15"},
};
static struct axi_adc_dev *axi_adc_dev[16];
static int dev_index = 0;
static dev_t devno;
static struct cdev adc_cdev;
static struct class *axi_adc_class;
static void dma_slave_tx_callback(void *completion)
{
complete(completion);
}
/* File operations */
int axi_adc_dma_open(struct inode *inode, struct file *filp)
{
unsigned int mn;
mn = iminor(inode);
/*Assign minor number for later reference */
filp->private_data = (void *)mn;
return SUCCESS;
}
int axi_adc_dma_release(struct inode *inode, struct file *filp)
{
return SUCCESS;
}
ssize_t axi_adc_dma_write(struct file *filep, const char __user *buf,
size_t count, loff_t *f_pos)
{
int minor = 0;
/* Query minor number.
* @To be extended for multiple channel support
*/
minor = (int)filep->private_data;
axi_adc_dev[minor]->cyclic_len = count;
/* Validation for read size */
if (count > axi_adc_dev[minor]->dma_len_bytes)
{
axi_adc_dev[minor]->cyclic_len = axi_adc_dev[minor]->dma_len_bytes;
}
copy_from_user(axi_adc_dev[minor]->dsts[0], buf, axi_adc_dev[minor]->cyclic_len);
return count;
}
/* IOCTL calls provide interface to configure ,start and stop
DMA engine */
static long axi_adc_dma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
enum dma_ctrl_flags flags;
enum dma_status status;
int ret = 0;
int i;
int minor = (int)file->private_data;
struct dma_device *tx_dev