当前位置: 首页 > 工具软件 > Rico > 使用案例 >

RICO BOARD驱动探索之旅_环境搭建与点亮LED

宗晟
2023-12-01
今天开始更新我的博客,一个专题: RICO BOARD驱动探索之旅
机缘巧合,我申请到了RICO BOARD,一直想自己玩玩,现在开播!!!
这款板的渊源我就不赘述了,它是以TI Sitara AM437X为处理器,优点我也省略,反正我是小白。
既然是小白,我就以小白的目光来开发这一套板的驱动(基于LINUX)

驱动开发准备工作:
(1)编译内核
     我用的是官方带的内核,按照说明书移植好编译工具链,然后直接按照说明编译即可;
     不过得改改Makefile
     ARCH 改为      ARCH    ?=arm
     ARCH 改为      CROSS_COMPILE    ?=arm-linux-gnueabihf-
(2)搭建NFS
     小白在此声明,板子和你的宿主机之间必须得传文件,你可用FTP,NFS,U盘,但是最方便的莫过于用NFS了。
     NFS的搭建网上是有的,但是在这里我还是写一写(搬)吧:
     (1)sudo apt-get install nfs-kernel-server
     (2)sudo vi /etc/exports
            添加/home/king/nfs *(rw,subtree_check,no_root_squash,no_all_squash,sync) //这是之后放文件的目录
     (3)chmod 777 –R /home/king/nfs
     (4)sudo vi /etc/default/nfs-kernel-server
            将RPCMOUNTDOPTS改为   RPCMOUNTDOPTS="-p 13100"
     (5)sudo service nfs-kernel-server restart
     到这儿应该就搭好了,我们做个测试:
     sudo mount -t nfs 127.0.0.1:/home/king/nfs /mnt    //咱先自个儿挂自个儿上面,然后试试
    
    
完成了以上工作,我们开始我们的第一个驱动开发:
    我是电子专业出身,所以玩过几款单片机,形成一个习惯:拿到一个板子,总想着先点亮一盏LED看看,这次就让我再任
    性一次吧,我们先来点个灯玩玩。
    
    我们先来说说单片机点灯要哪些个过程:
    (1)搞清楚灯在哪个管脚?
    (2)看芯片的手册,看看如何把对应的管脚配置成输出模式?
    (3)然后再看看怎样改变管脚的电平,使其变为低电平,LED就亮了?
    
    我们做LINUX驱动开发LED点亮呢?
    (1)搭建一个可用的字符设备驱动框架;
    (2)搞清楚灯在哪个管脚?
    (3)看芯片的手册,找到相关的寄存器的内存地址;
    (4)在初始化中完成寄存器的地址映射;
    (5)将LED操作函数填充到字符设备驱动框架中;
    
    有了这个思维,我想我们的工作就会简单很多。我们一步一步的走,不就点个灯嘛。。。
    (1)字符设备驱动框架代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define DEV_NAME "king"

struct class *king_class;
static struct device *this_dev;

int major=0;
dev_t dev_nr=0;

struct cdev king_cdev;

static int king_open(struct inode *pinode , struct file *pfile)
{
    printk("king_open  :  Tring to open !!!\n");
    return 0;
}

static int king_release(struct inode *pinode , struct file *pfile)
{
    printk("king_open  :  Tring to release !!!\n");
}

ssize_t king_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    printk("king_read  :  king_dev is reading!!!\n");
    return count;
}

ssize_t king_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{    
    printk("king_write  :  king_dev is writing!!!\n");
    return count;
}

static struct file_operations dev_ops =   //本设备的操作函数结构体
{
    .owner = THIS_MODULE,
    .open  = king_open,
    .read  = king_read,
    .write = king_write,
    .release = king_release,
};

int  hello_init(void)  //初始化一个字符设备
{
    int ret=0;
    printk("hello_init  :  king_dev is ins!!!\n");
    alloc_chrdev_region(&dev_nr,0,1,DEV_NAME);
    major = MAJOR(dev_nr);
    printk(DEV_NAME"'s major is %d (hello_init)\n",major);
    cdev_init(&king_cdev,&dev_ops);
    king_cdev.owner = THIS_MODULE;
    ret = cdev_add(&king_cdev,dev_nr,1);
    king_class = class_create(THIS_MODULE,"king_cdev");
    this_dev = device_create(king_class,NULL,MKDEV(major,0),NULL,DEV_NAME);
    printk("hello_init : enjoy yourself!\n");
    return 0;
}

int  hello_exit(void)
{
    device_destroy(king_class,MKDEV(major,0));
    class_destroy(king_class);
    cdev_del(&king_cdev);
    unregister_chrdev_region(dev_nr,1);
    printk(KERN_ALERT"kernel:\t%s,Bye\n",KERN_ALERT);
    return 0;

}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");  


        这个是我自己的一个字符设备驱动框架,亲测绝逼可用。然后我们要写一个Makefile:

KERN_DIR = /home/king/work/rico/kernel/linux-3.12.10-ti2013.12.01  #这个是你的内核目录

all:
    make -C $(KERN_DIR) M=`pwd` modules

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m    += king.o  #要编译的模块的名字


