文章目录
- 一、修改并且编译设备树
- (1)修改设备树
- (2)修改开发板设备树进行reboot
- 二、硬件连接
- 三、驱动开发与测试
- (1)编写设备驱动
- (2)编写测试代码
- (3)Makefile
- (4)运行结果
- 四、代码重难点分析
- (1)ds18b20时序解析
- 【1】宏定义
- 【2】复位脉冲和应答脉冲
- 【3】主机写时序
- 【4】主机读时序
- (2)移位获取每个byte进行发送
- (3)获取ds18b20发送的数据
对ds18b20不了解的可以查看这篇文章,讲解的比较详细的:STM32一线协议-DS18B20温度传感器采样实现
源码是根据上一届学长的,想要参考的可以去拜访一下gitee:代码链接
一、修改并且编译设备树
(1)修改设备树
在路径linux-imx/arch/arm/boot/dts/
下修改设备树igkboard.dts
:
主节点:
w1: w1 {
compatible = "ds18b20-gpio";
status = "disabled";
};
从节点:
&w1 {
compatible = "ds18b20-gpio";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_w1>;
w1-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
};
在源码路径下执行make dtbs
进行编译:
wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx$ make dtbs
DTC arch/arm/boot/dts/igkboard.dtb
成功。
(2)修改开发板设备树进行reboot
涉及到的tftp相关的知识不懂的可以参考这篇文章:wpa_supplicant无线网络配置imx6ull以及搭建tftp服务器
将我们源码下的igkboard.dts
和zImage
文件加载到开发板:
wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx/arch/arm/boot$ ls -l zImage
-rwxrwxr-x 1 wangdengtao wangdengtao 9466168 4月 9 16:18 zImage
wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx/arch/arm/boot$ cd dts/
wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx/arch/arm/boot/dts$ ls -l igkboard.dts
-rw-rw-r-- 1 wangdengtao wangdengtao 16742 4月 9 17:06 igkboard.dt
然后tftp命令将我们的两个文件上传到我们的开发板即可。
修改开发板上的这两个文件:
root@igkboard:~# find / -name zImage
/run/media/mmcblk1p1/zImage
root@igkboard:~# find / -name igkboard.dtb
/run/media/mmcblk1p1/igkboard.dtb
修改好只有只需要进行sudo reboot
命令即可。
成功启动后你会在开发板的proc/device-tree/
路径下看见设备树节点:
root@igkboard:~# ls /proc/device-tree/w1/
compatible name phandle pinctrl-0 pinctrl-names status w1-gpios
root@igkboard:~# cat /proc/device-tree/w1/compatible
ds18b20-gpioroot@igkboard:~#
二、硬件连接
三、驱动开发与测试
(1)编写设备驱动
驱动源码:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include "ds18b20.h"
#define DEV_NAME "ds18b20"
#ifndef DEV_MAJOR
#define DEV_MAJOR 0
#endif
struct ds18b20_priv {
struct cdev cdev;
struct class *dev_class;
struct device *dev;
spinlock_t lock;
};
struct gpio_desc *g_ds18b20_gpiod;
// int ds18b20_gpio;
static int dev_major = DEV_MAJOR;
/* @description: 温度读取函数
*
* @parm : priv - 私有数据结构体指针
* @parm :
* @return: 温度,或者直接返回错误码
*/
static short temp_recv_from_sensor(struct ds18b20_priv *priv)
{
//struct ds18b20_priv *priv = container_of(&devp, struct ds18b20_priv, dev);
unsigned long flags;
uint8_t temp_l = 0;
uint8_t temp_h = 0;
short temp = 0;
short ret = 0;
spin_lock_irqsave(&priv->lock, flags);
if(ds18b20_reset() != 0)
{
printk("%d ds18b20 reset failure.\n", __LINE__);
ret = -EFAULT;
goto undo_spin_unlock;
}
ds18b20_write_byte(CMD_DS18B20_SKIP_ROM_ID);
ds18b20_write_byte(CMD_DS18B20_CONVERT_TEMP);
spin_unlock_irqrestore(&priv->lock, flags);
msleep(750);
spin_lock_irqsave(&priv->lock, flags);
if(ds18b20_reset() != 0)
{
printk("%d ds18b20 reset failure.\n", __LINE__);
ret = -EFAULT;
goto undo_spin_unlock;
}
ds18b20_write_byte(CMD_DS18B20_SKIP_ROM_ID);
ds18b20_write_byte(CMD_DS18B20_READ_DATA);
temp_l = ds18b20_read_byte();
temp_h = ds18b20_read_byte();
spin_unlock_irqrestore(&priv->lock, flags);
temp = (temp_h << 8) | temp_l;
//printk("temp :%d temp_h : %d temp_l : %d \n", temp, temp_h, temp_l);
return temp;
undo_spin_unlock:
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
/* @description: 打开设备
*
* @parm : inode - 传递给驱动的inode
* @parm : filp - 设备文件,利用其私有数据成员
* @return: 0 successfully , !0 failure
*/
static int ds18b20_open(struct inode *inode, struct file *filp)
{
struct ds18b20_priv *priv = container_of(inode->i_cdev, struct ds18b20_priv, cdev);
filp->private_data = priv;
#if 0
//测试udelay
DS18B20_IO_OUT();
for (i = 0; i < 20; i++) {
DS18B20_IO_WRITE(0);
udelay(1);
DS18B20_IO_WRITE(1);
udelay(1);
}
#endif
//printk("open ds18b20 successfully.\n");
return 0;
}
/* @description: 从设备读取文件
*
* @parm : filp - 设备文件,文件描述符
* @parm : buf - 返回给用户空间的数据缓冲区
* @parm : cnt - 要读取的数据长度
* @parm : offt - 相对于文件首地址的偏移
* @return: 读取的字节数,负数 - 读取失败
*/
static ssize_t ds18b20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
struct ds18b20_priv *priv = filp->private_data;
int ret = 0;
short temp = 0;
temp = temp_recv_from_sensor(priv);
if(copy_to_user(buf, &temp, sizeof(temp)))
{
ret = -EFAULT;
}
else
{
ret = sizeof(temp);
}
return ret;
}
/* @description: poll函数
*
* @parm : filp - 要打开的设备文件 fd
* @parm : poll_table - 等待列表
* @return: 设备或者资源状态,mask掩码相关
*/
static unsigned int ds18b20_poll(struct file *filp, struct poll_table_struct *poll_table)
{
//TODO: 待完善
return 0;
}
/* @description: 关闭设备
*
* @parm : inode - 传递给驱动的inode
* @parm : filp - 设备文件,file结构体有个私有数据区可以使用
* @return: 0 successfully , !0 failure
*/
static int ds18b20_release(struct inode *inode, struct file *filp)
{
return 0;
}
//设备操作函数
static struct file_operations ds18b20_fops = {
.owner = THIS_MODULE,
.open = ds18b20_open,
.release = ds18b20_release,
.read = ds18b20_read,
.poll = ds18b20_poll,
};
/* @description: sysfs - 温度属性显示函数
*
* @parm : devp - 设备指针,创建file时候会指定dev
* @parm : attr - 设备属性,创建时候传入
* @parm : buf - 传出给sysfs中显示的buf
* @return: 显示的字节数
* @TODO: 函数不够正规,了解PAGE_SIZE
*/
static ssize_t temp_show(struct device *devp, struct device_attribute *attr, char *buf)
{
short temp = 0;
//struct ds18b20_priv *priv0 = container_of(&devp, struct ds18b20_priv, dev);
struct ds18b20_priv *priv = dev_get_drvdata(devp);
#if 0
dev_info(devp, "%s driver probe okay.\n", DEV_NAME);
dev_info(priv0->dev, "%s driver probe okay.\n", DEV_NAME);
dev_info(priv1->dev, "%s driver probe okay.\n", DEV_NAME);
printk("priv0 %p priv1 %p\n", priv0, priv1);
printk("priv0->dev %p priv1->dev %p\n", priv0->dev, priv1->dev);
printk("devp %p\n", devp);
printk("priv0->lock %p priv1->lock %p\n", &priv0->lock, &priv1->lock);
#endif
temp = temp_recv_from_sensor(priv);
return sprintf(buf, "temp=%d\n", temp*62);//1000倍
}
/* @description: sysfs - echo写入属性函数
*
* @parm : dev - 设备指针,创建file时候会指定dev
* @parm : attr - 设备属性,创建时候传入
* @parm : buf - 用户空间的buf
* @parm : count - 传入buf的size
* @return: 写入的buf大小
*/
static ssize_t temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
char k_buf[10] = {0,};
snprintf(k_buf, sizeof(k_buf), "%s", buf);
dev_info(dev, "Don't echo to me -> [%s] size [%d]\n", k_buf, count);
return count;
}
static DEVICE_ATTR(temp, 0644, temp_show, temp_store);
/* @description: ds18b20 驱动安装 probe函数
*
* @parm : pdev - platform 设备指针
* @parm :
* @return: 0 successfully , !0 failure
*/
static int ds18b20_probe(struct platform_device *pdev)
{
struct ds18b20_priv *priv = NULL;
dev_t devno;
int rv = 0;
//0.给priv分配空间
priv = devm_kzalloc(&pdev->dev, sizeof(struct ds18b20_priv), GFP_KERNEL);
if(!priv)
{
return -ENOMEM;
}
//1. 获取gpio
g_ds18b20_gpiod = gpiod_get(&pdev->dev, "w1", GPIOD_ASIS);
if(IS_ERR(g_ds18b20_gpiod))
{
dev_err(&pdev->dev, "Get %s gpiod failure.\n", DEV_NAME);
return PTR_ERR(g_ds18b20_gpiod);
}
//ds18b20_gpio = desc_to_gpio(g_ds18b20_gpiod);
//printk("request ds18b20_gpio [%d] \n", ds18b20_gpio);
//2.创建设备号
if(0 != dev_major)
{
devno = MKDEV(dev_major, 0);
rv = register_chrdev_region(devno, 1, DEV_NAME); //静态创建
}
else
{
rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);//动态创建
dev_major = MAJOR(devno);//获主设备号
}
if(rv < 0)
{
dev_err(&pdev->dev, "%s driver can't get major %d\n", DEV_NAME, dev_major);
return rv;
}
//2.注册字符设备
cdev_init(&priv->cdev, &ds18b20_fops); //初始化cdev
priv->cdev.owner = THIS_MODULE;
rv = cdev_add(&priv->cdev, devno, 1);
if(0 != rv)
{
dev_err(&pdev->dev, "error %d add %s device failure.\n", rv, DEV_NAME);
goto undo_major;
}
//3.创建类,驱动进行节点创建
priv->dev_class = class_create(THIS_MODULE, DEV_NAME);
if(IS_ERR(priv->dev_class))
{
dev_err(&pdev->dev, "%s driver create class failure.\n", DEV_NAME);
rv = -ENOMEM;
goto undo_cdev;
}
//4.创建设备
priv->dev = device_create(priv->dev_class, NULL, devno, NULL, DEV_NAME);
if(IS_ERR(priv->dev))
{
rv = -ENOMEM;
goto undo_class;
}
//5. 初始化自旋锁
spin_lock_init(&priv->lock);
//6. 创建sys 属性 在platform下
if(device_create_file(priv->dev, &dev_attr_temp))
{
rv = -ENOMEM;
goto undo_device;
}
//7. 保存私有数据
platform_set_drvdata(pdev, priv);
dev_set_drvdata(priv->dev, priv);
dev_info(&pdev->dev, "%s driver probe okay.\n", DEV_NAME);
// dev_info(priv->dev, "%s driver probe okay.\n", DEV_NAME);
return 0;
undo_device:
device_destroy(priv->dev_class, devno);
undo_class:
class_destroy(priv->dev_class);
undo_cdev:
cdev_del(&priv->cdev);
undo_major:
unregister_chrdev_region(devno, 1);
devm_kfree(&pdev->dev, priv);
return rv;
}
/* @description: 驱动卸载 执行函数
*
* @parm : pdev - 设备指针
* @parm :
* @return: 0 successfully , !0 failure
*/
static int ds18b20_remove(struct platform_device *pdev)
{
struct ds18b20_priv *priv = platform_get_drvdata(pdev);
dev_t devno = MKDEV(dev_major, 0);
//删除sys中的属性
device_remove_file(priv->dev, &dev_attr_temp);
//设备销毁
device_destroy(priv->dev_class, devno);
//注销类
class_destroy(priv->dev_class);
//删除字符设备
cdev_del(&priv->cdev);
unregister_chrdev_region(devno, 1);
//释放gpiod
gpiod_put(g_ds18b20_gpiod);
//释放堆
devm_kfree(&pdev->dev, priv);
dev_info(&pdev->dev, "%s driver remove.\n", DEV_NAME);
return 0;
}
static const struct of_device_id of_ds18b20_match[] = {
{.compatible = "ds18b20-gpio"},
{},
};
MODULE_DEVICE_TABLE(of, of_ds18b20_match);
static struct platform_driver ds18b20_driver = {
.probe = ds18b20_probe, //驱动安装时候会执行的钩子函数
.remove = ds18b20_remove, //驱动卸载时候
.driver = {
.name = "ds18b20-gpios", //不建议用的name域
.of_match_table = of_ds18b20_match,
},
};
static int __init platdrv_ds18b20_init(void)
{
int rv = 0;
rv = platform_driver_register(&ds18b20_driver); //注册platform驱动
if(rv)
{
printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__, __LINE__, rv);
return rv;
}
printk("Regist imx ds18b20 Platform Driver successfully!\n");
return 0;
}
static void __exit platdrv_ds18b20_exit(void)
{
printk("%s():%d remove ds18b20 platform driver\n", __FUNCTION__, __LINE__);
platform_driver_unregister(&ds18b20_driver); //卸载驱动
}
//module_platform_driver(ds18b20_driver);
module_init(platdrv_ds18b20_init);
module_exit(platdrv_ds18b20_exit);
MODULE_AUTHOR("Wei Huihong <weihuihui586@gmail.com>");
MODULE_DESCRIPTION("i.MX6ULL ds18b20 driver platform driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:platdrv_ds18b20");
ds18b20.c
#include "ds18b20.h"
/* @description: ds18b20 复位 ,检测设备是否存在
*
* @parm :
* @parm :
* @return: 0 successfully , !0 failure
*/
int ds18b20_reset(void)
{
int ret = -1;
DS18B20_IO_OUT();
DS18B20_IO_WRITE(0);
DS18B20_UDELAY(480);
DS18B20_IO_WRITE(1);
DS18B20_UDELAY(70);
DS18B20_IO_IN();
ret = DS18B20_IO_READ();
DS18B20_UDELAY(10);
DS18B20_IO_OUT();
DS18B20_IO_WRITE(1); /* 释放总线 */
return ret;
}
/* @description: ds18b20 写一个字
*
* @parm : bit - 一个字
* @parm :
* @return:
*/
static void _ds18b20_write_bit(unsigned char bit)
{
DS18B20_IO_OUT();
/* 判断 bit */
bit = bit > 1 ? 1 : bit;
//printk("write bit [%d]\n", bit);
//mdelay(1);
DS18B20_UDELAY(50); //TODO: 此处未知
//写周期开始
DS18B20_IO_WRITE(0);
DS18B20_UDELAY(2);
//写0还是写1,拉高或者拉低后持续60微妙
DS18B20_IO_WRITE(bit);
DS18B20_UDELAY(60);
//然后大搞电平2us(写0),写1时候自己本身就是高的
DS18B20_IO_WRITE(1);
DS18B20_UDELAY(12);
}
/* @description: ds18b20 读一个字
*
* @parm :
* @parm :
* @return: 一个字 bit
*/
static unsigned char _ds18b20_read_bit(void)
{
unsigned char bit;
DS18B20_IO_OUT();
DS18B20_IO_WRITE(0);
DS18B20_UDELAY(2);
#if 0
DS18B20_IO_WRITE(1);
DS18B20_UDELAY(2);
#endif
DS18B20_IO_IN();
DS18B20_UDELAY(10); //NOTE: 根据实际进行修改
bit = DS18B20_IO_READ();
//printk("bit : %d\n", bit);
DS18B20_UDELAY(50);
return bit;
}
/* @description: ds18b20 写一个字节
*
* @parm : byte - 写入的字节
* @parm : gpiod - gpio 描述符
* @return: void
*/
void ds18b20_write_byte(unsigned char byte)
{
int i;
for(i=0; i<8; i++)
{
_ds18b20_write_bit((byte >> i) & 0x01);
}
}
/* @description: ds18b20 读一个字节
*
* @parm :
* @parm :
* @return: 读到的字节
*/
unsigned char ds18b20_read_byte(void)
{
uint8_t byte = 0;
uint8_t bit;
int i;
for(i=0; i<8; i++)
{
bit = _ds18b20_read_bit();
if(bit)
byte |= (0x01 << i);
//byte = _ds18b20_read_bit() ? (byte | (0x01 << i)) : byte;
}
return byte;
}
ds18b20.h
#ifndef __DS18B20_H__
#define __DS18B20_H__
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
/* ds18b20 CMD */
/* function */
#define CMD_DS18B20_CONVERT_TEMP 0x44//温度变换
#define CMD_DS18B20_READ_DATA 0xBE//读暂存存储器
#define CMD_DS18B20_WRITE_DATA 0x4E//写暂存存储器
#define CMD_DS18B20_COPY_DATA 0x48//复制暂存存储器
#define CMD_DS18B20_RESET_EPEROM 0xB8//重新调出
#define CMD_DS18B20_POWER_READ 0xB4//读电源
/* ROM */
#define CMD_DS18B20_SKIP_ROM_ID 0xCC//跳过ROM
#define CMD_DS18B20_MATCH_ROM_ID 0x55//匹配ROM
#define CMD_DS18B20_READ_ROM_ID 0x33 /* 读ROM总线仅一个设备使用 */
#define CMD_DS18B20_SEARCH_ROM_ID 0xF0//搜索ROM
#define CMD_DS18B20_SEARCH_ALARM 0xEC//告警搜索
/* for gpio */
#define DS18B20_IO_OUT() gpiod_direction_output(g_ds18b20_gpiod, 1)
#define DS18B20_IO_IN() gpiod_direction_input(g_ds18b20_gpiod)
#define DS18B20_IO_WRITE(bit) gpiod_set_value(g_ds18b20_gpiod, bit)
#define DS18B20_IO_READ() gpiod_get_value(g_ds18b20_gpiod)
#define DS18B20_UDELAY(us) udelay(us)
//extern int ds18b20_gpio;
extern struct gpio_desc *g_ds18b20_gpiod;
/* @description: ds18b20 复位 ,检测设备是否存在
*
* @parm :
* @parm :
* @return: 0 successfully , !0 failure
*/
extern int ds18b20_reset(void);
/* @description: ds18b20 写一个字节
*
* @parm : byte - 写入的字节
* @parm :
* @return: void
*/
extern void ds18b20_write_byte(unsigned char byte);
/* @description: ds18b20 读一个字节
*
* @parm : gpiod - gpio 描述符
* @parm :
* @return: 读到的字节
*/
extern unsigned char ds18b20_read_byte(void);
#endif
(2)编写测试代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define DEV_DS18B20 "/dev/ds18b20"
int main(int argc, char *argv[])
{
int fd_ds18b20 = -1;
int rv = -1;
short temp = 0;
float temperature = 0.0;
fd_ds18b20 = open(DEV_DS18B20, O_RDONLY);
if(fd_ds18b20 < 0)
{
printf("open %s failure.\n", DEV_DS18B20);
return -1;
}
printf("open %s successfully.\n", DEV_DS18B20);
while (1) {
if((rv = read(fd_ds18b20, &temp, sizeof(temp))) <= 0)
{
printf("read %s failure %s\n", DEV_DS18B20, strerror(errno));
return -2;
}
else
{
temperature = temp * 0.0625; //右移4bit,保证小数点数据正确
printf("[ds18b20] temp : %.3f℃ \n", temperature);
}
sleep(2);
}
close(fd_ds18b20);
return 0;
}
(3)Makefile
LINUX_SRC = /home/wangdengtao/imx6ull/imx6ull/bsp/kernel/linux-imx
CROSS_COMPILE=arm-linux-gnueabihf-
INST_PATH=/tftp
APP=ds18b20_app
PWD := $(shell pwd)
EXTRA_CFLAGS+=-DMODULE
obj-m += ds18b20_platdrv.o
ds18b20_platdrv-objs := ds18b20.o platdrv_ds18b20.o
modules:
@make clean
@echo ${LINUX_SRC}
@make -C $(LINUX_SRC) M=$(PWD) modules
@make clear
@$(CROSS_COMPILE)gcc $(APP).c -o $(APP)
uninstall:
rm -f ${INST_PATH}/*.ko
install: uninstall
cp -af *.ko ${INST_PATH}
clear:
@rm -f *.o *.cmd *.mod.c .*.cmd *.mod
@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
@rm -f .*ko.cmd .*.o.cmd .*.o.d
clean: clear
@rm -f *.ko
@rm -f $(APP)
(4)运行结果
root@igkboard:~# insmod ds18b20_platdrv.ko
root@igkboard:~# lsmod
Module Size Used by
ds18b20_platdrv 16384 0
rtl8188fu 999424 0
imx_rngc 16384 0
rng_core 20480 1 imx_rngc
secvio 16384 0
error 20480 1 secvio
root@igkboard:~# ls -l /dev/ds18b20
crw------- 1 root root 243, 0 Apr 22 08:39 /dev/ds18b20
root@igkboard:~# ./ds18b20_app
open /dev/ds18b20 successfully.
[ds18b20] temp : 23.375℃
[ds18b20] temp : 23.375℃
[ds18b20] temp : 23.375℃
[ds18b20] temp : 26.562℃
[ds18b20] temp : 28.000℃
[ds18b20] temp : 28.812℃
四、代码重难点分析
(1)ds18b20时序解析
【1】宏定义
函数声明与宏定义:
/* ds18b20 CMD */
/* function */
#define CMD_DS18B20_CONVERT_TEMP 0x44//温度变换
#define CMD_DS18B20_READ_DATA 0xBE//读暂存存储器
#define CMD_DS18B20_WRITE_DATA 0x4E//写暂存存储器
#define CMD_DS18B20_COPY_DATA 0x48//复制暂存存储器
#define CMD_DS18B20_RESET_EPEROM 0xB8//重新调出
#define CMD_DS18B20_POWER_READ 0xB4//读电源
/* ROM */
#define CMD_DS18B20_SKIP_ROM_ID 0xCC//跳过ROM
#define CMD_DS18B20_MATCH_ROM_ID 0x55//匹配ROM
#define CMD_DS18B20_READ_ROM_ID 0x33 /* 读ROM总线仅一个设备使用 */
#define CMD_DS18B20_SEARCH_ROM_ID 0xF0//搜索ROM
#define CMD_DS18B20_SEARCH_ALARM 0xEC//告警搜索
/* for gpio */
#define DS18B20_IO_OUT() gpiod_direction_output(g_ds18b20_gpiod, 1)//输出模式,默认高电平
#define DS18B20_IO_IN() gpiod_direction_input(g_ds18b20_gpiod)//输入模式
#define DS18B20_IO_WRITE(bit) gpiod_set_value(g_ds18b20_gpiod, bit)//
#define DS18B20_IO_READ() gpiod_get_value(g_ds18b20_gpiod)//
#define DS18B20_UDELAY(us) udelay(us)
extern struct gpio_desc *g_ds18b20_gpiod;
【2】复位脉冲和应答脉冲
主机首先发出一个480us - 960us的低电平脉冲(复位),然后释放总线变为高电平,并在随后的480微秒时间内,对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现一直都是高电平说明总线上无器件应答。
作为从机的DS18B20上电后就一直检测总线上是否有480us - 960us的低电平出现,如果检测到该复位脉冲则在总线变为高电平后,等待15us - 60us,之后将总线电平拉低60us - 240us(响应存在脉冲),告诉主机本器件已做好准备。若检测不到复位脉冲则一直处于检测等待。
我们简单来看就是:主机将DQ拉成低电平保持最少480us后释放总线,延时15 ~ 60us后的60~240us时间内检测DQ是否为低电平,再延时240us保持起始时序的完整。(从主机角度看进行初始化的过程)
代码实现:
int ds18b20_reset(void)
{
int ret = -1;
DS18B20_IO_OUT();
DS18B20_IO_WRITE(0);
DS18B20_UDELAY(480);//输出480us的低电平
DS18B20_IO_WRITE(1);//随后高电平
DS18B20_UDELAY(70);//等待70us去查看总线,0表示有响应
DS18B20_IO_IN();//输入模式,读
ret = DS18B20_IO_READ();
DS18B20_UDELAY(10);
DS18B20_IO_OUT();
DS18B20_IO_WRITE(1); /* 释放总线 */
return ret;
}
【3】主机写时序
写周期最少为60微秒,最长不超过120微秒。写周期的开始,主机先把总线拉低大于1微秒表示写周期开始。
若主机想写0:则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平延时2s。
若主机想写1:在拉低总线电平1微秒后就释放总线为高电平,延时60us。
作为从机的DS18B20在检测到总线被拉低后等待15微秒后从15us ~ 45us对总线采样(典型时间15us),在采样时间内,若检测到总线为高电平则认为主机发送了1,若检测到总线为低电平则认为主机发送了0。
代码实现:
/*写一个位(字)*/
static void _ds18b20_write_bit(unsigned char bit)
{
DS18B20_IO_OUT();
/* 判断 bit */
bit = bit > 1 ? 1 : bit;
//printk("write bit [%d]\n", bit);
//mdelay(1);
DS18B20_UDELAY(50); //TODO: 此处未知
//写周期开始
DS18B20_IO_WRITE(0);
DS18B20_UDELAY(2);
//写0还是写1,拉高或者拉低后持续60微妙
DS18B20_IO_WRITE(bit);
DS18B20_UDELAY(60);
//然后大搞电平2us(写0),写1时候自己本身就是高的
DS18B20_IO_WRITE(1);
DS18B20_UDELAY(12);
}
【4】主机读时序
读周期最少为60微秒,最长不超过120微秒。读周期的开始,主机先把总线拉低大于1微秒,然后释放总线。主机释放总线后:
若DS18B20发送0:则把总线拉低并保持至少从读周期开始的15us,然后释放总线为高电平。
若DS18B20发送1:则在主机释放总线后不拉低总线(为高电平)。
主机须在读周期开始的15us内检测总线电平的高低,若检测到总线为低则表示DS18B20发送来0,若检测到总线为高则表示DS18B20发送来1。
代码实现:
static unsigned char _ds18b20_read_bit(void)
{
unsigned char bit;
DS18B20_IO_OUT();
DS18B20_IO_WRITE(0);
DS18B20_UDELAY(2);
DS18B20_IO_IN();
DS18B20_UDELAY(10); //NOTE: 根据实际进行修改
bit = DS18B20_IO_READ();
//printk("bit : %d\n", bit);
DS18B20_UDELAY(50);
return bit;
}
(2)移位获取每个byte进行发送
这个一般再放发送的时候用的比较多,移位与运算获取到每一个位。
/*********************************************************************************
* Copyright: (C) 2023 WangDengtao<1799055460@qq.com>
* All rights reserved.
*
* Filename: shift.c
* Description: This file
*
* Version: 1.0.0(2023年04月21日)
* Author: WangDengtao <1799055460@qq.com>
* ChangeLog: 1, Release initial version on "2023年04月21日 10时48分56秒"
*
********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#define CMD_DS18B20_CONVERT_TEMP 0x44//温度变换
/*0&x = 0, 1&x = x
0|x = x,1|x = 1 */
int main(int argc, char* argv[])
{
int i;
for(i=0; i<8; i++)
{
printf("%d ", (CMD_DS18B20_CONVERT_TEMP >> i) & 0x01);
}
printf("\n");
return 0;
}
结果:
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao$ gcc shift.c
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao$ ./a.out
0 0 1 0 0 0 1 0
(3)获取ds18b20发送的数据
ds18b20发送过来的数据我们需要获取,一个字地去获取,下面是举的一个例子,代码中的函数也是直接拿过来验证的。获取到的数据是相反的。因为ds18b20发送一个字节,连续调用8次发送一位的时序,依次发送一个字节的8位(低位在前),所以验证时候的数据是相反的。
实际代码中这样做是正确的。
/*********************************************************************************
* Copyright: (C) 2023 WangDengtao<1799055460@qq.com>
* All rights reserved.
*
* Filename: shift.c
* Description: This file
*
* Version: 1.0.0(2023年04月21日)
* Author: WangDengtao <1799055460@qq.com>
* ChangeLog: 1, Release initial version on "2023年04月21日 10时48分56秒"
*
********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//十进制转换为二进制
uint8_t dectobin(int n);
int main(int argc, char* argv[])
{
int i;
uint8_t byte_l = 0;
uint8_t byte_h = 0;
uint8_t temp_l[8] = {0,0,0,0,0,1,1,1};
uint8_t temp_h[8] = {1,1,0,1,0,0,0,0};
for(i=0; i<8; i++)
{
if(temp_l[i])
{
byte_l |= (0x01 << i);
}
//printf("%d ", byte_l);
}
for(i=0; i<8; i++)
{
if(temp_h[i])
{
byte_h |= (0x01 << i);
}
//printf("%d ", byte_h);
}
printf("原始的数据:\n");
dectobin(7);
dectobin(208);
//printf("[ 7] temp_l 0000 0111\n");
//printf("[208] temp_h 1101 0000\n");
printf("\n");
printf("转换后数据:\n");
dectobin(byte_l);
dectobin(byte_h);
return 0;
}
uint8_t dectobin(int n)
{
int a = n;
int sum = 0;
int y, x = 1; // y表示余数,x为叠加的系数
while (n != 0)
{
y = n % 2;
sum += x * y;
x *= 10;
n /= 2;
}
printf("[%3d] %08d\n", a, sum);
return sum;
}
结果:
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao$ gcc shift.c
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao$ ./a.out
原始的数据:
[ 7] 00000111
[208] 11010000
转换后数据:
[224] 11100000
[ 11] 00001011