分类 硬件技术 下的文章

我用ArduinoProMicro_SSD1351_MLX90614整红外测温计

我用ArduinoProMicro_SSD1351_MLX90614整红外测温计

前言:

最初我想玩屏,于是从某网友手里买了个分辨率128*96的RGB OLED屏,然后尝试通过网友提供的C51示例代码适配到ArduinoProMicro,学习了一下SPI驱动彩屏。然后买了个MLX90614挂上,配合着前面的硬件学习了一下I2C,说起来MLX90614是SMBus协议,和I2C还有点区别。目前程序实现了读写MLX90614,主函数仅实现了隔三秒读取温度显示一次。整个项目的代码已经上传到GitHub,目前的状态只能算是个硬件测试程序,距离实用还差的远。初学者朋友有兴趣的可以Fork玩一下,也欢迎大佬指导改进。

项目地址:

Github:https://github.com/LexsionLee/Thermometer_MLX90614_SSD1351.git

效果图片:

A01.jpg

A02.jpg

硬件:

MCU:ATMEGA32U4
Board:ProMicro (Arduino Leonardo)

OLED Driver IC:SSD1351

OLED Power IC:ASM1117 3V3

温度传感器:MLX90614

引脚使用:

命名 IO口 功能
OLED_CLK D15 SCL
OLED_DIN D16 SDIN
OLED_RES D10 RST
OLED_DC D9 D/C
OLED_CS D8 /CS

CS为 低电平有效

功能 IO口
MLX90614_I2C_SDA D2
MLX90614_I2C_SCL D3

实现:

我选取了硬件SPI的IO口用于驱动屏幕,虽然网友提供的代码是软件模拟SPI。这种选择源于后期使用硬件SPI的可能性的考虑。事实证明,软件模拟的SPI速度太慢,对于这种彩色OLED屏来说,刷屏速度无法接受,能明显看到刷屏动作,体检极差。最终重写了部分不支持C++编译的代码。换用了硬件SPI的方式。这样果然快多了,画面瞬间刷完。

我用取模软件对数字显示字体进行了取模,推荐字体如下:

Lucida Console
MS Gothic
Rockwell
Rockwell Conden

I2C软件模拟部分,分别对通信协议中基础的片段进行了模拟,然后分层次拼接实现具体的读写功能。与某些I2C通信不同,这个SMBus协议中传输了一位PEC数据,用来校验传输的数据是否正确。我们读取时可以忽略,但是尝试写入时必然要发送PEC的。PEC的值是用本次读写操作中所有数据连在一起通过CRC-8校验得出的。比如写EEPROM时发送的PEC是由从机地址、欲写入的寄存器地址、数据低8位、数据高8位这些数据经过CRC-8校验得出。具体算法没弄懂,索性抄了网友的PEC生成函数,在PC上写了个临时程序验证OK,就这样用了。

手册(Datasheet)中给出的各种地址是需要拼上一个所谓的Opcode的,RAM部分的拼的是0,EEPROM拼的是001X XXXX。具体参考8.4.5. Commands。

EEPROM地址EMISS包含发射率参数(工厂默认1.0=0x FFFF),这是个16位数值。计算公式为:Emissivity = dec2hex[ round( 65535 x ε) ],其实就是65535乘以发射率(范围0.1~1.0),然后得出的结果转换为16进制值。修改EMISS需要先向该地址写0x0000,然后再将新的值写入。

实时的红外测温数据从TOBJ中读出,数据格式为4位16进制值,最高位为错误标记位。我使用的版本只有TOBJ1,所以我程序中读取的TOBJ1的数据。该数据乘以0.02得出的值即为当前红外传感器测得的绝对温度,即开氏温度。众所周知,此温度减去273.15即为摄氏温度。传感器量程能报告的最高温度为382.19℃(0x7FFF),如果报告数据最高位(MSB)为1(0x8XXX)则表示数据有错误,程序上需要做对应处理(目前程序中没有对此做处理)。另外我们可以从TA中读出传感器本身的当前温度数据(线性输出极限范围-38.2~+125℃),处理方式同上。

最后我们将得到的温度数据输出到屏幕上显示即可。

踩坑记录:

因为博主技术太菜,所以会遇到一些低级错误,记下来防止再犯错误!

In function XXX :XXX.cpp:XXXXX :"undefined reference to XXX"

Arduino(AVR) 使用的是C++语言,它使用的编译器是g++ 。在编译时,.ino文件会被改成.cpp,作为C++文件编译。我们认为C++是C的超集,如此看来,我们使用C的语法来写程序应当是可行的。但如果我们的项目由多个.c和.h文件构成且其他.c与.h文件中有函数在.ino文件中调用时,即c文件和cpp文件混编的情况,编译器便会报错。编译器会报告ino文件中调用的函数没有定义,即:In function XXX :XXX.cpp:XXXXX :"undefined reference to XXX" 。具体什么原因,这里不详细展开。这种情况,在合适位置加上extern "C"即可。具体如下:

#ifndef _XXX_H_
#define _XXX_H_

#ifdef __cplusplus
extern "C" {
#endif

void afunctionA(void);
void afunctionB(void);

#ifdef __cplusplus
}
#endif

error: old-style parameter declarations in prototyped function definition

编译项目的时候,提示“old-style parameter declarations in prototyped function definition”。看函数写的也没啥问题,这个项目之前是能够编译通过的,今天改了几段代码,增加了几个函数,然后编译才出现的错误。百度搜索了一下,居然是头文件中这个函数声明时少打了个分号!!!

unknown type name 'class'; did you mean 'labs'

Arduino编译时遇到unknown type name 'class'; did you mean 'labs'这个错误,一番查资料才明白,C++和C混编导致此问题。C文件中调用了C++的函数,而C文件编译时调用的C编译器不支持C++中的类。一般可以把.c文件重命名为.cpp解决。但需要处理C文件中不符合C++语言的代码。

#error: duplicate 'unsigned'

在将C文件更名为.cpp文件后编译出现此错误,是因为 #define u8 unsigned char 造成的。这是写C51时的宏定义习惯,大家都知道,这么写就不需要每次都写那么长的一串了。百度搜索了一下,据说在C++编译器中这种写法不规范,应当使用 typedef unsigned char u8; 来定义类型。我也不知道他说的是否准确、严谨,但是我尝试使用typedef的方式替代之前的写法,再次尝试编译果然可以了。题外话,Arduino的代码库中已经有定义了u16类型,我自己写的就把u16写成了uu16来规避这个问题。