电源设计技术实用资料点击免费获取>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 开源硬件 » 芯灵思Sinlinx A64 linux LED驱动

共1条 1/1 1 跳转至

芯灵思Sinlinx A64 linux LED驱动

助工
2019-03-01 17:41:16    评分
开发平台 芯灵思Sinlinx A64
内存: 1GB   存储: 4GB
详细参数 https://m.tb.cn/h.3wMaSKm
开发板交流群 641395230原理图:

1.jpg参考资料:
2.jpg

寄存器位置

一些概念:
字符设备驱动框架

  • 必须有一个设备号,用在众多到设备驱动中进行区分,

  • 用户必须知道设备驱动对应到设备节点(设备文件),linux把所有到设备都看成文件

  • 对设备操作其实就是对文件操作,应用空间操作open,read,write的时候实际在驱动代码有对应到open, read,write


字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED设备等。
Linux 2.6 标准字符设备核心结构

  • struct cdev {

  •     struct kobject kobj;

  •     struct module *owner;

  •     const struct file_operations *ops;  /* 设备文件操作方法 */

  •     struct list_head list;

  •     dev_t dev;                             /* 32 位设备号:包含主和次 */

  •     unsigned int count;                 /*占用多少个连续的次设备号 */

  • };

  • Linux2.6版本以上内核,使用dev_t来表示一个设备号,dev_t实际上是一个u32类型。其中高12位是主设备号,低20位是次设备号。

  • 主设备号:dev_t 高 12 位, 2^12=4K (10 是给杂项设备使用)范围 0 ~ 4095

  • 次设备号: dev_t 低 20 位, 2^20=1M   范围 0 ~ 1M-1

  • 合成设备号:

  •     MKDEV(A,B):A: 主设备号; B: 次设备号

  • 分解设备号:

  •     MAJOR(dev) : 从设备号 dev 中分解出主设备号

  •     MINOR(dev) : 从设备号 dev 中分解出次设备号


alloc_chrdev_region函数,来让内核自动给我们分配设备号
(1)register_chrdev_region是在事先知道要使用的主、次设备号时使用的;要先查看cat /proc/devices去查看没有使用的。
(2)更简便、更智能的方法是让内核给我们自动分配一个主设备号,使用alloc_chrdev_region就可以自动分配了。
(3)自动分配的设备号,我们必须去知道他的主次设备号,否则后面没法去mknod创建他对应的设备文件。   
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)   

  •         这个函数的第一个参数,是输出型参数,获得一个分配到的设备号。可以用MAJOR宏和MINOR宏,将主设备号和次设备号,提取打印出来,看是自动分配的是多少,方便我们在mknod创建设备文件时用到主设备号和次设备号。 mknod /dev/xxx c 主设备号 次设备号

  •         第二个参数:次设备号的基准,从第几个次设备号开始分配。

  •         第三个参数:次设备号的个数。

  •         第四个参数:驱动的名字。

  •         返回值:小于0,则错误,自动分配设备号错误。否则分配得到的设备号就被第一个参数带出来。

int copy_from_user(void * to, const void __user * from, unsigned long n)

  • 将数据从用户空间拷贝到内核空间,一般是在驱动中chr_drv_write()用 参数1:内核驱动中的一个buffer

  • 参数2:应用空间到一个buffer

  • 参数3:个数

自动创建设备结点
struct class *class_create(owner, name)//创建一个类
参数1: THIS_MODULE
参数2: 字符串名字,自定义
返回一个class指针

//创建一个设备文件
struct device *device_create(struct class * class, struct device * parent, dev_t devt, void * drvdata, const char * fmt,...)

  • 参数1: class结构体,class_create调用之后到返回值

  • 参数2:表示父亲,一般直接填NULL

  • 参数3: 设备号类型 dev_t

  • dev_t devt

  • #define MAJOR(dev)    ((unsigned int) ((dev) >> MINORBITS))

  • #define MINOR(dev)    ((unsigned int) ((dev) & MINORMASK))

  • #define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))

  • 参数4:私有数据,一般直接填NULL

  • 参数5和6:表示可变参数,字符串,表示设备节点名字


驱动代码

