Linux下的ds18b20驱动(编译成模块)

本实验基于Tiny6410开发板上的模块


代码如下:代码声明是参考友善之臂论坛里面的,注释是自己添加的。

#include     //最基本的文件,支持动态添加和卸载模块。Hello World驱动要这一个文件就可以了
#include     //包含了文件操作相关struct的定义,例如大名鼎鼎的struct file_operations
#include    
#include    
#include    
#include     //对字符设备结构cdev以及一系列的操作函数的定义。//包含了cdev 结构及相关函数的定义。
#include     //包含了device、class 等结构的定义
#include    
#include    


#define    DEVICE_NAME "temp"
static struct cdev cdev; //linux/cdev.h
struct class *tem_class;
static dev_t devno;
static int major = 243;


void tem_reset(void)
{
    s3c_gpio_cfgpin(S3C64XX_GPN(8), S3C_GPIO_SFN(1)); //设置gpio8为为中断8使能
    
    /* S3C64XX_GPN(8)在arch\arm\plat-s3c64xx\Irq_eint.c中定义;
     * s3c_gpio_cfgpin(S3C64XX_GPN(offs), 0x2 << (offs * 2)); offs指的是S3C64XX的哪个中断,参考Tiny6410硬件手册知道,本温度传感器硬件用到的是EINT8;
     * int offs = eint_offset(irq); 定义了offs是如何得来的,
     * #define eint_offset(irq) ((irq) - IRQ_EINT(0)) 其中的IRQ_EINT(0)意思是外部中断0吧,irq代表你要用到的中断号;
***********************************************************/
 
 
/* S3C_GPIO_SFN(1)的声明:如下在include\plat\Gpio-cfg.h相关的定义
* #define S3C_GPIO_SFN(x) (S3C_GPIO_SPECIAL(x))
* #define S3C_GPIO_SPECIAL(x) (S3C_GPIO_SPECIAL_MARK | (x))
* #define S3C_GPIO_SPECIAL_MARK (0xfffffff0)
 
******************************************************************
 
*#define s3c_gpio_is_cfg_special(_cfg) \
* (((_cfg) & S3C_GPIO_SPECIAL_MARK) == S3C_GPIO_SPECIAL_MARK)
*主要目的是判断传入的unsigned int _cfg这个参数,
*这个参数可以为0-0xfffffffx,其中x有如下表示:
*[3:0]
*0000=input   0001=output   0010=UART RXD[0]
*0011=Reserved   0100=Reserved   0101=Reserved
*0110=Reserved   0111=External Interrupt Group 1[0]
********************************************************************/






    gpio_set_value(S3C64XX_GPN(8), 1); //linux/gpio.h设置gpn8为输出模式
    udelay(100); //gpn8:00为输入模式,01为输出模式
    gpio_set_value(S3C64XX_GPN(8), 0); //设置gpn8为输入模式
    udelay(600);
    gpio_set_value(S3C64XX_GPN(8), 1);
    udelay(100);
    s3c_gpio_cfgpin(S3C64XX_GPN(8), S3C_GPIO_SFN(0));
}


void tem_wbyte(unsigned char data) //向temperature写一个字节?写一个字节的什么内容?为什么要读一个字节呢?
//写ROM命令0CCH跳过ROM匹配,写ROM命令44H启动温度转换,写ROM命令0BEH发读温度命令;
{
    int i;


    s3c_gpio_cfgpin(S3C64XX_GPN(8), S3C_GPIO_SFN(1));
    for (i = 0; i < 8; ++i)
    {
        gpio_set_value(S3C64XX_GPN(8), 0);
        udelay(1);


        if (data & 0x01)
        {
            gpio_set_value(S3C64XX_GPN(8), 1);
        }
        udelay(60);
        gpio_set_value(S3C64XX_GPN(8), 1);
        udelay(15);
        data >>= 1;
    }
    gpio_set_value(S3C64XX_GPN(8), 1);
}


