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

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 210

static 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);
    }
}

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值