首先谈谈1-Wired总线(因为DS18B20采用此接口)
1.首先交代常见的几种硬件通信接口
GPIO/UART/I2C/1-Wired/SPI
每种通信方式顺带着举例子
2.然后谈1-Wire串行总线定义
就七个字:一线式串行总线
对定义进行解释
"一线式":说明CPU和外设之间数据通信只需一根信号线
此信号线必然是数据线
并且数据线连接了一个上拉电阻,默认为高电平
"串行":说明CPU和外设的数据通信一个时钟周期传输
一个bit位,马上提出问题:
没有时钟控制信号线,哪来的时钟呢,怎么去
传输1个bit位呢?不像I2C总线的"低放高取",因为它有
时钟控制信号线
别着急待会儿后面揭晓答案
“总线”:说明这根数据线上可以连接挂接多个外设
此时此刻画出一个简要的硬件连接示意图,参见ds18b20.png
3.紧接着看图提三个问题:
1.CPU要想访问其中某个外设,那么CPU如何定位到这个外设呢?
2.CPU如果定位到了要访问的外设,那么CPU如何通过
一根数据线和这个外设通信呢?
3.由于没有时钟线,如何同步双方的数据呢?
答:答案在1-Wire总线传输协议中,协议就在外设的
手册中,重点关注手册的时序图
这里以DS18B20温度传感器为例,来解答以上三个
问题,侃侃DS18B20硬件特性,只有掌握了DS18B20的硬件特性
将来才能写出对应的驱动程序
关键以DS18B20为例掌握一线式总线传输协议!
4.紧接着掌控DS18B20硬件特性
粗看:
看连接位置
由于X6818开发板上没有焊接一个DS18B20
于是乎上某宝我们买一个模块单独用杜邦线连接到开发板上,
只需三根线
细看:先看原理图后看芯片手册
注意用最新的底板原理图:x6818bv3.pdf
首先看原理图,交代一下X6818,咱们自己的开发板上
要连接一个外置的DS18B20温度传感器模块,具体连接方法如下:
DS18B20芯片三根管脚:
具体连接参看:连线.png,实在没把握的,先连线后拍照片发群里然后确定没问题再上电,否则烧毁!
VDD电源:连接到X6818开发板上的5V电源
就是电源口旁边10个插针,其中内侧的五个插针为
5V电源接口
GND:连接到X6818开发板上地线
就是电源口旁边10个插针,其中外侧的五个插针为
GND地线接口
DQ:数据线,连接S5P6818的GPIOB10这根管脚(通过查看原理图,
将DQ数据线连接到开发板上CPU的引脚上,并且确认引脚的编号)
就是连接到X6818开发板GPIO接插件的最外侧的里网口最远的
右下角的那个针脚(GPIOB10)
并且DQ数据线必须连接一个上拉电阻,阻值必须是4.7K
(这个电阻模块上已经焊接上,无需关注)
注意:一线式总线接口的数据线DQ连接的CPU的引脚功能
为GPIO功能(输入或者输出)!
最后看DS18B20芯片手册,理清它的操作过程
目前为止应该有一个惯性思维:
1.一开始是研究S5P6818访问DS18B20,来获取到采集的温度值
2.通过连接图转为研究S5P6818访问数据线GPIOB10,将来S5P6818
通过这根数据线来获取到DS18B20采集的室内温度
3.具体CPU如何通过访问GPIOB10?关键看外设的手册的时序图
CPU只需根据外设的时序图的要求操作GPIOB10即可,即可获取温度
包围圈越来越小!
首先掌握DS18B20的硬件特性:
0.打开DS18B20芯片手册,DS18B20.pdf,掌握其相关的重要硬件信息如下:
1.DS18B20芯片内部集成了64bit容量的ROM(只读存储器),
存储每一个DS18B20唯一的序列码(类似身份证号)
所以:CPU将来要想访问某个DS18B20,只需通过ROM中的
序列码即可访问,也就是CPU只需向总线发送
对应的DS18B20的序列码即可访问某个外设
这也就解答了第一个问题!
序列码类似I2C总线的外设的设备地址
2.DS18B20同样芯片内部还集成了9字节容量的SRAM(不用初始化,直接访问)
DRAM(例如DDR)这类内存必须先初始化然后才能访问
SRAM的数据也会掉电丢失
这个SRAM用来当成DS18B20的片内寄存器,并且9字节的
SRAM的分配如下:
byte0:保存采集的温度的低字节
byte1:保存采集的温度的高字节
结论:温度值=byte1<<8|byte0
结论:研究对象转移:也就是CPU只需通过一根数据线
GPIOB10只要将DS18B20片内的SRAM中的byte0和byte1中的
数据读取上来即可,也就获取到了对应的采集温度
当然还有byte2....byte8,这里我们仅仅是为了
获取温度值,所需只需关注byte0和byte1即可,
类似CPU通过SDA和SCL两根信号线访问I2C外设片内寄存器一样!
马上再次提出问题:CPU如何通过一根数据线GPIOB10来
访问到byte0和byte1呢?
3.答:要想访问byte0和byte1,芯片手册说了,必须遵循以下三步骤P10:
1.第一步CPU向总线发送初始化复位信号,类似I2C的START信号,UART的起始位
2.第二步CPU向总线发送ROM命令,为了找到要访问的外设,类似I2C总线发送设备地址
3.第三步CPU向总线发送功能命令(一旦找到外设以后,下面就是读还是写还是其他功能)
紧接着分别对着三步骤进行一一说明
4.三步骤之"CPU向总线发送初始化复位信号",类似
I2C总线的START信号,UART的起始位
CPU发送初始化复位信号的时序图具体参见P15,
切记切记务必把这个时序图背下来,
并且将来面试一定要能画出来!
画完时序图以后,顺便写出或者说出相应的代码实现:
void ds18b20_reset(void)
{
int ret;
gpio_direction_output(PAD_GPIO_B+10, 0);//拉低
udelay(500); //持续500us的低电平
gpio_direction_output(PAD_GPIO_B+10, 1);//帮上拉电阻拉高
udelay(30);//持续30us高电平
gpio_direction_input(PAD_GPIO_B+10);//释放总线,交给DS18B20来控制
ret = gpio_get_value(PAD_GPIO_B+10); //类似获取I2C总线的ACK信号
if(ret==0)
设备存在
else
异常
}
5.三步骤之"CPU向总线发送ROM命令"
如果总线上连接了多个外设,CPU只需向总线发送
要访问的外设的序列码即可,类似I2C总线的设备地址的发送
由于X6818连接了1个DS18B20,无需匹配,CPU直接向
总线发送SKIP ROM命令,跳过匹配的过程,SKIP ROM的
命令字等于0xCC,也就是CPU只需通过一根数据线将
0xCC发送给DS18B20外设即可,问:CPU如何只需通过一根数据线将
0xCC发送出去呢,并且要保证对方能够接收到0xCC,
也就是保证数据同步!并且此时此刻没有时钟控制
信号线,没法低放高取,如何保证数据同步呢?
注意:DS18B20数据传输从低位开始!
如果不考虑数据的同步,CPU发送0xCC给外设,代码
及其简单:
void ds18b20_write8(unsigned char data)
{
//data = 0xCC(二进制11001100)
int i;
for (i = 0; i < 8; i++) {
if (data & 0x1) //写1
gpio_set_value(PAD_GPIO_B+10,1);
else //写0
gpio_set_value(PAD_GPIO_B+10,0);
data = data >> 1;
}
}//由于CPU的处理速度远远快于外设,CPU执行以上
代码的速度相当之快,外设势必无法及时处理!
要想让CPU和DS18B20进行数据同步,必须严格按照
DS18B20的操作时序图来进行,务必将涉及的四个
时序图画出来!P16,根据时序图,得到数据同步以后的
代码:
void ds18b20_write8(unsigned char data)
{
//data = 0xCC(11001100)
int i;
for (i = 0; i < 8; i++) {
if (data & 0x1) //写1{
gpio_set_value(PAD_GPIO_B+8,0);
udelay(3);
gpio_set_value(PAD_GPIO_B+8,1);//帮上拉电阻拉高
udelay(80);//在此期间,设备读取数据线的状态为高电平,完成写1操作
}else //写0 {
gpio_set_value(PAD_GPIO_B+8,0);
udelay(80);//设备读取GPIO状态,获取0,完成写0操作
gpio_set_value(PAD_GPIO_B+8,1);//帮上拉电阻拉高
udelay(3);
}
data = data >> 1;
}
}
同理,CPU从外设DS18B20读取数据,同样也是一个bit位
一个bit位的读,也涉及到读0和读1,根据相关的操作时序图务必会画相关数据同步的代码:
unsigned char ds18b20_read8(void)
{
unsigned char data = 0;
unsgined char ret = 0;
int i;
for (i = 0; i < 8; i++) {
gpio_direction_ouput(PAD_GPIO_B+8,0);//获取控制权
udelay(3);
gpio_direction_input(PAD_GPIO_B+8);//释放控制权交给外设或者上拉电阻
ret = gpio_get_value();//CPU获取数据线的状态,此时这个状态要不是外设给的0,要不是上拉电阻给的1
data |= ret << i;
}
return data;
}
6.三步骤之"CPU向总线发送功能性命令"
DS18B20功能性命令如下:
1.CONVET T命令,命令字等于0x44,此命令用于让DS18B20
启动温度的采集,采集的温度,DS18B20硬件上自动保存
温度值到SRAM的byte0和byte1中
例如:CPU发送0x44命令给外设:ds18b20_write8(0x44);
2.READ SRAM命令,命令字等于0xBE,此命令用于CPU从
DS18B20的SRAM中读取数据
例如:发送0xbe:ds18b20_write8(0xbe);
目前知道的读取温度的思路:
1.CPU向总线发送初始化复位信号
ds18b20_reset();
2.CPU向总线发送SKIP ROM命令0xCC,跳过匹配过程
ds18b20_write8(0xCC);
3.CPU向DS18B20先发送0x44,让他采集温度
ds18b20_write8(0x44);
4.CPU再向DS18B20发送0xBE,CPU读取采集的温度(位于SRAM的byte0和byte1中)
ds18b20_write8(0xbe);
问:虽然以上三步骤,都搞清楚,但是CPU读取采集的温度
整个流程还是稀里糊涂,接下来只需看硬件操作流程图即可P13
务必将以CPU读取温度为例,告诉考官整个操作读取温度流程:
以CPU读取温度,QT将来要显示温度值为例,具体的流程:
1.CPU向总线发送初始化复位信号
ds18b20_rest();
2.CPU向总线发送SKIP ROM命令0xCC,跳过匹配过程
ds18b20_write8(0xCC);
3.CPU先向总线发送温度采集命令0x44,让DS18B20进行
硬件的温度采集,采集温度自动保存在SRAM的byte0和byte1中
dsb18b20_write8(0x44);
4.CPU再向总线发送初始化复位信号
ds18b20_rest();
5.CPU向总线发送SKIP ROM命令0xCC,跳过匹配过程
ds18b20_write8(0xCC);
6.CPU向总线发送读取SRAM数据命令0xBE功能性命令
dsb18b20_write8(0xbe);
7.CPU开始从总线上读取byte0数据
tl = ds18b20_read8();
8.CPU继续从总线上读取byte1数据
th = ds18b20_read8();
9.CPU向总线发送复位信号,停止后面SRAM数据的读取
ds18b20_rest();
10.最后进行温度值的最终换算:
根据芯片手册换算出对应:
实际温度值=(th<<8|tl)*0.0625 //给人看的
th<<8|tl:给计算机看的,也是温度值
ADC:模拟信号转数字型号转换器
DAC:数字信号转模拟信号转换器
支持DS18B20硬件特性掌控完毕!
5.接下来就是设计软件的框架
务必画图展示软件的框架,参见ds18b20_sw.png
5.1.编写DS18B20硬件驱动
ds18b20_drv.rar
建议采用platform+GPIO库函数实现,面试时谈谈platform机制的特点,各种画图
5.2.编写DS18B20硬件操作库和测试用例
ds18b20_hwlib.rar
5.3.编写QT图形界面,定时采集温度并且显示
ehome_ds18b20.tar.bz2
要求:利用Qtimer定时器每隔5秒读取温度,并且QT界面显示
如果温度值高于某个阈值了,需要做出逻辑:
启动报警器并且让LED1告警指示灯每隔
500ms闪烁一次,并且QT界面有个按钮来回闪烁,提示用户温度过高!
提示:QThread
6.最后谈谈项目实施的心得
当我在调试DS18B20模块时,发现有时候温度读取不正常,
但是大部分时间是没有问题,于是乎尽心跟踪调试
拿示波器抓取数据线的时序波形,发现异常发生时,
实际的波形不符合芯片手册的时序图,比如延时要求
在15~60us,但是实际的波形要远远超过60us,说明
某个任务打断了这个延时,造成延时的加长,由于
这个模块是基于linux系统,所以想到了linux系统的
并发和竞态问题,考虑有可能有高优先级的进程或者
中断来打断了我的代码执行路径,于是乎利用相关的
linux内核解决竞态的方法来解决我的bug,首先我用
自旋锁来保护我的代码,我用自旋锁的目的就是屏蔽
进程之间的抢占,但是呢发现还是不能解决我的问题,
所以我的代码被打断不应该是进程的抢占,而应该
是中断,于是乎我用另一个方法中断屏蔽,发现可以解决我的
bug,然后我由尝试用衍生自旋锁(顺便讲讲衍生自旋锁的
特点)也能够解决我的bug,这个仅仅是我的其中的一个调试
心得,当然还有其他的