完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
最近要用52单片机读取PT100温度探头和K型热电偶的温度,在设计并焊接完硬件电路之后,发现用C语言读取具体的温度是个麻烦事。具体来说就是不知道怎么用C语言根据芯片的时序图编写驱动,所以也就不知道怎么读取AD芯片和Max6675芯片的数据。要完成这个工作,实际上要做一件横跨硬件电路设计、制作、驱动编写、纯C软件编写的工程,更进一步的话还需要涉及到PCB板的设计,对于我这个硬件新手来说有不小的难度。在完成这些工作之后 (没有设计PCB板),我特意写这篇文章,用来描述根据时序图写C语言驱动的流程,作为对这份工作的纪念,也希望能为后来者提供一点帮助。
对于PT100温度探头,我采用桥式电路检测探头的电阻,电阻信息被转化成电压信号,并用OP07运算放大器对电压信号进行放大。OP07的输出电压用ADS7822芯片进行模数转换。由于本文只涉及C语言驱动的编写,所以这里不贴具体的电路图了。下面是ADS7822工作的时序图: 其中CS是片选,当CS从1跳变为0时,ADS7822开始工作。 DCLOCK是时钟,默认有可能是0或1。 Dout是ADS7822的输出结果。 初始化: 由于在CS=0之后,DCLOCK从0跳变到1就意味着开始计数了。所以一定要在CS设置为0之前先把DCLOCK设置为0。这一点从下表中Tcsd的描述:CS falling to DCLOCK low也可以看出来。 下面三行是ADS7822的初始化 //ad_cs = 1; //驱动代码最后就是ad_cs = 1,所以这里可以省略这个语句 ad_dclock = 0; //时序图上ad_dout=1,但实际上ad_dout是硬件自己决定变化的,而不是用C语言决定的 //ad_dout = 1; 开始工作: CS = 0表示ADS7822开始工作。 之后DCLOCK经历2次脉冲 (从低电平跳转到高电平,并保持到高电平结束),这期间Dout保持高电平。 DCLOCK经历1次脉冲,Dout输出一个无效位。 用代码表示: ad_cs = 0;//启动AD转换 ad_dclock = 1; ad_dclock = 0; ad_dclock = 1; ad_dclock = 0; ad_dclock = 1; DCLOCK从高电平变成低电平,完成这个语句后,Dout正处于一个稳定的有效数据位,单片机可以读取数据。DCLOCK从低电平变成高电平时,Dout的有效数据结束,即将输出下一个数据。用代码表示: ad_dclock = 0; j = (uint)(ad_dout); //读取数据 ad_dclock = 1; 这个过程一共要进行12个循环,输出12位有效数据。这里有一个问题:最先输出的一位数据是最高位还是最低位?关于这个问题,需要参考我的前一篇文章:查找算法:逐次逼近。根据这篇文章,我们可以知道,第一个输出的数据能表示2048,第二个输出数据表示1024,第三个输出数据表示512…,最后一个输出数据表示1。由此,我们可以知道最先输出的一位数据是最高位。因此,每循环一次,原有数据就要左移一位。所以这12轮的循环用C语言可以表示为: result = 0; for(i = 0; i <= 11; i++) //读取采样数据 { //时钟低电平的时候,dout处于稳定的状态,这个时候可以读取dout ad_dclock = 0; j = (uint)(ad_dout); //读取数据,并转化成uint类型 result = result << 1; //原有数据左移一位 result = result | j; //数据移位后与前面数据相或 ad_dclock = 1; //时钟高电平。准备输出下一位数据 } 结束工作: CS拉回高电平,恢复初始状态。即: ad_cs = 1; 返回值: 得到的数据一共有2^12= 4096种可能。如果ADS7822的参考电压为2.5V,则每一份表示2.5/4096V。假设二进制结果为1111101000,换成十进制为1000,就表示1000 * 2.5 / 4096 = 0.6104V。 所以读取ADS7822的总体代码为: uint readADS7822() { uint result, j; //初始化 //ad_cs = 1; //驱动代码最后就是ad_cs = 1,所以这里可以省略这个语句 ad_dclock = 1; //时序图上ad_dout=1,但实际上ad_dout是硬件自己决定变化的, //ad_dout = 1; ad_cs = 0;//启动AD转换 ad_dclock = 1; ad_dclock = 0; ad_dclock = 1; ad_dclock = 0; ad_dclock = 1; //工作状态 result = 0; for(i = 0; i <= 11; i++) //读取采样数据 { //时钟低电平的时候,dout处于稳定的状态,这个时候可以读取dout ad_dclock = 0; j = (uint)(ad_dout); //读取数据,并转化成uint类型 result = result << 1; //原有数据左移一位 result = result | j; //数据移位后与前面数据相或 ad_dclock = 1; //时钟高电平。准备输出下一位数据 } //结束工作: ad_cs = 1; return result; //返回值,每一份表示2.5/4096V。 } 上述代码虽然能实现读取ADS7822的功能,但是代码比较凌乱。我们需要一个比较好的逻辑:依次进行初始化、完整的读取数据、结束读取、处理数据、返回等5个部分。而不是直接忽略掉部分无效数字,另外我们再删掉一些重复的代码,就可以得到最终的驱动程序。 //这是ADS7822的C语言驱动程序 unsigned int read_ADS7822() { int i;//定义数据读取次数的变量 uint j;//定义采样数据暂存单元 uint result; //定义采样数据储存单元,16位数据 //初始化 ad_dclock = 1; //开始采集数据 ad_dclock = 0; //ad_dclock在CS之前设置为低电平 ad_cs = 0; //启动AD转换 for(i = 0; i < 15; i++) //读取采样数据,一共15个循环 { ad_dclock = 0; //送读取脉冲 j = (uint)(ad_dout); //读取数据 result = result << 1; //总数据移位 result = result | j; //总数据移位后与前面数据相或 ad_dclock = 1; } //读取数据完毕,恢复初始化工作 ad_cs = 1; //一次采样完成,片选设置为高电平 //处理数据 //16位的数据,最高位为0,接着三位没有意义,剩下12位是有效数字 //所以要用下面两行代码除去最高的4位数据。 result = result << 4; result = result >> 4; return result; //返回采样值,result个2.5/4096V } 通过ADS7822读取到放大运算器的输出电压后,我们就可以用电压值推算出PT100的电阻值。接着根据电阻值去查表,就可以得到当前的温度了 (当然,这个过程也是软件完成的)。将这个温度与煤油温度计进行对比,发现精度较高,误差 < 1℃。 第二个例子是用K型热电偶测温。 我买的K型热电偶测温用MAX6675芯片驱动,MAX6675芯片通过5根线与单片机连接。MAX6675芯片已经帮我们进行了温度补偿,所以,我们可以读取MAX6675芯片获得温度值。 MAX6675芯片的时序图如下: 说明: CS是片选,CS从1跳变为0表示MAX6675芯片开始工作。 SCK表示时钟 SO输出数据。 初始化: 由于CS从1跳变为0表示MAX6675芯片开始工作,所以在这之前,要把SCK设置为0。代码为: SCK = 0;//由于最后需要把SCK设置为低电平,所以这个语句可以不要。 工作状态: CS从1跳变为0表示MAX6675芯片开始工作。 SCK变成高电平时,SO处于有效数字,这时可以用单片机读取数据。读取完之后,SCK设置为0,完成一个时钟脉冲。代码如下: SCK = 1; j = SO; SCK=0; MAX6675芯片一共要输出16位数据,也就是要循环16次。代码如下: for(i = 0; i < 16; i++) { SCK = 1; //SCK为高电平时,输出一位数据 dat = dat<<1; //读取一位数据,默认读到的是0 if( SO==1 ) dat = dat|0x01; //如果SO是高电平,则把读取到的一位数据设置为1 SCK = 0; //得到一位数据后重新把SCK拉低 } MAX6675芯片的输出的16位数据中,D15是无效位;D2=1表示K型热电偶不在线,D1是MAX6675芯片的标识位,D0是无效位。所以,只有D14~D3这12位是有效数据。根据这些信息,我们继续对得到的信息进行处理: 1、去除dat的最高位: dat = dat << 1; dat = dat >> 1; 其中16位的数据左移一位后,得到的仍旧是16位数据,只不过最高位溢出 (丢失)。 2、去除dat的最低三位: dat = dat >> 3; 数据右移三位,得到的仍旧是16位数据,只不过最低三位溢出 (丢失)。 合并这三条代码: dat = dat << 1; dat = dat >> 4; 最后工作: SCK设置为低电平,其实这里没有必要。 CS设置为高电平。 代码为: CS = 1; 总的代码为: unsigned int MAX6675_ReadReg(void) { unsigned char i; //循环变量 unsigned int dat; //16位的正数据,存放从Max6675中得到的数据 CS = 0; //片选,低电平有效。Max6675开始工作 SCK = 0; //开始检测温度 //时序图上要操作16次,但只有12位有效数据 for(i = 0; i < 16; i++) { SCK = 1; //SCK为高电平时,输出一位数据 dat = dat<<1; //读取一位数据,默认读到的是0 if( SO==1 ) dat = dat|0x01; //如果SO是高电平,则把读取到的一位数据设置为1 SCK = 0; //得到一位数据后重新把SCK拉低 } CS = 1; //读取数据完成,重新把片选设置为1。 //16位的数据左移一位之后,数据仍然是16位,但是最高位溢出 dat = dat << 1; //略去dat的最高位,这一位始终无效 //后三位也没有用。但由于刚刚左移了一位,所以要右移4位才能删除原先的低三位 //D2 = 1表示热电偶端口;D1是热电偶的标识位,D0无效 dat = dat >> 4; return dat; //返回有效数字,单位为0.25度 } MAX6675芯片得到的是12位数据,精度为0.25度,所以返回的数据是dat个0.25度。可以用result * 0.25表示最终的温度。实测检测结果与煤油温度计的读数很接近,误差 < 1℃。 注:读取MAX6675芯片的程序是厂家赠送的。 总结: 对于 (简单的) 时序图来说,最重要的是弄清初始状态,以及什么时候读取数据。另外,芯片得到的数据有时不是每一位都是有效的,需要根据datasheet中的提示,酌情删除无效位,才能最终得到有效数据。一定要搞清楚从芯片中得到的数据代表了什么,怎样把这些数据转化成自己真正需要的数据。 成本:数字温度传感器DS18B20 << K型热电偶 < PT100 < PT1000。 温度检测范围:K型热电偶 > PT100 > PT1000 > DS18B20。 其中K型热电偶的检测范围为:0~1024℃;PT100和PT1000可以检测0℃以下的温度;PT1000最高可以达到0.1℃的精度。 |
|
|
|
只有小组成员才能发言,加入小组>>
3209 浏览 9 评论
2896 浏览 16 评论
3402 浏览 1 评论
8844 浏览 16 评论
3997 浏览 18 评论
9627浏览 3评论
1004浏览 3评论
520浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
523浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2250浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-9-30 18:01 , Processed in 0.763829 second(s), Total 47, Slave 38 queries .
Powered by 电子发烧友网
© 2015 www.ws-dc.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号