最近在测试设备的时候需要监测设备的温度,平常测试时都是通过红外热成像仪测试,然后手动记录数据。这样测试的话工作量比较大,需要几分钟就记录一次数据。于是想着能不能用单片机做一个测试温度的装置,用电脑自动记录数据。手头刚好有STM8S003单片机和 5K的NTC热敏电阻,于是就做了一个简易的温度测试装置,并使用伏特加上位机软件将温度波形显示出来。
最终测试效果如图所示:
下面将制作步骤分享出来。
首先设计NTC热敏电阻的采样电路。
这里使用最简答电阻分压电路,将热敏电阻和一个10K电阻串联分压,然后将电压值直接送到单片机的AD采样口,由于热敏电阻的阻值会随着温度变化,所以送入单片机口的电压值也会随着温度变化,这样就可以通过单片机AD口采样的电压值推算出当前的温度值。
温度采样电路设计了两路,输出的电压值分别送到单片机的AD采样口 19、20引脚,然后通过单片机的串口也就是2、3引脚将温度数据通过串口模块发送到电脑上。
硬件电路很简单,连接完成图如下:
将两路温度采样电路焊接在一个小板上,然后连接到STM8S003单片机最小系统上,USB转串口的模块也用杜邦线连接到单片机上。
接下来开始编写代码,代码也很简单,就用到了ADC采样和串口部分。
ADC采样代码如下:
#include "adc.h"
#include "main.h"
u16 DATAH = 0; //ADC转换值高8位
u16 DATAL = 0; //ADC转换值低8位
_Bool ADC_flag = 0; //ADC转换成功标志
//AD通道引脚初始化
void ADC_GPIO_Init( void )
{
PD_DDR &= ~( 1 << 2 ); //PD2 设置为输入 电压
PD_CR1 &= ~( 1 << 2 ); //PD2 设置为悬空输入
PD_DDR &= ~( 1 << 3 ); //PD3 设置为输入 电流
PD_CR1 &= ~( 1 << 3 ); //PD3 设置为悬空输入
}
//ch 为单片机的对应管脚
void ADC_CH_Init( u8 ch )
{
char l = 0;
ADC_CR1 = 0x00; //fADC = fMASTER/2, 8Mhz 单次转换,禁止转换
ADC_CSR = ch + 1; //控制状态寄存器 选择要 AD输入通道 如:PD2(AIN3)
ADC_CR2 = 0x00; //默认左对齐 读数据时先读高在读低
ADC_TDRL = ( 1 << ( ch + 1 ) ); //禁止相应通道 施密特触发功能 1左移ch+1位
ADC_CR1 |= 0x01; //使能ADC并开始转换
ADC_CSR |= 0x20; //EOCIE 使能转换结束中断 EOC中断使能
for( l = 0; l < 100; l++ ); //延时,保证ADC模块的上电完成 至少7us
ADC_CR1 = ADC_CR1 | 0x01; //再次将CR1寄存器的最低位置1 使能ADC 并开始转换
}
//采集PD2电压值
u16 ReadVol_CH2( void )
{
u16 voltage = 0;
ADC_CH_Init( 2 );
if( ADC_flag )
{
ADC_flag = 0;
voltage = ( DATAH << 2 ) + DATAL ;
ADC_CR1 = ADC_CR1 | 0x01;
};
return voltage;
}
//采集PD3电压值
u16 ReadVol_CH3( void )
{
u16 voltage = 0;
ADC_CH_Init( 3 );
if( ADC_flag )
{
ADC_flag = 0;
voltage = ( DATAH << 2 ) + DATAL ; //得到十位精度的数据 0--1024
ADC_CR1 = ADC_CR1 | 0x01; //再次将CR1寄存器的最低位置1 启动下一次转换
};
return voltage;
}
//AD中断服务函数 中断号22
#pragma vector = 24 // IAR中的中断号,要在STVD中的中断号上加2
__interrupt void ADC_Handle( void )
{
ADC_CSR &= ~0x80; // 转换结束标志位清零 EOC
//默认左对齐 读数据时先读高高8位 再读低8位
DATAH = ADC_DRH; // 读出ADC结果的高8位
DATAL = ADC_DRL; // 读出ADC结果的低8位
ADC_flag = 1; // ADC中断标志 置1
}
串口相关代码如下:
#include "uart.h"
#include "stdio.h"
#include "main.h"
//在Library Options中将Printf formatter改成Large
//重新定向putchar函数,使支持printf函数
int putchar(int ch)
{
while(!(UART1_SR&0X80));//循环发送,直到发送完毕
UART1_DR = (u8) ch;
return ch;
}
void Uart1_IO_Init(void)
{
PD_DDR |= (1<<5); //输出模式 TXD
PD_CR1 |= (1<<5); //推挽输出
PD_DDR &= ~(1<<6); //输入模式 RXD
PD_CR1 &= ~(1<<6); //浮空输入
}
//波特率最大可以设置为38400
void Uart1_Init( unsigned int baudrate )
{
unsigned int baud;
baud = 16000000 / baudrate;
Uart1_IO_Init();
UART1_CR1 = 0;
UART1_CR2 = 0;
UART1_CR3 = 0;
UART1_BRR2 = ( unsigned char )( ( baud & 0xf000 ) >> 8 ) | ( ( unsigned char )( baud & 0x000f ) );
UART1_BRR1 = ( ( unsigned char )( ( baud & 0x0ff0 ) >> 4 ) );
UART1_CR2_bit.REN = 1; //接收使能
UART1_CR2_bit.TEN = 1; //发送使能
UART1_CR2_bit.RIEN = 1; //接收中断使能
}
由于单片机只需要通过串口发送数据,而不需要接收数据,所以这里就没有串口接收相关代码。
接下来就是最重要的部分了,需要将串口采样到的ADC值转换为对应的温度值。这里需要查阅NTC热敏电阻的资料,将热敏电阻的阻值和温度变化做成一张表,然后单片机通过查表获取温度值。
在网上查找资料,找到了一张NTC热敏电阻的阻值和温度对照表,这张表中温度对应的是电阻阻值,所以还需要将NTC的阻值和单片机ADC采样值对应起来。
由于采样电路是10K固定电阻和NTC分压所得。假设NTC的阻值为R,那么根据分压公式可以算出,两个电阻分压后的电压值为 Vo = 5V / (10K + R) * R ,,将热敏电阻的阻值和温度对照表复制到Excel表格中,然后根据分压公式计算出每个温度对应的分压电压值。
由于单片机采样的AD值并不是电压值,所以还需要将计算出来的电压值转换为单片机的采样值,这里使用的单片机AD精度为10位,所以最大的采样值为2^10=1024,单片机位5V供电,使用公式 采样ADC值 = 温度电压值 / 5V * 1024,计算采样值和分压值的对应关系。可以直接在表格中使用公式计算。
这样就将单片机的采样值和温度值对应起来了,为了程序编写的方便,这里温度选择直接从0度开始,0度以下的数据不考虑。
将从0度开始的单片机ADC采样值存在数组中,单片机从AD口读取到数据后,在数组中查找当前采样的数据最接近数组中的哪个数据,然后对应数组的下标刚好就是温度值,比如采样值为340,那么在数组中最接近的数字就是341,而341在数组中第25位,那么当前的温度值就是25℃。
为了减小单片机的计算量,将表格中计算出来的单片机采样值统一变成整数,将转换好的整数存放在数组中。
接下来在主程序中就可以读取ADC采样值,然后查表去找出对应的温度值了。主函数代码如下:
void main( void )
{
static u16 temp1 = 100, temp2 = 200;
static u16 value1 = 0, value2 = 0;
u16 i = 0, j = 0;
u32 sum = 0;
SysClkInit();
__asm( "sim" ); //禁止中断
LED_GPIO_Init();
delay_init( 16 );
Uart1_Init( 9600 );
__asm( "rim" ); //开启中断
while( 1 )
{
sum = 0;
for( i = 0; i < 16; i++ ) //测量两路温度数据
{
sum += ReadVol_CH3();
delay_ms( 10 );
}
value1 = sum >> 4;
temp1 = find_by_seq( tem_table, NUM, value1 ); //通过采样值查表计算对应温度值
sum = 0;
for( i = 0; i < 16; i++ )
{
sum += ReadVol_CH2();
delay_ms( 10 );
}
value2 = sum >> 4;
temp2 = find_by_seq( tem_table, NUM, value2 ); //通过采样值查表计算对应温度值
LED = ~LED;
printf( "%d,%d,%d,%d\r\n", value1, value2, temp1, temp2 );
}
}
为了避免采样到的温度值跳变,所以这里每个通道采样16次,然后取平均值,每次采样间隔10ms。计算出采样的平均值之后,通过查找函数检测当前的采样值最接近数组中的那个数字,然后返回对应的下标。这里查找数据使用了最简单的顺序查找法。
/* * 顺序查找法 * @description : 在数组中寻找和数据最接近的那个数字,并返回数字所在数组的下标 * @param - arr : 数组首地址 * @param - length : 数组大小 * @param - num : 要查找的数据 * @return : 最接近的数组所在数组下标 * * 基本思路:数组按照顺序排列好之后,从数组头开始依次比较各个数据。 */
int find_by_seq( int *arr, int length, int num )
{
int i;
int min = abs( *arr - num ); // 要查找的数据和数组中第一个数字的差作为最小值
int index = 0;
for( i = 0; i < length; i++ )
{
if( abs( arr[i] - num ) < min )
{
min = abs( arr[i] - num );
index = i;
}
}
//return arr[index]; //返回数据
return index; //返回下标
}
虽然顺序查找法效率不高,但是这个程序本身也很简单,同时外部设备温度的变化也比较缓慢,所以使用顺序查找法对系统的运行也没有多大的影响。查找的原理就是采样值依次和数组中的每个数据相减然后求绝对值,最后返回绝对值最小的数据索引。由于数组中的数据是从0度开始的,每次递增一度,所以数组的下标刚好就是温度值,不需要再进行换算。
最后通过printf()函数将数据从串口发送到 上位机软件上,进行波形显示。在烧水器上少了一杯开水,直接将NTC热敏电阻放到水里面测试。
在上位机软件上测试波形如下:
逐渐上升的两条曲线是采样到的原始值,逐渐下降的两条曲线是查表后换算出来的温度值。可以将波形放大,查看实时显示的值。
这时波形动态打印效果图。
为了使温度看起来更方便,可以在界面上添加两个温度显示表盘。
通过最右边的两个表盘就可以实时显示温度值,通过左边的曲线查看温度变化趋势。
用手捏了一下热敏电阻,可以看到温度反应还是比较灵敏的。到此这个简易的温度巡检仪就算搞定了。
关于这个上位机软件的使用,可以看 串口示波器—伏特加 彻底的爱了 这篇文章。
本文中的这个工程也打包上传了,下载地址: STM8单片机自制简易温度巡检仪
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_20222919/article/details/121536850
内容来源于网络,如有侵权,请联系作者删除!