??最近,在测试设备时,我们需要监测设备的温度。通常,我们通过红外热成像仪进行测试,然后手动记录数据。这样,测试的工作量相对较大。记录数据需要几分钟。所以我想知道我是否可以用单片机制作测试温度的装置,并自动用计算机记录数据。手头碰巧有STM8S003单片机和 5K的NTC热敏电阻,所以做了一个简单的温度测试装置,用伏特和位机软件显示温度波形。
??如图所示:
??下面分享制作步骤。
??首先设计NTC热敏电阻采样电路。 这里使用最简答电阻分压电路,将热敏电阻和10个K电阻串联分压,然后将电压值直接送到单片机AD采样口,由于热敏电阻的电阻值会随温度而变化,送入单片机口的电压值也会随温度而变化,因此可以通过单片机AD口采样的电压值计算出当前的温度值。
??温度采样电路设计有两条路,输出电压值分别送到单片机AD取样口 19、20引脚,然后通过串口模块通过单片机的串口即2、3引脚将温度数据发送到计算机。
硬件电路非常简单,连接完成图如下:
将两个温度采样电路焊接在小板上,然后连接到STM8S在003单片机最小系统上,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); //浮空输入}//波特率最大可以设置为38400void 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,那么根据分压公式可以算出,两个电阻分压后的电压值为 ,,将热敏电阻的阻值和温度对照表复制到Excel表格中,然后根据分压公式计算出每个温度对应的分压电压值。
由于单片机采样的AD值并不是电压值,所以还需要将计算出来的电压值转换为单片机的采样值,这里使用的单片机AD精度为10位,所以最大的采样值为2^10=1024,单片机位5V供电,使用公式 ,计算采样值和分压值的对应关系。可以直接在表格中使用公式计算。 这样就将单片机的采样值和温度值对应起来了,为了程序编写的方便,这里温度选择直接从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单片机自制简易温度巡检仪