0 前言
🔥
这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。
为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是
🚩 毕业设计 基于单片机的示波器设计与实现(源码+硬件+论文)
🥇学长这里给一个题目综合评分(每项满分5分)
- 难度系数:3分
- 工作量:3分
- 创新点:4分
🧿 项目分享:文章来源:https://www.toymoban.com/news/detail-460512.html
https://gitee.com/sinonfin/sharing文章来源地址https://www.toymoban.com/news/detail-460512.html
1 主要功能
本机采用芯片:原装进口STC8A8K64S4A12(LQFP48封装)
量程:0~30V
采样率:250KHz
操作说明:
主界面-选项设置模式:
单击编码器:切换开始/暂停采样
双击编码器:切换波形水平滚动模式/选项设置模式
长按编码器:进入设置界面
旋转编码器:修改当前参数
按下同时旋转编码器:在参数间切换
主界面-波形水平滚动模式:
单击编码器:切换开始/暂停采样
双击编码器:切换波形水平滚动模式/选项设置模式
长按编码器:进入设置界面
旋转编码器:水平滚动波形(在暂停采样状态下有效)
按下同时旋转编码器:垂直滚动波形(仅在暂停采样状态下有效)
设置界面:
单击编码器:无效
双击编码器:无效
长按编码器:返回主界面
旋转编码器:修改当前选项
按下同时旋转编码器:切换选项
主界面参数:
横轴时间区间:"500ms", "200ms", "100ms", "50ms", "20ms", "10ms","5ms", "2ms", "1ms", "500us", "200us", "100us"(100us仅在自动触发模式下可用)
纵轴电压区间:设定电压量程上限,最大30V,顺时针旋转编码器将电压上限设置为0则进入自动量程,逆时针旋转进入手动量程
触发值:设定触发电压值,触发位置被放在屏幕中心
触发方向:上下箭头,分别代表上升沿触发和下降沿触发
触发方式:
Auto(自动触发) :连续采样,指示灯始终点亮,通过单击编码器控制启停;触发成功时,触发点位于屏幕中心,触发失败则波形无规律滚动,屏幕左侧显示Fail标志
Normal(普通触发):等待预采样,待指示灯亮起表示预采样结束,这时可以输入信号;
触发成功后,显示波形,并继续等待下次触发;
如果有新的触发成功,则自动显示新的波形,如没有新的触发成功则屏幕波形保持并一直等待下次触发;
需注意设置正确的触发值,否则一直处于等待触发状态,屏幕不会显示波形
Single(单次触发):等待预采样,待指示灯亮起表示预采样结束,这时可以输入信号;
触发成功后,显示波形,并停止采样,需用户单击编码器开始下次采样
需注意设置正确的触发值,否则一直处于等待触发状态,屏幕不会显示波形
主界面状态显示:
Run:正在采样
Stop:停止采样
Fail:自动模式下,触发值超出波形范围引起触发失败
Auto:自动量程
设置界面选项:
PMode(绘图模式):
Vector:波形以矢量显示
Dots :波形以点阵显示
LSB(采样系数):分压系数的100倍,比如用10k加2k的电阻进行分压,分压系数为(10+2/2)=6,LSB理论取值为6x100=600,根据实际电阻精度进行微调
BRT:OLED亮度
保存设置:长按编码器退出设置界面时,设置界面和主界面的所有参数被保存到EEPROM
2 硬件设计(原理图)
3 核心软件设计
学长设计的示波器采用了一套成本低廉但高效的硬件,配合层次化的软件框架,实现对常规低频信号波形的采集、分析、显示。
硬件部分的主要工作有:电源管理(5V、2.5V、-5V);对信号的衰减、限幅、偏置。最后将处理好的电压信号送入MCU的ADC外设,进行数据采集。由于ADC外设只能采集正电压,因此我们通过增加偏置电压的方式,将负电压抬高来采集。
软件部分的主要工作有:底层驱动SDK框架、ADC滤波算法、波形数据处理、人机交互界面。SDK提供基本的模块和外设驱动支持,滤波算法去除噪声和毛刺,波形处理计算电压值和频率,并为波形显示做准备;人机交互界面提供屏幕显示(OLED屏)和操作方式(旋钮和按键)。
5 部分实现代码
部分核心代码:
/**********************************************************
简介:ADC1-CH6初始化函数
***********************************************************/
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PA6 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在非连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; //转换由定时器2的通道2触发(只有在上升沿时可以触发)
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_DMACmd(ADC1, ENABLE); //ADC的DMA功能使能
ADC_ResetCalibration(ADC1); //使能复位校准
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_1Cycles5 );//ADC1通道6,采样时间为239.5周期
ADC_ResetCalibration(ADC1);//复位较准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
}
/******************************************************************
函数名称:TIM2_PWM_Init(u16 arr,u16 psc)
函数功能:定时器3,PWM输出模式初始化函数
参数说明:arr:重装载值
psc:预分频值
备 注:通过TIM2-CH2的PWM输出触发ADC采样
*******************************************************************/
void TIM2_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能定时器2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
//设置该引脚为复用输出功能,输出TIM2 CH2的PWM脉冲波形 GPIOA.1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM2 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
TIM_OCInitStructure.TIM_Pulse=1000; //发生反转时的计数器数值,用于改变占空比
TIM_OC2Init(TIM2, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM2
TIM_CtrlPWMOutputs(TIM2, ENABLE);//使能PWM输出
TIM_Cmd(TIM2, ENABLE); //使能TIM2
}
/******************************************************************
函数名称:MYDMA1_Config()
函数功能:DMA1初始化配置
参数说明:DMA_CHx:DMA通道选择
cpar:DMA外设ADC基地址
cmar:DMA内存基地址
cndtrDMA通道的DMA缓存的大小
备 注:
*******************************************************************/
void MYDMA1_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA_CHx); //将DMA的通道1寄存器重设为缺省值
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外设ADC基地址
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,从外设读取发送到内存//
DMA_InitStructure.DMA_BufferSize = cndtr; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA_CHx, &DMA_InitStructure); //ADC1匹配DMA通道1
DMA_ITConfig(DMA1_Channel1,DMA1_IT_TC1,ENABLE); //使能DMA传输中断
//配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_Cmd(DMA1_Channel1,ENABLE);//使能DMA通道
}
#define NPT 1024//一次完整采集的采样点数
/******************************************************************
函数名称:GetPowerMag()
函数功能:计算各次谐波幅值
参数说明:
备 注:先将lBufOutArray分解成实部(X)和虚部(Y),然后计算幅值(sqrt(X*X+Y*Y)
*******************************************************************/
void GetPowerMag(void)
{
float X,Y,Mag,magmax;//实部,虚部,各频率幅值,最大幅值
u16 i;
//调用自cr4_fft_1024_stm32
cr4_fft_1024_stm32(fftout, fftin, NPT);
//fftin为傅里叶输入序列数组,ffout为傅里叶输出序列数组
for(i=1; i<NPT/2; i++)
{
X = (fftout[i] << 16) >> 16;
Y = (fftout[i] >> 16);
Mag = sqrt(X * X + Y * Y);
FFT_Mag[i]=Mag;//存入缓存,用于输出查验
//获取最大频率分量及其幅值
if(Mag > magmax)
{
magmax = Mag;
temp = i;
}
}
F=(u16)(temp*(fre*1.0/NPT));//源代码中此公式有误,将此复制进去
LCD_ShowNum(280,180,F,5,16);
}
u16 magout[NPT];
/******************************************************************
函数名称:InitBufInArray()
函数功能:正弦波值初始化,将正弦波各点的值存入magout[]数组中
参数说明:
备 注:
*******************************************************************/
void InitBufInArray(void)
{
u16 i;
float fx;
for(i=0; i<NPT; i++)
{
fx = sin((PI2*i)/NPT);
magout[i] = (u16)(2048+2048*fx);
}
}
/******************************************************************
函数名称:sinout()
函数功能:正弦波输出
参数说明:
备 注:将此函数置于定时器中断中,可模拟输出正弦波
*******************************************************************/
void sinout(void)
{
static u16 i=0;
DAC_SetChannel1Data(DAC_Align_12b_R,magout[i]);
i++;
if(i>=NPT)
i=0;
}
void Dac2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitType;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
DAC_InitType.DAC_Trigger=DAC_Trigger_T4_TRGO; //定时器4触发
DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_Noise;//产生噪声
//DAC_WaveGeneration_Triangle产生三角波
DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_TriangleAmplitude_4095;//幅值设置为最大,即3.3V
DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1
DAC_Init(DAC_Channel_2,&DAC_InitType); //初始化DAC通道2
DAC_Cmd(DAC_Channel_2, ENABLE); //使能DAC-CH2
DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值
}
4 实现效果
5 最后
包含内容
🧿 项目分享:
https://gitee.com/sinonfin/sharing
到了这里,关于stm32毕设 示波器设计与实现(源码+硬件+论文)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!