#include <linux/module.h> 
#include <linux/init.h>
#include <linux/fs.h> 
#include <linux/device.h> 
#include <linux/slab.h>
#include <linux/cdev.h>
#define MY_DEVICE_NAME "my_led_device"
//物理地址
#define GPL_BASE   0x01F02C00  
volatile unsigned long *gpl_base = NULL;
volatile unsigned long *gpl_r0 = NULL;
volatile unsigned long *gpl_r1 = NULL;
volatile unsigned long *gpl_dat = NULL;
volatile unsigned long *gpl_pul0 = NULL;
//setp 3 :实现函数
static int my_open (struct inode *node, struct file *filp)
{
    if (GPL_BASE) 
    {
        printk("ioremap  0x%x\n", GPL_BASE);
    }
    else 
    {
        return -EINVAL;
    }
    return 0;
}

static ssize_t my_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
{
    unsigned char val;        
    copy_from_user(&val, buf, 1);

    if (val)
    {
        *gpl_dat |= ( (1<<7)  | (1<<10) | (1<<12) );
    }
    else
    {
        *gpl_dat  &= ( ~(1<<7)  | ~(1<<10) | ~(1<<12) );
    }

    return 1; 
}


static const struct file_operations my_led_fops = {
    //step 1 :定义file_operations结构体
    .open = my_open,
    .write = my_write,    
};

//step 1 :
static struct class *led_class;
static struct cdev *pcdev;      //定义一个cdev指针
static dev_t n_dev;            //第一个设备号(包含了主和次)
static int __init led_device_init(void)
{//step 2 :注册 
    int ret = -1;
    pcdev = cdev_alloc();//分配cdev结构空间
    if(pcdev == NULL) {
        printk(KERN_EMERG" cdev_alloc  error\n");
        ret = -ENOMEM;   /* 分配失败 */
        return ret;
    }
    //2. 动态申请设备号
    ret = alloc_chrdev_region(&n_dev, 0 , 2, MY_DEVICE_NAME);
    if(ret < 0 ) {
        //释放前面成功的资源
        kfree(pcdev);                              /*释放cdev结构空间 */
        printk(KERN_EMERG"alloc_chrdev_region  error\n");
        return ret;
    }    
    cdev_init(pcdev, &my_led_fops);     //初始化cdev结构           /* 建立cdev和file_operations之间的连接 */ 
    /*
        或这样初始化cdev结构
        pcdev->owner = THIS_MODULE;
        pcdev->ops = &my_led_fops;
    */
    ret = cdev_add(pcdev, n_dev, 2) ;//4. 向内核里面添加一个驱动,注册驱动
    if(ret < 0 ) {
        //释放前面成功的资源
        unregister_chrdev_region(n_dev,  2);       /*  释放前面申请的调和号*/
        kfree(pcdev);                               /* 释放cdev结构空间 */
        printk(KERN_EMERG"alloc_chrdev_region  error\n");
        return ret;
    }

    /*自动创建设备节点/dev/SinlinxA64_LED*/
    led_class = class_create(THIS_MODULE, "myled");    
    device_create(led_class, NULL, n_dev, NULL, "SinlinxA64_LED"); 
    /*虚拟地址映射*/
    gpl_base = (volatile unsigned long *)ioremap(GPL_BASE, 1);
    gpl_r0 = gpl_base;
    gpl_r1 = gpl_base + 0x04;
    gpl_dat = gpl_base + 0x10;
    gpl_pul0 = gpl_base + 0x1c;
    /* 初始化寄存器 */
    *gpl_r0   &= ~(7<<28); 
    *gpl_r1   &= ( ~(7<<16) | ~(7<< 8) );
    *gpl_dat  &= ( ~(1<<7)  | ~(1<<10) | ~(1<<12) );
    *gpl_pul0 &= ( ~(3<<14) | ~(3<<20) | ~(3<<24) );
    /*上拉输出*/
    *gpl_r0   |=  (1<<28);
    *gpl_r1   |=  ( (1<<16) | (1<<8) );
    *gpl_pul0 |=  ( (1<<14) | (1<<20) | (1<<24) );
    printk(KERN_EMERG"cdev ok\n");    
    return 0;
}

static void __exit led_device_exit(void)
{    //step 2 :注销

    //注销cdev结构
    cdev_del(pcdev);
    //释放设备号
    unregister_chrdev_region(n_dev, 2); /*起始设备号(主、次) 连续的次设备号数量*/
    //释放cdev结构空间
    kfree(pcdev);
    
    device_destroy(led_class, n_dev);
    class_destroy(led_class);
    iounmap(GPL_BASE);
    printk(KERN_EMERG"cdev_del ok\n");
}

module_init(led_device_init);
module_exit(led_device_exit);
MODULE_LICENSE("GPL");




共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]