【ZYNQ】Linux驱动之梦开始的地方

这篇具有很好参考价值的文章主要介绍了【ZYNQ】Linux驱动之梦开始的地方。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

软件版本:Vivado2021.1
操作系统:WIN10 64bit、Ubuntu18.04
硬件平台:ZYNQ UltraScale

1.1系统框图

在 PS 端接有两个 LED 灯,这些灯接在了 MIO 上,而 MIO 可以复用成为 GPIO,因此控制 GPIO 其实就是控制了 LED 等器件。本文通过读写寄存器来实现对 GPIO 的控制。
【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ

1.2介绍

1.2.1寄存器查询手册

  使用 GPIO 需要设置两个寄存器,一个是设置 GPIO 的管脚复用的 IOU_SLCR Module 寄存器,一个是设置GPIO 的管脚功能的 GPIO Module 寄存器。查询寄存器地址需要用到一个官方文档,编号的 ug1087。Xilinx 官方网站:https://docs.xilinx.com/r/en-US/ug1087-zynq-ultrascale-registers/MIO_PIN_41-IOU_SLCR-Register。

1.2.2物理地址与虚拟地址

  提到寄存器,那就不得不说说地址了。寄存器作为系统中存储数据的器件,每个数据都有其自身的物理地址。而与物理地址相对的则是 Linux 系统下的虚拟地址。在裸机等通常程序里,是不存在虚拟地址一说的,对地址的操作就是对物理地址的操作。但是在 Linux 下,对物理地址直接访问是行不通的,需要通过虚拟地址间接访问,因此所有对寄存器的操作都需要通过地址映射。
  之所以要使用虚拟地址是因为以前的电脑内存非常小,程序也是非常小,因此靠手动便可以管理内存。但是日新月异,今天的程序已经非常庞大了,虽然内存也在飞速发展,但是无法跟上日益臃肿的应用程序。众所周知要想运行一个程序,就必须将其完整地读入内存,如果程序本身就大于内存,那除了加内存可以运行程序,那剩下的唯一办法就是使用虚拟地址了。
虚拟地址可以模拟出几倍于物理地址的内存空间,当程序运行时,被运行的部分读入内存,其他部分依旧存放于外存。物理地址于虚拟地址的关系如下图。
【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ

  虚拟内存与物理内存是对应的,但是没有顺序。这个顺序是由 MMU 管理的,MMU 全称 Memory Management Unit,即内存管理单元。它的功能包括虚拟地址到物理地址的转换(即虚拟内存管理)、内存保护、中央处理器高速缓存的控制等等。
  在本章驱动编写时,会使用 ioremap、iounmap 这两个函数,是用来申请物理地址对应的虚拟地址的。因为在Linux 内核启动之后,会初始化 MMU,将物理地址映射为虚拟地址。这个时候,不能向寄存器的物理基地址写入
数据,而是应该用 ioremap 向系统申请的虚拟地址,然后在虚拟地址上进行寄存器的写入和读取操作。

1.2.3MIO介绍

  ZYNQ Ultrascale+ MPSOC PS 部分的 IO 包括 PS-MIO 和 PS-EMIO,MIO 分布在 PS 的 Bank0 、BANK1 和Bank2,EMIO 分布在 PS 的 Bank3、BANK4 和 Bank5。PS-MIO 具有 78 个 GPIO ,PS-EMIO 具有最多 96 个。PS-MIO的 IO 位置是固定好的,功能也是预先定义好了,而 PS-EMIO 是通过把芯片内部 PS 的 PS-EMIO 引线接到了 PL 部分的 FPGA Pin 脚上,所以 PS-MIO 满足必要常用的外设 IO 需求,比如串口、SDIO、以太网等,而 PS-EMIO 按需配置,用多少,配置多少。
  芯片有 78 个 MIO(multiuse I/O),它们派发在 GPIO 的,隶属于 PS 部分。这些 IO 与 PS 直接相连。不需要添加引脚约束,MIO 信号对 PL 部分是透明的,不可见。所以对 MIO 的操作可以看作是纯 PS 的操作。GPIO 控制和状态寄存器是从基地址 0xFF0A_0000 开始的存储器映射,并受 XPPU 保护。
