linux字符设备DS18B20驱动源码
DS18B20是常用的数字温度传感器,经常用单片机来控制,本文基于arm11芯片来实现ds18b20驱动,系统是linux。驱动是大学的时候实现的,里面主要涉及到寄存器的读写和ds18b20时序操作。需要参考arm11的芯片手册和ds18b20的芯片手册。想想都好多年没碰过驱动了。驱动是linux系统必不可少的一环,了解驱动流程开发也是对整个系统能更加深刻理解。
·
DS18B20是常用的数字温度传感器,经常用单片机来控制,本文基于arm11芯片来实现ds18b20驱动,系统是linux。
驱动是大学的时候实现的,里面主要涉及到寄存器的读写和ds18b20时序操作。需要参考arm11的芯片手册和ds18b20的芯片手册。想想都好多年没碰过驱动了。驱动是linux系统必不可少的一环,了解驱动流程开发也是对整个系统能更加深刻理解。保存的年代有点久,可能会有些纰漏,放在这里,主要是想给自己提个醒,回忆起整个驱动流程。
基于linux的ds18b20驱动:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
#include <mach/map.h>
#include <mach/gpio-bank-n.h>
#include <mach/regs-clock.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
#include <linux/slab.h>
#include <linux/poll.h> //轮询文件
#include <linux/wait.h> //等代队列相关头文件//内核等待队列,它包含了自旋锁的头文件
#include<linux/semaphore.h> //使用信号量必须的头文件
#define DQ 8
#define CFG_IN 0
#define CFG_OUT 1
#define DEV_NAME "ds18b20"
#define DS18B20_MAJOR 243 /*预设的globalmem的主设备号*/
#define DS18B20_MINOR 0
int i=0,j=0; //用于中断服务程序
static int ds18b20_major = DS18B20_MAJOR;
static int ds18b20_minor = DS18B20_MINOR;
static void second_timer_handle(unsigned long arg);
struct class *ds18b20_class;
unsigned int current_len = 0; //fifo有效数据长度
unsigned char result[16];
//globalmem设备结构体
struct ds18b20_dev
{
struct cdev cdev; //cdev结构体
struct semaphore sem; //sem用于两个文件之间的互斥,sem1用于读写互斥
struct semaphore sem1;
struct timer_list s_timer; //设备要使用的定时器
wait_queue_head_t r_wait; //阻塞读用的等待队列头
struct fasync_struct *async_queue; //异步结构体指针,用于读
};
struct ds18b20_dev *ds18b20_devp;
void s3c6410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
//s3c_gpio_cfgpin(pin,function);
unsigned int tmp;
tmp = readl(S3C64XX_GPNCON);
tmp = (tmp & ~(3<<pin*2))|(function<<pin*2);
writel(tmp, S3C64XX_GPNCON);
}
void s3c6410_gpio_pullup(unsigned int pin, unsigned int to)
{
//s3c_gpio_setpull(pin,to);
unsigned int tmp;
tmp = readl(S3C64XX_GPNPUD);
tmp = (tmp & ~(3<<pin*2))|(to<<pin*2);
writel(tmp, S3C64XX_GPNPUD);
}
unsigned int s3c6410_gpio_getpin(unsigned int pin)
{
unsigned int tmp;
tmp = readl(S3C64XX_GPNDAT);
tmp =( tmp & (1 << (pin))) >> 8;
return tmp;
}
void s3c6410_gpio_setpin(unsigned int pin, unsigned int dat)
{
unsigned int tmp;
tmp = readl(S3C64XX_GPNDAT);
tmp &= ~(1 << (pin));
tmp |= ( (dat) << (pin) );
writel(tmp, S3C64XX_GPNDAT);
}
unsigned char ow_reset(void)
{
// u32 presence=1;
s3c6410_gpio_cfgpin(DQ, CFG_OUT);
s3c6410_gpio_setpin(DQ, 1);
udelay(1); //查看原理图,GPN8在空闲的时候被上拉电阻抬高电平,所以这里设不设置无所谓
s3c6410_gpio_setpin(DQ, 0); //pull SetDqValue(1) line low
udelay(500); //至少480us,一般500到600都可以
s3c6410_gpio_setpin(DQ, 1); // allow line to return high
udelay(60); //这里等待15us到60us就可以了
s3c6410_gpio_cfgpin(DQ, CFG_IN);
// presence = GetDqValue(); // get presence signal
udelay(450); // wait for end of timeslot 这里的值加上udelay(60)要不少于480us
return 0;
}
void write_bit(char bitval)
{
s3c6410_gpio_cfgpin(DQ, CFG_OUT);
s3c6410_gpio_setpin(DQ, 0); // pull SetDqValue(1) low to start timeslot
udelay(2); //写时序开始后的15us释放总线,这里小于15us
if(bitval==1) s3c6410_gpio_setpin(DQ, 1); // return SetDqValue(1) high if write 1
udelay(40);//在写时序开始的15us到60us,开始采样,稍微大点
//delay(5); // hold value for remainder of timeslot
s3c6410_gpio_setpin(DQ, 1); //每次采样完成把总线抬高
}// Delay provides 16us per loop, plus 24us. Therefore delay(5) = 104us
void write_byte(char val)
{
unsigned char i;
unsigned char temp;
for (i=0; i<8; i++) // writes byte, one bit at a time
{
udelay(2); //相邻两次采样必须大于1us
temp = val>>i; // shifts val right 'i' spaces
temp &= 0x01; // copy that bit to temp
write_bit(temp); // write bit in temp into
}
udelay(120);//???
}
unsigned char read_bit(void)
{
unsigned char va;
s3c6410_gpio_cfgpin(DQ, CFG_OUT);
s3c6410_gpio_setpin(DQ, 0); // pull SetDqValue(1) low to start timeslot
udelay(2); //经过测试,该值必须小于等于11才不会出错
s3c6410_gpio_setpin(DQ, 1); // then return high
udelay(15); // delay 15us from start of timeslot从DSds18b20输出的数据在读时序的下降沿出现后15us内有效。
s3c6410_gpio_cfgpin(DQ, CFG_IN);
va=s3c6410_gpio_getpin(DQ);
return(va); // return value of SetDqValue(1) line
}
unsigned char read_byte(void)
{
unsigned char i;
unsigned char value = 0;
for (i=0;i<8;i++)
{
udelay(1);//相邻两次读周期必须大于1us
if(read_bit()) value|=0x01<<i;
// reads byte in, one byte at a time and then shifts it left
udelay(60); //所有的读时序至少60us
//delay(6); // wait for rest of timeslot
}
return(value);
}
static ssize_t ds18b20_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
int ret;
//struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针
if (down_interruptible(&ds18b20_devp->sem1)) /* 获取信号量 */
{
return - ERESTARTSYS;
}
/* FIFO为空 */
if (current_len == 0)
{
if (filp->f_flags & O_NONBLOCK) //非阻塞
{
ret = -EAGAIN;
goto out;
}
up(&ds18b20_devp->sem1);
wait_event_interruptible(ds18b20_devp->r_wait, (current_len > 0));
if (down_interruptible(&ds18b20_devp->sem1)) /* 获取信号量 */
{
return - ERESTARTSYS;
}
}
/* 拷贝到用户空间 */
//if (count > dev->current_len)
// count = dev->current_len;
if (copy_to_user(buf, result, 2)) //读成功将唤醒写等待队列
{
ret = -EFAULT;
goto out;
}
else
{
memcpy(result,result + 2, current_len - 2); //fifo数据前移2个字节
current_len -= 2; //有效数据长度减少
printk(KERN_INFO "read 2 bytes(s),current_len:%d\n",current_len);
ret = 2;
}
out: up(&ds18b20_devp->sem1); //释放信号量
return ret;
//return 0;
}
//mask注意什么意思,在哪里引用,然后信号量怎么使用
static unsigned int ds18b20_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
if (down_interruptible(&ds18b20_devp->sem1)) /* 获取信号量 */
{
return - ERESTARTSYS;
}
poll_wait(filp, &ds18b20_devp->r_wait, wait);
/*fifo非空*/
if (current_len != 0)
{
mask |= POLLIN | POLLRDNORM; /*标示数据可读*/
//POLLIN表示设备可以无阻塞地读;POLLRDNORM表示数据已经就绪,可以读取
}
printk(KERN_INFO "poll mask %x.\n", mask);
up(&ds18b20_devp->sem1);
return mask;
}
/*定时器处理函数*/
static void second_timer_handle(unsigned long arg)
{
//unsigned char result[2] = {0x00, 0x00};
//char get[10];
if(i == 0)
{
ow_reset();
write_byte(0xCC); //Skip ROM
write_byte(0x44); // Start Conversion
j=0;
}
i++;
//delay(5); // >750ms
if(j == 8)
{
ow_reset();
write_byte(0xCC); // Skip ROM
write_byte(0xBE); // Read Scratch Pad
if(current_len<16)
{
if (current_len == 0)
{
wake_up_interruptible(&ds18b20_devp->r_wait); //唤醒读等待队列
/* 产生异步读信号 */
if (ds18b20_devp->async_queue)
kill_fasync(&ds18b20_devp->async_queue, SIGIO, POLL_IN);
}
result[current_len]=read_byte();
result[current_len+1]=read_byte();
current_len = current_len + 2 ;
printk(KERN_INFO "written 2 bytes(s),current_len:%d\n", current_len);
current_len = current_len + 2;
}
else
{
//如果已经将result写满,则fifo前移,把旧的数据摈弃,写入新的数据
memcpy(result,result + 2, current_len - 2); //fifo数据前移
result[14]=read_byte();
result[15]=read_byte();
printk(KERN_INFO "written 2 bytes(s),current_len:%d\n", current_len);
}
i = 0;
}
j++;
// err = copy_to_user(buf, &result, sizeof(result));
// return err ? -EFAULT : min(sizeof(result),count);
mod_timer(&ds18b20_devp->s_timer,jiffies + HZ/10);
//atomic_inc(&ds18b20_devp->counter);
printk(KERN_NOTICE "current jiffies is %ld\n", jiffies);
}
static int ds18b20_fasync(int fd, struct file *filp, int mode)
{
printk("driver: ds18b20_fasync\n");
return fasync_helper(fd, filp, mode, &ds18b20_devp->async_queue);
}
int ds18b20_open(struct inode *inode, struct file *filp)
{
if (down_interruptible(&ds18b20_devp->sem)) /* 获取信号量 */
{
return - ERESTARTSYS;
}
/*初始化定时器*/
init_timer(&ds18b20_devp->s_timer);
ds18b20_devp->s_timer.function = &second_timer_handle;
ds18b20_devp->s_timer.expires = jiffies + HZ/10;
add_timer(&ds18b20_devp->s_timer); /*添加(注册)定时器*/
// atomic_set(&second_devp->counter,0); /*计数清0*/
return 0;
}
/*文件释放函数*/
int ds18b20_release(struct inode *inode, struct file *filp)
{
up(&ds18b20_devp->sem); /* 释放信号量 */
ds18b20_fasync( - 1, filp, 0); /* 将文件从异步通知列表中删除 */
del_timer(&ds18b20_devp->s_timer);
return 0;
}
static struct file_operations ds18b20_fops = {
.owner = THIS_MODULE,
.read = ds18b20_read,
.open = ds18b20_open,
.release = ds18b20_release,
.poll = ds18b20_poll,
.fasync = ds18b20_fasync,
};
/*设备驱动模块加载函数*/
int ds18b20_init(void)
{
int result;
dev_t devno= MKDEV(ds18b20_major,ds18b20_minor);
ds18b20_devp = kmalloc(sizeof(struct ds18b20_dev), GFP_KERNEL);
cdev_init(&ds18b20_devp->cdev, &ds18b20_fops);
ds18b20_devp->cdev.owner = THIS_MODULE;
sema_init(&ds18b20_devp->sem,1);
sema_init(&ds18b20_devp->sem1,1);
//init_MUTEX(&ds18b20_devp->sem); /* 初始化信号量 */
// init_MUTEX(&ds18b20_devp->sem1); /* 初始化信号量 */
init_waitqueue_head(&ds18b20_devp->r_wait); /*初始化读等待队列头*/
/*
ds18b20_major 如果初始化为0,就默认选择动态分配,可以在ds18b20.h中定义
ds18b20_minor 也要初始化
*/
/* 申请设备号,ds18b20_major初始化为0,选择动态分配*/
if(ds18b20_major){
result = register_chrdev_region(devno,1,DEV_NAME);
}else{
result = alloc_chrdev_region(&devno, ds18b20_minor , 1 ,DEV_NAME);
ds18b20_major = MAJOR(devno);
}
if (result < 0){
printk(KERN_WARNING"can't get major %d\n",ds18b20_major);
return result;
}
ds18b20_class = class_create(THIS_MODULE, DEV_NAME);
device_create(ds18b20_class, NULL, MKDEV(ds18b20_major, ds18b20_minor), NULL, DEV_NAME);
result=cdev_add(&ds18b20_devp->cdev,devno,1); //注册设备
return result;
}
/*模块卸载函数*/
void ds18b20_exit(void)
{
device_destroy(ds18b20_class, MKDEV(ds18b20_major,ds18b20_minor));
class_destroy(ds18b20_class); //销毁类
cdev_del(&ds18b20_devp->cdev); /*注销cdev*/
kfree(ds18b20_devp); /*释放设备结构体内存*/
unregister_chrdev_region(MKDEV(ds18b20_major,ds18b20_minor), 1); /*释放设备号*/
}
MODULE_AUTHOR("Chen Ting");
MODULE_LICENSE("Dual BSD/GPL");
module_init(ds18b20_init);
module_exit(ds18b20_exit);
接下来看看两个例子如何调用这个驱动:
运用fcntl 和signal 的例子:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
void input_handler(int signum);
int fd, oflags;
unsigned char buf[2];
float temperature;
main()
{
fd = open("/dev/ds18b20", O_RDWR, S_IRUSR | S_IWUSR);
if (fd != - 1)
{
//启动信号驱动机制
signal(SIGIO, input_handler); //让input_handler()处理SIGIO信号
fcntl(fd, F_SETOWN, getpid());
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);
while(1)
{
sleep(100);
}
}
else
{
printf("device open failure\n");
}
}
/*接收到异步读信号后的动作*/
void input_handler(int signum)
{
printf("receive a signal from globalfifo,signalnum:%d\n",signum);
read(fd, buf, sizeof(buf));
buf[1] <<= 4;
buf[1] += ((buf[0])&0xf0)>>4;
temperature = (float)(buf[1]) + (float)((buf[0])&0x0f)*0.0625;
printf("the temperature is %4.2f degrees\n",temperature);
}
运用select的例子:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#define FIFO_CLEAR 1
#define BUFFER_LEN 20
int main()
{
int fd;
fd_set rfds, wfds;
unsigned char buf[2];
float temperature;
/*以非阻塞方式打开/dev/globalmem设备文件*/
fd = open("/dev/ds18b20", O_RDONLY | O_NONBLOCK);
if (fd < 0)
printf("Device open failure.\n");
while(1)
{
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(fd, &rfds);
FD_SET(fd, &wfds);
select(fd + 1, &rfds, &wfds, NULL, NULL);
printf("the read is OK!");
/*数据可获得*/
if (FD_ISSET(fd, &rfds))
{
read(fd, buf, sizeof(buf));
buf[1] <<= 4;
buf[1] += ((buf[0])&0xf0)>>4;
temperature = (float)(buf[1]) + (float)((buf[0])&0x0f)*0.0625;
printf("the temperature is %4.2f degrees\n",temperature);
}
}
return 0;
}
更多推荐
已为社区贡献5条内容
所有评论(0)