用法:
现象:
./fpga_led_app /dev/fpga_led 1
点亮LED[2]
./fpga_led_app /dev/fpga_led 0
熄灭LED[2]
FPGA底层设计:
module fpga_led( input clk, input reset_n, input as_read, input as_address, input as_write, output reg [31:0] as_readdata, input [31:0] as_writedata, output led); reg [31:0]led_register; //写寄存器 always@(posedge clk or negedge reset_n) begin if(!reset_n) begin led_register <= 32'd0; end else if(as_write && (as_address == 0))begin led_register <= as_writedata; end else begin led_register <= led_register; end end assign led = led_register[0]; //读寄存器 always@(posedge clk or negedge reset_n) begin if(!reset_n) as_readdata <= 32'd0; else if(as_read && (as_address == 0))begin as_readdata <= led_register[0];end end endmodule
Linux驱动设计:
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/printk.h> #include <linux/list.h> #include <linux/uaccess.h> #include <asm/io.h> #include "hps_0.h" /*************************************************************** 文件名: fpga_led.c 作者: Doreen 版本: V1.0 2024/01/04 描述: led驱动文件 博客:https://blog.csdn.net/weixin_47841246?spm=1000.2115.3001.5343 知乎: https://www.zhihu.com/people/fen-dou-de-nian-qing-ren-1 论坛: http://www.myfpga.org ***************************************************************/ /* 寄存器物理地址 */ #define H2F_LW_BASE_ADDR (0xFF200000) #define MAP_FPGA_LED_ADDR (H2F_LW_BASE_ADDR + FPGA_LED_0_BASE) #define FPGA_LED_0_BASE 0x7000 #define FPGA_LED_0_SPAN 8 /* 映射后的寄存器虚拟地址指针 */ static void __iomem *LWH2F_bridge_addr; /* fpga_led设备结构体 */ struct fpga_led_dev{ dev_t devid; /* 设备号 */ struct cdev cdev; /* cdev */ struct class *class; /* 类 */ struct device *device; /* 设备 */ int major; /* 主设备号 */ int minor; /* 次设备号 */ }; struct fpga_led_dev fpga_led; /* fpga_led设备 */ /*定义设备名称*/ #define DEVICE_NAME "fpga_led" static int fpga_led_open(struct inode *inode, struct file *filp ) { filp->private_data = &fpga_led; /* 设置私有数据 */ return 0; } static int fpga_led_release(struct inode *inode, struct file *filp ) { filp->private_data = NULL; //printk("fpga_led_close()\n"); return 0; } ssize_t fpga_led_read(struct file *filp, char *buf, size_t count,loff_t *f_pos) { int ret,register_data; char char_data[32]; //printk("kernel fpga_led_read\n"); register_data = ioread32(LWH2F_bridge_addr + 0);/*读 fpga_led 寄存器0*/ /*将读取到的数据拷贝到用户空间*/ sprintf(char_data,"%d",register_data); ret = copy_to_user(buf, char_data, sizeof(char_data)); printk("register_data= %s;\n",char_data); if (ret !=0) { return -EFAULT; } //printk("read done\n"); return count; } ssize_t fpga_led_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { int ret; int data; char char_data[32]=""; //printk("kernel fpga_led_write\n"); /*从用户空间将数据拷贝到 char_data 数组中*/ ret=copy_from_user(char_data, buf, 32); printk("copy_from_user=%s \n",char_data); if (ret !=0) { return -EFAULT; } sscanf(char_data,"%d",&data); iowrite32(data, LWH2F_bridge_addr + 0);/*写 fpga_led 寄存器 0*/ printk("kernel set fpga_led :%d\n",data); //printk("write done\n"); return count; } /*定义驱动程序操作函数*/ struct file_operations fpga_led_fops = { .owner = THIS_MODULE, .open = fpga_led_open, .write = fpga_led_write, .read = fpga_led_read, .release = fpga_led_release, }; /* 驱动入口函数 */ static int __init fpga_led_init(void) { // u32 val = 0; int ret; LWH2F_bridge_addr = ioremap_nocache(MAP_FPGA_LED_ADDR, FPGA_LED_0_SPAN); /* 注册字符设备驱动 */ /* 1、创建设备号 */ if (fpga_led.major) { /* 定义了设备号 */ fpga_led.devid = MKDEV(fpga_led.major, 0); ret = register_chrdev_region(fpga_led.devid, 1, DEVICE_NAME); if(ret < 0) { pr_err("cannot register %s char driver [ret=%d]\n",DEVICE_NAME, 1); goto fail_map; } } else { /* 没有定义设备号 */ ret = alloc_chrdev_region(&fpga_led.devid, 0, 1, DEVICE_NAME); /* 申请设备号 */ if(ret < 0) { pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DEVICE_NAME, ret); goto fail_map; } fpga_led.major = MAJOR(fpga_led.devid); /* 获取分配号的主设备号 */ fpga_led.minor = MINOR(fpga_led.devid); /* 获取分配号的次设备号 */ } printk(KERN_ERR "fpga_led major=%d,minor=%d\r\n",fpga_led.major, fpga_led.minor); /* 2、初始化cdev */ fpga_led.cdev.owner = THIS_MODULE; cdev_init(&fpga_led.cdev, &fpga_led_fops); /* 3、添加一个cdev */ ret = cdev_add(&fpga_led.cdev, fpga_led.devid, 1); if(ret < 0) goto del_unregister; /* 4、创建类 */ fpga_led.class = class_create(THIS_MODULE, "fpga_led"); if (IS_ERR(fpga_led.class)) { goto del_cdev; } /* 5、创建设备 */ fpga_led.device = device_create(fpga_led.class, NULL, fpga_led.devid, NULL, DEVICE_NAME); if (IS_ERR(fpga_led.device)) { goto destroy_class; } return 0; /* 注销字符设备 */ del_cdev: cdev_del(&fpga_led.cdev); /* 销毁类 */ destroy_class: class_destroy(fpga_led.class); /* 注销设备号 */ del_unregister: unregister_chrdev_region(fpga_led.devid, 1); /* 释放内存映射 */ fail_map: iounmap(LWH2F_bridge_addr);/*取消虚拟地址映射*/ return -EIO; } /*驱动出口函数*/ static void __exit fpga_led_exit(void) { /* 取消映射 */ iounmap(LWH2F_bridge_addr); /* 注销字符设备 */ cdev_del(&fpga_led.cdev); /* 注销设备号 */ unregister_chrdev_region(fpga_led.devid, 1); /* 销毁设备 */ device_destroy(fpga_led.class, fpga_led.devid); /* 销毁类 */ class_destroy(fpga_led.class); } module_init(fpga_led_init); module_exit(fpga_led_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Doreen");
测试程序设计:
#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" /*************************************************************** 文件名 : fpga_led_app.c 作者 : Doreen 版本 : V1.0 描述 : led 测试应用程序。 使用方法 :./fpga_led_app /dev/fpga_led 1 or 0 博客:https://blog.csdn.net/weixin_47841246?spm=1000.2115.3001.5343 知乎: https://www.zhihu.com/people/fen-dou-de-nian-qing-ren-1 论坛: http://www.myfpga.org ***************************************************************/ /* * @description : main 主程序 * @param - argc : argv 数组元素个数 * @param - argv : 具体参数 * @return : 0 成功;其他 失败 */ int main(int argc, char *argv[]) { int ret = 0; int fd = 0; char *filename; char data_w[32]=""; char data_r[32]; if(argc != 3){ printf("Error Usage!\r\n"); return -1; } filename = argv[1]; /* 打开 pwm 驱动 */ fd = open(filename, O_RDWR); if(fd < 0){ printf("file %s open failed!\r\n", argv[1]); return -1; } /*调用read方法读取/dev/pwm设备的寄存器数据到用户空间的data数组中*/ /* 向/dev/pwm 文件写入数据 */ strcat(data_w,argv[2]); ret = write(fd, data_w, sizeof(data_w)); if(ret < 0){ printf("User write Failed!\r\n"); close(fd); return -1; } else {/*打印你写入的数据*/ printf("write successfully;write data=%s;\r\n",data_w); } usleep( 100*1000 ); /* 从/dev/pwm 文件读出数据 */ ret = read(fd,data_r,32); if(ret < 0){ printf("read error \n"); return -1;} else {/*打印读取到的数据*/ printf("user read %s;\r\n",data_r); } /* 关闭文件 */ ret = close(fd); if(ret < 0){ printf("file %s close failed!\r\n", argv[1]); return -1; } return 0; }
无