前言
玩电子不能没有示波器,毕业后也无法再白嫖学校示波器。俗话说:没有条件创造条件。那没有示波器,就创造一个示波器。听起来高大上,其实并不难,网上也有许多开源示波器,最具代表性的是老刘示波器。虽然电路简单,功能强大,懒得动手党来说也有很大的门槛,不想焊接,不想画PCB,更不想挑各种元器件,就想要一个简单实用、成本低廉、快速实现、方便拓展的示波器,懒得找了,还是自己造轮子吧。
提示:本项目仅使用某宝成品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 .\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规则组触发源
void TIM_ReSet(u16 Period,u16 Prescaler)
{
static TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
static TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseStructure.TIM_Period = Period-1;
TIM_TimeBaseStructure.TIM_Prescaler = Prescaler-1;
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_OCInitStructure.TIM_Pulse = Period>>1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC4Init(TIM4, &TIM_OCInitStructure);
TIM_Cmd(TIM4, ENABLE);
}
2.2 DMA配置函数
搬运到
ADC->DR数据寄存器
ADC_Buf数组
u16 buf_len = 512;
u16 ADC_Buf[512]={0};
void DMA_ReSet(u16 len)
{
static DMA_InitTypeDef DMA_InitStructure;
buf_len = len;
DMA_ClearFlag(DMA1_FLAG_TC1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;
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_InitStructure.DMA_Priority = DMA_Priority_High ;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
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;
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);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
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;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_DMACmd(ADC1, ENABLE);
DMA_DeInit(DMA1_Channel1);
DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);
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);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)){};
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_RegularChannelConfig(ADC1, 1, 1, ADC_SampleTime_1Cycles5);
TIM_InternalClockConfig(TIM4);
TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
TIM_UpdateDisableConfig(TIM4, DISABLE);
}
2.4 USB串口中断服务函数
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 5A | XX XX | XX XX | XX XX | FF |
2.5 DMA传输完成中断
USB发送所有缓存
清空缓存并关闭TIM4和DMA
清楚中断标志位
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);
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;
TIM_TimeBaseStructure.TIM_Prescaler = Prescaler-1;
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_OCInitStructure.TIM_Pulse = Period>>1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
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);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_Cmd(TIM2, ENABLE);
}
int main(void)
{
Buf_Dataset(LED_Breathe_Buf);
delay_init();
LED_Init();
USB_Init();
ADC1_CH1_Init();
TIM2_Init(1200,60);
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'
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()
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:
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)
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:
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:
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功能,正好根据官方库撸个信号发送器代码,如下:
#include "debug.h"
#include "math.h"
#define LED PCout(13)
#define buf_len 50
u32 DAC_Buf[buf_len];
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);
}
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);
}
void DAC1_DMA_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_StructInit( &DMA_InitStructure);
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);
}
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;
}
}
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_OCInitStructure.TIM_Pulse = arr>>1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_Cmd(TIM3, ENABLE);
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
Gpio_Init();
Dac_Init();
DAC_Data_Init();
DAC1_DMA_Init();
TIM3_Init(10,36);
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不便产生高频正弦波。测量结果仅供参考
|