ZYNQ学习笔记-Win下基于WSL的嵌入式Linux开发环境搭建
由于常规在虚拟机上编写嵌入式Linux驱动非常麻烦,而且还没有代码提示,这里给出一种在Win下利用WSL搭建的开发环境。
安装依赖
安装WSL2
首先需要开启WSL功能,并且需要是WSL2,网上教程很多,这里不再多说。
安装交叉编译链
然后需要安装Linux交叉编译器,使用如下命令安装:
sudo apt install gcc-arm-linux-gnueabihf
安装完成后使用如下命令检验:
arm-linux-gnueabihf-gcc -v
编译linux内核源码
驱动编写需要引用内核中的源码,这里需要下载xilinx-linux内核源码。对于petalinux来说,在build/tmp/work-shared/plnx-zynq7/kernel-source下,复制文件夹到便于使用的位置。
或者直接到github复制,注意版本需要与实际使用的一致:
git clone --depth 1 git@github.com:Xilinx/linux-xlnx.git
下载完成后,使用ZYNQ的配置编译源码。这里使用的是Xilinx 官方的 ZYNQ ZC702 EVK 开发板的配置,由于我们只需要一些BSP无关的生成文件,因此这里直接用就行。
make ARCH=arm xilinx_zynq_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
ARCH指定架构,CROSS_COMPILE指定编译器,-j12指定编译使用12核心。
编译测试
这里编译一个LED驱动进行测试。
源码准备
新建一个文件夹,创建并编写一个简单的字符程序myled.c,这里不多说了。
然后编写测试app。
源码在最后附录。
Makefile文件
这里需要使用make moudule功能,在上面的文件夹中创建一个Makefile文件,填写以下内容:
KDIR := /home/lo/kernel-source #内核源码目录
obj-m := led.o
all:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KDIR) M=`pwd` modules
clean:
make -C $(KDIR) M=`pwd` clean
编写完成后直接make进行编译,得到.ko文件。
使用make clean清除编译产生的文件。
app编译即可:
arm-linux-gnueabihf-gcc ledapp.c -o ledapp
复制文件
使用scp命令将两个文件复制到开发板中,并进行测试。
scp led.ko ledapp root@192.168.2.138:/home/root/test
加载:
insmod led.ko
由于驱动中有自动创建节点的代码,因此不需要mknod。
进行测试:
./ledapp /dev/gpioled0 1 //点亮 LED 灯
./ledapp /dev/gpioled0 0 //关闭 LED 灯
卸载驱动:
rmmod led.ko
使用CLion
首先注意一点,Clion使用CMake进行工程构建,而linux内核源码使用Make,不能直接使用,因此这里只能查看代码不能编译。
新版Clion支持了Makefile,可以自行查看,这里没有更新。
Clion连接WSL
打开Clion,点击远程开发中的WSL。
点击连接到主机,选择使用的WSL实例(仅支持WSL2)。根据设置安装好对应WSl上的Clion版本,进行安装,Clion会自动进行,安装完成后选择项目目录打开。
此时应该会提示项目未设置。
创建CMake
创建CMakeLists.txt文件,并编写内容:
cmake_minimum_required(VERSION 3.26)# Clion自动生成
project(cliondev C)# Clion自动生成
set(CMAKE_C_STANDARD 99)# Clion自动生成,修改为C99
set(CMAKE_C_COMPILER "arm-linux-gnueabihf-gcc")
set(KERNEL_DIR "/home/lo/kernel-source") #设置内核目录
include_directories("${KERNEL_DIR}/include")
include_directories("${KERNEL_DIR}/include/uapi")
include_directories("${KERNEL_DIR}/include/generated")
include_directories("${KERNEL_DIR}/arch/arm/include")
include_directories("${KERNEL_DIR}/arch/arm/include/uapi")
include_directories("${KERNEL_DIR}/arch/arm/include/generated")
include_directories("${KERNEL_DIR}/arch/arm/include/generated/uapi")
add_definitions(-D__KERNEL__)
#add_compile_options("-includeautoconf.h")
add_executable(cliondev
led.c)# Clion自动生成
根据实际修改上面的内核目录。
之后重新加载目录,即可发现代码提示,可以愉快的开始编写了。
编译
Clion不能直接编译驱动,使用之前的Makefile脚本进行编译。
打开左下角终端,可以直接打开wsl的终端,在目录下输入make指令即可。
当然也可以使用CLion的脚本运行,在右上角配置中添加make脚本,直接运行即可(还可以将scp命令也添加到脚本中,实现编译后自动复制到开发板)。
使用Vscode
主要注意需要在设置文件中添加上面Cmake中写的头文件和定义,这里不多说了。
附录
led.c:
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled0"
/* dtsled 设备结构体 */
struct gpioled_dev {
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
int led_gpio; /* LED 所使用的 GPIO 编号 */
};
static struct gpioled_dev gpioled; /* led 设备 */
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled; /* 设置私有数据 */
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
int ret;
char kern_buf[1];
struct gpioled_dev* pgpioled=(struct gpioled_dev*)filp->private_data;
ret = copy_from_user(kern_buf, buf, cnt); // 得到应用层传递过来的数据
if(0 > ret) {
printk(KERN_ERR "kernel write failed!\r\n");
return -EFAULT;
}
if (0 == kern_buf[0])
gpio_set_value(pgpioled->led_gpio, 0); // 如果传递过来的数据是 0 则关闭 led
else if (1 == kern_buf[0])
gpio_set_value(pgpioled->led_gpio, 1); // 如果传递过来的数据是 1 则点亮 led
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* 设备操作函数 */
static struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
static int __init led_init(void)
{
const char *str;
int ret;
/* 1.获取 led 设备节点 */
gpioled.nd = of_find_node_by_path("/leds/led0");
if(NULL == gpioled.nd) {
printk(KERN_ERR "gpioled: Failed to get /led node\n");
return -EINVAL;
}
/* 2.读取 status 属性 */
ret = of_property_read_string(gpioled.nd, "status", &str);
if(!ret) {
if (strcmp(str, "okay"))
return -EINVAL;
}
/* 2、获取 label 属性值并进行匹配 */
ret = of_property_read_string(gpioled.nd, "label", &str);
if(0 > ret) {
printk(KERN_ERR "gpioled: Failed to get compatible property\n");
return ret;
}
if (strcmp(str, "ps_led")) {
printk(KERN_ERR "gpioled: Compatible match failed\n");
return -EINVAL;
}
printk(KERN_INFO "gpioled: device matching successful!\r\n");
/* 4.获取设备树中的 led-gpio 属性,得到 LED 所使用的 GPIO 编号 */
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(!gpio_is_valid(gpioled.led_gpio)) {
printk(KERN_ERR "gpioled: Failed to get led-gpio\n");
return -EINVAL;
}
printk(KERN_INFO "gpioled: led-gpio num = %d\r\n", gpioled.led_gpio);
/* 5.向 gpio 子系统申请使用 GPIO */
ret = gpio_request(gpioled.led_gpio, "LED-GPIO");
if (ret) {
printk(KERN_ERR "gpioled: Failed to request led-gpio\n");
return ret;
}
/* 6.将 led gpio 管脚设置为输出模式 */
gpio_direction_output(gpioled.led_gpio, 0);
/* 7.初始化 LED 的默认状态 */
ret = of_property_read_string(gpioled.nd, "default-state", &str);
if(!ret) {
if (!strcmp(str, "on"))
gpio_set_value(gpioled.led_gpio, 1);
else
gpio_set_value(gpioled.led_gpio, 0);
} else
gpio_set_value(gpioled.led_gpio, 0);
/* 8.注册字符设备驱动 */
/* 创建设备号 */
if (gpioled.major) {
gpioled.devid = MKDEV(gpioled.major, 0);
ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
if (ret)
goto out1;
} else {
ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
if (ret)
goto out1;
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("gpioled: major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
/* 初始化 cdev */
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
/* 添加一个 cdev */
ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
if (ret)
goto out2;
/* 创建类 */
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if (IS_ERR(gpioled.class)) {
ret = PTR_ERR(gpioled.class);
goto out3;
}
/* 创建设备 */
gpioled.device = device_create(gpioled.class, NULL,
gpioled.devid, NULL, GPIOLED_NAME);
if (IS_ERR(gpioled.device)) {
ret = PTR_ERR(gpioled.device);
goto out4;
}
return 0;
out4:
class_destroy(gpioled.class);
out3:
cdev_del(&gpioled.cdev);
out2:
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
out1:
gpio_free(gpioled.led_gpio);
return ret;
}
static void __exit led_exit(void)
{
/* 注销设备 */
device_destroy(gpioled.class, gpioled.devid);
/* 注销类 */
class_destroy(gpioled.class);
/* 删除 cdev */
cdev_del(&gpioled.cdev);
/* 注销设备号 */
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
/* 释放 GPIO */
gpio_free(gpioled.led_gpio);
}
/* 驱动模块入口和出口函数注册 */
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("LO");
MODULE_DESCRIPTION("LO ZYNQ GPIO LED Driver");
MODULE_LICENSE("GPL");
ledapp.c:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
/*
* @description : main 主程序
* @param - argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd, ret;
unsigned char buf[1];
if(3 != argc) {
printf("Usage:\n"
"\t./ledApp /dev/led 1 @ close LED\n"
"\t./ledApp /dev/led 0 @ open LED\n"
);
return -1;
}
/* 打开设备 */
fd = open(argv[1], O_RDWR);
if(0 > fd) {
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
/* 将字符串转换为 int 型数据 */
buf[0] = atoi(argv[2]);
/* 向驱动写入数据 */
ret = write(fd, buf, sizeof(buf));
if(0 > ret){
printf("LED Control Failed!\r\n");
close(fd);
return -1;
}
/* 关闭设备 */
close(fd);
return 0;
}
本次使用的设备树:
/include/ "system-conf.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include "dt-bindings/gpio/gpio.h"
#include "dt-bindings/input/linux-event-codes.h"
/{
model = "BX71 Development Board";
leds {
compatible = "gpio-leds";
led0 {
label = "ps_led";
led-gpio = <&gpio0 0 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
led1 {
label = "pl_led0";
led-gpio = <&gpio0 54 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
};
keys {
compatible = "gpio-keys";
autorepeat;
key0 {
label = "ps_key";
gpios = <&gpio0 47 GPIO_ACTIVE_LOW>;
linux,code = <KEY_UP>;
debounce-interval = <15>;
};
key1 {
label = "pl_key";
gpios = <&gpio0 55 GPIO_ACTIVE_LOW>;
linux,code = <KEY_DOWN>;
debounce-interval = <15>;
};
};
usb_phy0: phy0@e0002000 {
compatible = "ulpi-phy";
#phy-cells = <0>;
reg = <0xe0002000 0x1000>;
view-port = <0x0170>;
drv-vbus;
};
};
&usb0 {
dr_mode = "otg";
usb-phy = <&usb_phy0>;
};
&gem0 {
local-mac-address = [00 0a 35 00 11 55];
};
&phy0 {
reg = <0x5>;
};

浙公网安备 33010602011771号