Menu Close

petalinx 下,简单字符设备驱动程序的编写

我们完成了axi-lite寄存器控制led,接下来我们要编写petalinux 下的简单字符设备驱动程序

 

硬件: fii_7030

vivado : 2018.3

petalinux : 2018.3

 

  • 进入ubuntu :
  • 安装 petalinux 环境
  • 创建 petalinux 工程:/home/work/ 下

sptl

petalinux-create -t project –template zynq -n fii-module

fii-module 是我们创建的petalinux 的工程。

 

  • 导入创建fpga 工程中的sdk目录

将fpga 的sdk 工程目录FII_7030.sdk copy 到 ubuntu 的 home/work/ 下

 

  • 编辑fii_module 工程:

     进入fii_module 工程目录: cd fii_module

%title插图%num

执行sptl

 

petalinux-config –get-hw-description ~/work/FII_7030.sdk/

 

%title插图%num

保存, 退出

 

 

  • 添加创建fpga 自建驱动程序:

   petalinux-create -t modules –name fii-driver

  • 编辑 自建驱动程序:

进入~/work/fii_module/project-spec/meta-user/recipes-modules/fii-driver/files 目录

 

  • 编辑 fii-driver.c 文件
如下:
/**********************************************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h> /* for put_user */
#include <asm/errno.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
//#include <linix/of_platform.h>

/*
0x43c0_0000 + 4 = value
0x43c0_0000 + 8 = direction
*/

#define DEVICE_NAME “fii-module”
#define CLASS_NAME “fii-module_class”

static int major;
static struct class *swled_class;

static unsigned int *gpio_data;
static unsigned int *gpio_dir ;

static int swled_open(struct inode* node,struct file* filp){

unsigned int gpio_base = (0x43c00000);

gpio_data = ioremap(gpio_base,8);
if(gpio_data){
printk(“kernel: ioremap(0x%08x)=0x%08x \n”,gpio_base,gpio_data);
// printk(KERN_ERR “ioremap(0x%x)=0x%x \n”,gpio_base,gpio_data);
}
else{
return -EINVAL;
}


gpio_dir = gpio_data + 1;

// *gpio_dir = (0x43c00000);
printk(“kernel: gpio_data address = 0x%08x \n”, gpio_data);
printk(“kernel: gpio_dir address = 0x%08x \n”, gpio_dir);

return 0;
}


int swled_release(struct inode *inode, struct file *filp)
{
// MOD_DEC_USE_COUNT;
iounmap(gpio_data);
return 0;
}


/*
0x43c0_0000 + 4 = value
0x43c0_0000 + 8 = direction
*/

static ssize_t swled_read(struct file *filp, char *buf, size_t size, loff_t *offset)
{
unsigned char val[32];
unsigned int temp;
unsigned int *addr_p;
int i = 0, cnt;
printk(“kernel: swled_read start…. size = %d\n”, size);
// cnt = size – 1;
cnt = size;

addr_p = gpio_data;
temp = *addr_p ;
for(i = 0; i < cnt/4; i++ )
{
val[i*4 + 3] = temp & 0xff;
val[i*4 + 2] = (temp >> 8) & 0xff;
val[i*4 + 1] = (temp >> 16) & 0xff;
val[i*4 + 0] = (temp >> 24) & 0xff;
addr_p ++;
temp = *addr_p;
}

if(i % 4 == 1)
{
val[i*4 + 0] = temp & 0xff;
}
if(i % 4 == 2)
{
val[i*4 + 1] = temp & 0xff;
val[i*4 + 0] = (temp >> 8) & 0xff;
}
if(i % 4 == 3)
{
val[i*4 + 2] = temp & 0xff;
val[i*4 + 1] = (temp >> 8) & 0xff;
val[i*4 + 0] = (temp >> 16) & 0xff;
}

copy_to_user(buf, val, cnt);

// printk(“val[0] = 0x%x, val[1] = 0x%x, val[2] = 0x%x, val[3] = 0x%x \n”,
// val[0], val[1], val[2], val[3]);

return cnt;
// return size;
}


