搭建介电温谱自动测试数据采集系统(主要设备高低温箱,安捷伦4294a阻抗测试仪)

为实现电子元器件传感器阻抗特性的自动化测试,使用主控芯片统一统一控制附属各个设备工作

  1. 高低温箱:热电偶
  2. 多路开关继电器
  3. 阻抗测试设备
  4. 气体流量计

主要控制逻辑:测试不同温度下的被测样品的阻抗特性,并实时将记录上传至上位机保存

主要思路:使用恒流源为热电阻PT100供电,然后将其两端电压经运算电路映射到0-3.3V范围内供ADC采集,并获得足够的测试精度,单片机判断后接近设定值时,依次打开四路继电器,随机即发送命令要求阻抗仪测试,并传回测试数据,单片机获得数据后,将温度测试气体流量打包为一个数据帧发送至局域网,并设计命令接口,由上位机随时主动发送命令查询当前测试状态,防止测试人员不在现场时也能及时发现异常

硬件设计:

原理图设计:

单片机核心模块:

搭建介电温谱自动测试数据采集系统(主要设备高低温箱,安捷伦4294a阻抗测试仪)

模拟部分:恒流源电路为热电阻供电,并将输出结果映射至模数转换器的输入范围,模拟电源使用磁珠与数字电源隔离,模拟地使用0欧姆电阻实现与数字地的单点连接

搭建介电温谱自动测试数据采集系统(主要设备高低温箱,安捷伦4294a阻抗测试仪)

 

流量计控制器接口说明,使用mos管驱动两个继电器实现清洗阀控与关闭功能

 

 搭建介电温谱自动测试数据采集系统(主要设备高低温箱,安捷伦4294a阻抗测试仪)

搭建介电温谱自动测试数据采集系统(主要设备高低温箱,安捷伦4294a阻抗测试仪)

 

 

 

 

多路继电器使用485接口控制分别发送不同命令打开各路,

 

搭建介电温谱自动测试数据采集系统(主要设备高低温箱,安捷伦4294a阻抗测试仪)

 

 PCB设计:

搭建介电温谱自动测试数据采集系统(主要设备高低温箱,安捷伦4294a阻抗测试仪)

 

 主要是模拟部分与数字部分分开,485的两根差分线尽量等长。

STM32主要函数设计:

1、二分法查表,此查表函数返回与当前数值最接近的温度值

 1 unsigned char half(float x, unsigned char* s, unsigned char len)
 2 {
 3     unsigned char i = 0,j,result,num = 0;
 4     j = len-1;
 5 
 6     while(1)
 7 {
 8        if (x <(s[(i+j)/2]))
 9         j = (i+j)/2;
10     else
11         i = (i+j)/2;
12     if (j-i ==1)
13     {
14         if((s[j]-x)>(x-s[i]))
15          {num = i;
16             result=s[num];
17             }   
18             
19         else
20          {num = j;
21              result=s[num];
22             }   
23         break;
24     }    
25     
26 
27     
28 
29 }     
30 return result;    
31     
32     
33 }

 

2、ADC,DAC的使用

  读取温度:

初始化ADC模式:

       uint16_t ADC_ConvertedValue;