然后直接make就好,把生成的king.ko放到板子里面,然后insmod就好了,你会找到/dev/king,这样就对了!!!

    (2)不过不要开心的太早,接下来该看芯片手册了,如果正好你是小白且英语不好就随我看看,if not,跳过。
    找到general-purpose input/output这一章,其实就是GPIO,哈哈!!
    然后我们看看这段文字:
    The general-purpose interface combines six general-purpose input/output (GPIO) modules. Each GPIO
    module provides 32 dedicated general-purpose pins with input and output capabilities; thus, the
    generalpurpose interface supports up to 192 (6 × 32) pins
    讲的就是这款芯片的GPIO模块一共有6组,每组管脚有32个脚,都具有GPIO功能,最多达到192个管脚
    OK,我们略过对于GPIO管脚功能的大篇幅描述,直接取我们有用的信息,我长话短说:
        配置GPIO_OE Register:
            这个寄存器控制每个管脚的模式(输入,输出,中断),一共32位,代表32个管脚,将我们想要的管脚
            所对应的位设置为0就行。
        学习使用GPIO_CLRDATAOUT
            这个寄存器可以控制管脚的电平,只要对应的位为1,为0的位不受影响,则这个管脚的电平被拉低;
        学习使用GPIO_SETDATAOUT
            这个寄存器可以控制管脚的电平,只要对应的位为1,为0的位不受影响,则这个管脚的电平被拉高;
    好了,寄存器功能就搞定了!
    (3)下面你可以好好开心开心,我们找找LED在哪个GPIO口:
         在GPIO1_24~GPIO1_27,哈哈,我们这一步完成的很轻松;
    (4)找到寄存器的地址:
    首先去Memory Map 找GPIO1的地址,0x4804c000,其余寄存器找到其偏移值就行,加上去就好。
    (5)填入字符设备框架:

    
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define DEV_NAME "king"

struct class *king_class;
static struct device *this_dev;

volatile unsigned long *gpio1_oe = NULL;
volatile unsigned long *gpio1_dataout = NULL;
volatile unsigned long *gpio1_clrdata = NULL;
volatile unsigned long *gpio1_setdata = NULL;

int major=0;
dev_t dev_nr=0;

struct cdev king_cdev;

static int king_open(struct inode *pinode , struct file *pfile)
{
    printk("king_open  :  Tring to open !!!\n");

    *gpio1_oe &= ~(1<<25);  //对应的位清零
    *gpio1_oe &= ~(1<<26);
    *gpio1_oe &= ~(1<<27);

    return 0;
}

static int king_release(struct inode *pinode , struct file *pfile)
{
    printk("king_open  :  Tring to release !!!\n");
    *gpio1_setdata |= 1<<25;  //对应的位置1
    *gpio1_setdata |= 1<<26;
    *gpio1_setdata |= 1<<27;
}

ssize_t king_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    printk("king_write  :  king_dev is writing!!!\n");
    
    *gpio1_clrdata |= 1<<25;
    *gpio1_setdata |= 1<<26;
    *gpio1_setdata |= 1<<27;

    return count;
}

static struct file_operations dev_ops =
{
    .owner = THIS_MODULE,
    .open  = king_open,
    .write = king_write,
    .release = king_release,
};

int  hello_init(void)
{
    int ret=0;
    printk("hello_init  :  king_dev is ins!!!\n");
    alloc_chrdev_region(&dev_nr,0,1,DEV_NAME);
    major = MAJOR(dev_nr);
    printk(DEV_NAME"'s major is %d (hello_init)\n",major);
    cdev_init(&king_cdev,&dev_ops);
    king_cdev.owner = THIS_MODULE;
    ret = cdev_add(&king_cdev,dev_nr,1);
    king_class = class_create(THIS_MODULE,"king_cdev");
    this_dev = device_create(king_class,NULL,MKDEV(major,0),NULL,DEV_NAME);
    
    gpio1_oe = (volatile unsigned long *)ioremap(0x4804c000+0x0134 , 32);
    gpio1_dataout = (volatile unsigned long *)ioremap(0x4804c000+0x013c , 32);  //映射寄存器
    gpio1_clrdata = (volatile unsigned long *)ioremap(0x4804c000+0x0190 , 32);
    gpio1_setdata = (volatile unsigned long *)ioremap(0x4804c000+0x0194 , 32);
    
    printk("hello_init : enjoy yourself!\n");
    return 0;
}

int  hello_exit(void)
{
    device_destroy(king_class,MKDEV(major,0));
    class_destroy(king_class);
    cdev_del(&king_cdev);
    unregister_chrdev_region(dev_nr,1);
    
    iounmap(gpio1_setdata);
    iounmap(gpio1_clrdata);
    iounmap(gpio1_dataout);
    iounmap(gpio1_oe);
    
    printk(KERN_ALERT"kernel:\t%s,Bye\n",KERN_ALERT);
    return 0;

}

module_init(hello_init);
module_exit(hello_exit);


MODULE_LICENSE("GPL");


下面给一个测试代码:

#include <stdio.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <unistd.h>

#define DEV_NAME "/dev/king"

int  main(int argc, char const *argv[])
{
    int fd = 0;
    int ret = 0,i=0;
    int led=0;
    fd = open(DEV_NAME,O_RDWR);
    if (fd<0)
    {
        /* code */
        printf("You are wrong!!!\n");
        exit(0);
    }

    ret = write(fd,&led,1);
    printf("I am writing %d\n",ret);
    sleep(5);
    close(fd);
    return 0;
}


操作流程:
(1)make,生成ko文件,拷到板子;
(2)编译测试代码:arm-linux-gnueabihf-gcc -o app app.c
    生成app,拷到板子;
(3)insmod king.ko
    ./app
    看到板子灯亮5S后灭掉。


    
    
    
    
    
    
    
    

   
 类似资料: