1.通过查询原理图可知:
传输协议:
1.起始信号:
平时是高电平,芯片拉低电平480us,然后变高电平,对方设备收到信号后回应低电平,设备准备好后拉高电平。
2.传输数据
主控芯片发出八位数据选中DS18B20,再发出八位数据命令,让设备转换采集温度。延时等待转换完成
3.复位
发出起始信号,主控芯片发出八位数据选中DS18B20, 再发出八位数据命令,读取设备上的数据。
4.设备模块发送8位数据+8位数据
写一位数据
写0:
主控拉低电平,提醒设备,主控芯片要发送数据了。30us后,设备读取引脚电平。
写1:
主控拉低电平,提醒设备,主控芯片要发送数据了。30us内主控芯片拉高电平,30us后,设备读取引脚电平。
读一位数据
读数据:
0
1
主控芯片至少拉低1us,通知设备,设备会马上设置引脚,发送0则会拉低电平,发送1则拉高电平,主控芯片在15us后读取电平。
驱动程序步骤:
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>
#include <linux/version.h>
static int major;
static struct class *ds18b20_class;
static struct gpio_desc *ds18b20_data_pin;
static int irq;
static int ds18b20_data = 0;
static wait_queue_head_t ds18b20_wq;
void ds18b20_delay_us(int us)
{
u64 pre,last;
pre = ktime_get_boot_ns();
while(1)
{
last = ktime_get_boot_ns();
if(last-pre>=us*1000)
{
break;
}
}
}
int ds18b20_wait_for_ack(void)
{
int timeout_count = 500;
/*如果是高电平,等待*/
while(gpiod_get_value(ds18b20_data_pin) && --timeout_count)
{
udelay(1);
}
if(!timeout_count)
{
return -1;
}
/*此时为低电平,是ds18b20发出的回应信号,等待变为高电平*/
timeout_count = 500;
while(!gpiod_get_value(ds18b20_data_pin) && --timeout_count)
{
udelay(1);
}
if(!timeout_count)
{
return -1;
}
return 0;/*此时成功*/
}
static int ds18b20_reset(void)
{
int ret;
/*1.发出低电平脉冲*/
gpiod_direction_output(ds18b20_data_pin,0);
/*2.维持480us*/
ds18b20_delay_us(480);/*因为udelay不准,需要用到ktime*/
/*设置为输入模式*/
ret = gpiod_direction_input(ds18b20_data_pin);
if(ds18b20_wait_for_ack())/*设置超时时间,当没有设备时,返回非零值*/
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
else
return 0;
}
static void ds18b20_write_byte(unsigned char data)
{
/*优先传输最低位*/
int i;
for(i = 0;i<8;i++)
{
if(data & (1<<i))
{
/*输出1*/
gpiod_direction_output(ds18b20_data_pin,0);
ds18b20_delay_us(2);
/*设置为输入引脚,由于有上拉电阻,引脚默认为高电平*/
gpiod_direction_input(ds18b20_data_pin);
ds18b20_delay_us(60);
}
else
{
/*输出0*/
gpiod_direction_output(ds18b20_data_pin,0);
ds18b20_delay_us(60);
/*设置为输入引脚,由于有上拉电阻,引脚默认为高电平*/
gpiod_direction_input(ds18b20_data_pin);
ds18b20_delay_us(2);
}
}
}
unsigned char ds18b20_read_byte(void)
{
unsigned char data =0;
int i;
for(i = 0;i<8;i++)
{
/*设置输入引脚*/
gpiod_direction_output(ds18b20_data_pin,0);
ds18b20_delay_us(2);
/* 设置为输入 */
gpiod_direction_input(ds18b20_data_pin);
/* 7us之后读引脚 */
ds18b20_delay_us(7);
if(gpiod_get_value(ds18b20_data_pin))
{
data|=(1<<i);
}
/*读取到数据后等待60us*/
ds18b20_delay_us(60);
}
return data;
}
/* 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t ds18b20_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
unsigned long flags;
unsigned char tempL=0,tempH=0;
unsigned int integer;
unsigned char decimal1,decimal2,decimal;
if (size != 5)
return -EINVAL;
local_irq_save(flags); // 关中断
if (ds18b20_reset())
{
gpiod_direction_output(ds18b20_data_pin, 1);
local_irq_restore(flags);/*回复中断*/
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return -ENODEV;
}
ds18b20_write_byte(0xcc);//忽略rom指令,直接使用功能指令
ds18b20_write_byte(0x44);//温度转换指令
gpiod_direction_output(ds18b20_data_pin, 1);/*设置回输出引脚,保持高电平*/
local_irq_restore(flags);//让出cpu资源
/*使用schedule_timeout函数时要先设置当前线程的状态,否则不起效果*/
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ);//等待转换,延时1s()
local_irq_save(flags); // 关中断
if (ds18b20_reset())
{
gpiod_direction_output(ds18b20_data_pin, 1);
local_irq_restore(flags);/*回复中断*/
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return -ENODEV;
}
ds18b20_write_byte(0xcc);//忽略rom指令,直接使用功能指令
ds18b20_write_byte(0xbe);//读暂存器中的数据
tempL=ds18b20_read_byte();//读低八位温度
tempH=ds18b20_read_byte();//读高八位温度
if(tempH>0x7f) //最高位为1时温度是负
{
tempL = ~tempL; //补码转换,取反加一
tempH = ~tempH+1;
integer = tempL/16+tempH*16; //整数部分
decimal1 = (tempL&0x0f)*10/16; //小数第一位
decimal2 = (tempL&0x0f)*100/16%10; //小数第二位
decimal = decimal1*10+decimal2; //小数两位
}
else
{
integer = tempL/16+tempH*16; //整数部分
decimal1 = (tempL&0x0f)*10/16; //小数第一位
decimal2 = (tempL&0x0f)*100/16%10; //小数第二位
decimal = decimal1*10+decimal2; //小数两位if(tempH>0x7f)
}
local_irq_restore(flags);
gpiod_direction_output(ds18b20_data_pin, 1);
/*拷贝整数部分*/
copy_to_user(buf,&integer,4);
/*拷贝小数部分*/
copy_to_user(buf+4,&decimal,1);
return 5;
}
static unsigned int ds18b20_drv_poll(struct file *fp, poll_table * wait)
{
// printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
// poll_wait(fp, &ds18b20_wait, wait);
return 0;
}
/* 定义自己的file_operations结构体 */
static struct file_operations ds18b20_fops = {
.owner = THIS_MODULE,
.read = ds18b20_drv_read,
.poll = ds18b20_drv_poll,
};
/* 1. 从platform_device获得GPIO
* 2. gpio=>irq
* 3. request_irq
*/
static int ds18b20_probe(struct platform_device *pdev)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
/* 1. 获得硬件信息 */
ds18b20_data_pin = gpiod_get(&pdev->dev, NULL, GPIOD_OUT_HIGH );
//irq = gpiod_to_irq(ds18b20_echo);
//request_irq(irq, ds18b20_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "ds18b20", NULL);
/* 2. device_create */
device_create(ds18b20_class, NULL, MKDEV(major, 0), NULL, "myds18b20");
return 0;
}
static int ds18b20_remove(struct platform_device *pdev)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(ds18b20_class, MKDEV(major, 0));
gpiod_put(ds18b20_data_pin);
return 0;
}
static const struct of_device_id ask100_ds18b20[] = {
{ .compatible = "100ask,ds18b20" },
{ },
};
/* 1. 定义platform_driver */
static struct platform_driver ds18b20_driver = {
.probe = ds18b20_probe,
.remove = ds18b20_remove,
.driver = {
.name = "100ask_ds18b20",
.of_match_table = ask100_ds18b20,
},
};
/* 2. 在入口函数注册platform_driver */
static int __init ds18b20_init(void)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
/* 注册file_operations */
major = register_chrdev(0, "ds18b20", &ds18b20_fops);
ds18b20_class = class_create(THIS_MODULE, "ds18b20_class");
if (IS_ERR(ds18b20_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "ds18b20");
return PTR_ERR(ds18b20_class);
}
init_waitqueue_head(&ds18b20_wq);
err = platform_driver_register(&ds18b20_driver);
return err;
}
/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
* 卸载platform_driver
*/
static void __exit ds18b20_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&ds18b20_driver);
class_destroy(ds18b20_class);
unregister_chrdev(major, "ds18b20");
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(ds18b20_init);
module_exit(ds18b20_exit);
MODULE_LICENSE("GPL");