2021暑假一起练----用STM32F072制作双通道示波器
2021暑假一起练----用STM32F072制作双通道示波器
标签
嵌入式系统
阿萨姆
更新2021-09-13
1190

项目1 制作双通道示波器

  1. 通过STM32F072的ADC采集外部模拟信号,信号范围最大10Vpp,频率为DC - 100KHz
  2. 将采集到的波形显示在240*240的LCD上,并以触发的方式显示波形
  3. 执行FFT并将频谱显示在LCD上
  4. 能够自动测量波形的参数 - 峰峰值、平均值、频率/周期
  5. 能够通过按键来对波形进行缩放查看

硬件介绍

   本次项目的芯片是STM32F072CBT6,,是主流ARM Cortex-M0 USB系列MCU,具有128 KB Flash、48 MHz CPU、USB、CAN和CEC功能。开发板上集成了一个波轮按键和两个用户按键以及一个240*240的LCD显示屏幕。

实现

   我本来打算电赛之后完成这个项目,没想到电赛推迟导致实验室关门,东西被锁在实验室里,另一方面返乡心切,不想再耽搁时间找老师开门,就只好等待返校实验室重新开门再着手完成项目,这样一来就只剩5天时间,这5天虽然不是电赛,强度却胜似电赛,平均每天呆在实验室10个小时,算是提前感受了。这时候电子森林的网站上已经有了两个案例发布了,分别是信号发生源和直流电压源,个人感觉这两个项目比示波器简单,虽说只有5天,但我还是想试着挑战一下他们没做的示波器。很遗憾5天的时间还是太短了,我到最后也未能完成任务指标上的全部功能。

   首先是最高频率,我的代码底层完全借鉴刚才说的那个直流电压源项目,其ADC使用的办法是轮询,速度很慢,对于直流电压源来说足够了,但对于示波器则是远远不够,我也尝试过用DMA的方式,速度会更快,但始终存在我无法解决的bug,无奈放弃。本次使用的平台使stm32f0也是一个比较小众的平台,网上信息较少,例程几乎没有,相比之下我之前用过的f1和f4系列正点原子都有非常详细的例程可供参考,可以说是被惯坏了,那么拿到这块板子要如何写底层呢?在电子森林网站上我才知道可以使用stm32cubemx生成代码,看起来挺高大上的样子,实则只有对底层有充分的了解才能生成正确的代码,在尝试了好多次失败后,只能照抄案例,使用慢速的轮询方式,实测采集20次数据至少需要1.2ms,支持的最大频率也就2kHz左右了,远远达不到要求的100kHz。

   然后是傅里叶变换,一开始在网上找到的教程说导入几个汇编文件后,就可以调用函数实现傅里叶变换,尝试后发现汇编有报错,无法拒绝,可能是stm32f0不支持,后来在本网站有找到c语言的傅里叶变换文件,导入后可以正确运行,但是绘图代码会出现执行异常的问题,进入硬件错误的死循环里,尝试更换另一个版本的绘图函数代码,这次初始化就出现问题,直接黑屏,然后硬件错误,个人觉得硬件错误是内存泄漏造成的,只能减少代码量,减少变量所占内存空间,别人的软件没有问题,这只能说明是我自己写的代码优化太差,内存申请过多,时间有限,无法优化了。

   那么有没有什么比较成功的地方呢,也是有的,屏幕驱动的速度很慢,刷一次屏要2秒钟,所以绘图就只有一种办法:只画变化的地方,对此我设计了两个数组存放上一次和这一次的波形轨迹,每次绘图时,将之前的轨迹点画上背景色,新的轨迹画上线的颜色,就能实现示波。按键操作在中断里完成,但实测发现在这个中断里调用绘图函数效果十分不稳定,很大概率不会执行,原因未知,解决办法在中断里改变标志位,再在其他函数读取标志位执行绘图。

for(int i=0;i<160;i++)
	{
		for(int j=0;j<2;j++)
		{
			if(i%20==0)
			{
				LCD_DrawPoint(i+20,100-old_points[j][i+old_trigger[j]],BLACK);
			}
			else if(old_points[j][i+old_trigger[j]]%20==0)
			{
				LCD_DrawPoint(i+20,100-old_points[j][i+old_trigger[j]],BLACK);
			}
			else
			{
				LCD_DrawPoint(i+20,100-old_points[j][i+old_trigger[j]],WHITE);
			}
			if(j==0)
				LCD_DrawPoint(i+20,100-points[j][i+trigger[j]],RED);
			else
				LCD_DrawPoint(i+20,100-points[j][i+trigger[j]],BLUE);
		}
	}

