linux系统之字符设备驱动——DS18B20温度传感器
1. 原理图
2. 驱动程序
- 驱动程序
ds18b20.c
/*
* @Author: your name
* @Date: 2021-02-06 19:41:29
* @LastEditTime: 2021-02-23 21:25:00
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \3_ds18b20\ds18b20.c
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include "ds18b20.h"
/********************** define ******************************/
#define DEBUG_ON
#ifdef DEBUG_ON
#define DEBUG(fmt, args...) printk("[FILE = %s][FUNC = %s][LINE = %d] [" fmt "]\n", __FILE__, __FUNCTION__, __LINE__, ##args)
#else
#define DEBUG(fmt, args...) \
do \
{ \
} while (0)
#endif
#define NAME "DS18B20" // 设备节点名字
struct ds18b20_dev
{
unsigned int major;
struct class *class; // 设备节点目录
struct device *dev; // 设备
} ds18b20Device;
const int alive_5 = 5 * 32 + 5;
unsigned char temp_limit[2];
unsigned short temp[3] = {0, 85, 0};
void ds18b20_reset(void) // 复位
{
gpio_direction_output(alive_5, 1);
msleep(5);
gpio_set_value(alive_5, 0);
udelay(700);
gpio_set_value(alive_5, 1);
gpio_direction_input(alive_5);
//等待从机给主机低电平
while (gpio_get_value(alive_5))
;
while (!gpio_get_value(alive_5))
;
}
unsigned char ds18b20_read(void) // 读数据
{
int i;
unsigned char temp = 0xff;
gpio_direction_output(alive_5, 1);
for (i = 0; i < 8; i++)
{
gpio_set_value(alive_5, 0);
udelay(3);
gpio_direction_input(alive_5);
udelay(8);
//ds18b20 数据地位在前
if (!gpio_get_value(alive_5))
temp &= ~(1 << i);
gpio_direction_output(alive_5, 1);
udelay(42);
}
gpio_direction_output(alive_5, 1);
return temp;
}
void ds18b20_write(unsigned char val) // 写数据
{
int i;
gpio_direction_output(alive_5, 1);
for (i = 0; i < 8; i++)
{
gpio_set_value(alive_5, 0);
udelay(8);
if ((val >> i) & 0x1)
gpio_set_value(alive_5, 1);
udelay(40);
gpio_direction_output(alive_5, 1);
udelay(3);
}
gpio_direction_output(alive_5, 1);
}
unsigned short ds18b20_read_data(void)
{
unsigned short t_l, t_h;
//1.先发送开始
ds18b20_reset();
udelay(50);
ds18b20_write(0xcc);
ds18b20_write(0x44);
//2.读数据
mdelay(10);
ds18b20_reset();
udelay(400);
ds18b20_write(0xcc);
ds18b20_write(0xbe);
t_l = ds18b20_read();
t_h = ds18b20_read();
return ((t_h << 8) | (t_l & 0xff));
}
void set_accuracy(int args)
{
mdelay(10);
ds18b20_reset();
ds18b20_write(0xcc);
ds18b20_write(0x4e);
ds18b20_write(85);
ds18b20_write(-10);
switch(args){
case ACC_9:ds18b20_write(0x1f);break;
case ACC_10:ds18b20_write(0x3f);break;
case ACC_11:ds18b20_write(0x5f);break;
case ACC_12:ds18b20_write(0x7f);break;
}
}
int check_accuracy(int args)
{
int ret = -1;
//2.读config 数据
ds18b20_reset();
ds18b20_write(0xcc);
ds18b20_write(0xbe);
ds18b20_read(); //当前温度低字节
ds18b20_read();//当前温度高字节
ds18b20_read();//温度报警的上线
ds18b20_read();//温度报警的下线
ret = ds18b20_read();
//printk("ret = %#x\n",ret);
switch(args){
case ACC_9:if(ret == 0x1f) ret = 0;break;
case ACC_10:if(ret == 0x3f) ret = 0;break;
case ACC_11:if(ret == 0x5f) ret = 0;break;
case ACC_12:if(ret == 0x7f) ret = 0;break;
}
return (ret?-1:0);
}
int ds18b20_char_open(struct inode *inode, struct file *file)
{
return 0;
}
int ds18b20_char_close(struct inode *inode, struct file *file)
{
return 0;
}
ssize_t ds18b20_char_write(struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
int ret;
if (size > sizeof(temp_limit))
size = sizeof(int);
ret = copy_from_user(temp_limit, ubuf, size);
if (ret)
{
printk("copy data from user error.\n");
return -E2BIG;
}
temp[1] = temp_limit[0];
temp[2] = temp_limit[1];
return size;
}
ssize_t ds18b20_char_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
int ret;
unsigned short dat;
dat = ds18b20_read_data();
temp[0] = dat;
if (size > sizeof(temp))
size = sizeof(temp);
ret = copy_to_user(ubuf, temp, size);
if (ret)
{
printk("copy data to user error\n");
return -EAGAIN;
}
return size;
}
long ds18b20_char_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
int ret;
if (cmd == SET_ACCURACY)
{
if (args >= ACC_9 && args <= ACC_12)
set_accuracy(args);
ret = check_accuracy(args);
if (ret)
{
return -EINVAL;
}
}
return 0;
}
const struct file_operations fops =
{
.open = ds18b20_char_open,
.release = ds18b20_char_close,
.read = ds18b20_char_read,
.unlocked_ioctl = ds18b20_char_ioctl,
.write = ds18b20_char_write,
};
static int __init DS18B20_Init(void)
{
int ret = -1;
// 1. 注册字符设备驱动
ds18b20Device.major = register_chrdev(ds18b20Device.major, NAME, &fops);
if (ds18b20Device.major < 0)
{
DEBUG("register_chrdev faild");
goto ERR1;
}
// 2. 创建设备驱动目录
ds18b20Device.class = class_create(THIS_MODULE, "cds18b20dev");
if (IS_ERR(ds18b20Device.class))
{
DEBUG("class_create faild");
goto ERR2;
}
// 3. 创建设备驱动节点
ds18b20Device.dev = device_create(ds18b20Device.class, NULL, MKDEV(ds18b20Device.major, 0), NULL, NAME);
if (IS_ERR(ds18b20Device.dev))
{
DEBUG("device_create faild");
ret = PTR_ERR(ds18b20Device.dev);
goto ERR3;
}
// 注册gpio子系统
ret = gpio_request(alive_5, NULL);
if (ret)
{
DEBUG("request alive_5 error");
goto ERR4;
}
ret = gpio_direction_output(alive_5, 1);
if (ret)
{
DEBUG("alive_5 direction output error");
goto ERR5;
}
DEBUG("DS18B20_Init succeed");
return 0;
ERR5:
gpio_free(alive_5);
ERR4:
device_destroy(ds18b20Device.class, MKDEV(ds18b20Device.major, 0));
ERR3:
class_destroy(ds18b20Device.class);
ERR2:
unregister_chrdev(ds18b20Device.major, NAME);
ERR1:
return ret;
}
static void __exit DS18B20_Exit(void)
{
gpio_free(alive_5);
device_destroy(ds18b20Device.class, MKDEV(ds18b20Device.major, 0));
// 3. 销毁设备驱动目录
class_destroy(ds18b20Device.class);
// 4. 注销字符设备驱动
unregister_chrdev(ds18b20Device.major, NAME);
DEBUG("DS18B20_Exit succeed");
}
module_init(DS18B20_Init);
module_exit(DS18B20_Exit);
MODULE_LICENSE("GPL");
- 中间文件
ds18b20.h
/*
* @Author: your name
* @Date: 2021-02-06 19:41:29
* @LastEditTime: 2021-02-23 21:25:00
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \3_ds18b20\ds18b20.h
*/
#ifndef __DS18B20_H__
#define __DS18B20_H__
#define SET_ACCURACY _IOW('k',0,int)
#define ACC_9 9
#define ACC_10 10
#define ACC_11 11
#define ACC_12 12
#endif
- 应用程序
main.c
/*
* @Author: your name
* @Date: 2021-02-04 10:02:54
* @LastEditTime: 2021-02-23 21:39:12
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \3_DS18B20\main.c
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "ds18b20.h"
float ds18b20_really_data(unsigned short data)
{
int sig = 0;
float value = 0;
if((data >> 11) & (0x01))
{
sig = -1;
data = ~data + 1;
data &= ~(0xF8 << 8);
}
else
{
sig = +1;
}
value = data * sig * 0.0625;
return value;
}
int main(void)
{
unsigned short data = 0;
int fd = open("/dev/DS18B20", O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}
ioctl(fd,SET_ACCURACY,ACC_9);
while(1)
{
read(fd,&data,sizeof(data));
printf("Temperature = %.4f\r\n", ds18b20_really_data(data));
usleep(100000);
}
close(fd);
return 0;
}