Bootstrap

一分钟教你自制家用红外热成像仪(附详细代码)

一、选摄像头

1、海康 TB-4117-3/S 像素较高,无奈鄙人囊中羞涩。

2、MLX90640-BAA 视场角度110*75度,角度大,适合近距离测量。

3、MLX90640-BAB 视场角度55*35度,角度小,适合远距离测量。

二、选芯片

1、STM32F103C8T6 这颗芯片无需多言,男女老少都爱用。

2、GD32F470ZGT6 这颗芯片整体性能提高了一个档次,且学生可免费申请。

三、选屏幕

ST7789V3

1、屏幕没有太多的忌口,选择自己喜欢的就行。我选择的是1.47寸 LCD SPI通信 显示屏,驱动芯片为ST7789V3,以上两款的引脚扇出方式略有差异,可自行选择。

四、接线

1、方案搭配:MLX90640-BAB + GD32F470ZGT6 + ST7789V3。

2、如下是摄像头与开发板的引脚对应:

摄像头开发板
VIN3V3
GNDGND
时钟引脚 SCLPB6
数据引脚 SDAPB7

3、如下是屏幕与开发板的引脚对应:

屏幕开发板
VCC3V3
GNDGND
时钟引脚 CLK(SCL)PB13
数据引脚 DIN(SDA)PB15
片选引脚 CSPB12
数据/命令控制引脚 DCPC6
复位引脚 RST(RES)低有效,可悬空
背光控制引脚 BL(BLK)默认打开,可悬空

4、使用导电率 ≥ 61.0% IACS 的物体连接上述对应引脚。

五、炫代码

1、驱动下载:

2、软件 Keil uVision5  开!

主函数:

int main(void)
{
	uint16_t state;			        // 摄像头采集准备就绪-->状态位
	LCD_Init();				        // 屏幕初始化
	mlx90640_init();                // 摄像头初始化

	while (1)
	{
		MLX90640_I2CRead(MLX90640_ADDR, 0x8000, 1, &state);    //读取状态位
		if (state & 0x0008)    //如果就绪
		{
			Temp2RGB();							    // 1、将温度数据转化为RGB数据
			LCD_ShowPicture(0, 0, 224, 168, rgb8);  // 2、设置图片显示位置、大小 
            Serial_SendPacket();                    // 3、发送给屏幕
            
            //显示数据
            LCD_ShowString(230, 0, "Max:", RED, BLACK, 16, 0);
            LCD_ShowFloatNum1(270, 0, max_temp, 4, RED, BLACK, 16);//显示最大温度值
            LCD_ShowString(230, 50, "Min:", BLUE, BLACK, 16, 0);
            LCD_ShowFloatNum1(270, 50, min_temp, 4, BLUE, BLACK, 16);//显示最小温度值
            LCD_ShowString(230, 100, "Ta:", GREEN, BLACK, 16, 0);
            LCD_ShowFloatNum1(270, 100, Ta, 4, GREEN, BLACK, 16);//显示环境温度
            LCD_ShowString(230, 150, "VDD:", YELLOW, BLACK, 16, 0);
            LCD_ShowFloatNum1(270, 150, VDD, 4, YELLOW, BLACK, 16);//显示供电电压
		}
	}
}

数据转化函数:

// 将温度数据转化成RGB数据
void Temp2RGB(void)
{
	// 1、调用官方提供的函数计算温度
	int status = MLX90640_GetFrameData(MLX90640_ADDR, Frame);    // 从摄像头获取一帧数据
	VDD = MLX90640_GetVdd(Frame, &MLXPars);                      // 读供电电压
	Ta = MLX90640_GetTa(Frame, &MLXPars);                        // 读环境温度
	MLX90640_CalculateTo(Frame, &MLXPars, 0.95, Ta - 8, Temp);   // 计算温度

	// 2、找出最小和最大温度及其位置
	max_temp = -40;
	min_temp = 300;
	for (uint16_t x = 0; x < 768; x++)
	{
		if (Temp[x] > max_temp)
		{
			max_temp = Temp[x];
			max_temp_pos = x;
		}
		if (Temp[x] < min_temp)
		{
			min_temp = Temp[x];
			min_temp_pos = x;
		}
	}

	// 3、温度值-->灰度值
	float k = 255 / (max_temp - min_temp); // 线性映射系数
	for (uint16_t i = 0; i < 768; i++)
	{
		gray[i] = (Temp[i] - min_temp) * k;
	}

	// 4、对24*32灰度图像进行"双线性插值"放大7倍到168*224
	int srcWidth = 32;
	int srcHeight = 24;
	int targetWidth = 224;
	int targetHeight = 168;

	for (int i = 0; i < targetHeight; i++)
	{
		for (int j = 0; j < targetWidth; j++)
		{
			float x = (float)j / targetWidth * srcWidth;
			float y = (float)i / targetHeight * srcHeight;

			int x1 = (int)x;
			int y1 = (int)y;
			int x2 = x1 < srcWidth - 1 ? x1 + 1 : x1;
			int y2 = y1 < srcHeight - 1 ? y1 + 1 : y1;

			float dx = x - x1;
			float dy = y - y1;

			unsigned char q11 = gray[y1 * srcWidth + x1];
			unsigned char q21 = gray[y1 * srcWidth + x2];
			unsigned char q12 = gray[y2 * srcWidth + x1];
			unsigned char q22 = gray[y2 * srcWidth + x2];

			gray_bi[i * targetWidth + j] = (unsigned char)(q11 * (1 - dx) * (1 - dy) + q21 * dx * (1 - dy) + q12 * (1 - dx) * dy + q22 * dx * dy);
		}
	}

	// 5、双插灰度值-->8位RGB伪彩色-->16位RGB565
	for (uint32_t i = 0; i < 37632; i++)
	{
        float R, G, B;
        if( ((float)gray_bi[i]>=0) && (gray_bi[i]<=63) )  
        {
            R=0;
            G=round(((float)gray_bi[i]-0)/64*255);
            B=255;
        }
        else if( (gray_bi[i]>=64) && (gray_bi[i]<=95) )  
        {
            R=0;
            G=255;
            B=round((95-(float)gray_bi[i])/32*255);
        }
        else if( (gray_bi[i]>=96) && (gray_bi[i]<=127) )  
        {
            R=round(((float)gray_bi[i]-96)/32*255);
            G=255;
            B=0;
        }
        else if( (gray_bi[i]>=128) && (gray_bi[i]<=191) )  
        {
            R=255;
            G=round((191-(float)gray_bi[i])/64*255);
            B=0;
        }
        else if( (gray_bi[i]>=192) && (gray_bi[i]<=255) )  
        {
            R=255;
            G=round(((float)gray_bi[i]-192)/64*255);
            B=round(((float)gray_bi[i]-192)/64*255);
        }

		uint16_t rbits = (R * 0.125f);
		uint16_t gbits = (G * 0.250f);
		uint16_t bbits = (B * 0.125f);
		gray_bi[i] = (rbits << 11) | (gbits << 5) | bbits;
	}

	// 6、将16位RGB565分为2个字节存储,方便传输
	for (uint32_t i = 0; i < 37632; i++)
	{
		rgb8[i * 2] = gray_bi[i] & 0x00FF;
		rgb8[i * 2 + 1] = gray_bi[i] >> 8;
	}
}

六、成品实测

1、RGB伪彩色从左到右,温度越高,最高为白色。

2、继电器工作发热主触点温度实测如下:

七、结束语

若家人们感兴趣,点赞收藏较多,后期将编写Python用串口连接到电脑上直接显示图像。

;