1 GPIO_InitTypeDef GPIO_InitStructure; 2 3 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); 4 5 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; 6 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; 7 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; 8 GPIO_Init(GPIOC, &GPIO_InitStructure); 9 /*配置DMA,和ADC寄存器*/ 10 DMA_InitTypeDef DMA_InitStructure; 11 ADC_InitTypeDef ADC_InitStructure; 12 ADC_CommonInitTypeDef ADC_CommonInitStructure; 13 // ------------------DMA Init 结构体参数 初始化-------------------------- 14 15 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); 16 17 DMA_InitStructure.DMA_PeripheralBaseAddr = RHEOSTAT_ADC_DR_ADDR; 18 19 DMA_InitStructure.DMA_Memory0BaseAddr = (u32)&ADC_ConvertedValue; 20 21 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; 22 23 DMA_InitStructure.DMA_BufferSize = 1; 24 25 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 26 27 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; 28 29 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 30 31 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 32 33 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ; 34 35 DMA_InitStructure.DMA_Priority = DMA_Priority_High; 36 37 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; 38 39 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; 40 DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; 41 DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; 42 43 DMA_InitStructure.DMA_Channel = DMA_Channel_0; 44 45 DMA_Init(DMA2_Stream0, &DMA_InitStructure); 46 47 DMA_Cmd(DMA2_Stream0, ENABLE); 48 49 //ADC结构体配置 50 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 , ENABLE); 51 // -------------------ADC Common 结构体 参数 初始化------------------------ 52 53 ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; 54 55 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; 56 57 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; 58 59 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; 60 ADC_CommonInit(&ADC_CommonInitStructure); 61 // -------------------ADC Init 结构体 参数 初始化-------------------------- 62 ADC_StructInit(&ADC_InitStructure); 63 64 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; 65 66 ADC_InitStructure.ADC_ScanConvMode = DISABLE; 67 68 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; 69 70 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; 71 72 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; 73 74 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; 75 76 ADC_InitStructure.ADC_NbrOfConversion = 1; 77 ADC_Init(ADC1, &ADC_InitStructure); 78 79 ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 1, ADC_SampleTime_56Cycles); 80 81 82 ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); 83 84 ADC_DMACmd(ADC1, ENABLE); 85 86 87 ADC_Cmd(ADC1, ENABLE);

启用DMA后转换完的ADC数值即存于ADC_ConvertedValue变量中,使用定时器触发

定时器配置

TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);     
    TIM_TimeBaseStructure.TIM_Prescaler = 900;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;
    TIM_TimeBaseStructure.TIM_Period = 10;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
    TIM_Cmd(TIM2, ENABLE);    

DAC配置:DAC使用软件配置触发

 1   GPIO_InitTypeDef GPIO_InitStructure;
 2     DAC_InitTypeDef  DAC_InitStructure;
 3 
 4 
 5   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);    
 6 
 7   RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
 8 
 9   GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4;
10   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
11   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
12     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;  
13   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
14   GPIO_Init(GPIOA, &GPIO_InitStructure);
15 
16   DAC_InitStructure.DAC_Trigger = DAC_Trigger_Software;                        
17   DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;    
18   DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;    
19 
20     DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_TriangleAmplitude_4095;
21   DAC_Init(DAC_Channel_1, &DAC_InitStructure);
22   DAC_Cmd(DAC_Channel_1, ENABLE);

 

3、与485接口的通信

485使用 串口2通信:

初始化函数配置PB10 PB11管脚为usart2,并配置串口2

主要发送函数:

 继电器控制命令为,每个命令的执行为打开对应路,并关闭其他路:

1 u8 open1_cmd[4] ="*A1#"; 
2 u8 open2_cmd[4] ="*B2#"; 
3 u8 open3_cmd[4] ="*C3#"; 
4 u8 open4_cmd[4] ="*D4#"; 
为了方便访问定义为2维数组
unsigned char open_cmd[][5]={"*A1#", "*B2#", "*C3#","*D4#"};

 

 1 void send_uart2(char *p)
 2     
 3 {    u8 i =0;
 4     
 5     while(i<strlen(p))
 6     {     
 7         USART2->DR=p[i];
 8         
 9         while((USART2->SR&0X40)==0);
10         
11         i++;
12     }
13             
14 }

 

4、与阻抗仪的通信,查询仪器使用手册查得主要使用功能命令为:

/*主要使用命令为*/
频率设定:WrtCmd(“FREQ 1KHZ”)
WrtCmd(“FUNC:IMP RX”);
WrtCmd(“TRIG”);
WrtCmd(“FETC?”);

 单片机使用UART4访问同惠232串口,初始化函数配置PC10 PC11管脚,启用串口4;

定义以上命令为字符串:

 


u8 set_fre[]=“FREQ 1KHZ”;
u8 set_fun[]=“FUNC:IMP RX”;
u8 trig[]=“TRIG”;
u8 fetch[]=“FETC?”;


