优快云仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2019/02/15/AM335X——1-Wire和IrDA驱动/#more
记录DS18B20温度传感器、DH11温湿度传感器、红外遥控驱动。
1-wire(单总线协议)就是只使用一条线(GPIO)实现时钟/数据的双向传输。
DS18B20是比较标准的1-wire协议,可以通过逻辑分析仪显示出含义,DH11不是很标准(专利原因?),需要自己参考芯片手册理解含义。
但它们原理都差不多,且都对时序要求比较高(us级的延时)。
IrDA也是一根线,原理也差不多,因此也把它放在一起记录。
AM335X没有1-wire的控制器,因此使用GPIO模拟。
1. DS18B20
1.1 基础知识
1.1.1 性能参数
分辨率:9~12位可编程(上电默认12位)
精度:±0.5℃(在-10~+85℃)
量程:-55°C ~ 125°C
转换时间:750ms(12位分辨率)
1.1.2 温度数据格式
一次返回的温度数据为16位,前五位表示正负,中间七位表示整数部分,最低四位为小数部分;
温度传感器的分辨率为用户可编程的9、10、11或12位, 分别对应0.5℃、0.25℃、0.125℃和 0.0625℃。
因此温度计算结果为:(正负)整数部分+小数部分*分辨率

以0000 0000 1010 0010
为例,前五位为0,即温度为零上;中间七位0001010
,即温度整数部分为10;最后四位0010
,即温度小数部分为2*0.625=0.125
,因此温度为+10.125
。
1.1.3 64Bits只读数据
低八位用于CRC校验,中间48位是DS18B20唯一序列号,高八位是产品系列号(为28h)

1.1.4 操作步骤
每次对DS18B20操作,都必须严格按照以下步骤:
1、初始化;
2、ROM指令;
3、功能指令;
1.1.5 ROM指令和功能指令
ROM指令 | ||
---|---|---|
指令名称 | 指令代码 | 指令功能 |
读ROM | 33H | 读ROM中64Bits的只读数据 |
ROM匹配 | 55H | 发出此命令后接着发64Bits的ROM编码,使单总线上与编码匹配的DS18B3做出响应 |
搜索ROM | F0H | 用于确定挂在总线上的DS18B20的个数和识别64Bits的ROM地址 |
跳过ROM | CCH | 忽略64BitsROM只读数据,接着发出功能指令,进行温度转换或读取温度 |
警报搜索 | ECH | 只有温度超过设定上限或下限的DS18B20才做出响应 |
功能指令 | ||
---|---|---|
指令名称 | 指令代码 | 指令功能 |
温度转换 | 44H | 启动温度转换,结果将保存在内部RAM中 |
读取温度 | BEH | 读取内部RAM中的温度数据 |
设置报警温度 | 4EH | 设置上或下限报警温度指令,接着应发送两字节数据 |
保存报警温度 | 48H | 将RAM中的报警温度数据,复制到EEPROM中保存 |
恢复RAM | B8H | 将EEPROM中的报警温度数据恢复到RAM |
读供电方式 | B4H | 寄生供电返回0,外界电源供电返回1 |
1.1.5 初始化时序

初始化DS18B20的时序如上,先拉低480us,然后拉高释放总线,随后在60-240us内,DS18B20将会拉低总线进行响应。
此时检测总线释放被拉低既可判断出DS18B20是否初始化成功。
1.1.6 读写时序

上面的图是写0或1的时序:
如果写0,拉低至少60us(写周期为60-120us)即可;如果写1,先拉低至少1us,然后拉高,整个写周期至少为60us即可。
下面的图是读0或1的时序:
先拉低至少1us,随后读取电平,如果为0,即读到的数据是0,如果为1,即可读到的数据是1。
整个过程必须在15us内完成,15us后引脚都会被拉高。
1.2 内核驱动
内核中自带1-Wire和DS18B20的驱动。
drivers/w1/masters/w1-gpio.c
是单总线的IO操作方法,用于模拟单总线时序;
drivers/w1/slaves/w1_therm.c
是DS18B20的寄存器操作方法,和IO时序无关;
1.2.1 加入内核
{% codeblock lang:Makefile %}
Device Drivers —>
<> Dallas’s 1-wire support —>
[] Userspace communication over connector
1-wire Bus Masters —>
<> GPIO 1-wire busmaster
1-wire Slaves —>
<> Thermal family implementation
{% endcodeblock %}
1.2.2 修改设备树
{% codeblock lang:dts %}
onewire@0 {
compatible = “w1-gpio”;
gpios = <&gpio0 13 0>;
//pinctrl-0 = <&ds18b20_dq_pin>;
};
{% endcodeblock %}
1.2.3 应用测试
cat /sys/bus/w1/drivers/w1_slave_driver/28-01d58c07010c/w1_slave

1.3 自己驱动
这次驱动,吸取了前面AM335X——hwmon和input子系统的经验。
1.3.1 完整代码
{% codeblock lang:c %}
//cat /sys/class/hwmon/hwmon0/device/temperature
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/irqflags.h>
struct ds18b20 {
struct device *hwmon_dev;
struct mutex lock;
int dq_pin;
u8 value[2];
u8 family_code;
u8 serial_num[6];
u8 crc;
};
static struct ds18b20 ds;
static int ds18b20_init(void)
{
int ret = 1;
mutex_lock(&ds.lock);
gpio_direction_output(ds.dq_pin, 1);
udelay(2);
gpio_direction_output(ds.dq_pin, 0); //Low level 480us for reset
udelay(480);
gpio_direction_output(ds.dq_pin, 1); //Pull high release bus
udelay(60);
gpio_direction_input(ds.dq_pin); //Read response pulse
ret = gpio_get_value(ds.dq_pin);
udelay(240); //Waiting for the corresponding end
mutex_unlock(&ds.lock);
return ret;
}
static void write_byte(unsigned char data)
{
int i = 0;
unsigned long flags;
mutex_lock(&ds.lock);
local_irq_save(flags); //Save interrupt
//local_irq_disable(); //Close all interrupts
gpio_direction_output(ds.dq_pin, 1);
for (i = 0; i < 8; i ++)
{
gpio_direction_output(ds.dq_pin, 1);
udelay(2);
gpio_direction_output(ds.dq_pin, 0); //Start at a low level greater than 1us
udelay(5);
gpio_direction_output(ds.dq_pin, data & 0x01);
udelay(60); //Write cycle is greater than 60us
data >>= 1;
}
local_irq_restore(flags); //Recovery interrupt
//local_irq_enable(); //Open all interrupts
mutex_unlock(&ds.lock);
}
static unsigned char read_byte(void)
{
int i;
unsigned long flags;
unsigned char data = 0;
mutex_lock(&ds.lock);
local_irq_save(flags);
//local_irq_disable();
for (i = 0; i < 8; i++)
{
gpio_direction_output(ds.dq_pin, 1);
udelay(2);
gpio_direction_output(ds.dq_pin, 0); //Start at a low level greater than 1us
udelay(5);
gpio_direction_output(ds.dq_pin, 1); //Pull high release bus
udelay(1);
data >>= 1;
gpio_direction_input(ds.dq_pin);
if (gpio_get_value(ds.dq_pin)) //Must be read within 15us after being pulled low
data |= 0x80;
udelay(60); //Read cycle is greater than 60us;
}
local_irq_restore(flags);
//local_irq_enable();
mutex_unlock(&ds.lock);
return data;
}
static ssize_t ds18b20_get_sensor_value(struct device *dev, struct device_attribute *devattr, char *buf)
{
int ret = -1;
unsigned int m, n;
ret =