【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ
PS-MIO:
Bank0:MIO[0 :25] GPIO PIN 脚号:0~25
Bank1:MIO[26:51] GPIO PIN 脚号:26~51
Bank2:MIO[52:77] GPIO PIN 脚号:52~77
PS-EMIO:
Bank3:EMIO[ 0:31] 可以分配到任意的 FPGA IO
Bank4:EMIO[32:63] 可以分配到任意的 FPGA IO
Bank5:EMIO[64:95] 可以分配到任意的 FPGA IO

注意:GPIO [92:95]四个输出可以用作 PL 中用户定义逻辑的复位信号。 GPIO EMIO 信号的数量取决于在 Vivado PS
配置向导(PCW)中选择的 PL 结构复位的数量。 例如,如果选择一个复位,则将 GPIO [95]分配为复位信号。如果选择两个,则分配
GPIO [95:94]。

MIO 内部构造:
【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ
DATA_RO:
此寄存器使能软件观察 PIN 脚,当 GPIO 被配置成输出的时候,这个寄存器的值会反应输出的 PIN 脚情况。
DATA:
此寄存器控制输出到 GPIO 的值,读这个寄存器的值可以读到最后一次写入该寄存器的值。
MASK_DATA_LSW:
位操作寄存器,写入 GPIO 低 16bit 其他没有改变的位置保存原先的状态
MASK_DATA_MSW:
位操作寄存器,写入 GPIO 高 16bit 其他没有改变的位置保存原先的状态
DIRM:
此寄存器控制输出的开关,当 DIRM[x]==0 时候,禁止输出
OEN:
输出使能,当 OEN[x]==0 的时候输出关闭,PIN 脚处于三态。
因 此 , 如果要读 IO 状态就得读DATA_RO 的值 , 如果是对某一位 进行操作就是写
MASK_DATA_LSW/MASK_DATA_MSW

1.2.4PS的LED 引脚介绍

【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ
从原理图可以看出,当 MIO 高电平的时候,LED 可以点亮。低电平的时候,MIO 变暗。
还可以查到如下数据:

  • LED1:MIO42
  • LED2:MIO40

查看 1.2.1寄存器手册,这四个管脚均属于 bank1,如果有 MIO 在其他 bank 上,我们需要将一样的代码再写一遍以控制全部LED,这里我们只控制 bank1 的 LED1 和 LED2。

1.3搭建工程

在IP核中勾选 GPIO1 MIO:
【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ
制作开发板系统.

1.4程序分析

1.4.1驱动程序分析

//添加头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>

static int led_major;
static struct class *led_cls;

/* gpio 内存映射地址 */
static unsigned long *gpio_addr = NULL;
static unsigned long *iou_slcr = NULL;

/* gpio 寄存器物理基地址 */
#define GPIO_BASE_ADDR 0xFF0A0000
#define IOU_SLCR_ADDR 0xFF180000
/* gpio 寄存器所占空间大小 */
#define GPIO_BASE_SIZE 0x368
#define IOU_SLCR_SIZE 0x714

/* gpio 引脚设置GPIO功能*/
#define GPIO_PIN_40 (unsigned int *)((unsigned long)iou_slcr + 0x000000A0)
#define GPIO_PIN_42 (unsigned int *)((unsigned long)iou_slcr + 0x000000A8)

/* gpio 控制电流0*/
#define IOU_SLCR_BANK1_CTRL0 (unsigned int *)((unsigned long)iou_slcr + 0x00000154)
/* gpio 控制电流1*/
#define IOU_SLCR_BANK1_CTRL1 (unsigned int *)((unsigned long)iou_slcr + 0x00000158)
/* gpio 输入引脚选择Schmitt或CMOS*/
#define IOU_SLCR_BANK1_CTRL3 (unsigned int *)((unsigned long)iou_slcr + 0x0000015C)
/* gpio 设置上下拉*/
#define IOU_SLCR_BANK1_CTRL4 (unsigned int *)((unsigned long)iou_slcr + 0x00000160)
/* gpio 使能上下拉*/
#define IOU_SLCR_BANK1_CTRL5 (unsigned int *)((unsigned long)iou_slcr + 0x00000164)
/* gpio 引脚速率选择*/
#define IOU_SLCR_BANK1_CTRL6 (unsigned int *)((unsigned long)iou_slcr + 0x00000168)

