1。历史的车轮总是向前,技术更替。在linus 同学发出那句 WFK 后内核进入了设备树时代(站在驱动工程师角度)。
前几天我已经被mach-imx 中的文件折磨的夜不能眠。我终于在一个清晨,喝完一杯咖啡后决定放弃蹩脚的传统device描述方式。
这里我先不讨论内核实现流程的源代码,简单描述下语法,和我的第一个test_platform_device
设备树文件 arch\arm\boot\dts 在修改dst文件后要make dtbs,
http://www.xuebuyuan.com/2128963.html 这篇文章可以恶补下设备书的基础
如图是一个完整节点
Documentation\devicetree\bindings 文件夹中有很多的样例可以供开发人员参考
2。 我的测试
我在我的设备树中添加如下代码(这里我把一个ds18b20做成platform设备,仅仅为了练习)
my-ds18b20 {
compatible = "ds18b20";
gpios = <&gpio2 3 1>; //有更改,以这里为准
};
我ds18b20使用的是GPIO2_3 管脚。
这里的gpios = <&gpio2 3 0>; 在 imx6qdl.dtsi 文件中定义
然后执行 # make dtbs
生成的dtbs文件在dts文件同一目录,烧写 内核 和 DTBS 文件
编写ds18b20的driver 端(为了使结构简单明了,我屏蔽了其他代码,留下了骨架)
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/ethtool.h>
#include <linux/dm9000.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/io.h>
static int ds18b20_probe(struct platform_device *pdev)
{
struct resource *addr_res = NULL; /* resources found */
printk("probe!!!!!!!!!! \n");
addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (addr_res == NULL) printk("get_re error");
return 0;
}
static int ds18b20_drv_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id ds18b20_of_matches[] = {
{ .compatible = "my-ds18b20", }, //和dts文件中名字匹配
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ds18b20_of_matches);
static struct platform_driver ds18b20_driver = {
.driver = {
.name = "ds18b20", //可以与dts文件中名字不同
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ds18b20_of_matches),
},
.probe = ds18b20_probe,
.remove = ds18b20_drv_remove,
};
module_platform_driver(ds18b20_driver);
MODULE_LICENSE("GPL");
编译,拷贝,加载模块
/************************以下为完全测试*************************/
成功获取到温度,有个小bug就是第一获取时温度有问题
以下是完全代码
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_net.h>
#include <linux/ethtool.h>
#include <linux/dm9000.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/io.h>
int ds18_gpio = -1;
#define GPIO_DS18B20 ds18_gpio
#define DS18B20_IO_UP gpio_set_value(GPIO_DS18B20, 1)
#define DS18B20_IO_DOWN gpio_set_value(GPIO_DS18B20, 0)
#define DS18B20_OUT gpio_direction_output(GPIO_DS18B20, 1)
#define DS18B20_IN gpio_direction_input(GPIO_DS18B20)
static void ds18_write(uint16_t data )
{
uint8_t i, temp;
DS18B20_OUT;
for(i=0; i<8; i++)
{
temp = data & 0x01;
data = data >> 1;
if(temp) //写1
{
DS18B20_IO_DOWN;
udelay(6);
DS18B20_IO_UP;
udelay(64);
}
else
{
DS18B20_IO_DOWN;
udelay(60);
DS18B20_IO_UP;
udelay(10);
}
}
}
static uint8_t ds18_read(void) //读位
{
uint8_t data;
DS18B20_OUT;
DS18B20_IO_DOWN;
udelay(6);
DS18B20_IO_UP;
DS18B20_IN;
udelay(9);
if(gpio_get_value(GPIO_DS18B20)==1){
data =1;
}else{
data =0;
}
udelay(45);
return data;
}
static uint8_t ds18_reads(void)
{
uint8_t i = 0,
temp = 0,
mydata = 0;
for(i=0;i<8;i++)
{
temp = ds18_read();
mydata = mydata | (temp<<i);
}
udelay(2);
return mydata;
}
static uint8_t ds18_reset(void)
{
DS18B20_OUT;
DS18B20_IO_DOWN;
udelay(300);
udelay(300);
DS18B20_IO_UP;
DS18B20_IN; // 600 us
udelay(100);
if (gpio_get_value(GPIO_DS18B20) == 0){
printk("reset bingo \n");
return 0;
}
printk("reset fail \n");
return -1;
}
static long ds18b20_ctl(struct file * file,unsigned int cmd,unsigned long num)
{
uint8_t tp_msb = 0,
tp_lsb = 0;
uint32_t data;
if(cmd){ //read
printk("star read \n");
if (ds18_reset() != 0)goto error1;
ds18_write(0xCC);
udelay(1);
ds18_write(0x44); //转换温度
//mdelay(100);
//ssleep(1);
if (ds18_reset() != 0)goto error1;
ds18_write(0xCC);
udelay(1);
ds18_write(0xBE); //读取温度
tp_lsb= ds18_reads();
udelay(1);
tp_msb= ds18_reads();
data = tp_msb<<8;
data = data | tp_lsb;
if( data < 0 )
data = (~data+1) * 625;
else
data = data * 625;
printk("tmp = %d \n", data);
}
return 0;
error1:
printk("read error \n");
return -1;
}
struct file_operations ds18b20_fops = {
.unlocked_ioctl= ds18b20_ctl,
};
struct miscdevice ds18b20_misc={
.minor = 200,
.name = "misc_ds18b20",
.fops = &ds18b20_fops,
};
static int ds18b20_probe(struct platform_device *pdev)
{
struct resource *addr_res = NULL; /* resources found */
int re = -1;
printk("probe!!!!!!!!!! \n");
ds18_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);
printk("%d\n", ds18_gpio);
re = gpio_request(ds18_gpio, "ds18b20");
if (re != 0) return -1;
if (ds18_gpio > 0){
gpio_direction_output(ds18_gpio, 1);
}
re = ds18_reset(); //检测是否存在
if (!re){
printk("finded ds18b20 \n");
misc_register(&ds18b20_misc);
return 0;
}else{
gpio_free(GPIO_DS18B20);
printk("no find ds18b20 \n");
return -1;
}
return 0;
}
static int ds18b20_drv_remove(struct platform_device *pdev)
{
gpio_free(ds18_gpio);
return 0;
}
static const struct of_device_id ds18b20_of_matches[] = {
{ .compatible = "ds18b20", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ds18b20_of_matches);
static struct platform_driver ds18b20_driver = {
.driver = {
.name = "ds18b20",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ds18b20_of_matches),
},
.probe = ds18b20_probe,
.remove = ds18b20_drv_remove,
};
module_platform_driver(ds18b20_driver);
MODULE_LICENSE("GPL");