TQ2440-2.6.30.4内核下的ds28b20模块驱动

来源:互联网 发布:qq群监控软件 编辑:程序博客网 时间:2024/04/30 03:06
TQ2440-2.6.30.4内核下的ds28b20模块驱动  from:   http://keyemb.com/?p=12

因为要做个温度采集的东西,所以用到了ds18b20温度传感器。但是以前只在单片机上写过ds18b20的程序,现在对arm-linux下的驱动程序得编写还不是很熟练,所以在网络上找了个程序下了下来用了一下。文章的地址忘了。。。在这里感谢原作者。网上有许多TQ2440的驱动,但是我试了好几个,只有这个是好使的..

驱动程序:ds18b20.c

#include <linux/platform_device.h>#include <linux/delay.h>#include <linux/fs.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/uaccess.h>#include <linux/clk.h>#include <mach/regs-gpio.h>#include <mach/io.h>#include <mach/map.h>#include <mach/regs-clock.h>#define   DQ             S3C2410_GPG11#define   CPG_IN         S3C2410_GPG11_INP#define   CPG_OUT        S3C2410_GPG11_OUTP#define DEVICE_NAME "tq18b20"  //自定义驱动称为“tq18b20”。#define DS18B20_MAJOR 210static int opencount = 0;static char data[2];//ds18b20复位,返回0成功,返回1失败static unsigned char init_ds18b20(void){    unsigned char ret = 0;    s3c2410_gpio_cfgpin(DQ, CPG_OUT);   // 配置GPG11输出模式    //s3c2410_gpio_pullup(DQ, 0);    s3c2410_gpio_setpin(DQ, 1); // 向18B20发送一个上升沿,并保持高电平状态约100微秒    udelay(100);    s3c2410_gpio_setpin(DQ, 0); // 向18B20发送一个下降沿,并保持低电平状态约500微秒    udelay(500);    s3c2410_gpio_setpin(DQ, 1); //将18b20总线拉高,以便在15~60us后接收18b20发出的存在脉冲    udelay(50);    s3c2410_gpio_cfgpin(DQ, CPG_IN);  // 通过再次配置GPG11引脚成输入状态,可以检测到DS18B20是否复位成功    //若存在脉冲是一个60~240us的低电平信号,则通信双方已达成基本的协议    ret = s3c2410_gpio_getpin(DQ);    //接下来是控制器与18b20的数字通信    udelay(200);    return ret;}//向18b20写一个字节static void write_onechar(char data){    unsigned char i = 0;    s3c2410_gpio_cfgpin(DQ, CPG_OUT); //配置GPG11输出模式    s3c2410_gpio_pullup(DQ, 1);    for(i=0; i<8; i++)    {    s3c2410_gpio_setpin(DQ, 0);      //每一位的发送前至少15us低电平起始位    udelay(15);        s3c2410_gpio_setpin(DQ, data&0x01);  //在采样时间内,如果控制器将总线拉高,表示写1,拉低表示写0    udelay(60);    s3c2410_gpio_setpin(DQ, 1);    udelay(2);    data >>= 1;}}//从18b20读一个字节static unsigned char read_onechar(void){    unsigned char i;    unsigned char data=0;    for(i=0; i<8; i++)    {        s3c2410_gpio_cfgpin(DQ, CPG_OUT); // 配置GPG11输出模式        s3c2410_gpio_setpin(DQ, 0);   //读时间间隙时也是必须先有主机产生至少1us的低电平,表示读时间的起始        udelay(1);        data >>= 1;        s3c2410_gpio_setpin(DQ, 1);        s3c2410_gpio_cfgpin(DQ, CPG_IN);            if(s3c2410_gpio_getpin(DQ))    // 若总线在我们设它为低电平之后若1微秒之内变为高        // 则认为从DS18B20处收到一个“1”信号        data |= 0x80;        udelay(50);    }    return data;}//18b20的读函数,读出温度static ssize_t read_ds18b20(struct file *filp, char *buffer,         size_t count, loff_t *ppos){    if(init_ds18b20()) //初始化成功,init_ds18b20()返回值为0,否则为1        return -1;    write_onechar(0x0cc); //跳过读序列号的操作    write_onechar(0x44); //启动温度转换    udelay(5);    while(init_ds18b20());    udelay(200);    write_onechar(0x0cc); //跳过读序列号的操作    write_onechar(0x0be); //读取温度寄存器    data[0] = read_onechar(); //读低8位    data[1] = read_onechar(); //读高8位    // copy_to_user(buffer, &data, 2);    buffer[0]=data[0];    buffer[1]=data[1];    return 1;}//对应应用程序的open函数static int open_ds18b20(struct inode *node, struct file *file){    unsigned char flag;    if(opencount == 1)        return -EBUSY;    flag = init_ds18b20();    if(flag&0x01)    {        printk("uable to open device!\n");        return -1;    }    else    {        opencount++;        printk("device opened!\n");        return 0;    }}static int release_ds18b20(struct inode *node, struct file *file){    opencount--;    printk("device released!\n");    return 0;}static struct file_operations ds18b20_fops = {    .owner = THIS_MODULE,    .read = read_ds18b20,    .release = release_ds18b20,    .open = open_ds18b20,};static char __initdata banner[] = "TQ2440/SKY2440 ds18b20\n";//打印信息static struct class *ds18b20_class;static int __init ds18b20_init(void){    int ret;    printk(banner);    ret = register_chrdev(DS18B20_MAJOR, DEVICE_NAME, &ds18b20_fops);    if (ret < 0)    {        printk(DEVICE_NAME " can't register major number\n");        return ret;    }//错误处理    ds18b20_class = class_create(THIS_MODULE, DEVICE_NAME);    if(IS_ERR(ds18b20_class))    {        printk("Err: failed in tope-leds class. \n");        return -1;    }    device_create(ds18b20_class, NULL, MKDEV(DS18B20_MAJOR, 0), NULL, DEVICE_NAME);//创建一个设备节点,节点名为DEVICE_NAME    printk(DEVICE_NAME " initialized\n");//打印信息,内核中的打印用printk函数    return 0;}static void __exit ds18b20_exit(void){    printk(DEVICE_NAME " exit\n");//打印信息,内核中的打印用printk函数    unregister_chrdev(DS18B20_MAJOR, DEVICE_NAME);//取消注册设备    device_destroy(ds18b20_class, MKDEV(DS18B20_MAJOR, 0)); //删掉设备节点    class_destroy(ds18b20_class);     //注销类}module_init(ds18b20_init);module_exit(ds18b20_exit);MODULE_LICENSE("GPL");//遵循的协议

编译的方法是在当前目录下写一个Makefile文件,内容如下

obj-m := ds18b20.o

然后执行:

make -C 内核路径 M=`pwd` modules

就会生成ds18b20.ko的驱动模块文件。

测试程序:ds18b20_test.c

#include "stdio.h"#include "sys/types.h"#include "sys/ioctl.h"#include "stdlib.h"#include "termios.h"#include "sys/stat.h"#include "fcntl.h"#include "sys/time.h"main(){    int fd;    unsigned char buf[2];    float result;    if ((fd=open("/dev/tq18b20",O_RDWR | O_NDELAY | O_NOCTTY)) < 0)    {        printf("Open Device DS18B20 failed.\r\n");        exit(1);    }    else    {        printf("Open Device DS18B20 successed.\r\n");        while(1)        {            read(fd, buf, 1);            result = (float)buf[0];            result /= 16;            result += ((float)buf[1] * 16);     if(result<100)     {             printf("%f .C\r\n", result);             sleep(1);             }        }        close(fd);    }}

直接用交叉编译器编译即可。执行后就能在终端上看到不断输出的温度值。

原创粉丝点击