static ssize_t swled_write(struct file * filp, const char __user *buf, size_t size, loff_t * offset) {
unsigned char val[32];
unsigned int temp = 0;
unsigned int * addr_p;
int i,cnt;

memset(val,0,32);
addr_p = gpio_data;

printk(“kernel: swled_write start…. size = %d\n”, size);
copy_from_user(&val, buf, size);
cnt = size – 1;

printk(“kernel: val[0] = 0x%08x \n”, val[0]);

if(val[0] == ‘w’)
{
temp = val[2];
temp = temp << 8 | val[3];
temp = temp << 8 | val[4];
temp = temp << 8 | val[5];

*addr_p = temp;
printk(“kernel: gpio_data = 0x%08x \n”, temp);
}

else if(val[0] == ‘d’)
{
addr_p ++;
temp = val[2];
temp = temp << 8 | val[3];
temp = temp << 8 | val[4];
temp = temp << 8 | val[5];

*addr_p = temp;
printk(“kernel: gpio_dir = 0x%08x \n”, temp);
}
else
{
printk(“kernel: invalid parameter \n”);
}

#if 0
temp = val[0] & 0xff;
temp = temp << 8 | (val[1] & 0xff);
temp = temp << 8 | (val[2] & 0xff);
temp = temp << 8 | (val[3] & 0xff);

printk(“val[0] = 0x%x, val[1] = 0x%x, val[2] = 0x%x, val[3] = 0x%x \n”,
val[0], val[1], val[2], val[3]);

*gpio_data = temp;
printk(“gpio_data = 0x%08x \n”, temp);


for(i = 0; i < cnt/4; i++)
{
temp = val[i*4 + 0];
temp = temp << 8 | val[i*4 + 1];
temp = temp << 8 | val[i*4 + 2];
temp = temp << 8 | val[i*4 + 3];

*addr_p = temp;
addr_p ++;
printk(“kernel: gpio_data[%d] = 0x%08x \n”, i, temp);
}

if(cnt %4 == 1)
{
temp = val[i*4 + 0];
*addr_p = temp;
printk(“kernel: gpio_data[%d] = 0x%08x \n”, i, temp);
}
if(cnt %4 == 2)
{
temp = val[i*4 + 0];
temp = temp << 8 | val[i*4 + 1];

*addr_p = temp;
printk(“kernel: gpio_data[%d] = 0x%08x \n”, i, temp);
}
if(cnt %4 == 3)
{
temp = val[i*4 + 0];
temp = temp << 8 | val[i*4 + 1];
temp = temp << 8 | val[i*4 + 2];

*addr_p = temp;
printk(“kernel: gpio_data[%d] = 0x%08x \n”, i, temp);
}

#endif

return size;
}


static struct file_operations swled_oprs = {
.owner = THIS_MODULE,
.open = swled_open,
.write = swled_write,
.read = swled_read,
.release= swled_release,
};


static int swled_init(void){ /* register device */
/*
*
* int register_chrdev (unsigned int major,
* const char *name,
* struct file_operations*fops);
*
* major => 0, automatic mode; others major device ID
*/
major=register_chrdev(0, DEVICE_NAME, &swled_oprs);
if (major < 0) {
printk (“Registering the character device failed with %d\n”, major);
return major;
}


swled_class = class_create(THIS_MODULE, CLASS_NAME);
device_create(swled_class, NULL, MKDEV(major,0), NULL, DEVICE_NAME);

return 0;
}

static void swled_exit(void){

unregister_chrdev(major, DEVICE_NAME);
device_destroy(swled_class,MKDEV(major,0));
class_destroy(swled_class);

};

module_init(swled_init);
module_exit(swled_exit);

MODULE_LICENSE(“GPL”);

 


/**********************************************************************************************/

  • 添加petalinux 驱动选项:

petalinux-config -c rootfs

%title插图%num
%title插图%num

