台湾燃太TN905模块是我使用的第三家公司做的红外测温产品,前两款是TI的TMP006和Melexis的MLX90614,TN905模块吸人的地方是它已经高度集成了各个模块,拿过来几乎就可以直接使用了,测量距离比较长,探头前面已经设计好了菲涅尔透镜,探头引脚出来也多了一些用于数据处理的电路,当然价格也相对比较昂贵。

TN905的测量物距比为5:1,TN901位1:1,一张图来解释什么是物距比(测量热源距离探头的距离与热源的直径的比值):

然后看看模块的引脚说明,一共有5个引脚,VCC和GND,然后是CLOCK和Data,最后一个引脚是ActionKey:

所以我只需要3个GPIO口和一根电源线以及一根地线就可以了,看时序图觉得程序应该很好写,数据手册上写的是SPI通信,但是具体和SPI通信协议还是有很大区别的,应该是他们家公司自定义的。数据包的格式也是事先定义好了的,你只需要读出数据,然后对数据加以判断就可以读出温度了。先看看时序图:

时序是不是很简单,当你觉得它很简单时,你应该注意了,但是它确实很简单。于是开始写驱动程序,我使用的STM32开发板来进行测试,使用了3个GPIO口,PC0接A,PC1接C,PC2接D。时钟信号周期500us应该是大概的,我用IO口模拟一下这个时钟信号就可以了,时钟信号先拉高,在将A脚拉低,准备读数据,然后拉低时钟产生一个下降沿开始读数据,后面就接着读,一种读满5个字节,然后延时大于0.1ms开始读下一个数据包,对数据加以处理就可以算出环境温度和目标温度了。

数据包的格式:

写好代码后就开始进行测试,测试后才发现读出来的数据没有一个数据包是正确的,几乎全是FF,于是开始反思是哪儿代码写错了,是SPI时序不对吗,SPI是不是上升沿读数据,于是改成上升沿读数据,结果好像没什么变化,再修改延时,也没用。思索半天无果,这时你就需要一台示波器了,连上示波器观察时钟和数据信号,看时钟信号,波形是很好的方波,和我软件模拟的一样,周期也是对的,但是数据几乎全是高电平,但是发现时钟会突然周期性的出现一个纹波,数据在这时改变,时钟和数据完全不同步,看到这里就恍然大悟了,原来我的开发板不是主机,这个模块才应该是主机,时钟信号被我强行用IO口改变了,TN905模块有一个自己的时钟,为2KHz,我的开发板应该设置成从机,按照它的时钟来读数据就可以了。于是修改程序,数据就正确了,数据包的头和尾都是正确的。

将代码贴上:

TN905.h

#ifndef __TN905_H
#define __TN905_H
#include "sys.h"//IO方向设置#define DATA_IN()  {GPIOC->CRL&=0XFFFFF0FF;GPIOB->CRL|=(u32)8<<8;}
#define DATA_OUT() {GPIOB->CRL&=0XFFFFF0FF;GPIOB->CRL|=(u32)3<<8;}//IO操作函数
#define SPI_CLK     PCout(1) //SCL
#define SPI_DATA    PCout(2) //SDA
#define READ_CLK    PCin(1) //SCL
#define READ_DATA   PCin(2)  //输入SDA
#define AK_TN9      PCout(0)  //输出ActionKey //初始化TN9IO口和检验和计算
void TN9_Init(void);
int Check_Sum(u8 Data[5]);#endif

TN905.c

#include "TN905.h"//初始化TN9IO口
void TN9_Init(void)
{                        GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOC, ENABLE ); //使能GPIOC时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;               //上拉输入GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure);GPIO_SetBits(GPIOC,GPIO_Pin_0);                                               //PC0输出高
}//校验和计算
int Check_Sum(u8 Data[5])
{u16 temp = 0;temp = Data[0] + Data[1] + Data[2];temp = temp & 0xff;if(temp == Data[3])return 1;else return 0;
}

main.c

<span style="font-size:14px;">#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "TN905.h"     #include "math.h"int main(void){ u16 Tdie_Temp;u16 Tobj_Read;float Tdie;float Tobj;u8 TN905_Read[5];u8 i,flag;u8 data_tmp=0;u8 DATA_NUM=0;u8 DATA_INDEX=0;int Tdie_Pot,Tobj_Pot;      //变量定义delay_init();          </span><span style="font-size:14px; white-space: pre;"> </span><span style="font-size:14px;">//延时初始化   LCD_Init();               //初始化LCD                TN9_Init();         //TN9初始化 while(1){POINT_COLOR=RED;//设置字体为红色 flag = 0;AK_TN9 = 1;delay_ms(10);AK_TN9 = 0;delay_ms(10);DATA_INDEX = 0;while(DATA_INDEX<5)               //读一个数据包(5个字节){if(!READ_CLK)</span><span style="font-size:14px; white-space: pre;">      </span><span style="font-size:14px;">//时钟的下降沿{if(flag==0){ flag=1;            DATA_NUM++;data_tmp<<=1;   if(READ_DATA)   //读数据一个bit{data_tmp=data_tmp|0x01;}else{data_tmp=data_tmp&0xfe;}  if(DATA_NUM==8){TN905_Read[DATA_INDEX]=data_tmp;DATA_NUM=0;DATA_INDEX++;data_tmp=0;}}}else{if(flag==1){flag=0;}}}</span><span style="font-size:14px; white-space: pre;">       </span><span style="font-size:14px;">//数据处理if(TN905_Read[0] == 0x4C)</span><span style="font-size:14px; white-space: pre;">     </span><span style="font-size:14px;">//第一个字节为4C表示读取的温度为目标温度{if(Check_Sum(TN905_Read)&(TN905_Read[4]==0x0d))     //检验校验和和结尾字节是否正确{LCD_ShowString(30,110,200,16,16,"The Obj temperature is:");Tobj_Read = TN905_Read[1]*256 + TN905_Read[2];</span><span style="font-size:14px; white-space: pre;">   </span><span style="font-size:14px;">//计算温度值Tobj = (float)Tobj_Read/16 - 273.15;LCD_ShowxNum(30,130,Tobj,8,16,0x00);Tobj_Pot = (int)(Tobj*10)%10;LCD_ShowString(94,130,200,16,16,".  C");LCD_ShowxNum(98,130,Tobj_Pot,1,16,0x00);Tobj = 0;}}else if(TN905_Read[0] == 0x66)</span><span style="font-size:14px; white-space: pre;">      </span><span style="font-size:14px;">//第一个字节为0x66则表示数据包文环境温度{if(Check_Sum(TN905_Read)&(TN905_Read[4]==0x0d))</span><span style="font-size:14px; white-space: pre;">     </span><span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size:12px;">//检验校验和和结尾字节是否正确</span></span><span style="font-size:14px; white-space: pre;">
</span><span style="font-size:14px;">                {LCD_ShowString(30,150,200,16,16,"The Amb temperature is:");Tdie_Temp = TN905_Read[1]*256 + TN905_Read[2];<span style="white-space:pre">       </span>//温度计算Tdie = (float)Tdie_Temp/16 - 273.15;LCD_ShowxNum(30,170,Tdie,8,16,0x00);Tdie_Pot = (int)(Tdie*10)%10;<span style="white-space:pre">           </span>//保留小数点后一位LCD_ShowString(94,170,200,16,16,".  C");LCD_ShowxNum(98,170,Tdie_Pot,1,16,0x00);Tdie = 0;}}<span style="white-space:pre">    </span> }}
</span>

发射率默认设置为0.95,修改发射率就是往EEPROM里面写数据,数据包的格式数据手册上也写了,由于我并没有修改发射率,所以那段代码就没写了。最后说一下测试的效果,首先说一下优点,距离长,TN905能测的距离很长,测量我手心的温度至少可以达到40CM,也就是距离40CM测量都没有问题,要知道普通的10CM都达不到,所以在距离方面确实有很大的优势。然后就是测量热气流的温度,对准我的笔记本散热口,测量到的温度为40多到50度,算是正常,效果不错。再试一下用热吹风吹一下,这一吹就有问题了,读数和TI的TMP006一样,很快目标温度就变成0了。环境温度读数逐渐上升,应该是正常的。另外,如果我用手去捂热传感器探头,并没有遮挡探头接收红外光,理论上此时测得的目标温度应该是环境温度25℃左右,但是此时目标温度的读数确是50多度,很明显不正确。关于此的疑问,我专门问了燃太公司的技术人员,给我的回复是当环境温度剧烈变化时会造成目标温度读数不正确,如果用手加热探头外壁,不仅探头温度上升,探头前面的菲涅尔透镜也会被加热,所以测得的温度就会不准确了。另外,对于两个数据包之间的延时也会有相对较高的要求,数据手册上说的是大于0.1sec,但是过大读取的数据包只有目标温度而没有环境温度,没有延时读出的数据几乎全是错的,我程序用的延时大约几个毫秒。最后想吐槽一下数据手册,比如这个模块作为SPI主机应该在数据手册上说明一下,只能说这个模块有点特别吧。

最后附上TN905官网资料:点击打开链接

台湾燃太TN905/TN901模块的使用及驱动程序相关推荐

  1. 燃太TN901红外测温模块LCD1602显示

    山远的博客 学以致用 首页 旧版 首页 标签 留言本 单片机 文学 趣闻 美图 C#编程 燃太TN901红外测温模块LCD1602显示 燃太TN901红外测温模块LCD1602显示,可设置高温低温报警 ...

  2. 台湾Cirocomm太盟光电车载导航天线定位天线 GT5-1S头GPS通用天线带增强反射板

    全新原装正品,台湾原厂制造,顶级GT5-1S日本HRS专利接头,线长5米,有源增益天线,内有芯片,此天线有磁吸,带增强信号的反射板. 适合车型: TOYOTA(丰田原厂车载DVD导航) Alpine( ...

  3. 解决 Vue项目——node.modules文件太大问题(模块瘦身完整版)

    自己在做公司小项目的时候,发现文件node.modules文件夹占用很多内存,去网上搜索了一些解决办法,结合自己的实践,把这个方法再次更新整合一下 1.找到你所在的项目,打开控制台(VScode按 C ...

  4. Android 8.0 linux内核,在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序---Android8.0版本实现-对照老罗版本...

    老罗版本参见:https://blog.csdn.net/luoshengyang/article/details/6573809 在Android硬件抽象层(HAL)概要介绍和学习计划一文中,我们简 ...

  5. android驱动开发 老罗,在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序(老罗学习笔记3)...

    在Android硬件抽象层(HAL)概要介绍和学习计划一文中,我们简要介绍了在Android系统为为硬件编写驱动程序的方法.简单来说,硬件驱动程序一方面分布在Linux内核中,另一方面分布在用户空间的 ...

  6. [嵌入式开发模块]DS3231时钟芯片 驱动程序

    刚刚写完DS3231时钟芯片的驱动程序.这里开源出来供大家使用.需要的自取. 先简单介绍下这个芯片,然后直接给驱动代码以及示例代码. DS3231简介 简介 DS3231是一款低成本.高精度I2C实时 ...

  7. 鸟哥的Linux私房菜(基础篇)- 第二十章、启动流程、模块管理与 Loader

    第二十章.启动流程.模块管理与 Loader 最近升级日期:2009/09/14 系统启动其实是一项非常复杂的程序,因为核心得要侦测硬件并加载适当的驱动程序后,接下来则必须要呼叫程序来准备好系统运行的 ...

  8. swift 组件化_打造完备的iOS组件化方案:如何面向接口进行模块解耦?

    作者 | 黑超熊猫zuik,一个修行中的 iOS 开发,喜欢搞点别人没搞过的东西,钻研过逆向工程.VIPER 架构和组件化. 关于组件化的探讨已经有不少了,在之前的文章 iOS VIPER架构实践(三 ...

  9. Python模块包中__init__.py文件的作用(转载)

    用django做开发已经一年多的时间,但基本没注意python模块中__init__.py文件存在的意义,偶然看到对它的介绍吓一大跳,这个文件太重要了 模块包: 包通常总是一个目录,目录下为首的一个文 ...

最新文章

  1. PHP-redis中文文档
  2. linux 使用退格键时出现^H解决方法
  3. [BZOJ1026] [SCOI2009] windy数 (数位dp)
  4. 一个程序员的水平能差到什么程度
  5. 信息学奥赛一本通(1312:【例3.4】昆虫繁殖)
  6. Kotlin学习笔记 第四章 Java调用Kotlin
  7. java捕鱼达人源码_捕鱼达人java源码(完整功能)
  8. 关于icon小图标的实现
  9. 使用QPainter绘制饼状图,并显示所占百分比
  10. 你们要的炒股软件来啦,同花顺等 6 款工具类软件上架优麒麟软件商店
  11. 【单片机】51单片机使用总结
  12. 高通WLAN框架学习(27)-- Types of regulatory 和WCNSS_qcom_cfg.ini配置参数
  13. crontab——定时周期性执行任务
  14. 列表的join方法,类方法formkeys,删除,集合,深浅拷贝赋值,冒泡排序
  15. 浏览器同源政策之ajax请求不能发送
  16. 宿骆氏亭寄怀崔雍崔衮
  17. 用canvas绘制三角形
  18. 头条系 巨量算数 _signature
  19. java去除图片水印的解决办法
  20. Grafana系列教程–Grafana基本概念

热门文章

  1. python 实现股票MACD计算
  2. 网页审查元素css乱码,CSDN网页打印乱码重叠解决办法
  3. 位运算符之无符号右移(十进制整数)
  4. 这是一个普通的WIN32扫雷程序
  5. Nowa Flutter开发教程之 07 Nowa 可视化编程
  6. 【什么是长尾关键词(Long Tail Keyword)】
  7. AD导出Gerber
  8. 174. Dungeon Game(H)
  9. 标准曲线制作、jiance检验、使用知识大全
  10. 以太坊源码深入分析(10)-- 以太坊Bloom过滤器实现原理及应用场景分析