unsigned char tem_rbyte(void) //从temperature读一个字节?读温度数据
{
    int i;
    unsigned char ret = 0;


    for (i = 0; i < 8; ++i)
    {
        s3c_gpio_cfgpin(S3C64XX_GPN(8), S3C_GPIO_SFN(1));
        gpio_set_value(S3C64XX_GPN(8), 0);
        udelay(1);
        gpio_set_value(S3C64XX_GPN(8), 1);


        s3c_gpio_cfgpin(S3C64XX_GPN(8), S3C_GPIO_SFN(0));
        ret >>= 1;
        if (gpio_get_value(S3C64XX_GPN(8)))
        {
            ret |= 0x80;    
        }
        udelay(60);
    }
    s3c_gpio_cfgpin(S3C64XX_GPN(8), S3C_GPIO_SFN(1));
    return ret;
}


static ssize_t tem_read(struct file *filp, char *buf, size_t len, loff_t *offset)
{
    unsigned char low, high;


    tem_reset();
    udelay(420);
    tem_wbyte(0xcc); //SKIP_ROM
    tem_wbyte(0x44); //CONVERT_T


    mdelay(750);
    tem_reset();
    udelay(400);
    tem_wbyte(0xcc);
    tem_wbyte(0xbe); //READ_SCRATCHPAD


    low = tem_rbyte();
    high = tem_rbyte();


    *buf = low / 16 + high * 16;


    *(buf + 1) = (low & 0x0f) * 10 / 16 + (high & 0x0f) * 100 / 16 % 10;
    return 0;
}


static struct file_operations tem_fops = //linux/fs.h
{
    .owner    = THIS_MODULE,
    .read    = tem_read,
};


static int __init tem_init(void) //_init定义在linux/init.h中
{
    int result;
    devno = MKDEV(major, 0);


    result = register_chrdev_region(devno, 1, DEVICE_NAME);
    if (result)
    {
        printk("register failed\n");     //linux/kernel.h
        return result;
    }


    cdev_init(&cdev, &tem_fops);
    cdev.owner = THIS_MODULE;
    cdev.ops = &tem_fops;
    result = cdev_add(&cdev, devno, 1);
    if (result)
    {
        printk("cdev add failed\n");    
        goto fail1;
    }


    tem_class = class_create(THIS_MODULE, "tmp_class");
    if (IS_ERR(tem_class))
    {
        printk("class create failed\n");    
        goto fail2;
    }


    device_create(tem_class, NULL, devno, DEVICE_NAME, DEVICE_NAME);
    return 0;
fail2:
    cdev_del(&cdev);
fail1:
    unregister_chrdev_region(devno, 1);
    return result;
}


static void __exit tem_exit(void)
{
    device_destroy(tem_class, devno);
    class_destroy(tem_class);
    cdev_del(&cdev);
    unregister_chrdev_region(devno, 1);
}


module_init(tem_init);
module_exit(tem_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("[email protected]");


第一步:

将源码拷贝到系统源码(不管是哪个版本的系统源码,我的是2.6.38)drivers\char目录下面。

第二步:

修改char目录下的Makefile文件,在末尾添加如下一行: (ds18b20.o这个目标取决于你的源码文件名字)

obj-m += ds18b20.o  

第三步:

回到linux系统源码的根目录下去,也就是linux2.6.38,进行编译:make modules

编译成功的话,就会在char目录下生产ds18b20.ko文件

第四步:

把ds18b20.ko拷贝到开发板上,接下来就是加载设备驱动程序模块了。

1.创建设备设备驱动程序的进入点mknod /dev/DS18B20 c 243 0   此处的主设备号243和你在程序中设置的一样,创建的设备接口文件(DS18B20)要和测试程序中的保持一样。

2.加载驱动程序insmod ds18b20.ko

3.用lsmod来查看,是否驱动模块加载成功。

下面是测试代码:

#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];




    if ((fd=open("/dev/temp",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, sizeof(buf));


            printf("%d.%dC\r\n", buf[0], buf[1]);
            sleep(1);


        }
        close(fd);
    }

}

实验结果:



如有疑问请留言

你可能感兴趣的:(linux)