基于STM32平台 完成DDS任意信号发生器
利用板载stm32芯片学习平台,实现DDS任意信号发生器,可实现DC-100Hz的信号产生,并通过示波器进行观察。
标签
嵌入式系统
DDS
2022 寒假在家练
爱与风华
更新2022-03-03
中山大学
2950

一.项目需求:

  1. 通过芯片的PWM + 板上LPF电路生成常用到的正弦波、三角波、方波
  2. 频率为DC - 100KHz,并可以通过旋转编码器、按键进行调节
  3. 信号的幅度从0-3V,并可以通过旋转编码器、按键进行调节,调节精度1%
  4. 在OLED显示屏设计信号发生器的界面,包含基本的波形区域、参数设置区域

 

二.成品展示:

下图为设计完成后的项目实拍图:

波形参数设置界面:

FoGDgWXWDr74AbJvQn972UmamIAX

Fm9KlOUKV56uhO1KsVWQz53Cxf7s

FoqqlhdEkfs7z06HobWyuYDsDtRR

 

通过示波器观察产生的波形:

Fg3cmH95gtUYQRVSXCflKcSLJ6ox

FrV-PbRsTDTWBRYMCcjR6oGrng4l

 

三.思路及框架构建:

按照题目要求,需要用到的板卡模块主要为PWM+板上低通电路,其余外设为旋转编码器和按键以及OLED屏幕,利用波形生成模块产生信号后,通过旋转编码器和按键的操作进行参数设置和改变并显示到OLED屏幕上即可。其电路结构如下(PWM+板上低通):

FiGHZrzuChFl9ukOlKXMeRZ6wj2I

思路确定以后,按照模块先分别完成各部分的功能,然后综合起来即可完成项目的要求。

主要有以下几个方面:

(1).PWM+LPF 波形的生成;

(2).按键和旋转编码器的设置;

(3).OLED屏幕的显示;

FgrEtr08j4q5P1ZNoH5BAGSgx0qF

 

2.1根据平台的原理图,进行管脚的选择和设置;

本次项目需要用到的管脚不多,主要为两个按键输入,旋转编码器的两路输入,以及OLED屏幕的四路输出控制,一路PWM输出。通过STM32CUBEIDE的配置图如下所示:

FkVo9ZHnTKGnI_Kbv9Wh29JzN1PM

其中PA4管脚控制幅度、频率、波形的上调,PA5管脚控制幅度、频率、波形的下调。旋转编码器两相A相由PA15控制,B相由PB4控制,负责参数的调整。OLED屏幕使用的是SPI协议与stm32进行通信,四根管脚分别为PB5,PB6,PB7,PB8。PWM输出信号管脚为PB0。

确定管脚后就要进行管脚的设置,这里把两个按键输入设置为外部中断,按下按键时进行相应的响应对参数进行调整。然后是旋转编码器的两相设置,根据相关知识,在这里把A相作为参考并设置为外部中断,当检测到下降沿时,判断B相的电平,高和低分别对应两个方向的选择。对于OLED屏幕,设置SPI通信方式以后,就可以进行相应的显示了。最后是PWM输出,需要设置为PWM输出模式,同时使能DMA通道,将存储好的波形数据通过DMA对PWM的占空比进行相应的调整,通过低通网络后从而实现波形的生成。

 

四.代码实现:

本次平台的使用采用的是STM32CUBEIDE和相应的程序下载软件,代码通过图形化设置后自行生成底层代码,我们只需要编写核心部分的代码即可。

3.1 PWM+LPF波形输出

通过使能DMA通道,并载入不同的占空比的值,从而改变输出电压的幅值,以下代码为波形的参数设置部分:

static void setparameters(void){
	uint16_t i;
	tim = 64000000 / LEN / frequency;

	TIM3->CNT = 0;
	__HAL_TIM_SET_AUTORELOAD(&htim3, (tim-1));

	switch(WAVESTATE){
	    case 0:
		    for (i = 0; i < LEN; i++){
		    	send_Buf[i]=(uint16_t)((50-50*amplitude/165) * (tim-1) / 50);
		    }
			break;
	    case 1:
	    	for (i = 0; i < LEN; i++){
	    	    send_Buf[i]=(uint16_t)((sine_wave_value[i]-50) * (tim -1) / 100 * (amplitude) / 165 + tim /2);
	    	}
		    break;
	    case 2:
	    	for (i = 0; i < LEN; i++){
	    		send_Buf[i]=(uint16_t)((50 + 50*(amplitude)/165 * (i<LEN/2?1:-1)) * (tim-1) / 100);
	    	}
	    	break;
	    case 3:
	    	for (i = 0; i < LEN; i++){
	    	    send_Buf[i]=(uint16_t)((triangle_wave_value[i]-50) * tim / 100 * (amplitude) / 165 + tim /2);
	    	}
	    	break;
	    default:
	    	break;
	}
}