/* gpio 方向寄存器 */
#define GPIO_DIRM_1 (unsigned int *)((unsigned long)gpio_addr + 0x00000244)
/* gpio 使能寄存器 */
#define GPIO_OEN_1 (unsigned int *)((unsigned long)gpio_addr + 0x00000248)
/* gpio 输出数据寄存器 */
#define GPIO_DATA_1 (unsigned int *)((unsigned long)gpio_addr + 0x00000044)
/* gpio 输出数据控制寄存器1 */
#define GPIO_MASK_DATA_1_LSW (unsigned int *)((unsigned long)gpio_addr + 0x00000008)
/* gpio 输出数据控制寄存器2 */
#define GPIO_MASK_DATA_1_MSW (unsigned int *)((unsigned long)gpio_addr + 0x0000000C)

const struct file_operations led_fops = {};

//实现装载入口函数和卸载入口函数
static __init int led_drv_v1_init(void)
{
	printk("-------^v^-------\n");
	printk("-led drv v1 init-\n");
	//申请主设备号
	led_major = register_chrdev(0, "led_drv_v1", &led_fops);
	if (led_major < 0)
	{
		printk("register chrdev faile!\n");
		return led_major;
	}
	printk("register chrdev ok!\n");

	//创建设备节点
	//创建设备的类别
	led_cls = class_create(THIS_MODULE, "led_class");
	printk("class create ok!\n");

	//创建设备
	device_create(led_cls, NULL, MKDEV(led_major, 0), NULL, "Myled%d", 0);
	printk("device create ok!\n");


	//1.内存映射
	gpio_addr = ioremap(GPIO_BASE_ADDR, GPIO_BASE_SIZE);
	iou_slcr = ioremap(IOU_SLCR_ADDR, IOU_SLCR_SIZE);
	printk("gpio_addr init over!\n");


	//2.MIO40 MIO42 设置成GPIO,参考手册设置
	iowrite32((ioread32(GPIO_PIN_40) & 0x0), GPIO_PIN_40);
	printk("gpio MIO40 init over!\n");
	iowrite32((ioread32(GPIO_PIN_42) & 0x0), GPIO_PIN_42);
	printk("gpio MIO42 init over!\n");

	// //3.MIO40 MIO42设置输出驱动电流大小
	// // 将 0x3FFFFFF 和 0x0 转换为二进制。
	// // 0011 1111 1111 1111 1111 1111 1111
	// // 0000 0000 0000 0000 0000 0000 0000
	// // 参考手册,可以知道每一位的地址都是由 CTRL0 和 CTRL1 控制的,在这里都是 10, 查看手册得 8 mA。
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL0) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL0);
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL1) & 0x0), IOU_SLCR_BANK1_CTRL1);

	//4.选择引脚是Schmitt还是CMOS
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL3) & 0x0), IOU_SLCR_BANK1_CTRL3);

	//5.输出管脚上下拉及使能
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL4) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL4);
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL5) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL5);

	//6.MIO速率的选择
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL6) & 0x0), IOU_SLCR_BANK1_CTRL6);

	//7.MIO40 MIO42 设置成输出
	// 40 - 26 = 14, 42 - 26 = 16
	// 0000 0000 000|0 0|000 0000 0000 0000
 	//              |16 |14   
	// 0000 0000 000 1 0 100 0000 0000 0000
	// 0000 0000 0001 0100 0000 0000 0000
	// 0x00014000
	iowrite32((ioread32(GPIO_DIRM_1) | 0x00014000), GPIO_DIRM_1);
	//MIO40 MIO42 使能
	iowrite32((ioread32(GPIO_OEN_1) | 0x00014000), GPIO_OEN_1);

	/* MASK_DATA方式按灭LED1,LED2,这个需要自己修改相关参数 */
	//iowrite32((ioread32(GPIO_MASK_DATA_0_LSW ) & 0xFEFFEFFF), GPIO_MASK_DATA_0_LSW );
	//printk("GPIO_MASK_DATA_0_LSW = 0x%x\n", *GPIO_MASK_DATA_0_LSW);
	//iowrite32((ioread32(GPIO_MASK_DATA_0_MSW ) & 0xFEFFEFFF), GPIO_MASK_DATA_0_MSW );
	//printk("GPIO_MASK_DATA_0_MSW = 0x%x\n", *GPIO_MASK_DATA_0_MSW);
	
	//8.DATA方式按灭LED1,LED2
	// 1111 1111 1111 111|1 1|111 1111 1111 1111
	//                   |0  |0
	// 1111 1111 1111 1110 1011 1111 1111 1111
	// 0xFFFEBFFF
	// iowrite32((ioread32(GPIO_DATA_1) & 0xFFFEBFFF), GPIO_DATA_1);
	iowrite32((ioread32(GPIO_DATA_1) & 0xFFFEFFFF), GPIO_DATA_1);

	printk("GPIO_DATA_1 = 0x%x\n", *GPIO_DATA_1);

	return 0;
}

