优快云仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2017/08/12/AM437x——LED驱动/#more
本文主要记录AM437X驱动的LED。含简单的字符设备驱动、设备驱动模型、设备树以及LED子系统。
目前就Linux驱动的理解是:
Linux驱动 = 裸机 + 框架
关于框架,目前的理解是:
以LED驱动为例,之前印象中就是韦老大的思路,先在init里注册字符设备、硬件初始化,然后应用层open()、read()就调用了file_operations里面的drv_open()、drv_write()等,算是最简单的驱动框架。
然后韦老大又提及了总线设备驱动模型,将设备和驱动分离,慢慢的体会到了新的框架。不久前,简单接触了下设备树,感觉就是总线设备驱动模型的修改(升级),将原来的设备部分,不再单独放在代码里,而是放在dts里面,开机在U-boot加载,然后与驱动匹配获取硬件资源。因此,感觉驱动的框架在一步一步的发展,优化,最原始的注册字符设备、open等框架,还是不变。
同时,了解到了除输入子系统的其它子系统,加深了对这一模式的理解。感觉就是,将某个硬件资源无缝的融入现有的环境中,而无须改变应用层的程序。
这就是目前的一点小小理解吧,算是打开了个入口,希望以后了解得更加全面、细致。
1.搭建开发环境
1.1安装TI_SDK
先在TI官网下载ti-processor-sdk-linux-am437x-evm-01.00.00.03-Linux-x86-Install.bin
在Ubuntu(only Ubuntu 12.04 LTS and Ubuntu 14.04 LTS are supported)下,对该文件加入可执行权限,然后直接运行。安装目录选择默认即可。完成之后,便在当前用户的home目录生成了所有所需文件。

1.2编译内核
在当前生成ti-processor-sdk-linux-am437x-evm-01.00.00.03目录下,有个Makefile,打开后可以看到相关的编译选项,如:
- 编译全部文件:
make all - 编译内核:
make linux - 编译u-boot:
make u-boot-spl
以及make的依赖:-include Rules.make。在本层目录里,打开Rules.make,可以知道内核的默认配置文件:
#defconfig
DEFCONFIG=tisdk_am437x-evm_defconfig
通过查找,tisdk_am437x-evm_defconfig在~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/board-support/linux-3.14.43+gitAUTOINC+875c69b2c3-g875c69b/arch/arm/configs里。
这里通过修改该配置文件,然后重新编译内核,即可关闭系统LED相关的驱动,在后面自己写LED驱动时,防止互相干扰。
因此将tisdk_am437x-evm_defconfig配置文件里的所有有关LED的配置都关闭掉。
最后在顶层目录执行make linux,编译完成后,生成~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/board-support/linux-3.14.43+gitAUTOINC+875c69b2c3-g875c69b/arch/arm/boot/zImage文件。
1.3烧写SD卡
回到~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/bin下,TI制作了很多脚本,其中的create-sdcard.sh就是制作SD卡的。Ubuntu插上SD卡,然后切换成root用户,执行该脚本,根据提示一路选择下去即可。

