记录DS18B20温度传感器、DHT11温湿度传感器、红外遥控驱动。
1-wire(单总线协议)就是只使用一条线(GPIO)实现时钟/数据的双向传输。
DS18B20是比较标准的1-wire协议,可以通过逻辑分析仪显示出含义,DHT11不是很标准(专利原因?),需要自己参考芯片手册理解含义。
但它们原理都差不多,且都对时序要求比较高(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指令和功能指令
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 加入内核
|
|
1.2.2 修改设备树
|
|
1.2.3 应用测试
cat /sys/bus/w1/drivers/w1_slave_driver/28-01d58c07010c/w1_slave

1.3 自己驱动
这次驱动,吸取了前面AM335X——hwmon和input子系统的经验。
1.3.1 完整代码
|
|
驱动内容比较简单,严格按照前面的时序操作即可。
值得一提的是,因为时序是us级的,如果在发送时序过程中,产生中断就可能导致时序出错,因此在读写函数里加入local_irq_save(flags);
、local_irq_restore(flags);
临时开/关中断。
1.3.2 设备树
|
|
1.3.3 应用测试

2. DHT11
2.1 基础知识
2.1.1 性能参数
温度
分辨率:1°C
精度:±2℃
检测范围:-20°C ~ 60°C湿度
分辨率:1%RH
精度:±5%RH (0~50°C)
检测范围:5%RH ~ 95%RH (25°C)
采样周期间隔不得低于1秒钟。
可以看到无论是测量温度的精度还是范围、采样周期,都比较烂。。
2.1.2 数据格式
一次返回的数据长度为40Bits,高位在前。
8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和
可以看到数据分小数部分和整数部分,当前小数部分用于以后扩展,现读出为零。
另外还有数据校验,如果”8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据”所得结果的末8位,等于”8bit校验和”即数据正确。
2.1.3 开始时序

开始时,主机拉低至少18ms,随后拉高20-40us,然后释放总线,完成开始信号。
DHT11随后拉低80us,再拉高80us,最后发送40Bits数据。
2.1.4 读时序

先读到50us的低电平,随后如果是26-28us的高电平即收到的是数据0,如果是70us的高电平即收到的数据是1。
2.2 内核驱动
本来以为内核不含DHT11驱动,后面又看到了,在iio子系统里面,路径为:linux-4.1.18/drivers/iio/humidity/dht11.c
。
2.2.1 内核代码
|
|
2.3 自己驱动
2.3.1 完整代码
|
|
因为DHT11的采样周期长达1S,如果应用层稍微读快一点,就会报错比较明显。
因此,这里采取的方案是,驱动设置个定时器每隔一定时间,不断读取传感器,保存数据。应用层读取的是不久前驱动才缓存下的数据。
因此在probe()
函数里,先设置了一个定时器:
定时器每隔1.2s调用dht11_timer_callback()
函数。
再设置了一个工作队列:
将dht11_work_callback()
函数加入到工作队列里。
dht11_timer_callback()
里使用schedule_work()
读取DHT11数据并重新设置定时器周期反复。
2.3.2 设备树
|
|
2.3.3 应用测试

3. IrDA
3.1 基础知识
3.1.1 红外原理
当遥控器按下不同的按键时,遥控器上的红外发射头,会发出人眼看不到(可通过手机摄像头看到)的光波给模块上的红外接收端。
红外接收端收到光波后,会在IRD引脚上产生相应的电平,通过对电平解析,就知道是按的遥控器哪一个键。

3.1.2 数据格式
按键一次的接收到数据结构如下,可以分解成五部分:引导码/连发码、系统码1、系统码2、数据码、数据反码。

3.1.3 引导码/连发码
最开始的一部分是用来判断是否是连按操作和表示信号开始,分为引导码和连发码。
如果是第一按下或非连按,就是先9ms低电平,再4.5ms的高电平,后面接32Bits数据;
如果一直按着该键不放,下一个周期就发送的是连发码,先9ms低电平,再只有2.25ms的高电平,后面接32Bits数据;

3.1.4 数据电平
数据0和1前面都是0.56ms的低电平,那么就是后面的高电平持续时间不同,0为0.56ms,1为1.685ms

3.2 内核驱动
内核中自带红外遥控器的驱动,但没有我使用的遥控器布局文件。drivers/medi/rc/gpio-ir-recv.c
是GPIO模拟红外遥控驱动。drivers/media/rc/keymaps/
下是遥控器键盘布局文件。
3.2.1 加入内核
|
|
3.2.2 添加键盘布局
|
|
将rc-hceng-nec.c
放在drivers/media/rc/keymaps/
下,并修改Makefile,加入rc-hceng-nec.o
。
3.2.3 修改设备树
|
|
3.2.4 应用测试
测试程序:

3.2.5 其它
码值关系
遥控器产生一个原始数据码,rc-hceng-nec
里,将原始数据码和输入子系统中的按键编号进行对应,
最后用户态读到的code是输入子系统中的按键编号值。
比如:12原始数据码 -------> 按键编号 -------->用户层读取0x16 KEY_0/11 11打印原始数据
修改drivers/media/rc/ir-nec-decoder.c
,添加打印:123456183 } else {184 /* Normal NEC */185 scancode = address << 8 | command;186 //IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);187 printk("NEC scancode 0x%04x\n", scancode);188 }