第一章CH03:ZYNQ使用linux点灯

想必学习过单片机的‘童鞋们’都很怀念这个词:“点灯”。一闪一闪的灯亮瞎你的猫眼…咳咳…言归正传,笔者毫不夸张地说:“点灯这件事看似简单,实则贯穿着无数的知识点。”只是一千个读者有一千个哈姆雷特罢了。好了好了,我们回到今天的内容。

首先说一下文章的内容排布,驱动篇的内容安排几乎都一样的,具体安排如下5个步骤:

在vivado中建立硬件工程。

在设备树中添加设备信息(非必要)。

针对添加的硬件设备编写驱动程序。

编写应用层的控制程序。

下载到板子调试验证。

       好了,大家大致了解了我们驱动的编写步骤之后,我们就开始干活吧。

步骤1:在工程中添加IP核:AXI GPIO

我们使用第一章CH01中的工程,添加ip核axi gpio , 设置为4位的输出模式(输出位数以自己实际板子为本),见图3.1.

图3.1

添加完ip核后,配置引脚,重新生成工程,然后你就会在Address Editor窗口看到ip核axi gpio在实际内存中的映射地址了,见图3.2.

图3.2

       笔者的axi gpio的设备基地址是0x4120_0000,一定要记住这个地址,在编写linux驱动的时候需要知道该地址才能点灯啊!

步骤2:在设备树中添加设备信息

本章节先不使用设备树,所以读者可以跳过步骤2。 虽然不使用设备树,但我们也简单介绍一下设备树的作用吧,读者可以先简单理解为:“linux内核通过读取设备树来了解它到底能掌控哪些设备,这些设备有什么样的参数”。

步骤3:写驱动程序

       刚学习完SOC跑裸机的“童鞋们已经迫不及待了”,终于可以穿上一件衣服了,以后不用再裸奔了。

       首先声明,linux驱动需要在Ubuntu下编译的,所以大家先把你们移植linux内核的Ubuntu系统打开吧,然后再切换到移植linux内核的目录,我们先在该目录下建立一个driver_app目录,我知道读者可能有点疑惑,直接上图吧,见图3.3.

图3.3

 

注意:跟linux内核是在同一级目录的,别弄错了,因为驱动程序的编译需要用到内核的支持,而笔者的Makefile文件指定的路径就是图3.3中的linux-xlnx-master文件夹。。读者如果不按照笔者的顺序来的话,就需要自己改Makefile文件了,想必读者也是一位懒人吧。

       我们在./ driver_app/axi_gpio/dev/目录下新建一个驱动文件led_dev.c

 

驱动程序如下:

 

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/device.h>

#include <asm/io.h>

#include <linux/init.h>

#include <linux/platform_device.h>

#include <linux/miscdevice.h>

#include <linux/ioport.h>

#include <linux/of.h>

#include <linux/uaccess.h>



static volatile unsigned int *led_base;  //led寄存器基地址



//打开设备触发的服务函数

static int led_open(struct inode * inode, struct file * filp)  

{

    printk(KERN_ERR "dev is open!");

    return 0;

}



//写设备触发的服务函数 file:设备  buf:数据缓存区   count:数据个数单位字节

static int led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

{

    int val,ret;

    //把buff缓存数据拷贝到val地址空间

ret = copy_from_user(&val, buf, count);



    //把val的值写进led_base寄存器

    iowrite32(val, led_base);

    printk(KERN_ERR "led : Write 0x%x to 0x%x.\n", val, (unsigned int)led_base);

    return 0;

}



//LED函数接口结构体

static const struct file_operations led_fops =

{

    .owner = THIS_MODULE,

    .open = led_open,

    .write = led_write,  

};



//LED设备结构体

static struct miscdevice led_dev=

{

    .minor = MISC_DYNAMIC_MINOR,

    .name = "led_dev",  // /dev/目录下的设备节点名

    .fops = &led_fops,

};



//LED设备初始化

static int led_init(void)

{

    int ret;



    //地址映射:把物理地址转换为虚拟地址

    led_base = ioremap(0x41200000, 4);

    printk(KERN_ERR "LED: Access address to device is:0x%x\n", (unsigned int)led_base);

   

    //注册设备到/dev/目录

    ret = misc_register(&led_dev);

    if(ret)

    {

        printk(KERN_ERR "led:[ERROR] Misc device register failed.\n");

        return ret;

    }

    printk(KERN_ERR "Module init complete!\n");

    return 0;

}

//LED设备退出

static void led_exit(void)

{

    printk(KERN_ERR "Module exit!\n");

    iounmap(led_base);      //取消物理地址的映射

    misc_deregister(&led_dev);  //删除/dev/目录下的设备节点

}



module_init(led_init);  //模块初始化接口

module_exit(led_exit);  //模块推出接口



//以下代码可解决一些错误信息

MODULE_AUTHOR("Nightmare@EEFOCUS");

MODULE_DESCRIPTION("ledDriver");

MODULE_ALIAS("It's only a test");

MODULE_LICENSE("Dual BSD/GPL");

 

代码已经贴出来了,我们来了解一下代码的结构:1-11行是相关头文件的定义, 第13行是设备基地址的定义,通过第59行的地址映射,获取设备的虚拟基地址。16-35行是open、read、write函数,这个三个函数是映射到应用层的,当应用层在该设备下使用这三个函数时,其实它会调用底层的这三个函数,也就是16-35行的open、read、write函数。这就是应用层与底层的交互。38-43行是一个函数接口的结构体,它管理着一个设备与底层交互的接口函数。46-51行是一个设备节点,linux内核通过该节点来识别和控制设备。54-72行在加载模块时会自动调用,主要是设备基地址映射和设备结点的注册,就是通知内核,我要添加一个设备。74-79行在卸载模块时调用,主要是取消设备地址映射和撤销设备结点。

读者可以看到,映射的地址不正是我们在添加axi gpio时记录下来的地址吗,然后通过映射地址来配置axi gpio设备里面的寄存器。这个配置过程跟裸机一模一样,笔者就不多说了。哦!还有一点就是:在映射地址时,要选择映射地址的长度,单位是字节。

好了,笔者提供了makefile文件,读者在命令行下直接执行make生成.ko可加载模块。

步骤4:编写应用层的控制程序

应用层没啥好说的,直接上代码,makefile文件笔者会打包发出来的,注意:makefile里面的交叉编译器要改成自己现在使用的交叉编译器。

 

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <unistd.h>



int main(int argc, char **argv)

{

    int i;

    int fd;

    int val=1;



    fd = open("/dev/led_dev", O_RDWR);  //打开设备



    //LED流水灯

    while(1)

    {

        for(i=0;i<4;i++)

        {

            val = (1<<i);

            write(fd,&val,4);   //把val的值写到fd设备中,大小为4字节

            sleep(1);

        }

    }

    return 0;

}

 

                    

 

步骤4:下载到板子调试验证

①重新生成boot.bin文件。

②驱动程序和应用程序在linux系统下编译完成后,把应用程序.o 和驱动程序.ko拉进开发板,使用insmod命令加载.ko模块,然后运行应用程序.o 然后就会看到开发板的led灯跑起来了。

 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值