用法:

 

现象:

./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;
}