static __exit void led_drv_v1_exit(void)
{
	iowrite32((ioread32(GPIO_DATA_1) | 0xFFFFFFFF), GPIO_DATA_1);
	//9.内存释放
	iounmap(gpio_addr);
	iounmap(iou_slcr);
	//删除设备
	device_destroy(led_cls, MKDEV(led_major, 0));
	//删除类
	class_destroy(led_cls);
	//注销主设备号
	unregister_chrdev(led_major, "led_drv_v1");

	printk("-------^v^-------\n");
	printk("-led drv v1 exit-\n");
}

//申明装载入口函数和卸载入口函数
module_init(led_drv_v1_init);
module_exit(led_drv_v1_exit);

//添加GPL协议
MODULE_LICENSE("GPL");
MODULE_AUTHOR("msxbo");

行 9~11,用来储存映射后虚拟地址的基地址。87、88 行赋值。
行 13~15,是我们要控制的两个寄存器物理地址的基地址。
行 16~18,是两个寄存器的完整大小,在地址转换的时候直接映射整个寄存器,方便理解。
行 20~22,定义设置复用功能的寄存器。
行 24~35,六个控制电气特性的寄存器。
行 37~46,GPIO 的控制寄存器。
行 113~117,是使用 mask_data 的方法熄灭 led,和 data 方法熄灭效果一样。
行 119~126,使用 data 方法熄灭 led。

1:内存映射
static __init int led_drv_v1_init(void)
{

	//1.内存映射
	gpio_addr = ioremap(GPIO_BASE_ADDR, GPIO_BASE_SIZE);
	iou_slcr = ioremap(IOU_SLCR_ADDR, IOU_SLCR_SIZE);
	printk("gpio_addr init over!\n");


	return 0;
}

含义:内存映射的目的是为了把物理地址映射为虚拟地址,方便对寄存器进行操作。

  • GPIO_BASE_ADDR:这是寄存器手册中查询到的“GPIO Module”的“Base Address”。
    【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ
  • GPIO_BASE_SIZE:这是该寄存器的容量,也就是“GPIO Module”的容量。从“GPIO Module”的最后一行可以看到,它的最后一位是“0x0000000364”,位宽是 32 位。简单的算下,就是 4 字节,所以“GPIO Module”的容量为“0x0000000368”。
2:设置 MIO 功能
static __init int led_drv_v1_init(void)
{
	//2.MIO40 MIO42 设置成GPIO,参考手册设置
	iowrite32((ioread32(GPIO_PIN_40) & 0x0), GPIO_PIN_40);
	printk("gpio MIO40 init over!\n");
	iowrite32((ioread32(GPIO_PIN_42) & 0x0), GPIO_PIN_42);
	printk("gpio MIO42 init over!\n");


	return 0;
}

含义:将 MIO 的功能设置为 GPIO。
具体分析:

• iowrite32:这个函数会把前面的值写入到后面的目的地址。
• ioread32:这个函数则是读取目的地址的值。
• 0x0:这个部分需要查询寄存器手册的“IOU_SLCR Module”部分,找到需要设置的 MIO 引脚的序号,然后根据自己的需要设置手册中的相应的值。
•GPIO_PIN_40:这是在之前确定好的“iou_slcr”进行偏移的地址。偏移地址可以在寄存器手册中查询到
【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ

3:设置 MIO 输出电流的大小
static __init int led_drv_v1_init(void)
{
	// //3.MIO40 MIO42设置输出驱动电流大小
	// // 将 0x3FFFFFF 和 0x0 转换为二进制。
	// // 0011 1111 1111 1111 1111 1111 1111
	// // 0000 0000 0000 0000 0000 0000 0000
	// // 参考手册,可以知道每一位的地址都是由 CTRL0 和 CTRL1 控制的,在这里都是 10, 查看手册得 8 mA。
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL0) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL0);
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL1) & 0x0), IOU_SLCR_BANK1_CTRL1);


	return 0;
}

含义:将 Bank1 的电流都设置为 8mA,Bank1 控制的是 MIO 引脚 [26:51]。
具体分析:
寄存器部分的操作都是按位计算的,想要什么功能,在手册中查到之后,就需要通过寄存器把想要控制的引脚的地址写入对应功能所需的设置。这里以控制电流为例。因为需要控制的引脚为 MIO40 和 MIO42,40 和 42 都是 bank1 所负责的区域。因此,在寄存器手册中找到对应的bank1 部分。
【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ
  这个部分的寄存器都是控制 bank1 部分功能的,我们先介绍 ctrl0 和 ctrl1 两个部分。这两个部分是控制电流的。
  通过查看“bank1_crtl0 (IOU_SLCR) Register”的“Description”可以知道,ctrl0 得和 ctrl1 两个部分才能控制电流的大小。
【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ
我们需要控制的是 MIO40 和 MIO42,这里为了方便起见,将 ctrl0 全部设置为 1,将 ctrl1 全部设置为 0,查表可以知道是 8mA。

  • &:按位与,可以理解为交集,交集是指 A 与 B 共有的。
  • | :按位或,可以理解为并集,并集是指 A 和 B 所有的。
4:设置引脚是 Schmitt Trigger 还是 CMOS Input
static __init int led_drv_v1_init(void)
{
	//4.选择引脚是Schmitt还是CMOS
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL3) & 0x0), IOU_SLCR_BANK1_CTRL3);


	return 0;
}

含义:设置 MIO 引脚的功能,这里选择 CMOS Input。

5:设置引脚的上下拉及使能
static __init int led_drv_v1_init(void)
{
	//5.输出管脚上下拉及使能
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL4) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL4);
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL5) | 0x3FFFFFF), IOU_SLCR_BANK1_CTRL5);


	return 0;
}

含义:设置 MIO 引脚的上拉或者下拉,并且使能。
具体分析:在 ctrl4 部分将其全部设置为 1,对照寄存器手册可以知道是上拉,并且 bank1 上所有的都是上拉。同理在 ctrl5部分也将其全部设置为 1,对照寄存器手册可以看到 bank1 上 [26:51] 的 MIO 都被启动了。

6:设置 MIO 的速率
static __init int led_drv_v1_init(void)
{
	//6.MIO速率的选择
	iowrite32((ioread32(IOU_SLCR_BANK1_CTRL6) & 0x0), IOU_SLCR_BANK1_CTRL6);


	return 0;
}

Ctrl6 可以设置 MIO 的速率,0 为低速,1 为高速。

7:设置 GPIO 的输入输出
static __init int led_drv_v1_init(void)
{
	//7.MIO40 MIO42 设置成输出
	// 40 - 26 = 14, 42 - 26 = 16
	// 0000 0000 000|0 0|000 0000 0000 0000
 	//              |16 |14   
	// 0000 0000 000 1 0 100 0000 0000 0000
	// 0000 0000 0001 0100 0000 0000 0000
	// 0x00014000
	iowrite32((ioread32(GPIO_DIRM_1) | 0x00014000), GPIO_DIRM_1);
	//MIO40 MIO42 使能
	iowrite32((ioread32(GPIO_OEN_1) | 0x00014000), GPIO_OEN_1);


	return 0;
}

