文章目次
- 前言
- 一、先看结果
- 二、使用步调
- 1 运行软件
- 1.1 Windows用户直接运行打包好的软件
- 1.2 安装Python情况运行源代码(入坑)
- 2 选择端口
- 三、源码分析
- 1 步调总体流程图
- 1.1 USB串口中断服务函数
- 1.2 ADC采样过程
- 1.3 DMA传输过程
- 2 下位机源代码
- 2.1 TIM设置函数
- 2.2 DMA设置函数
- 2.3 ADC设置函数
- 2.4 USB串口中断服务函数
- 2.5 DMA传输完成中断
- 2.6 方波发生器及主函数
- 3 上位机源代码
- 四、性能测试
- 1 再造信号发送器
- 2 方波测试
- 2.1 1kHz方波量结果
- 2.2 10kHz方波量结果
- 2.3 100kHz方波量结果
- 2.4 400kHz方波量结果
- 3 正弦波测试
- 3.1 50Hz正弦波量结果
- 3.2 1kHz正弦波量结果
- 3.3 10kHz正弦波量结果
提示:本项目仅使用某宝制品CH32F103C8T6开发板,硬件简朴,可自行根据需要自行添加软硬件功能。
一、先看结果
阐明:A1管脚为信号输入管脚,A0输出1KHz方波信号举行校正,具有以下功能和特点:
- A0口有3.3V 1KHz根本方波输出,用于校准。
- A1信号输入口信号测量范围0~3.3V。
- 32单片机最高支持1M采样率,但体系时钟为72M时最高支持857.1k采样率(跟ADC时钟分频系数有关)。符合奈奎斯特采样定律,采样精度12位。
- 上位机采取Python开发,兼容Linux、Windows体系,通过32单片机的USB假造串口通信。
- USB假造串口通信数据包缓存最大1200字节,故步调中限定单次采样点数为512个(一个采样点占两个字节)。
- 具有根本测量功能:测量频率,脉宽,占空比(自己读数)。
- 仅有自动上升沿触发功能。
- 添加贝塞尔插值功能
- 项目开源https://github.com/ClassmateXie/32Oscilloscopes
二、使用步调
1 运行软件
1.1 Windows用户直接运行打包好的软件
CH32示波器.exe
1.2 安装Python情况运行源代码(入坑)
进入CH32示波器.py文件所在目次,右键打开终端
输入下令行
复制代码 若报错需根据提示自行安装对应的python库,比方:
- pip install pyqtgraph
- pip install numpy
- pip install pyserial
- pip install PyQt5
- pip install scipy
复制代码
2 选择端口
初始界面如下:
修改假造串口为对应的端口(装备管理器中检察,默认COM7),通信波特率默认1000000
三、源码分析
项目开发过程的大部分时间都在巩固根本知识,虽然项目团体难度不大,但是在开发过程中对32单片机的ADC、DMA、TIM、NVIC以及USB等功能有了更加深刻的明确,特此记录开发过程。
1 步调总体流程图
1.1 USB串口中断服务函数
USB接收中断 状态机剖析数据 重新设置TIM4和DMA 启动TIM4和DMA 1.2 ADC采样过程
触发 请求 产生 TIM4_CH4 ADC采样 DMA搬运 DMA完成中断 1.3 DMA传输过程
搬运到 DMA传输完成中断 ADC->DR数据寄存器 ADC_Buf数组 USB发送全部缓存 清空缓存并关闭TIM4和DMA 2 下位机源代码
2.1 TIM设置函数
设置TIM4通道4产生PWM的频率,作为ADC规则组触发源
触发 TIM4_CH4 ADC采样
- /*******************************************************************************
- * 函 数 名 : TIM_ReSet
- * 函数功能 : TIM重启
- * 输 入 : Period重装载值,Prescaler分频因子
- * 输 出 : 无
- *******************************************************************************/
- void TIM_ReSet(u16 Period,u16 Prescaler)
- {
- static TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- static TIM_OCInitTypeDef TIM_OCInitStructure;
- TIM_TimeBaseStructure.TIM_Period = Period-1; //设置TIM2比较的周期
- TIM_TimeBaseStructure.TIM_Prescaler = Prescaler-1;//系统主频72M,这里分频
- TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//下面详细说明
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//TIM_OutputState_Disable;
- TIM_OCInitStructure.TIM_Pulse = Period>>1;
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//如果是PWM1要为Low,PWM2则为High
- TIM_OC4Init(TIM4, &TIM_OCInitStructure);
- TIM_Cmd(TIM4, ENABLE);
- }
复制代码
2.2 DMA设置函数
搬运到 ADC->DR数据寄存器 ADC_Buf数组 - u16 buf_len = 512; //USB发送缓存最大1200字节,buf_len必须小于600
- u16 ADC_Buf[512]={0};
- /*******************************************************************************
- * 函 数 名 : DMA_ReSet
- * 函数功能 : DMA重启
- * 输 入 : len传输数据量
- * 输 出 : 无
- *******************************************************************************/
- void DMA_ReSet(u16 len)
- {
- static DMA_InitTypeDef DMA_InitStructure;
- buf_len = len;
- //========DMA配置=============/
- DMA_ClearFlag(DMA1_FLAG_TC1);
- DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;//ADC地址
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_Buf; //内存地址
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从外设到内存)
- DMA_InitStructure.DMA_BufferSize = len; //传输内容的大小
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ; //外设数据单位
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ; //内存数据单位
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //DMA模式:单次传输
- DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //优先级:高
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输
- DMA_Init(DMA1_Channel1, &DMA_InitStructure); //配置DMA1的1通道
- DMA_Cmd(DMA1_Channel1,ENABLE);
- }
复制代码
2.3 ADC设置函数
触发 请求 产生 TIM4_CH4 ADC采样 DMA搬运 DMA完成中断 - void ADC1_CH1_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
- ADC_InitTypeDef ADC_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- /* Configure one bit for preemption priority */
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//使能TIM4时钟
- RCC_ADCCLKConfig(RCC_PCLK2_Div6);//设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
- //==========端口设置====================//
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;//ADC
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; //模拟输入
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
- //==========ADC配置====================//
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
- ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;//定时器4通道4触发检测
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
- ADC_InitStructure.ADC_NbrOfChannel = 1;//1个转换在规则序列中 也就是只转换规则序列1
- ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
-
- ADC_DMACmd(ADC1, ENABLE);//使能ADC1模块DMA
-
- //=========定时器配置==============//
- // TIM_ReSet(100,72);//10K采样率
- //========DMA配置=============/
- DMA_DeInit(DMA1_Channel1);
- DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);//开启DMA传输完成中断
- // DMA_DeInit(DMA1_Channel1);
- // DMA_ReSet(buf_len);
- // DMA_ITConfig(DMA1_Channel1,DMA_IT_HT,ENABLE);
- // DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);
- // DMA_ITConfig(DMA1_Channel1,DMA_IT_TE,ENABLE);
- //=======NVIC配置============//
- /* 配置DMA1_Channel1为中断源 */
- NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
- ADC_Cmd(ADC1, ENABLE);//开启AD转换器
- ADC_ResetCalibration(ADC1);//重置指定的ADC的校准寄存器
- while(ADC_GetResetCalibrationStatus(ADC1)){};//获取ADC重置校准寄存器的状态
- ADC_StartCalibration(ADC1);//开始指定ADC的校准状态
- while(ADC_GetCalibrationStatus(ADC1));//获取指定ADC的校准程序
- ADC_ExternalTrigConvCmd(ADC1, ENABLE);
- ADC_RegularChannelConfig(ADC1, 1, 1, ADC_SampleTime_1Cycles5); //ADC1,ADC通道1,序号1,1.5个周期
-
- TIM_InternalClockConfig(TIM4);
- TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
- TIM_UpdateDisableConfig(TIM4, DISABLE);
- }
复制代码
2.4 USB串口中断服务函数
- //处理从USB虚拟串口接收到的数据
- //databuffer:数据缓存区
- //Nb_bytes:接收到的字节数.
- void USB_To_USART_Send_Data(u8* data_buffer, u16 Nb_bytes)
- {
- u16 i,arr,div,num;
- static u8 temp,step,buf[6],cnt;
- for(i=0;i<Nb_bytes;i++)
- {
- temp = data_buffer[i];
- switch(step)
- {
- case 0:if(temp==0xa5)step=1;break;
- case 1:if(temp==0x5a)step=2;else if(temp==0xa5)step=1;else step=0;break;
- case 2:buf[cnt++]=temp;if(cnt>=6)step=3,cnt=0;break;
- case 3:if(temp==0xff)
- {
- arr=buf[0]*256+buf[1];
- div=buf[2]*256+buf[3];
- num=buf[4]*256+buf[5];
- DMA_ReSet(num);
- TIM_ReSet(arr,div);
- step=0;
- }
- else if(temp==0xa5)step=1;
- else step=0;
- break;
- }
- }
- }
复制代码
接收数据帧格式
帧头重装载值分频因子采样点数帧尾A5 5AXX XXXX XXXX XXFF2.5 DMA传输完成中断
USB发送全部缓存 清空缓存并关闭TIM4和DMA 清晰中断标志位 - /*******************************************************************************
- * 函 数 名 : DMA1_Channel1_IRQHandler
- * 函数功能 : DMA通道1的中断
- * 输 入 : 无
- * 输 出 : 无
- *******************************************************************************/
- void DMA1_Channel1_IRQHandler(void)
- {
- u16 i;
- if(DMA_GetFlagStatus(DMA1_FLAG_TC1))
- {
- TIM_Cmd(TIM4, DISABLE);
- DMA_Cmd(DMA1_Channel1,DISABLE);
- for(i=0;i<buf_len;i++)USB_USART_SendData((ADC_Buf[i]>>8)),USB_USART_SendData(ADC_Buf[i]&0xff);
- DMA_ClearFlag(DMA1_FLAG_TC1); //清除全部中断标志
- LED=!LED;
- }
- }
复制代码
2.6 方波发生器及主函数
产生 产生 产生 改变 赋值给 TIM2设置为1KHz 1通道PWM方波 A0口输出 3通道PWM方波 A2口输出 定时器更新中断 3通道占空比 LED端口PC13 呼吸灯结果 - u16 LED_Breathe_Buf[1000];
- void Buf_Dataset(u16* buf)
- {
- u16 temp;
- for(temp=0;temp<500;temp++)buf[temp]=temp*2+100;
- for(temp=0;temp<500;temp++)buf[temp+500]=999-temp*2+100;
- }
- void TIM2_Init(u16 Period,u16 Prescaler)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- TIM_OCInitTypeDef TIM_OCInitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
-
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使能TIM2时钟
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA,ENABLE);
-
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_2;
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
-
- TIM_TimeBaseStructure.TIM_Period = Period-1; //设置TIM2比较的周期
- TIM_TimeBaseStructure.TIM_Prescaler = Prescaler-1;//系统主频72M,这里分频
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
-
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//下面详细说明
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//TIM_OutputState_Disable;
- TIM_OCInitStructure.TIM_Pulse = Period>>1;
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//如果是PWM1要为Low,PWM2则为High
- TIM_OC1Init(TIM2, &TIM_OCInitStructure);
- TIM_OC3Init(TIM2, &TIM_OCInitStructure);
-
- NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
- NVIC_Init(&NVIC_InitStructure);//初始化中断,设置中断的优先级
-
- TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启定时器中断
-
- TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);//定时器2 通道1
- TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);//定时器2 通道2
-
- TIM_Cmd(TIM2, ENABLE);
- }
- int main(void)
- {
- Buf_Dataset(LED_Breathe_Buf);
- delay_init(); //延时函数初始化
- LED_Init(); //LED端口初始化
- USB_Init(); //USB虚拟串口初始化
- ADC1_CH1_Init(); //ADC初始化
- TIM2_Init(1200,60); //72000000/1200/60=1000Hz
- while(1)
- {
- LED=PAin(2);
- }
- }
- void TIM2_IRQHandler(void)
- {
- if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
- {
- static u16 cnt;
- TIM_SetCompare3(TIM2,LED_Breathe_Buf[cnt]);
- if(cnt++>=1000)cnt=0;
- TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除中断标志位
- }
- }
复制代码
3 上位机源代码
- pyqtgraph库举行科学画图,官方文档:https://www.pyqtgraph.org/
- pyserial库举行串口通信,官方文档:https://pyserial.readthedocs.io/
- PyQt5库计划GUI
完备代码:
- import pyqtgraph as pg
- import numpy as np
- import serial
- import time,struct
- import pyqtgraph.parametertree as ptree
- from pyqtgraph.Qt import QtCore, QtGui, QtWidgets, QT_LIB
- from scipy import interpolate
- from PyQt5.QtWidgets import *
- N = 512 # 采样点数
- k = 3.3/4096 # 比例因子
- arr = 400 # 重装载值
- div = 72 # 分频因子
- Fs = 72000000/arr/div # 采样率
- Ts = 1/Fs # 采样周期
- COM = 'COM7'
- # GUI设计
- app = pg.mkQApp()
- #============波形显示窗口=====================#
- w = pg.GraphicsLayoutWidget()
- w.setWindowTitle("CH32示波器-By纯粹")
- # 坐标系控件
- p = w.addPlot()
- p.showGrid(x=True,y=True)
- p.setRange(xRange=[-80,80],yRange=[-1,4],padding=0)
- p.setLabels(left='电压 / V',bottom="t / ms",title='CH32示波器')
- # 时基线和触发门限
- inf1 = pg.InfiniteLine(movable=True, angle=90, label='时基={value:0.2f}ms',
- labelOpts={'position':0.1, 'color': (200,200,100), 'fill': (200,200,200,50), 'movable': True})
- inf2 = pg.InfiniteLine(movable=True, angle=0, pen=(0, 0, 200), bounds = [-20, 20], hoverPen=(0,200,0), label='触发={value:0.2f}V',
- labelOpts={'color': (0,200,200), 'movable': True, 'fill': (0, 0, 200, 100)})
- inf1.setPos([0,0])
- inf2.setPos([0,1])
- p.addItem(inf1)
- p.addItem(inf2)
- curve = p.plot(pen='y') # 曲线控件
- #============参数调节窗口=====================#
- children=[
- dict(name='采样配置', title='采样配置', type='group', children=[
- dict(name='采样率', type='float', limits=[0.0001, 857.143], value=Fs/1000, units='kHz'),
- dict(name='重装载值', type='int', limits=[2, 65535], value=arr),
- dict(name='分频因子', type='int', limits=[1, 65536], value=div),
- dict(name='采样点数', type='int', limits=[0, 512], value=N),
- dict(name='比例系数', type='float', value=1),
- ]),
- dict(name='虚拟串口', type='str', value=COM),
- dict(name='波特率', type='int', limits=[4800, 20000000], value=1000000),
- dict(name='触发', type='float', value=inf2.getYPos(), units='V'),
- dict(name='时基', type='float', value=inf1.getXPos(), units='ms'),
- dict(name='曲线样式', type='pen', value=pg.mkPen()),
- dict(name='贝塞尔插值', type='bool', value=True),
- ]
- params = ptree.Parameter.create(name='调整参数', type='group', children=children)
- def onChanged0(param, val):
- global arr,div,Fs,Ts
- temp = int(72000/val/arr)
- if 1 < temp < 65536:
- params.child('采样配置').child('分频因子').setValue(temp)
- else:
- params.child('采样配置').child('分频因子').setValue(1)
- temp = int(72000/val)
- if 2 < temp < 65536:
- params.child('采样配置').child('重装载值').setValue(temp)
- def onChanged1(param, val):
- global arr,div,Fs,Ts
- if 72000000/val/div > 857143:
- params.child('采样配置').child('重装载值').setValue(arr)
- return
- arr = val
- Fs = 72000000/arr/div
- Ts = 1/Fs
- params.child('采样配置').child('采样率').setValue(Fs/1000)
- def onChanged2(param, val):
- global arr,div,Fs,Ts
- if 72000000/val/arr > 857143:
- params.child('采样配置').child('分频因子').setValue(div)
- return
- div = val
- Fs = 72000000/arr/div
- Ts = 1/Fs
- params.child('采样配置').child('采样率').setValue(Fs/1000)
- def onChanged3(param, val):
- global N
- N = val
- def onChanged4(param, val):
- inf1.setPos([val,0])
- def onChanged5(param, val):
- inf2.setPos([0,val])
- def onPenChanged(param, pen):
- curve.setPen(pen)
- params.child('采样配置').child('采样率').sigValueChanged.connect(onChanged0)
- params.child('采样配置').child('重装载值').sigValueChanged.connect(onChanged1)
- params.child('采样配置').child('分频因子').sigValueChanged.connect(onChanged2)
- params.child('采样配置').child('采样点数').sigValueChanged.connect(onChanged3)
- params.child('时基').sigValueChanged.connect(onChanged4)
- params.child('触发').sigValueChanged.connect(onChanged5)
- params.child('曲线样式').sigValueChanged.connect(onPenChanged)
- def On_inf1Changed():
- params.child('时基').setValue(inf1.getXPos())
- inf1.sigPositionChanged.connect(On_inf1Changed)
- def On_inf2Changed():
- params.child('触发').setValue(inf2.getYPos())
- inf2.sigPositionChanged.connect(On_inf2Changed)
- pt = ptree.ParameterTree(showHeader=False)
- pt.setParameters(params)
- # 按钮
- StartBtn = QtGui.QPushButton('开始')
- StopBtn = QtGui.QPushButton('暂停')
- ContinueBtn = QtGui.QPushButton('继续')
- EndBtn = QtGui.QPushButton('停止')
- run_flag = False
- def On_Start():
- global run_flag,ser
- try:
- ser = serial.Serial(params.child('虚拟串口').value(),params.child('波特率').value())
- run_flag = True
- StartBtn.setEnabled(False)
- StopBtn.setEnabled(True)
- ContinueBtn.setEnabled(False)
- EndBtn.setEnabled(True)
- except:
- QtWidgets.QMessageBox(QMessageBox.Warning, '警告', '虚拟串口打开失败').exec_()
-
- def On_Continue():
- global run_flag
- run_flag = True
- StartBtn.setEnabled(False)
- StopBtn.setEnabled(True)
- ContinueBtn.setEnabled(False)
- EndBtn.setEnabled(True)
- def On_Stop():
- global run_flag
- run_flag = False
- StartBtn.setEnabled(False)
- StopBtn.setEnabled(False)
- ContinueBtn.setEnabled(True)
- EndBtn.setEnabled(False)
- def On_End():
- global run_flag,ser
- ser.close()
- run_flag = False
- StartBtn.setEnabled(True)
- StopBtn.setEnabled(False)
- ContinueBtn.setEnabled(False)
- EndBtn.setEnabled(False)
- StartBtn.clicked.connect(On_Start)
- StopBtn.clicked.connect(On_Stop)
- ContinueBtn.clicked.connect(On_Continue)
- EndBtn.clicked.connect(On_End)
- StartBtn.setEnabled(True)
- StopBtn.setEnabled(False)
- ContinueBtn.setEnabled(False)
- EndBtn.setEnabled(False)
- #================主窗口=====================#
- win = QtGui.QMainWindow()
- win.resize(1000,600)
- win.setWindowTitle("CH32示波器-By纯粹")
- #================主窗口内添加控件=====================#
- cw = QtGui.QWidget()
- win.setCentralWidget(cw)
- layout = QtGui.QGridLayout()
- layout.addWidget(w, 1, 1, 6, 1)
- layout.addWidget(pt, 1, 2, 1, 2)
- layout.addWidget(StartBtn, 2, 2, 1, 2)
- layout.addWidget(StopBtn, 3, 2, 1, 2)
- layout.addWidget(ContinueBtn, 4, 2, 1, 2)
- layout.addWidget(EndBtn, 5, 2, 1, 2)
- cw.setLayout(layout)
- win.show()
- #================打开虚拟串口=====================#
- # ser = serial.Serial(params.child('虚拟串口').value(),params.child('波特率').value())
- #=======滑动均值滤波器======#
- fps_buf = np.linspace(0,0,100)
- fps_buf_ptr = 0
- def fps_mean(fps):
- global fps_buf,fps_buf_ptr
- fps_buf[fps_buf_ptr] = fps
- fps_buf_ptr += 1
- if fps_buf_ptr >= 100:
- fps_buf_ptr = 0
- return np.mean(fps_buf)
- #==========================#
- #=======滑动均值滤波器======#
- fc_buf = np.linspace(0,0,20)
- fc_buf_ptr = 0
- def fc_mean(fc):
- global fc_buf,fc_buf_ptr
- fc_buf[fc_buf_ptr] = fc
- fc_buf_ptr += 1
- if fc_buf_ptr >= 20:
- fc_buf_ptr = 0
- return np.mean(fc_buf)
- #==========================#
- fps_t0 = cnt = data0 = data1 = 0
- start_flag = False
- data_buf = np.array([]) # 数据缓存
- #更新数据并显示波形
- def display_data():
- global cnt,start_flag,data0,data1,data_buf,t0,fps_t0,run_flag
- if run_flag == False:
- return
- if start_flag:
- buf_len = ser.in_waiting
- if buf_len == N*2:
- num = buf_len>>1
- temp = struct.unpack('>%dH'%(num),ser.read(num*2)) # 读取并解析数据
- if temp[0] > 4096: # 12位ADC数据小于4096
- ser.read() # 数据出错,清空接收缓存
- return
- if len(data_buf) < N:
- data_buf = np.append(data_buf,temp)
- else:
- data_buf = np.append(data_buf,temp)
- data_buf = data_buf[-N:]
- output = data_buf*k*params.child('采样配置').child('比例系数').value() # 单位转换 转化为电压
- output_len = len(output)
- t = np.linspace(-500*Ts*output_len,500*Ts*output_len,output_len) # 生成初始时间轴 单位ms
- if params.child('贝塞尔插值').value():
- t_new = np.linspace(-500*Ts*output_len,500*Ts*output_len,output_len*10)
- tck = interpolate.splrep(t, output)
- output_bspline = interpolate.splev(t_new, tck)
- output_bspline_len = len(output_bspline)
- comparator = np.array(output_bspline > inf2.getYPos(),dtype='int8')
- rising = np.where(np.diff(comparator) == 1)[0]
- if len(rising) > 1:
- dt = np.mean(np.diff(rising))*Ts/10
- fc = fc_mean(1/dt)
- start_point = int(output_bspline_len/2+inf1.getXPos()/Ts/100) # 开始触发点索引
- end_point_index = np.where( rising > start_point)[0]
- if len(end_point_index) > 0:
- # 触发成功生成新时间轴 单位ms
- t_new = np.linspace(-100*Ts*(rising[end_point_index[0]]+1)+inf1.getXPos(),
- 100*Ts*(output_bspline_len-rising[end_point_index[0]]-1)+inf1.getXPos(),output_bspline_len)
- else:
- fc = 0
- curve.setData(t_new,output_bspline) # 绘制曲线
- else:
- comparator = np.array(output > inf2.getYPos(),dtype='int8')
- rising = np.where(np.diff(comparator) == 1)[0]
- if len(rising) > 1:
- dt = np.mean(np.diff(rising))*Ts
- fc = fc_mean(1/dt)
- start_point = int(output_len/2+inf1.getXPos()/Ts/1000) # 开始触发点索引
- end_point_index = np.where( rising > start_point)[0]
- if len(end_point_index) > 0:
- # 触发成功生成新时间轴 单位ms
- t = np.linspace(-1000*Ts*(rising[end_point_index[0]]+1)+inf1.getXPos(),
- 1000*Ts*(output_len-rising[end_point_index[0]]-1)+inf1.getXPos(),output_len)
- else:
- fc = 0
- curve.setData(t,output) # 绘制曲线
- fps_t = fps_mean(time.time()-fps_t0) # 帧率均值滤波
- fps_t0 = time.time()
- p.setTitle('CH32示波器 %0.2f fps Fs = %0.3f kHz fc = %0.3f Hz' % (1/fps_t,Fs/1000,fc))
- cnt += num
- if cnt >= N or time.time() - t0 > N*Ts+0.01:# 接收数据满或接收超时
- cnt = 0
- start_flag = False
- else:
- ser.read(ser.in_waiting)
- data_buf = np.array([])
- result=ser.write(bytes([0xa5, 0x5a, arr>>8, arr&0xff, div>>8, div&0xff, N>>8, N&0xff, 0xff]))# 发送数据
- t0 = time.time()
- while time.time() - t0 < N*Ts+0.01:# 等待回应
- if ser.in_waiting:
- start_flag = True
- t0 = time.time()
- break
- timer = pg.QtCore.QTimer()
- timer.timeout.connect(display_data)
- timer.start(0)
- app.exec_()
复制代码
四、性能测试
1 再造信号发送器
对比STM32和CH32惊奇的发现CH32还有DAC功能,正好根据官方库撸个信号发送器代码,如下:
- /********************************** (C) COPYRIGHT *******************************
- * File Name : main.c
- * Author : WCH
- * Version : V1.0.0
- * Date : 2019/10/15
- * Description : Main program body.
- *******************************************************************************/
- #include "debug.h"
- #include "math.h"
- /* Global define */
- #define LED PCout(13)
- #define buf_len 50
- /* Global Variable */
- u32 DAC_Buf[buf_len];
- /*******************************************************************************
- * Function Name : Gpio_Init
- * Description : Initializes GPIO collection.
- * Input : None
- * Return : None
- *******************************************************************************/
- void Gpio_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOC, &GPIO_InitStructure);
- GPIO_SetBits(GPIOC,GPIO_Pin_13);
- }
- /*******************************************************************************
- * Function Name : Dac_Init
- * Description : Initializes DAC collection.
- * Input : None
- * Return : None
- *******************************************************************************/
- void Dac_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- DAC_InitTypeDef DAC_InitType;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA, ENABLE );
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE );
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- GPIO_SetBits(GPIOA,GPIO_Pin_4);
- DAC_InitType.DAC_Trigger=DAC_Trigger_T3_TRGO;
- DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;
- DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ;
- DAC_Init(DAC_Channel_1,&DAC_InitType);
- DAC_Cmd(DAC_Channel_1, ENABLE);
- DAC_DMACmd(DAC_Channel_1,ENABLE);
- }
- /*******************************************************************************
- * Function Name : DAC1_DMA_INIT
- * Description : Initializes DMA of DAC1 collection.
- * Input : None
- * Return : None
- *******************************************************************************/
- void DAC1_DMA_Init(void)
- {
- DMA_InitTypeDef DMA_InitStructure;
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
- DMA_StructInit( &DMA_InitStructure);
- /* Note:DAC1--->DMA1.CH3 DAC2--->DMA1.CH4 */
- DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(DAC->R12BDHR1);
- DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DAC_Buf;
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
- DMA_InitStructure.DMA_BufferSize = buf_len*4;
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
- DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Word;
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
- DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
- DMA_Init(DMA1_Channel3, &DMA_InitStructure);
- DMA_Cmd(DMA1_Channel3, ENABLE);
- }
- /*******************************************************************************
- * Function Name : DAC_Data_Init
- * Description : Initializes Data of DMA.
- * Input : None
- * Return : None
- *******************************************************************************/
- void DAC_Data_Init(void)
- {
- uint32_t Idx = 0;
- for (Idx = 0; Idx < buf_len; Idx++)
- {
- DAC_Buf[Idx] = 2000*sin(Idx*3.14159265*2/buf_len)+2048;
- }
- }
- /*******************************************************************************
- * Function Name : TIM3_Init
- * Description : Initializes TIM3 collection.
- * Input : arr: TIM_Period
- * psc: TIM_Prescaler
- * Return : None
- *******************************************************************************/
- void TIM3_Init(u16 arr,u16 psc)
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- GPIO_InitTypeDef GPIO_InitStructure;
- TIM_OCInitTypeDef TIM_OCInitStructure;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA,ENABLE);
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
- TIM_TimeBaseStructure.TIM_Period = arr-1;
- TIM_TimeBaseStructure.TIM_Prescaler = psc-1;
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
- TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//下面详细说明
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//TIM_OutputState_Disable;
- TIM_OCInitStructure.TIM_Pulse = arr>>1;
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//如果是PWM1要为Low,PWM2则为High
- TIM_OC1Init(TIM3, &TIM_OCInitStructure);
- TIM_Cmd(TIM3, ENABLE);
- }
- /*******************************************************************************
- * Function Name : main
- * Description : Main program.
- * Input : None
- * Return : None
- *******************************************************************************/
- int main(void)
- {
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
- Delay_Init();
- Gpio_Init();
- Dac_Init();
- DAC_Data_Init();
- DAC1_DMA_Init();
- TIM3_Init(10,36); //溢出率200kHz产生1kHz正弦波
- while(1)
- {
- LED = !LED;
- Delay_Ms(500);
- }
- }
复制代码
2 方波测试
2.1 1kHz方波量结果
2.2 10kHz方波量结果
2.3 100kHz方波量结果
2.4 400kHz方波量结果
3 正弦波测试
3.1 50Hz正弦波量结果
3.2 1kHz正弦波量结果
3.3 10kHz正弦波量结果
提示:CH32 DAC未便产生高频正弦波。测量结果仅供参考
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |