台湾燃太TN905/TN901模块的使用及驱动程序
台湾燃太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模块的使用及驱动程序相关推荐
- 燃太TN901红外测温模块LCD1602显示
山远的博客 学以致用 首页 旧版 首页 标签 留言本 单片机 文学 趣闻 美图 C#编程 燃太TN901红外测温模块LCD1602显示 燃太TN901红外测温模块LCD1602显示,可设置高温低温报警 ...
- 台湾Cirocomm太盟光电车载导航天线定位天线 GT5-1S头GPS通用天线带增强反射板
全新原装正品,台湾原厂制造,顶级GT5-1S日本HRS专利接头,线长5米,有源增益天线,内有芯片,此天线有磁吸,带增强信号的反射板. 适合车型: TOYOTA(丰田原厂车载DVD导航) Alpine( ...
- 解决 Vue项目——node.modules文件太大问题(模块瘦身完整版)
自己在做公司小项目的时候,发现文件node.modules文件夹占用很多内存,去网上搜索了一些解决办法,结合自己的实践,把这个方法再次更新整合一下 1.找到你所在的项目,打开控制台(VScode按 C ...
- Android 8.0 linux内核,在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序---Android8.0版本实现-对照老罗版本...
老罗版本参见:https://blog.csdn.net/luoshengyang/article/details/6573809 在Android硬件抽象层(HAL)概要介绍和学习计划一文中,我们简 ...
- android驱动开发 老罗,在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序(老罗学习笔记3)...
在Android硬件抽象层(HAL)概要介绍和学习计划一文中,我们简要介绍了在Android系统为为硬件编写驱动程序的方法.简单来说,硬件驱动程序一方面分布在Linux内核中,另一方面分布在用户空间的 ...
- [嵌入式开发模块]DS3231时钟芯片 驱动程序
刚刚写完DS3231时钟芯片的驱动程序.这里开源出来供大家使用.需要的自取. 先简单介绍下这个芯片,然后直接给驱动代码以及示例代码. DS3231简介 简介 DS3231是一款低成本.高精度I2C实时 ...
- 鸟哥的Linux私房菜(基础篇)- 第二十章、启动流程、模块管理与 Loader
第二十章.启动流程.模块管理与 Loader 最近升级日期:2009/09/14 系统启动其实是一项非常复杂的程序,因为核心得要侦测硬件并加载适当的驱动程序后,接下来则必须要呼叫程序来准备好系统运行的 ...
- swift 组件化_打造完备的iOS组件化方案:如何面向接口进行模块解耦?
作者 | 黑超熊猫zuik,一个修行中的 iOS 开发,喜欢搞点别人没搞过的东西,钻研过逆向工程.VIPER 架构和组件化. 关于组件化的探讨已经有不少了,在之前的文章 iOS VIPER架构实践(三 ...
- Python模块包中__init__.py文件的作用(转载)
用django做开发已经一年多的时间,但基本没注意python模块中__init__.py文件存在的意义,偶然看到对它的介绍吓一大跳,这个文件太重要了 模块包: 包通常总是一个目录,目录下为首的一个文 ...
最新文章
- PHP-redis中文文档
- linux 使用退格键时出现^H解决方法
- [BZOJ1026] [SCOI2009] windy数 (数位dp)
- 一个程序员的水平能差到什么程度
- 信息学奥赛一本通(1312:【例3.4】昆虫繁殖)
- Kotlin学习笔记 第四章 Java调用Kotlin
- java捕鱼达人源码_捕鱼达人java源码(完整功能)
- 关于icon小图标的实现
- 使用QPainter绘制饼状图,并显示所占百分比
- 你们要的炒股软件来啦,同花顺等 6 款工具类软件上架优麒麟软件商店
- 【单片机】51单片机使用总结
- 高通WLAN框架学习(27)-- Types of regulatory 和WCNSS_qcom_cfg.ini配置参数
- crontab——定时周期性执行任务
- 列表的join方法,类方法formkeys,删除,集合,深浅拷贝赋值,冒泡排序
- 浏览器同源政策之ajax请求不能发送
- 宿骆氏亭寄怀崔雍崔衮
- 用canvas绘制三角形
- 头条系 巨量算数 _signature
- java去除图片水印的解决办法
- Grafana系列教程–Grafana基本概念