含义:将 GPIO 的输入输出部分,MIO40 和 MIO42 的地方设置为输出。
具体分析
这个部分设置的是 GPIO 的相关功能,所以要去“GPIO Module”部分去找相关的寄存器的功能。

  • GPIO_DIRM_1:是指控制 GPIO 的输入输出方向。

  • GPIO_OEN_1:则是用来控制使能,也就是 enable 的意思。

8:通过 Data 的方式熄灭 LED
static __init int led_drv_v1_init(void)
{
	/* MASK_DATA方式按灭LED1,LED2,这个需要自己修改相关参数 */
	//iowrite32((ioread32(GPIO_MASK_DATA_0_LSW ) & 0xFEFFEFFF), GPIO_MASK_DATA_0_LSW );
	//printk("GPIO_MASK_DATA_0_LSW = 0x%x\n", *GPIO_MASK_DATA_0_LSW);
	//iowrite32((ioread32(GPIO_MASK_DATA_0_MSW ) & 0xFEFFEFFF), GPIO_MASK_DATA_0_MSW );
	//printk("GPIO_MASK_DATA_0_MSW = 0x%x\n", *GPIO_MASK_DATA_0_MSW);
	
	//8.DATA方式按灭LED1,LED2
	// 1111 1111 1111 111|1 1|111 1111 1111 1111
	//                   |0  |0
	// 1111 1111 1111 1110 1011 1111 1111 1111
	// 0xFFFEBFFF
	// iowrite32((ioread32(GPIO_DATA_1) & 0xFFFEBFFF), GPIO_DATA_1);
	iowrite32((ioread32(GPIO_DATA_1) & 0xFFFEFFFF), GPIO_DATA_1);

	printk("GPIO_DATA_1 = 0x%x\n", *GPIO_DATA_1);


	return 0;
}

含义:将已经配置 GPIO 的 MIO40,MIO42 的输出设置为 0,也就是关闭输出。
具体分析
这是通过控制 DATA 寄存器的方式熄灭 LED,还可以使用 MASK_DATA 的方式熄灭 LED。具体的设置的值需要自己查寄存器手册修改。DATA 寄存器的说明在介绍部分。

9:内存释放
static __init int led_drv_v1_init(void)
{
	//9.内存释放
	iounmap(gpio_addr);
	iounmap(iou_slcr);


	return 0;
}

含义:将申请的虚拟内存资源进行释放。
具体分析
在通过 ioremap 向系统申请了系统资源之后,在驱动退出的时候,需要将资源释放。释放用 iounmap 函数。文章来源地址https://www.toymoban.com/news/detail-622203.html

1.5交叉编译

  1. led_drv_v1.cMakefile文件在虚拟机中进行编译。
  2. 将编译生成的led_drv_v1.ko文件上传到开发板的Linux中。

1.6演示

  • 在终端中使用输入“ls”,查看是否存在“led_drv_v1.ko”文件。
    【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ
  • 确认存在之后,进入 root 权限,然后在终端输入“insmod led_drv_v1.ko”。
    【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ输入
  • “lsmod”查看是否安装成功。
    【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ
  • 在终端输入“rmmod led_drv_v1.ko”,PS的灯会亮起。
    【ZYNQ】Linux驱动之梦开始的地方,linux,fpga,ZYNQ