保存,退出

  • 编译内核

petalinux-build -c kernel

  • 编译驱动

petalinux-build -c fii-driver

  • 编译整个工程:

petalinux-build

  • 制作工程文件

petalinux-package –boot –fsbl ./images/linux/zynq_fsbl.elf –fpga –u-boot –force

  • Copy (~/work/fii_module/images/linux/ 目录下的文件)BIN , image.ub 到SD卡上
  • Copy 当前工程下的(~/work/fii_module/build/tmp/sysroots-components/plnx_zynq7/fii-driver/lib/modules/4.14.0-xilinx-v2018.2/extra/*.ko) 文件 到sd 卡上
  • 将sd卡插入到fii_7030开发板,启动fii_7030 开发板

 

  • 打开windows下的串口终端软件 , 配置串口为115200 波特率

%title插图%num

 

用户名root, 密码 root

 

  • 在串口终端上执行调试命令

cd /mnt

mkdir sd_0

mount /dev/mmcblk0p1 /mnt/sd_0

cd /mnt/sd_0

 

执行 insmod fii-driver.ko


%title插图%num

可以查看一下 /dev 目录下是否已经加载了fii-module 驱动

ls -l /dev/fii-module

如果存在, 说明驱动加载成功。

  • 设置方向寄存器

0x4300_0000 = 0 (output);  0x4300_0000 = 1 (最低位 input,其他位是 output);  

 

echo d 0000 > /dev/fii-module

%title插图%num

  • 设置gpio 口, 点亮led 灯

%title插图%num

至此, linux 就可以通过系统命令,设置 gpio(led)了

  • Linux 下,应用程序开发

在ubuntu 下创建一个test_func目录, 在test_func 目录下创建fii_app.c 文件

%title插图%num

 

  • 编辑c文件

/**********************************************************************************************/

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>   /* File Control Definitions           */

#include <termios.h> /* POSIX Terminal Control Definitions */

#include <unistd.h>  /* UNIX Standard Definitions    */

#include <errno.h>   /* ERROR Number Definitions           */

#include <string.h>

// Number of strings in array argv

//int main( int argc,            char *argv[],

 

//void main(void)

 

static void print_msg(void)

{

    printf(“=============================================================\n”);

    printf(“Usage :\n”);

    printf(“./fii_app.o dir <val> : val : 0 => output; 1 => input\n”);

    printf(“./fii_app.o wr  <val> : val : 0x43c0_0004 address value\n”);

    printf(“./fii_app.o rd        : \n”);

    printf(“=============================================================\n”);

 

    return;

}

 

static void func_read(int fd,  int read_cnt)

{

    char rd_buf[32];        /* Buffer to store the data received              */

    int  bytes_read = 0;    /* Number of bytes read by the read() system call */

    unsigned int temp;

 

    printf(“Press ctrl+C to exit \n”);

    tcflush(fd, TCIFLUSH);   /* Discards old data in the rx buffer */

    lseek(fd, 0, SEEK_SET);

 

    bytes_read = read(fd, rd_buf, read_cnt);

 

    printf(“\n\nBytes Rxed = %d \n”, bytes_read); /* Print the number of bytes read */

 

    temp = rd_buf[0];

    temp = (temp << 8) | rd_buf[1];

    temp = (temp << 8) | rd_buf[2];

    temp = (temp << 8) | rd_buf[3];

 

    printf(“read value = 0x%08x \n”, temp);

 

    temp = rd_buf[4];

    temp = (temp << 8) | rd_buf[5];

    temp = (temp << 8) | rd_buf[6];

    temp = (temp << 8) | rd_buf[7];

 

    printf(“read dir   = 0x%08x \n”, temp);

 

    printf(“\n +———————————-+\n\n\n”);

 

//    usleep(1000000);

 

    return;

}

 

static void func_write(int fd,  char *p)

{

char wr_buf[32];

ssize_t bytes_written = 0;

    unsigned int value;

 

    memset (wr_buf, 0x0a, 32);

 

    value = atoi(p);

    printf(“value = %x \n” , value);

 

 

    wr_buf[0] = ‘w’;

    wr_buf[1] = ‘ ‘;

    wr_buf[2] = value >> 24;

    wr_buf[3] = (value >> 16) & 0xff;

    wr_buf[4] = (value >> 8) & 0xff;

    wr_buf[5] = value & 0xff;

 

bytes_written = write(fd, wr_buf, 7);

    return;

}

 

static void func_direction(int fd,  char *p)

{

char wr_buf[32];

ssize_t bytes_written = 0;

    unsigned int value;

 

    memset (wr_buf, 0x0a, 32);

 

    value = atoi(p);

    printf(“value = %x \n” , value);

 

 

    wr_buf[0] = ‘d’;

    wr_buf[1] = ‘ ‘;

 

    wr_buf[2] = value >> 24;

    wr_buf[3] = (value >> 16) & 0xff;

    wr_buf[3] = (value >> 8) & 0xff;

    wr_buf[5] = value & 0xff;

 

bytes_written = write(fd, wr_buf, 7);

}

 

int main( int argc, char *argv[])

{

    int fd;/*File Descriptor*/

    unsigned int value = 0;

    char * cmd_p;

 

    /* O_RDWR    – Read/Write access to serial port     */

    /* O_RDONLY  – Read access to serial port           */

    /* O_WRONLY  – Write access to serial port          */

    /* O_NOCTTY  – No terminal will control the process */

 

    /* Open in blocking mode,read will wait             */

    fd = open(“/dev/fii-module”,O_RDWR );

 

    printf(“\n +———————————-+”);

    printf(“\n |     fii-driver function          |”);

    printf(“\n +———————————-+”);

 

 

 

    if(fd == -1) /* Error Checking */

        printf(“\n  Error! in Opening swled  \n”);

    else

        printf(“\n  fii-driver Opened Successfully \n”);

 

    cmd_p = argv[1];

 

    if(argc == 2)

    {

        if( strcmp (cmd_p , “rd”) == 0 )

        {

            func_read(fd,  8);

            return 0;

        }

        else

        {

            print_msg();

            close(fd); /* Close the serial port */

            return 1;

        }

    }

    else if (argc == 3)

    {

        if( strcmp (cmd_p , “wr”) == 0 )

        {

            func_write(fd, argv[2]);

            close(fd); /* Close the serial port */

            return 0;

        }

        else if( strcmp (cmd_p , “dir”) == 0 )

        {

            func_direction(fd, argv[2]);

            close(fd); /* Close the serial port */

            return 0;

        }

    }

    else

    {

        print_msg();

        close(fd); /* Close the serial port */

        return 1;

    }

 

 

    return 0;

}

/**********************************************************************************************/

 

  • c 编译

进入fii_app工程目录

sptl

arm-linux-gnueabihf-gcc fii_app.c -o fii_app.o

生成fii_app.o 文件

    COPY fii_app.o 文件到 sd 卡上

 

  • 加载fii-driver.ko, 执行sd 卡上上的 fii-driver.ko文件

insmod fii-driver.ko

  • 运行fii_app 软件

在串口终端中执行, ./fii_app.o,  可以看到这个程序的使用指南

%title插图%num

 

可根据上述命令选择: 方向寄存器,写操作, 读操作。

 

  • 举例:

./fii_app.o dir 0  设置所有gpio 为 output

 

./fii_app.o wr 0  设置最低位gpio 为 0

./fii_app.o wr 7  设置最低三位gpio 为 0

 

./fii_app.o dir 7  设置低三位gpio 为 input

./fii_app.o rd    读取gpio 的值, (用户可以波动switch,查看gpio 状态)

 

简单字符设备驱动程序,设计完成。

 

Posted in FPGA, IC, Linux内核, Linux开发与应用, SoC, SoC硬件开发, 教材与教案, 驱动开发

发表评论

相关链接