主函数里通过判断输出的状态来选择是否使能波形输出:

while (1)
  {
    /* USER CODE END WHILE */
	   if(PUTSTATE==1){
		   startoutput();
	   }
	   else{
		   stopoutput();
	   }
    /* USER CODE BEGIN 3 */
  }
void startoutput(void){
	HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_3,(uint32_t*)send_Buf,LEN);
}

void stopoutput(void){
	HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_3);
}

 

3.2 按键及旋转编码器的中断选择

根据不同的按键按下,以及旋转编码器的状态,要进行对应的幅值、频率、波形的改变,以下为中断函数代码:

void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
	 if(GPIO_Pin == Astate_Pin)
	 {
		 if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)==0){
		 	  NUM=NUM+1;
		   }
		   else{
		 	  NUM=NUM-1;
		   }
		   switch(NUM%4){
		   case 1:
			  OLED_ShowString(0, 100, "Amplitude:", 8, 1);
			  OLED_ShowString(0, 120, "Signal Output:", 8, 1);
		 	  OLED_ShowString(0, 90, "Signal Wave:", 8, 0);
		 	  OLED_Refresh();
		 	  BUTTONSTATE=1;
		 	  break;
		   case 2:
			  OLED_ShowString(0, 90, "Signal Wave:", 8, 1);
			  OLED_ShowString(0, 110, "Frequency:", 8, 1);
			  OLED_ShowString(0, 100, "Amplitude:", 8, 0);
		 	  OLED_Refresh();
		 	  BUTTONSTATE=2;
		 	  break;
		   case 3:
			  OLED_ShowString(0, 100, "Amplitude:", 8, 1);
			  OLED_ShowString(0, 120, "Signal Output:", 8, 1);
		 	  OLED_ShowString(0, 110, "Frequency:", 8, 0);
		  	  OLED_Refresh();
		  	  BUTTONSTATE=3;
		  	  break;
		   case 0:
			  OLED_ShowString(0, 110, "Frequency:", 8, 1);
			  OLED_ShowString(0, 90, "Signal Wave:", 8, 1);
		 	  OLED_ShowString(0, 120, "Signal Output:", 8, 0);
		   	  OLED_Refresh();
		   	  BUTTONSTATE=0;
		   	  break;
		   }
	 }
	 if(GPIO_Pin == KeyUp_Pin){
		 switch(BUTTONSTATE){
		    case 1:
		    	if(WAVESTATE>=3){
		    		WAVESTATE=0;
		    	}
		    	else{
		    		WAVESTATE++;
		    	}
				switch(WAVESTATE){
				   case 0:
						OLED_ShowString(78, 90, "DC  ", 8, 1);
						OLED_ShowPicture(0, 20, 128, 64, dc, 1);
						OLED_Refresh();
						setparameters();
						break;
				   case 1:
						OLED_ShowString(78, 90, "SINE", 8, 1);
						OLED_ShowPicture(0, 20, 128, 64, sin, 1);
						OLED_Refresh();
						setparameters();
					    break;
				   case 2:
						OLED_ShowString(78, 90, "SQUA", 8, 1);
						OLED_ShowPicture(0, 20, 128, 64, sqr, 1);
						OLED_Refresh();
						setparameters();
						break;
				   case 3:
						OLED_ShowString(78, 90, "TRIA", 8, 1);
						OLED_ShowPicture(0, 20, 128, 64, tri, 1);
						OLED_Refresh();
						setparameters();
						break;
				}
				break;
			case 2:
                increaseamplitude();
				break;
			case 3:
                increasefreq();
				break;
			case 0:
             switch(PUTSTATE){
                case 0:
             	   OLED_ShowString(90, 120, "ON ", 8, 1);
             	   OLED_Refresh();
             	   break;
                case 1:
             	   OLED_ShowString(90, 120, "OFF", 8, 1);
             	   OLED_Refresh();
             	   break;
             }
             if(PUTSTATE>=1){
             	 PUTSTATE=0;
             }
             else{
             	 PUTSTATE++;
             }
				break;
		 }
	 }
	 if(GPIO_Pin == KeyDown_Pin){
         switch(BUTTONSTATE){
		    case 1:
		    	if(WAVESTATE <= 0){
		    		WAVESTATE=3;
		    	}
		    	else{
		    		WAVESTATE--;
		    	}
				switch(WAVESTATE){
				   case 0:
						OLED_ShowString(78, 90, "DC  ", 8, 1);
						OLED_ShowPicture(0, 20, 128, 64, dc, 1);
						OLED_Refresh();
						setparameters();
						break;
				   case 1:
						OLED_ShowString(78, 90, "SINE", 8, 1);
						OLED_ShowPicture(0, 20, 128, 64, sin, 1);
						OLED_Refresh();
						setparameters();
					    break;
				   case 2:
						OLED_ShowString(78, 90, "SQUA", 8, 1);
						OLED_ShowPicture(0, 20, 128, 64, sqr, 1);
						OLED_Refresh();
						setparameters();
						break;
				   case 3:
						OLED_ShowString(78, 90, "TRIA", 8, 1);
						OLED_ShowPicture(0, 20, 128, 64, tri, 1);
						OLED_Refresh();
						setparameters();
						break;
				}
				break;
			case 2:
				decreaseamplitude();
				break;
			case 3:
                decreasefreq();
				break;
			case 0:
                switch(PUTSTATE){
                case 0:
             	   OLED_ShowString(90, 120, "ON ", 8, 1);
             	   OLED_Refresh();
             	   break;
                case 1:
             	   OLED_ShowString(90, 120, "OFF", 8, 1);
             	   OLED_Refresh();
             	   break;
                }
                if(PUTSTATE<=0){
             	 PUTSTATE=1;
                }
                else{
             	 PUTSTATE--;
                }
			    break;
		 }
	 }
}

