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后灭掉。