绘制波形

for(int i=0;i<2;i++)
	{
		for(int j=0;j<256;j++)
		{
			old_points[i][j]=points[i][j];
		}
	}
	for(int i=0;i<2;i++)
	{
		change_para(182,93+60*i,text[3],3,pp[i]);
		change_para(182,12+93+60*i,text[4],5,freq[i]);
	}
	if(change_sign[0]==1)
	{
		if(old_chose_option<3)
			pos=old_chose_option*12+33;
		else 
			pos=92+(old_chose_option-3)*12+60*old_chose_line;
		LCD_DrawLine(184,pos,230,pos,WHITE);
		if(chose_option<3)
			pos=chose_option*12+33;
		else
			pos=92+(chose_option-3)*12+60*chose_line;
		LCD_DrawLine(184,pos,230,pos,BLACK);
		old_chose_line = chose_line;
		old_chose_option = chose_option;
		change_sign[0]=0;
	}
	if(change_sign[1]==1)
	{
		change_para(184,20,text[0],5,time[159]/8.0);
		change_sign[1]=0;
	}
	if(change_sign[2]==1)
	{
		change_para(184,32,text[1],5,scope);
		change_sign[2]=0;
	}
	if(change_sign[3]==1)
	{
		if(chose_line==0)
		{
			LCD_Fill(185,128,194,137,WHITE);
			LCD_Fill(185,68,194,77,RED);
		}
		else
		{
			LCD_Fill(185,68,194,77,WHITE);
			LCD_Fill(185,128,194,137,BLUE);
		}
		change_sign[3]=0;
	}
	if(change_sign[4]==1)
	{
		change_para(184,80+60*chose_line,text[2],5,offset[chose_line]);
		change_sign[4]=0;
	}

参数更新

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	static int flag = 0;
	if(HAL_GetTick()-lasttick<=5)
	{			//15msΪ½çÏÞ 
		return ;
	}
	else
	{
		lasttick=HAL_GetTick();
	}
	if(GPIO_Pin == KEY_1_Pin)
	{
		if(chose_option<=2)
		{
			old_chose_option=chose_option;
			chose_option=chose_option+1;
			change_sign[0]=1;
		}
	}
	if(GPIO_Pin == KEY_2_Pin)
	{
		if(chose_option>=1)
		{
			old_chose_option=chose_option;
			chose_option=chose_option-1;
			change_sign[0]=1;
		}
	}
	if(flag==0)
	{
		if(GPIO_Pin == KEY_R_Pin)
		{/* KEY L */ 
			if(chose_option==0)
			{
				if(times>1)
				{
					times -= 1;
					change_sign[1]=1;
				}
			}
			else if(chose_option==1)
			{
				scope = scope/2.0;
				change_sign[2]=1;
			}
			else if(chose_option==2)
			{
				change_sign[5]=0;
			}
			else if(chose_option==3)
			{
				offset[chose_line] -= 10;
				change_sign[4]=1;
			}
		} 
		if(GPIO_Pin == KEY_O_Pin)
		{/* KEY O ²¦ÂÖ*/
			old_chose_line=chose_line;
			chose_line=1-chose_line;
			change_sign[3]=1;
			change_sign[0]=1;
		}
		if(GPIO_Pin == KEY_L_Pin)
		{/* KEY R */ 
			if(chose_option==0)
			{
				if(times<30)
				{
					times += 1;
					change_sign[1]=1;
				}
			}
			else if(chose_option==1)
			{
				scope = scope*2.0;
				change_sign[2]=1;
			}
			else if(chose_option==2)
			{
				change_sign[5]=1;
			}
			else if(chose_option==3)
			{
				offset[chose_line] += 10;
				change_sign[4]=1;
			}
		}
		flag=1;
	}
	else
		flag=0;
}

   ADC读回来的数据需要进行处理,才能转换成电压值,首先在multisim里进行仿真,得出实际电压和ADC检测电压之间的关系。FsB4bHC5E_UHf2-EsBbT1IrKkeZRFgtZZBiWTm6DQwi8UkX4TiKRo9N4

附件下载
stm32DC.zip
源代码
团队介绍
哈尔滨工业大学电信专业
团队成员
艾晟民
学生
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号