为实现电子元器件传感器阻抗特性的自动化测试,使用主控芯片统一统一控制附属各个设备工作
- 高低温箱:热电偶
- 多路开关继电器
- 阻抗测试设备
- 气体流量计
主要控制逻辑:测试不同温度下的被测样品的阻抗特性,并实时将记录上传至上位机保存
主要思路:使用恒流源为热电阻PT100供电,然后将其两端电压经运算电路映射到0-3.3V范围内供ADC采集,并获得足够的测试精度,单片机判断后接近设定值时,依次打开四路继电器,随机即发送命令要求阻抗仪测试,并传回测试数据,单片机获得数据后,将温度测试气体流量打包为一个数据帧发送至局域网,并设计命令接口,由上位机随时主动发送命令查询当前测试状态,防止测试人员不在现场时也能及时发现异常
硬件设计:
原理图设计:
单片机核心模块:
模拟部分:恒流源电路为热电阻供电,并将输出结果映射至模数转换器的输入范围,模拟电源使用磁珠与数字电源隔离,模拟地使用0欧姆电阻实现与数字地的单点连接
流量计控制器接口说明,使用mos管驱动两个继电器实现清洗阀控与关闭功能
多路继电器使用485接口控制分别发送不同命令打开各路,
PCB设计:
主要是模拟部分与数字部分分开,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()