
#训练营#基于立创STM32的数字示波器
简介
原理图上的GD32不用理会,就是我找不到STM32才用GD32,一样的插上去就可以用了
简介:原理图上的GD32不用理会,就是我找不到STM32才用GD32,一样的插上去就可以用了开源协议
:TAPR Open Hardware License
(未经作者授权,禁止转载)描述
|
实验 项目 名称 |
基于立创STM32的数字示波器(数字方波示波集成测试) |
实验项目类型 |
||||
|
验证 |
演示 |
综合 |
设计 |
其他 |
||
|
|
|
* |
* |
|
||
|
1. 系统设计方案分析 (1) 设计原理: 数字方波示波集成测试仪是一种产生方波、显示电信号波形的仪器,主要由以下电路: 模拟前端处理电路: 负责将输入的检测模拟信号进行处理后给单片机进行识别,具体电路包括了交直流耦合选择电路、电压衰减电路、信号处理电路以及频率检测电路,是整个电路的核心。 电源电路: 负责给运放提供正负电源以及系统供电,是保障电路正常运行的基础; 单片机电路: 给系统提供控制核心,负责对输入信号的采集与处理输出工作; 人机交互电路: 用于控制示波器功能,包括按键、旋钮、LED灯、显示屏以及其它输入输出接口,为系统功能的开发提供基础。 数字方波示波集成测试仪具有以下功能: ① 在TFT屏幕上显示采集的波形; ② 支持通过旋钮对波形进行放大与缩小查看; ③ 支持输出1K、2K、4K方波信号,能够被自身输入检测到并显示在示波器上; ④ 支持通过按键调整输出方波信号的占空比 ⑤ 支持稳定信号的频率测量和幅值测量 (2)设计步骤: 1.硬件原理图设计 使用嘉立创EDA画出相应的电路原理图和PCB布局图。电路原理图如下
PCB布局图如下: 3D预览图如下:
2.软件程序设计 ① LED和按键控制配置: Ø 开启GPIO的端口时钟 Ø 配置GPIO的模式 Ø 配置GPIO的输出 ② 串口配置: Ø 开启时钟(包括串口时钟和GPIO时钟) Ø 配置GPIO复用模式 Ø 配置GPIO的模式 Ø 配置GPIO的输出 Ø 配置串口(配置串口参数) Ø 使能串口(串口使能和发送使能) ③ 外部中断配置: Ø 开启时钟(包括GPIO时钟和系统配置时钟) Ø 配置GPIO的模式 Ø 使能NVIC中断并配置优先级 Ø 配置GPIO中断 Ø 使能中断和清除中断标志位 Ø 编写中断服务函数 ④ ADC配置: Ø 配置时钟 Ø 配置引脚 Ø 配置ADC模式 Ø 配置数据对齐方式 Ø 配置分辨率 Ø 配置采集通道与通道长度 Ø 配置触发方式 Ø 使能ADC Ø 开启校准 ⑤ 定时器配置: Ø 开启时钟(定时器时钟) Ø 配置定时器参数 Ø 配置中断优先级 Ø 使能中断事件和定时器 Ø 编写中断服务函数 ⑥ PWM配置: Ø 配置通道引脚GPIO Ø 配置定时器 Ø 配置输出结构体 Ø 配置定时器输出通道 Ø 配置定时器输出通道占空比 Ø 定时器自动重载使能 ⑦ 输入捕获配置: Ø 配置通道引脚GPIO&时钟 Ø 配置定时器 Ø 配置定时器输入通道 Ø 配置定时器中断 Ø 使能定时器 Ø 编写中断服务函数 ⑧ TFT屏幕驱动配置: Ø 配置TFT屏幕引脚GPIO&时钟 Ø 配置SPI驱动引脚&时钟 Ø 配置SPI通信参数 Ø 使能SPI Ø 编写SPI发送数据函数 Ø 编写TFT屏幕发送数据函数 Ø 编写TFT屏幕发送命令函数 Ø 编写TFT屏幕设置起始地址函数 Ø 编写TFT屏幕初始化函数 Ø 移植厂商屏幕驱动函数
2.系统主要功能电路模块分析 (1) 模拟前端处理电路 在整个示波器电路设计过程中,模拟前端处理电路是最为重要的,其中大量应用了模拟电路的知识,其中包括 ① 输入交直流耦合切换电路 SW2开关2与1引脚连接时为DC直流耦合,SW2开关2与3引脚连接时为AC交流耦合 信号类型可以分为直流信号和交流信号,现实中的信号往往都是都不是理想波形。比如直流电源信号应该是一条水平的直流信号,但都会存在电源纹波(交流信号):在采集交流信号时也可能混入直流信号对波形的峰峰值造成影响。为了保障对输入交流信号的准确测量,利用电容通交隔直的特性,将电容串联到电路中就可以过滤到信号中的直流分量。
② 输入信号衰减电路 信号经过交直流耦合选择电路后由开关sW3选择两个通道,开关2和3接到一起时,输入信号直接流入后级的电压跟随器电路:当开关2和1接到一起时,输入信号经过R6、R11、R13三个电阳构成的电阳分压网络后将信号衰减到了1/50倍,由此可知,当输入信号幅值较小时,可优先选择低压档位,如果测量时不确定输入信号幅值可先用高压档位测量后如满足低压范围内,可用低压档位测量以得到更为精准的测量结果,同时保护电路。
③ 信号调理电路 电压跟随器 电压跟随器的特点是输出的电压幅度和极性都与输入电压相同,且具有输入阻抗高,输出阳抗低的特点,常用于做缓冲级和隔离级。
比例放大器:输入信号从运算放大器的输入端输入,具有输出极性相同且放大输入信号的功能。
得到以下结论: 当SW3开关2和3接到一起时,可以测量的输入信号幅值为-1.6V-5V 当SW3开关2和1接到一起时,可以测量的输入信号幅值为-80V-250V
④ 比较器测频电路 为了实现频率检测的功能,将ADC输入信号通过一个滞回比较器对输入信号进行比较,实现频率的测量功能。滞回比较器是属于电压比较器中的一种,常规的电压比较器是一个单限比较器,电路中只有一个阈值电压,但在输入电压在阈值附近有微小变化时都会引起输出电压的越变。为了增强电路的抗干扰能力,在单限比较器的基础上引入了正反馈,保障了在一定范围内信号的稳定性。通过滞回比较器电路后输出一个方波信号,使用单片机的定时器捕获功能计算出输入波形的周期大小。
(2) 电源控制电路 除了电源输入电路外,为保障运算放大器对于负电压的测量性能,使用了XD7660负压产生电路得到个负电压,该芯片外围电路简单,只需要两个电容和一个二极管即可工作,理论上输入电压为+5V,也可以输出一个-5V的电压,由于芯片内部存在一定压降及转换效率,实际测量负电压为-4.3V左右,也能满足运算放大器的要求。
(3) 人机交互电路 1.8TFT 是一款彩色显示屏,具有 128x160 个彩色像素,使用四线SPI通信方式与单片机进行连接,一共有八根引脚,模块引脚说明及与单片机连接情况如上所示:
旋转编码器属于一种特殊的按键,系统使用的EC11旋转编码器有五个引脚,其中DE两个引脚类似于普通按键引脚,按下导通,松手断开,其余ABC三个引脚用于检测旋钮的转动方向,c脚为公共端,直接接地就行。在旋转编码器时,A和B两个信号引脚存在相位差,也就是有一个引脚信号变化后另一个引脚信号再跟着变化,即两个引脚不同时变化,通过检测哪个引脚先变就能判断是正转还是反转功能。
LED指示电路设计比较简答,采用低电平驱动的方式,当单片机引脚输出为低电平时,LED两端存在电势差,LED点亮:当单片机引脚输出为高电平时,LED灯熄灭:
系统使用了三个独立按键对系统进行控制,三个按键一侧直接接地,另一侧连接到单片机引脚,当单片机引脚检测到按键按下时,单片机引脚直接接到GND接地,单片机收到该引脚接地信号的反馈后再去实现对应的功能,为节约硬件成本,可以在软件设计时引入消抖功能,避免机械按键抖动时的误触发。
除了示波器检测功能外,单独引出了一个PWM信号用于模拟一个简易的函数发生器功能,可以通过改变输出PWM的频率和占空比输出一个简易方波信与输出。
3.系统程序设计分析 (1) TFT显示屏主要代码分析 * 函数内容:TFT发送单个字节数据 * 函数参数:无 * 返回值:无 void TFT_WR_DATA8(uint8_t data) { SPI_TFT_CS_LOW(); //拉低片选信号 SPI0_Write(data); SPI_TFT_CS_HIGH(); //拉高片选信号 }
* 函数内容:TFT发送2个字节数据 * 函数参数:无 * 返回值:无 void TFT_WR_DATA(uint16_t data) { SPI_TFT_CS_LOW(); //拉低片选信号 SPI0_Write(data>>8); SPI0_Write(data); SPI_TFT_CS_HIGH(); //拉高片选信号 }
* 函数内容:TFT发送命令数据 * 函数参数:无 * 返回值:无 void TFT_WR_REG(uint8_t reg) { SPI_TFT_CS_LOW(); //拉低片选信号 TFT_DC_LOW(); //拉低片选信号 SPI0_Write(reg); TFT_DC_HIGH(); //拉高命令信号 TFT_DC_HIGH();; //拉高片选信号 }
void TFT_Address_Set(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2) { if(USE_HORIZONTAL==0) { TFT_WR_REG(0x2a);//列地址设置 TFT_WR_DATA(x1); TFT_WR_DATA(x2); TFT_WR_REG(0x2b);//行地址设置 TFT_WR_DATA(y1); TFT_WR_DATA(y2); TFT_WR_REG(0x2c);//储存器写 } else if(USE_HORIZONTAL==1) { TFT_WR_REG(0x2a);//列地址设置 TFT_WR_DATA(x1); TFT_WR_DATA(x2); TFT_WR_REG(0x2b);//行地址设置 TFT_WR_DATA(y1); TFT_WR_DATA(y2); TFT_WR_REG(0x2c);//储存器写 } else if(USE_HORIZONTAL==2) { TFT_WR_REG(0x2a);//列地址设置 TFT_WR_DATA(x1); TFT_WR_DATA(x2); TFT_WR_REG(0x2b);//行地址设置 TFT_WR_DATA(y1); TFT_WR_DATA(y2); TFT_WR_REG(0x2c);//储存器写 } else { TFT_WR_REG(0x2a);//列地址设置 TFT_WR_DATA(x1); TFT_WR_DATA(x2); TFT_WR_REG(0x2b);//行地址设置 TFT_WR_DATA(y1); TFT_WR_DATA(y2); TFT_WR_REG(0x2c);//储存器写 } }
void TFT_Init(void) { TFT_GPIO_Init(); Init_SPI0_GPIO();
TFT_REST_LOW(); //复位 delay_ms(100); TFT_REST_HIGH(); // delay_ms(100);
TFT_BLK_HIGH(); //打开背光 delay_ms(100); (2) ADC主要代码分析 static void ADCx_Mode_Config(void) { //ADC句柄 ADC_InitTypeDef ADC_InitStructure; // 打开ADC时钟 ADC_APBxClock_FUN ( ADC_CLK, ENABLE ); // ADC 模式配置 // 只使用一个ADC,属于独立模式 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
// 禁止扫描模式,多通道才要,单通道不需要 ADC_InitStructure.ADC_ScanConvMode = DISABLE ; // 连续转换模式 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 不用外部触发转换,软件开启即可 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 转换结果右对齐 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 转换通道1个 ADC_InitStructure.ADC_NbrOfChannel = 1; // 初始化ADC ADC_Init(ADCx, &ADC_InitStructure); // 配置ADC时钟为PCLK2的8分频,即9MHz RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置 ADC 通道转换顺序为1,第一个转换,采样时间为239.5个时钟周期 ADC_RegularChannelConfig(ADC1,ADC_CHANNEL,1, ADC_SampleTime_239Cycles5); // 使能ADC DMA 请求 ADC_DMACmd(ADC1, ENABLE); // 开启ADC ,并开始转换 ADC_Cmd(ADC1, ENABLE); // 初始化ADC 校准寄存器 ADC_ResetCalibration(ADC1); // 等待校准寄存器初始化完成 while(ADC_GetResetCalibrationStatus(ADC1)); // ADC开始校准 ADC_StartCalibration(ADC1); // 等待校准完成 while(ADC_GetCalibrationStatus(ADC1)); /* 使能ADC1DMA */ ADC_DMACmd(ADC1, ENABLE); /* 由于没有采用外部触发,所以使用软件触发ADC转换 */ ADC_SoftwareStartConvCmd(ADC1, ENABLE); }
(3) 输入捕获主要代码分析 void TIM3_IRQHandler(void) { // 当要被捕获的信号的周期大于定时器的最长定时时,定时器就会溢出,产生更新中断 // 这个时候我们需要把这个最长的定时周期加到捕获信号的时间里面去 //TIM_GetITStatus():查询是否发生超时中断 if ( TIM_GetITStatus ( GENERAL_TIM3, TIM_IT_Update) != RESET ) { //超时自增 TIM_ICUserValueStructure.Capture_Period ++; //清除超时标志位 TIM_ClearITPendingBit ( GENERAL_TIM3, TIM_FLAG_Update ); }
// 上升沿捕获中断 if ( TIM_GetITStatus (GENERAL_TIM3, GENERAL_TIM3_IT_CCx ) != RESET) { // 第一次捕获 if ( TIM_ICUserValueStructure.Capture_StartFlag == 0 ) { // 计数器清0 TIM_SetCounter ( GENERAL_TIM3, 0 ); // 自动重装载寄存器更新标志清0 TIM_ICUserValueStructure.Capture_Period = 0; // 存捕获比较寄存器的值的变量的值清0 TIM_ICUserValueStructure.Capture_CcrValue = 0;
// // 当第一次捕获到上升沿之后,就把捕获边沿配置为下降沿 // GENERAL_TIM3_OCxPolarityConfig_FUN(GENERAL_TIM3, TIM_ICPolarity_Falling); // 开始捕获标准置1 TIM_ICUserValueStructure.Capture_StartFlag = 1; } // 下降沿捕获中断 else if(TIM_ICUserValueStructure.Capture_StartFlag == 1 ) // 第二次捕获 { // 获取捕获比较寄存器的值,这个值就是捕获到信号周期时间 TIM_ICUserValueStructure.Capture_CcrValue = GENERAL_TIM3_GetCapturex_FUN(GENERAL_TIM3); // // 当第二次捕获到下降沿之后,就把捕获边沿配置为上升沿,捕获上升沿,完成周期测量 // GENERAL_TIM3_OCxPolarityConfig_FUN(GENERAL_TIM3, TIM_ICPolarity_Rising); //自动重装载寄存器更新标志*定时器溢出时间+定时器捕获时间,定时器给的是1M的时钟,时间单位需要进行换算才能得到秒/次 oscilloscope.gatherFreq=(1.0f/(TIM_ICUserValueStructure.Capture_Period * (GENERAL_TIM3_PERIOD+1) + (TIM_ICUserValueStructure.Capture_CcrValue+1)))*1000000; // 开始捕获标志清0 TIM_ICUserValueStructure.Capture_StartFlag = 0; // 捕获完成标志置1 TIM_ICUserValueStructure.Capture_FinishFlag = 1; } TIM_ClearITPendingBit (GENERAL_TIM3,GENERAL_TIM3_IT_CCx); } }
4.主函数代码分析 void Init_Oscilloscope(volatile struct Oscilloscope *value); int main(void) { uint16_t i=0; float median=0;//中间值 float voltage=0,vpp=0; //峰峰值 float max_data=1.0f; //触发电压值 float gainFactor=0; //波形放大倍数 uint16_t Trigger_number=0; //触发沿标记 Init_Oscilloscope(&oscilloscope);//初始化示波器参数 SysTick_Init();//时钟初始化 Init_LED_GPIO(); //LED初始化 TFT_Init(); //屏幕初始化 ADC_DMA_Start();//初始化ADC\DMA配置 Init_EC11_GPIO(); //初始化EC11引脚 Init_Key_GPIO();//初始化按键引脚 Init_PWM_Output(oscilloscope.timerPeriod-1,oscilloscope.pwmOut);//初始化PWM输出 Init_FreqTimer();//初始化频率定时器 TFT_Fill(0,0,160,128,BLACK);//填充白色 TFT_StaticUI();//初始化静态UI while(1) { //按键扫描处理函数 Key_Handle(&oscilloscope); //如果获取电压值完成,开始刷屏 if(oscilloscope.showbit==1) { oscilloscope.showbit=0; oscilloscope.vpp=0; //转换电压值 for(i=0;i<300;i++) { oscilloscope.voltageValue[i]=(Get_ADC_Value(i)*3.3f)/4096.0f;
//获取整段数据的峰值电压 vpp=(5-(2.0f*oscilloscope.voltageValue[i])); // vpp=oscilloscope.voltageValue[i]; if((oscilloscope.vpp) < vpp) { oscilloscope.vpp = vpp; } if(oscilloscope.vpp <= 0.3) { oscilloscope.gatherFreq=0; } }
//刷屏的同时获取电压值 DMA_Cmd(DMA1_Channel1,ENABLE);
//搜索触发电压 for(i=0;i<200;i++) { //找到一个小于触发电压的采集点 if(oscilloscope.voltageValue[i] < max_data) { for(;i<200;i++) { //在小于触发电压点后再找一个大于触发电压的采集点,以此点为触发电压绘制波形 if(oscilloscope.voltageValue[i] > max_data) { Trigger_number=i; break; } } break; } }
//如果幅值过小,会出现放大倍数过大导致波形显示异常的问题 if(oscilloscope.vpp > 0.3) { //计算中间幅度,输入最低点是2.5V,最高是(5-vpp)/2,中间幅度就是最低值减去最高值/2, median = (2.5-(5 - oscilloscope.vpp)/2.0f)/2.0f;
//放大倍数,需要确定放大之后的区间,我将波形固定显示在(18.75~41.25中),(41.25-18.75)/2=11.25f gainFactor = 11.25f/median;
//最低的减去中间幅度等于中间值 median = 2.5 - median; }
//依次显示后续100个数据,这样可以防止波形滚动 for(i=Trigger_number;i { if(oscilloscope.keyValue == KEYD) { oscilloscope.keyValue=0; do { if(oscilloscope.keyValue == KEYD){ oscilloscope.keyValue=0; break; } }while(1); } voltage=oscilloscope.voltageValue[i]; if(voltage >= median) { voltage = 30 - (voltage - median)*gainFactor; } else { voltage = 30 + (median - voltage)*gainFactor; } drawCurve(80,voltage); } } } }
* 函数内容:初始化示波器参数结构体 * 函数参数:volatile struct Oscilloscope *value--示波器参数结构体指针 * 返回值:无 void Init_Oscilloscope(volatile struct Oscilloscope *value) { (*value).showbit =0; //清除显示标志位 (*value).sampletime =ADC_SampleTime_239Cycles5; //adc采样周期 (*value).keyValue =0; //清楚按键值 (*value).ouptputbit =0; //输出标志位 (*value).gatherFreq =0; //采集频率 (*value).outputFreq =1000; //输出频率 (*value).pwmOut =500; //PWM引脚输出的PWM占空比 (*value).timerPeriod=1000; //PWM输出定时器周期 (*value).vpp =0.0f; //峰峰值 }
5.系统调试结果分析 采用5V电源通过USB供电,通过调试,各电路和功能模块均正常工作,可实现以下内容: ⑥ 在1.8寸TFT屏幕上显示波形; ⑦ 支持通过旋钮对波形进行放大与缩小查看; ⑧ 支持输出1K、2K、4K方波信号,能够被自身输入检测到并显示在示波器上; ⑨ 支持通过按键调整占空比 ⑩ 支持频率测量 实物如下:
6.总结 1. 我们成功实现了基于STM32F103核心板的数字方波示波集成测试仪基本功能。 2. 我们掌握了数字方波示波集成测试仪的使用方法,以及STM32F103核心板的编程方法,可以熟练使用STM32的常用外设,并对项目进行必要的调试。 3.通过制作本项目提高了嵌入式系统设计与实际应用能力。 3. 不足之处: 系统除了方波外,还不能输出其他波形。 系统的量程梯度只有2个(-1.6V-5V,-80V-250V),还可以再细分。 |
||||||
电子元件有缺:没有0Ω电阻,本工程用了铁丝代替。
示波器无输入时有干扰,会出现乱波,磕几下更乱了,用电脑或者充电宝供电波形会稳很多。
设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM
克隆工程















评论