这里烧写完了,测试发现并没有使用之前编译的内核,分析脚本后发现,该脚本直接使用的~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/filesystem下的tisdk-rootfs-image-am437x-evm.tar.gz。脚本将该文件作为根文件系统放入SD卡,因此并没有使用之前编译的内核。解决方法要么在执行脚本的过程中根据提示输入相关的路径,要么在制作好SD卡后,将编译好的内核覆盖掉SD卡的内核即可。我选择的后者:cp ~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/board-support/linux-3.14.43+gitAUTOINC+875c69b2c3-g875c69b/arch/arm/boot/zImage /media/hceng/rootfs/boot/。
最后将制作好的SD卡插上开发板启动即可。
2.简单的字符驱动
先记录下几个重要类型或结构体:
- 表示设备号(32位机中:高12位表示主设备号,低20位表示次设备号)
typedef __kernel_dev_t dev_t;
- 描述字符设备
struct cdev {
struct kobject kobj; //内嵌kobject结构体,用于设备驱动模型管理
struct module *owner; //包含指向该结构的模块的指针,用于引用计数
const struct file_operations *ops; //指向字符设备操作函数集的指针
struct list_head list; //该结构将使用该驱动的字符设备连接成一个链表
dev_t dev; //该字符设备的其实设备号,一个设备可能有多个设备号
unsigned int count; //使用该字符设备驱动的设备数量
};
- 描述类
struct class{
const char *name; //类名称
struct module *owner; //对应模块
struct subsystem subsys; //对应的subsystem;
struct list_head children; //class_device链表
struct list_head interfaces; //class_interface链表
struct semaphore sem; /用于同步的信号锁
struct class_attribute *class_attrs; //类属性
int (*uevent)(struct class_device *dev,char **envp,int num_envp,
char *buffer,int buffer_size); //事件
void (*release)(struct class_device *dev); //释放类设备
void (*class_release)(struct class *class); //释放类
}
总结下,目前理解的字符设备编写流程:
1)驱动加载函数:
xx_drv_init()
1.1)申请设备号:alloc_chrdev_region()
1.2)cde初始化(绑定fops):cdev_init()
1.3)注册到内核:cdev_add()
1.4)创建类:class_create()
1.5)向类中添加设备(mdev自动创建设备节点):device_create()
1.6)硬件相关(内存映射):ioremap()
2)驱动卸载函数:xx_drv_exit()
2.1)移除设备:device_destroy()
2.2)移除类:class_destroy()
2.3)注销cdev:cdev_del()
2.4)释放设备号:unregister_chrdev()
2.5)释放内存:iounmap()
3)必要修饰:module_init(xx_drv_init);module_exit(xx_drv_exit);MODULE_LICENSE("GPL");
4)构造file_operations:struct file_operations xx_drv_fops;
5)实现file_operations里每个函数:xx_open()、xx_write()……
2.1驱动代码
{% codeblock lang:c [leds_drv.c] https://github.com/hceng/am437x/blob/master/drive/1th_led/v1.0/leds_drv.c %}
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/cdev.h>
#define TI_LEDS_CNT 4
int major;
static struct cdev leds_cdev;
static struct class *leds_cls;
static volatile unsigned long *PRCM_CM_PER_GPIO5_CLKCTRL = NULL;
static volatile unsigned long *CTRL_CONF_UART3_RXD = NULL;
static volatile unsigned long *CTRL_CONF_UART3_TXD = NULL;
static volatile unsigned long *CTRL_CONF_UART3_CTSN = NULL;
static volatile unsigned long *CTRL_CONF_UART3_RTSN = NULL;
static volatile unsigned long *GPIO_OE = NULL;
static volatile unsigned long *GPIO_SETDATAOUT = NULL;
static volatile unsigned long *GPIO_DATAOUT = NULL;
static int leds_drv_open(struct inode *inode, struct file *file)
{
int minor = iminor(file->f_inode);
printk(KERN_INFO"%s OK.\n",__func__);
*PRCM_CM_PER_GPIO5_CLKCTRL = (0x01<<1);
*CTRL_CONF_UART3_RXD &= ~(0x7<<0 | 0x01<<16 | 0x01<<17 | 0x01<<18);
*CTRL_CONF_UART3_RXD |= (0x7<<0 | 0x01<<17);
*GPIO_OE &= ~(0x01<<minor);
*GPIO_SETDATAOUT |= (0x01<<minor);
return 0;
}
static ssize_t leds_drv_write(struct file *file, const char __user *user_buf, size_t count, loff_t * ppos)
{
int minor = iminor(file->f_inode);
char buf;
printk(KERN_INFO"%s OK.\n",__func__);

最低0.47元/天 解锁文章
3091

被折叠的 条评论
为什么被折叠?



