/***************************************************************
Copyright 2024-2029. All rights reserved.
文件名 : drv_07_tree_ds18b20.c
作者 : tangmingfei2013@126.com
版本 : V1.0
描述 : ds18b20 驱动程序, GPIO4_PIN19-----ds18b20 port
其他 : 无
日志 : 初版V1.0 2024/1/29
使用方法:
typedef struct{
unsigned short temperatureVal; //温度数据, 真实值val = temperatureVal *0.0625
int sign; //符号位,1: 负数, 0: 正数
}Ds18b20Struc;
Ds18b20Struc ds_struc;
void convert_temp()
{
read(fd, &ds_struc, sizeof(Ds18b20Struc));
printf("ds18b20 Value: %.02f, sign: %d \n", ds_struc.temperatureVal*0.0625,
ds_struc.sign);
}
***************************************************************/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define DEVICE_NAME "tree_ds18b20" // dev/treeds18b20
typedef struct{
unsigned short temperatureVal; // 温度数据, 真实值val = temperatureVal *0.0625
int sign; // 符号位,1: 负数, 0: 正数
}Ds18b20Struc;
/* ds18b20dev设备结构体 */
struct ds18b20stru_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
struct device_node *node; /* 设备节点 */
struct gpio_desc *pin;
};
static wait_queue_head_t ds18b20_wq;
struct ds18b20stru_dev ds18b20dev; /* ds18b20设备 */
/* ds18b20 dirver */
static void tempudelay(int usecs)
{
int pre,last;
pre = ktime_get_boot_ns();
while(1){
last = ktime_get_boot_ns();
if( (last - pre) >= (usecs*1000))
break;
}
}
static int ds18b20_wait_for_ack(void)
{
int timeout_count = 500;
/* 如果是高电平,等待 */
while (gpiod_get_value( ds18b20dev.pin ) && --timeout_count)
{
udelay(1);
}
if (!timeout_count)
return -1;
/* 现在是低电平 */
timeout_count = 500;
while (!gpiod_get_value( ds18b20dev.pin ) && --timeout_count)
{
udelay(1);
}
if (!timeout_count)
return -1;
return 0;
}
static int ds18b20_check( void )
{
gpiod_direction_output(ds18b20dev.pin, 0);
tempudelay(480);
gpiod_direction_input(ds18b20dev.pin);
if (ds18b20_wait_for_ack())
{
return -1;
}
else
return 0;
}
static void ds18b20_write_byte( unsigned char byte)
{
unsigned char k;
// write data bit
for ( k = 0; k < 8; k++ )
{
if (byte & (1<<k))
{
gpiod_direction_output(ds18b20dev.pin, 0);
tempudelay(2);
gpiod_direction_input(ds18b20dev.pin);
tempudelay(65);
}
else
{
gpiod_direction_output(ds18b20dev.pin, 0);
tempudelay(65);
gpiod_direction_input(ds18b20dev.pin);
tempudelay(2);
}
}
}
static unsigned char ds18b20_read_byte( void )
{
unsigned char byteVal = 0;
unsigned char i;
for ( i = 0; i < 8; i++ )
{
gpiod_direction_output(ds18b20dev.pin, 0);
tempudelay(2);
gpiod_direction_input( ds18b20dev.pin );
tempudelay(7);
if( gpiod_get_value(ds18b20dev.pin) )
byteVal |= (1<<i);
tempudelay(60);
}
return byteVal;
}
static bool ds18b20_process( Ds18b20Struc *pdsStruc)
{
unsigned long tempval;
unsigned long flags;
unsigned char temp1, temp2;
local_irq_save(flags);
if (ds18b20_check() == -1)
{
gpiod_direction_output( ds18b20dev.pin, 1);
local_irq_restore(flags);
return false;
}
tempudelay(580);
ds18b20_write_byte(0xcc);
ds18b20_write_byte(0x44);
gpiod_direction_output( ds18b20dev.pin, 1);
local_irq_restore(flags);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ);
local_irq_save(flags);
if (ds18b20_check() == -1)
{
gpiod_direction_output(ds18b20dev.pin, 1);
local_irq_restore(flags);
return false;
}
tempudelay(580);
ds18b20_write_byte(0xcc);
ds18b20_write_byte(0xbe);
temp1 = ds18b20_read_byte();
temp2 = ds18b20_read_byte();
gpiod_direction_output(ds18b20dev.pin, 1);
pdsStruc->sign = 0;
if (temp2 > 0x7f) //最高位为1时温度是负
{
temp1 = ~temp1; //补码转换,取反加一
temp2 = ~temp2+1;
pdsStruc->sign = 1;
}
//read tempeture value
tempval = ((temp2 << 8) | temp1);
printk(" [%s line %d ] read value: 0x%04x \r\n",__FUNCTION__, __LINE__, (u16)tempval);
pdsStruc->temperatureVal = tempval;
local_irq_restore(flags);
return true;
}
/*
linux driver 驱动接口:
实现对应的open/read/write等函数,填入file_operations结构体
*/
static ssize_t ds18b20_drv_read (struct file *file, char __user *buf,
size_t size, loff_t *offset)
{
Ds18b20Struc dsStruc;
bool result;
result = ds18b20_process( &dsStruc );
if( result ){
result = copy_to_user(buf, &dsStruc, sizeof(Ds18b20Struc));
}
return sizeof(Ds18b20Struc);
}
static int ds18b20_drv_close(struct inode *node, struct file *file)
{
printk(" %s line %d \r\n", __FUNCTION__, __LINE__);
return 0;
}
static int ds18b20_drv_open(struct inode *inode, struct file *filp)
{
filp->private_data = &ds18b20dev; /* 设置私有数据 */
return 0;
}
/*
定义driver的file_operations结构体
*/
static struct file_operations ds18b20_fops = {
.owner = THIS_MODULE,
.read = ds18b20_drv_read,
.open = ds18b20_drv_open,
.release = ds18b20_drv_close,
};
/*
从platform_device获得GPIO
*/
static int ds18b20_probe(struct platform_device *pdev)
{
printk("ds18b20 driver and device was matched!\r\n");
/* 1. 获得硬件信息 */
ds18b20dev.pin = gpiod_get(&pdev->dev, NULL, GPIOD_OUT_HIGH);
if (IS_ERR(ds18b20dev.pin))
{
printk("%s line %d\n", __FUNCTION__, __LINE__);
return -1;
}
/* 2. device_create */
device_create( ds18b20dev.class, NULL,
MKDEV( ds18b20dev.major, 0 ), NULL,
DEVICE_NAME); // device name
return 0;
}
static int ds18b20_remove(struct platform_device *pdev)
{
printk("%s line %d\n", __FUNCTION__, __LINE__);
device_destroy( ds18b20dev.class, MKDEV( ds18b20dev.major, 0));
gpiod_put(ds18b20dev.pin);
return 0;
}
/*
match the gpio in .dtb
//mftang: user's ds18b20, 2024-1-26
// IO: GPIO-4-PIN19
userds18b20s {
compatible = "atk-dl6y2c,userds18b20s";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_ds18b20>;
gpios = <&gpio4 19 GPIO_ACTIVE_LOW>;
status = "okay";
};
*/
static const struct of_device_id atk_dl6y2c_ds18b20[] = {
{ .compatible = "atk-dl6y2c,testds18b20" },