到了这里,关于【ZYNQ】Linux驱动之梦开始的地方的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 现在开始学linux驱动内核好吗?

    一开始是觉得,内核诶,高大上。然后看了几个月驱动后,是认认真真的钻了几个月源码,写了很多学习笔记的那种。 先说好处吧,对基础能力的提升很明显。比如内核数据结构,指针,以及如何用c需要去完成面向对象的编程思想。还有硬件底层的认识。看着自己写的代码

    2024年02月12日
    浏览(47)
  • FPGA PCIE接口的Linux DMA Engine驱动

    英创嵌入式主板,如ESM7000系列、ESM8000系列等,均可配置标准的PCIE×1高速接口。连接NVMe模块作高速大容量数据存储、连接多通道高速网络接口模块都是PCIE接口的典型应用。此外,对于工控领域中的高速数据采集,还可采用FPGA的PCIE IP核实现PCIE EP端点,与英创嵌入式主板构成

    2024年02月15日
    浏览(35)
  • Linux下grep通配容易混淆的地方

    先上一张图:  我希望找到某个版本为8的一个libXXX.8XXX.so ,那么应该怎么写呢? 先看这种写法对不对:  是不是结果出乎你的意料之外? 那么我们来看一下规则:  这里的 \\\"*\\\"  表示匹配前一个字符的零个或多个  于是我们就不难理解了:     lib*8*.so   表示      包含l

    2024年02月12日
    浏览(28)
  • ZYNQ7045从flash启动linux

    自行安装linux版本的vivado,和xilinx开发环境。linux版本我选择的是Ubuntu18.04,ubuntu系统下xilinx开发环境我安装的是vivado 2018.3(安装时候要记得勾选SDK),安装好vivado后也就包含了xilinx的ARM部分交叉编译器。 petalinux 并不是一个特殊 Linux 内核,而是一套开发环境配置的工具,降低 ubo

    2023年04月12日
    浏览(33)
  • petalinux定制ZYNQ的Linux操作系统

    在进行Zynq-7000的Linux系统开发时,Xilinx官方提供了一个名为petalinux的工具。该工具运行在pc端的linux环境下,使用这个工具可以为目标板有量身定制kernel、rootfs等。该工具可与vivado设计工具一起配合使用,旨在简化 Zynq-7000 的Linux系统开发过程,提高设计生产力。本文将介绍pe

    2024年02月16日
    浏览(42)
  • 【C++】main开始的地方

    目录 1. C++ 2. 命名空间 2.1 命名空间定义 2.2 命名空间使用 3. C++输入输出 4. 缺省参数 4.1 缺省参数概念 4.2 缺省参数分类 5. 函数重载 5.1 函数重载概念 5.2 C++支持函数重载的原理--名字修饰(name Mangling) 6. 引用 6.1 引用概念 6.2 引用特性 6.3 常引用 6.4 使用场景 6.5 传值、传

    2024年02月06日
    浏览(35)
  • ZYNQ-Linux开发之(三)Vivado SDK使用,裸机开发调试,不带linux

    生成bit文件时候的开发和调试需要使用SDK,导出工程到SDK: 包含bit文件,点击OK:  工程目录下会新增一个.sdk的目录: 启动SDK: 使用SDK进行调试,SDK中,新建应用工程,选择File-New-Application Project: 在弹出的窗口中,输入Project name,单击Next: 在弹出的窗口中,默认选择He

    2024年02月10日
    浏览(60)
  • zynq Linux 启动之后动态布署PL方法摸索

    简介:在使用zynq 运行Linux时,如何在Linux启动之后,再布署PL,本教程在参考了正点原子领航者ZYNQ之嵌入式Linux开发指南第二十章,开源平台创龙的部分教程,结合Xilinx wiki官方教程。初步实现了zynq在Linux启动之后再加载PL。纯属摸索实现,有什么描述不正确的地方或者有更好

    2023年04月08日
    浏览(39)
  • ZYNQ AXI-DMA Linux Cache 一致

    平台为 ZYNQ MPSOC 项目使用到AXI-DMA ,ADC模块传输数据到DDR,应用层进行数据的读取。在此做些记录 用到了AXI-Stream , IP核用的 米联客的ui_axisbufw,可以把流数据转为AXI-Stream 接口 比较重要的参考链接 1.UltraScale+MPSoC+Cache+Coherency https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842098/Zynq+

    2024年02月03日
    浏览(44)
  • 在ZYNQ-Linux下操作GPIO和VDMA

    此前的文章介绍如何利用petalinux定制ZYNQ-Linux操作系统。当ZYNQ-Linux系统搭建完毕后,需要在这个系统上开发应用程序以完成特定任务,这里面就涉及到如何在ZYNQ-Linux系统上去操作系统硬件资源的问题。目前,网上介绍的比较多的是需要改写Linux操作系统底层的设备树,并编写

    2024年02月04日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包