void send_uart4(char *p) { u8 i =0; while(i<strlen(p)) { UART4->DR=p[i]; delay_us(100); while((UART4->SR&0X40)==0); i++; } }

发送以上命令后仪器会返回测试结果数据:

说明书给出的测试结果格式为:<DATA A>,<DATA B>格式: <DATA A>(主参测量数据),<DATA B>(副参测量数据)结束符

例如:b'+5.11094E+03,-1.19979E+05,+0\r\n' 以字节流形式返回

使用串口4接收此字节流

 

 1 void UART4_IRQHandler(void)
 2 {
 3   uint8_t ucTemp;
 4     
 5     if(USART_GetITStatus(UART4,USART_IT_RXNE)!=RESET)
 6     {        ;
 7         
 8     ucTemp = USART_ReceiveData(UART4);
 9     data4[u4_num] = ucTemp ;
10     u4_num++;        
11         if(ucTemp=='\n')
12         { data4[u4_num] = ucTemp ;
13             USART_ITConfig(UART4, USART_IT_RXNE, DISABLE);
14                 u4_finish_flag=1;
15         }        
16     }     
17 
18 } 

 

 

 

5、打包向上位机发送数据

 1 res = half(tem_value,tem, strlen(tem));
 2         if(tem_value-res<0.5)
 3         {    
 4             for(i=0; i<4; i++)
 5             {
 6             send_uart4(open_cmd[i])
 7             send_uart4(trig);
 8             send_uart4(fetch);
 9             while(u4_finish_flag==0);
10             data4[u4_num] = res ;
11             u4_num = 0;
12             send_usart1(data4);
13             
14             
15             } 
tem_value即为当前温度值,当此温度值与标准值小于设计误差时触发四路测试并将结果返回、

 

 python 上位机设计

 主循环中接收数据并存入excel中

 1 import xlwings as xw
 2 import time
 3 import socket
 4 import tkinter
 5 import time
 6 import win32api
 7 from tkinter import filedialog
 8 import os
 9 
10 #GetValue = rng.Cells(rng.Cells.Count).Value 访问单个单元格
11 import sys 
12 sys.setrecursionlimit(1000000)
13 root = tkinter.Tk()
14 root.minsize(1000, 600)
15 root.title('lxy')
16 global file_path
17 ser = serial.Serial('COM3', 9600, timeout=1)
18 
19 global number
20 global colnum
21 colnum=1
22 global wb
23 global sht1
24 global fig
25 
26 
27 def select():
28     global file_path
29     file_path = filedialog.askdirectory()
30     file_path = file_path.replace('/', '\\')
31 
32 def read():
33     global file_path
34     global colnum
35     global number
36     global fig
37     Cp=[]
38     D=[]
39     fre=[]
40     sampleName = en1.get()
41     path = os.path.join(file_path, sampleName)
42     wb = xw.Book(os.path.join(file_path, 'Cp.xlsx'))
43    
44     sht1 = wb.sheets[0]
45     while(1):
46         data = ser.readline()
47         tem = a[-1]
48         data = a.decode(encoding="utf-8")
49         R = data.split(',')[0]
50         X = data.split(',')[1]
51         sht1.cells(colnum,1).value = tem
52         sht1.cells(colnum,2).value = R
53         sht1.cells(colnum,3).value = X
54         colnum=colnum+1
55 
56     
57     
58 b1 = tkinter.Button(root, text='文件夹', font=('times new roman', 24), command=select)
59 b2 = tkinter.Button(root, text='测试', font=('times new roman', 24), command=test)
60 b3 = tkinter.Button(root, text='获取数据', font=('times new roman', 24), command=read)
61 en1 = tkinter.Entry(root, text='起始', font=('times new roman', 24))   
62 b1.place(x=0, y=100, width=200, height=100)
63 b2.place(x=210, y=0, width=200, height=100)
64 b3.place(x=510, y=0, width=200, height=100)
65 en1.place(x=0, y=0, width=200, height=100)      
66 
67 root.mainloop()

 

上一篇:GD32F330 | Uart收发 基于DMA方式


下一篇:串口1配合DMA接收不定长数据(空闲中断+DMA接收)