按键按下进行相应的波形参数设置:

void increaseamplitude(void){
	if (amplitude < 165)
	{
		amplitude = amplitude + 15;
		if(WAVESTATE == 0){
			AMP =(double) ( 3*((double)amplitude / (double)165 ));
		}
		else{
			AMP = (double)((0.5*tim * amplitude / 165 + tim/2 )/tim*3-1.5);
		}
		setparameters();
		ShowAMP(AMP);
	}
}

void decreaseamplitude(void){
	if (amplitude > 0)
	{
		amplitude = amplitude - 15;
		if(WAVESTATE == 0){
			AMP = (double) ( 3*((double)amplitude / (double)165 ));
		}
		else{
			AMP = (double)((0.5*tim * amplitude / 165 + tim/2 )/tim*3-1.5);
		}
		setparameters();
		ShowAMP(AMP);
	}
}

void increasefreq(void){
	if (frequency < 100000)
	{
		frequency += 1000;
		FRE=frequency/1000;
		setparameters();
		ShowFRE(FRE);
	}
}

void decreasefreq(void){
	if (frequency > 100)
	{
		frequency -= 1000;
		FRE=frequency/1000;
		setparameters();
		ShowFRE(FRE);
	}
}

 

3.3 OLED显示参数信息

连接好OLED后,直接调用相应的库函数进行显示即可,需要显示波形的参数信息以及简单的界面,并随着按键状态的改变而改变,主要代码如下:

void showbegin(void){
	  OLED_ShowPicture(0, 20, 128, 64, dc, 1);
	  OLED_ShowString(0, 0, "Signal Generator", 16, 1);
	  OLED_ShowString(0, 90, "Signal Wave:", 8, 1);
	  OLED_ShowString(78, 90, "DC  ", 8, 1);
	  OLED_ShowString(0, 100, "Amplitude:", 8, 1);
	  OLED_ShowString(66, 100, "3.00V", 8, 1);
	  OLED_ShowString(0, 110, "Frequency:", 8, 1);
	  OLED_ShowString(66, 110, "2KHz", 8, 1);
	  OLED_ShowString(0, 120, "Signal Output:", 8, 1);
	  OLED_ShowString(90, 120, "---", 8, 1);
	  OLED_Refresh();
}

void ShowAMP(double AMP)
{
	char str[5];
    sprintf(str, "%.2fV", AMP);
	OLED_ShowString(66, 100, str, 8, 1);
	OLED_Refresh();
}

void ShowFRE(uint16_t FRE)
{
	char str[6];
    sprintf(str, "%dKHz", FRE);
	OLED_ShowString(66, 110, str, 8, 1);
	OLED_Refresh();
}

 

五.项目总结:

完成本次项目比较简单,目的较为明确,其主要原因是stm的库函数功能强大,通过简单的学习即可调用并应用到实践中,我们只需要进行适量的修改就可以了。通过本次项目的学习,我大致了解了STM32系列单片机的使用,以及一般项目设计完成的流程与开发,完成本次项目,还提高了我的信息搜集能力,能够在网上找到很多自己需要的东西,这是很有用的。本次由于时间有限未进行示波器的功能实现,只实现了波形发生功能,以后可以再试试示波器的设计。

通过本次项目,我主要了解和掌握了:

  • SPI通信协议及其工作模式;
  • stm32开发软件的使用;
  • OLED的显示以及旋转编码器的使用;
  • PWM+LPF生成波形;

      设计制作中遇到的困难:

  • 对stm32cubeide的不熟悉;
  • 不清楚SPI的通信协议;
  • PWM设置有问题;

 

附件下载
Generator.bin
Generator.rar
团队介绍
中山大学 郤伟杰
团队成员
爱与风华
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号