基于STM32的波形发生器

设计实现的要求

基本要求

 

(1)具有产生正弦波、方波、三角波三种周期性波形的功能。    
(2)用键盘输入编辑生成上述三种波形(同周期)的线性组合波形,以及由基波及其谐波(5次以下)线性组合的波形。    
(3)具有波形存储功能。    
(4)输出波形的频率范围为100Hz~20kHz(非正弦波频率按10次谐波计算);重复频率可调,频率步进间隔≤100Hz。   
(5)输出波形幅度范围0~5V(峰-峰值),可按步进0.1V(峰-峰值)调整。    
(6)具有显示输出波形的类型、重复频率(周期)和幅度的功能。

拓展要求

 

(1)输出波形频率范围扩展至100Hz~200kHz。   
(2)用键盘或其他输入装置产生任意波形。    
(3)增加稳幅输出功能,当负载变化时,输出电压幅度变化不大于±10%(负载电阻变化范围:100Ω~∞)。   
(4)具有掉电存储功能,可存储掉电前用户编辑的波形和设置。    
(5)可产生单次或多次(1000次以下)特定波形(如产生1个半周期三角波输出)。

 

运行效果演示

开机动画演示

VID_20190530_222028_20190530225911.gif

GUI交互界面

VID_20190530_222028_20190530225813.gif

波形输出实例

sine.png

squre.png

triangle.png

 

 

系统方案

通过对本题的分析,斟酌各个方案,最终确立了我们的最终方案。

(1)   波形采用STM32自带的DAC+DDS产生。
(2)   主控模块采用STM32F103单片机,控制整个系统的软硬件操作。
(3)   显示模块采用液晶显示器实时显示当前输出的波形的类型、幅值和频率。
(4)   按键模块采用独立按键的方式设置输出波形的类型、幅值和频率等数据。

 

1.1.     系统总体软件设计

软件架构图如下:

a.png

      整个系统的交互采用按键和LCD显示屏实现。为了交互的更加便捷,本系统设计了多级的菜单界面,按键通过由菜单管理器切换菜单界面。菜单界面的底层是参数界面,参数界面用于设置和显示参数。设置的参数通过被放入参数管理器,可用于设置波形的频率和幅值。我们的设想是低频波形用单片机上的DAC实现,高频信号用AD8951也就是直接数字信号合成器合成产生波形。但是为了选择的多样性,无论高频还是低频,我们的DAC和DDS都会同时工作。

1.2.     交互模块

本系统的交互模块有需要用到LCD显示屏和按键。LCD显示模块主要由菜单显示模块和参数显示模块组成,分别由菜单管理器和参数管理器控制,通过按键设置菜单管理器和参数管理器的参数,可以设置显示不同的界面。参数管理器也是连接波形发生模块的桥梁。

下图为交互模块的程序框图:

b.png

 

1.3.     DAC波形发生模块

本系统使用的是STM32F103里面自带的DAC。为了提高DAC转换的速度,使用DMA传输波形数据,DMA的触发传输使用的是定时器的中断。本系统使用的正弦波波形数据是通过C语言数学库math.h里面的函数sin()计算得到的数组,三角波通过自定义函数计算波形数据,这两个波形一个周期内采样了512个数据点;而方波的则不同,方波每个周期只改变两次DAC的值。当用户通过交互界面改变幅值参数时,会重新计算波形数据数组。当用户改变频率参数时,会重新计算DMA定时器的周期。波形的输出和关闭则是通过改变DMA定时器中断使能和DMA传输使能实现。

       下图为DAC波形发生模块的程序框图:

c.png

1.4.     DDS波形发生模块

DDS本身就是用来产生波形的,故要控制DDS产生波形,只需要设置DDS相应的寄存器就好了。

下图为DDS波形发生的程序框图:

d.png

代码实现

数据结构

参数管理器

typedef struct ParameterManagerStruct
{
    bool isOutputEnable;
    ParameterSelected flagParaSelected;
    u32 am;
    u8 amArray[10];
    u8 amArrayPoint;
    u8 amLen;
    u16 amCoordinateX;
    u16 amCoordinateY;
    u32 fq;
    u8 fqArray[10];
    u8 fqArrayPoint;
    u8 fqLen;
    u16 fqCoordinateX;
    u16 fqCoordinateY;
    u32 har;
    u8 harArray[10];
    u8 harArrayPoint;
    u8 harLen;
    u16 harCoordinateX;
    u16 harCoordinateY;
} ParaMng;

菜单管理器

typedef enum MenuLayerEnum{
    MenuLayer0=0,
    MenuLayer1,
    MenuLayer2
} MenuLayer;
typedef struct MenuStruct{
    MenuLayer layer;
    u8 Layer0_Selected;
    u8 Layer1_Selected;
    u8 Layer2_Selected;
} Menu;

菜单项显示

typedef struct GUIMenuItem{
u8 *name;
u8 fontSize;
u32 fontColor;
u32 backgroundColor;
} GUI_Item;
typedef struct GUIMenuTypedef
{
GUI_Item itemList[10];
u8 itemNum;
u8 *topicName;
} GUIMenu;

 

主函数

int main(void)
{

  HAL_Init();
  SystemClock_Config();
   delay_init(72);
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_DAC_Init();
  MX_TIM2_Init();
  MX_USART1_UART_Init();
  MX_TIM4_Init();
  MX_TIM3_Init();
  MX_TIM6_Init();
    LCD_Init();//LCD初始化
    //菜单初始化 
    InitMenu();
    InitParaManager();//参数初始化
    menu.layer=MenuLayer0;
    menu.Layer0_Selected=Item_SignalGenerator;
    menu.Layer1_Selected=Item_SquareWave;
    menu.Layer2_Selected=Item_IsEnable;
    //开机动画­
    GUI_ShowPowerOn();
    while(KEY_Scan(0)==0)delay_ms(20);
    //菜单显示
    ShowMenu();
    while (1)
  {
        //IRHandle();
        KeyHandle();//按键处理
        //ad9851去除干扰
        ad9851_data_l;
        ad9851_fq_up_l;
        ad9851_rest_l;
        ad9851_w_clk_l;
        if(pm.flagParaSelected!=ParaRESET)
        ShowParameterNumber();//设置参数时界面显示
        delay_ms(10);
  }
}

 

按键处理

void KeyHandle(void)
{
  u8 t=KEY_Scan(0);
  if(t>0){
    switch(t){//按键扫描
      case WKUP_PRES://按下“下”键
      {
        if(pm.flagParaSelected==ParaRESET)//切换下一行
        {
          if(menu.layer==MenuLayer0)menu.Layer0_Selected=(menu.Layer0_Selected+1)%ITEM_NUM_LAYER0;
          else if(menu.layer==MenuLayer1)menu.Layer1_Selected=(menu.Layer1_Selected+1)%ITEM_NUM_LAYER1;
          else if(menu.layer==MenuLayer2)menu.Layer2_Selected=(menu.Layer2_Selected+1)%ITEM_NUM_LAYER2;
        }
        else if(pm.flagParaSelected==ParaAM)//设置幅值状态,
          pm.amArrayPoint=(pm.amArrayPoint+1)%pm.amLen;
        else if(pm.flagParaSelected==ParaFQ)//设置频率状态
          pm.fqArrayPoint=(pm.fqArrayPoint+1)%pm.fqLen;
        else if(pm.flagParaSelected==ParaHAR)//设置谐波状态
          pm.harArrayPoint=(pm.harArrayPoint+1)%pm.harLen;
      }
          if(pm.flagParaSelected!=ParaRESET)ShowParameterNumber();//显示设置参数
           break;
      //按下确认键
      case KEY1_PRES:
        if(menu.layer==MenuLayer0)menu.layer=MenuLayer1;
        else if(menu.layer==MenuLayer1)menu.layer=MenuLayer2;
        else if(menu.layer==MenuLayer2)//在参数显示界面按下确认键
        {
          if(menu.Layer2_Selected==Item_IsEnable){//打开或关闭信号输出
            if(pm.isOutputEnable==false){
              pm.isOutputEnable=true;
              EnableOutput();
            }
            else{
              pm.isOutputEnable=false;
              DisableOutput();
            }
          }
          else if(menu.Layer2_Selected==Item_Amplitude)//幅值参数修改
          {
            if(pm.flagParaSelected!=ParaAM)
              pm.flagParaSelected=ParaAM;
            else{
              pm.flagParaSelected=ParaRESET;
              PM_SetValueFromArray();
              SetAmplitude();
            }
          }
          else if(menu.Layer2_Selected==Item_Frequency)//频率参数修改
          {
            if(pm.flagParaSelected!=ParaFQ)
              pm.flagParaSelected=ParaFQ;
            else{
              pm.flagParaSelected=ParaRESET;
              PM_SetValueFromArray();
              SetFrequency();
            }
          }
          else if(menu.Layer2_Selected==Item_Harmonic)//谐波参数修改
          {
            if(pm.flagParaSelected!=ParaHAR)
              pm.flagParaSelected=ParaHAR;
            else
              pm.flagParaSelected=ParaRESET;
          }
          }
      break;
      //按下返回键
      case KEY0_PRES:
      {
        if(pm.flagParaSelected==ParaRESET){//当前菜单状态,切换菜单
          if(menu.layer==MenuLayer1)
            menu.layer=MenuLayer0;
          else if(menu.layer==MenuLayer2){
            menu.layer=MenuLayer1;
            DisableOutput();
          }
        }
        else if(pm.flagParaSelected==ParaAM)//当前在幅值设置状态,某一位增加
          pm.amArray[pm.amArrayPoint]=(pm.amArray[pm.amArrayPoint]+1)%10;
        else if(pm.flagParaSelected==ParaFQ)
          pm.fqArray[pm.fqArrayPoint]=(pm.fqArray[pm.fqArrayPoint]+1)%10;
        else if(pm.flagParaSelected==ParaHAR)
          pm.harArray[pm.harArrayPoint]=(pm.harArray[pm.harArrayPoint]+1)%10;
      }
      //参数设置状态时,显示设置参数
      if(pm.flagParaSelected!=ParaRESET)ShowParameterNumber();
        break;
    }
    LED0_Toggle();
    ShowMenuKeyUpdate();
  }
}

 

    菜单显示更新函数

void ShowMenuKeyUpdate(void){
  switch(menu.layer){//根据层数显示菜单
    case MenuLayer0:
      LCD_Clear(WHITE);
      ShowLayer0();//显示根目录
      break;
    case MenuLayer1:
      LCD_Clear(WHITE);
      switch(menu.Layer0_Selected){
        case Item_SignalGenerator:
          ShowLayer1();
          break;
        case Item_SoftWareAbout:
          ShowSoftwareAbout();
          break;
        }
        break;
     case MenuLayer2:
       LCD_Clear(WHITE);
       switch(menu.Layer0_Selected){
         case Item_SignalGenerator:
          ShowLayer2();
          break;
         case Item_SoftWareAbout:
          ShowSoftwareAbout();
          break;
     }
     break;
  }
}

 

    DDS操作代码

#define ad9851_rest_l HAL_GPIO_WritePin(AD9851_RESET_GPIO_Port, AD9851_RESET_Pin, GPIO_PIN_RESET)
#define ad9851_rest_h HAL_GPIO_WritePin(AD9851_RESET_GPIO_Port, AD9851_RESET_Pin, GPIO_PIN_SET)
#define ad9851_fq_up_l HAL_GPIO_WritePin(AD9851_FQ_UP_GPIO_Port, AD9851_FQ_UP_Pin, GPIO_PIN_RESET)
#define ad9851_fq_up_h HAL_GPIO_WritePin(AD9851_FQ_UP_GPIO_Port, AD9851_FQ_UP_Pin, GPIO_PIN_SET)
#define ad9851_w_clk_l HAL_GPIO_WritePin(AD9851_W_CLK_GPIO_Port, AD9851_W_CLK_Pin, GPIO_PIN_RESET)
#define ad9851_w_clk_h HAL_GPIO_WritePin(AD9851_W_CLK_GPIO_Port, AD9851_W_CLK_Pin, GPIO_PIN_SET)
#define ad9851_data_l HAL_GPIO_WritePin(AD9851_DATA_GPIO_Port, AD9851_DATA_Pin, GPIO_PIN_RESET)
#define ad9851_data_h HAL_GPIO_WritePin(AD9851_DATA_GPIO_Port, AD9851_DATA_Pin, GPIO_PIN_SET)

//串行口初始化
void ad9851_reset_serial()
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = AD9851_RESET_Pin|AD9851_FQ_UP_Pin|AD9851_W_CLK_Pin|AD9851_DATA_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
ad9851_w_clk_l;
ad9851_fq_up_l;
ad9851_rest_l;
ad9851_rest_h;
ad9851_rest_l;
ad9851_w_clk_l;
ad9851_w_clk_h;
ad9851_w_clk_l;
ad9851_fq_up_l;
ad9851_fq_up_h;
ad9851_fq_up_l;
}


//串行口写入DDS寄存器
void ad9851_wr_serial(u8 w0,u32 frequence)
{
	u8 i,w;
	frequence=frequence*4294967296/180000000;
	w=(frequence>>=0);
	for(i=0;i<8;i++)
	{
		if((w>>i)&0x01)
		ad9851_data_h;
		else ad9851_data_l;
		ad9851_w_clk_h;
		ad9851_w_clk_l;
	}
	w=(frequence>>8);
	for(i=0;i<8;i++)
	{
		if((w>>i)&0x01)
		ad9851_data_h;
		else ad9851_data_l;
		ad9851_w_clk_h;
		ad9851_w_clk_l;
	}
	w=(frequence>>16);
	for(i=0;i<8;i++)
	{
		if((w>>i)&0x01)
		ad9851_data_h;
		else ad9851_data_l;
		ad9851_w_clk_h;
		ad9851_w_clk_l;
	}
	w=(frequence>>24);
	for(i=0;i<8;i++)
	{
		if((w>>i)&0x01)
		ad9851_data_h;
		else ad9851_data_l;
		ad9851_w_clk_h;
		ad9851_w_clk_l;
	}
	w=w0;
	for(i=0;i<8;i++)
	{
		if((w>>i)&0x01)
		ad9851_data_h;
		else ad9851_data_l;
		ad9851_w_clk_h;
		ad9851_w_clk_l;
	}
	ad9851_fq_up_h;
	ad9851_fq_up_l;
}

 

根据参数管理器设置DDS和DAC的频率

void SetFrequency(void)
{
    //设置DDS频率
    ad9851_reset_serial();
    ad9851_wr_serial(0x01,pm.fq);
    //设置用于触发传输DAC-DMA的定时器中断时间
    if(menu.Layer1_Selected==0)
    {
        if(pm.fq<=100){
        TIM6->PSC=7199;
        TIM6->ARR=(u16)((10000/pm.fq)/2)-1;//不同的频率下 定时器的预分频系数不同
    }
    else if(pm.fq<=10000)
    {
        TIM6->PSC=71;
        TIM6->ARR=(u16)((1000000/pm.fq)/2)-1;
    }
    else
    {
        TIM6->PSC=1;
        TIM6->ARR=(u16)((72000000/pm.fq)/2)-1;
    }
    }
    else if(menu.Layer1_Selected==1)
    {
        TIM6->PSC=71;
        TIM6->ARR=(u16)((1000000/pm.fq)/2)-1;
    }
    
    else if(menu.Layer1_Selected==2)
    {
        TIM6->PSC=71;
        TIM6->ARR=(u16)((1000000/pm.fq)/2)-1;
    }
}

 

根据参数管理器设置Sine波形数据数组的值

void SetSineWaveData(void)
{
  double d = 2.0*PI / 512.0;
  double t = (double)pm.am / 500000.0;
  int bias = 4096* pm.am / 500000.0/2;
  for (int i = 0; i < 512; i++)
    waveDataArray[i]=(u16)(sin(i*d)*t*4096.0/2)+bias;
}

 

代码太多这里实在是贴不完,若想获

  • 21
    点赞
  • 95
    收藏
    觉得还不错? 一键收藏
  • 53
    评论
前言: 是德科技(NYSE:KEYS)-原安捷伦电子测量事业部,是全球电子测量技术和市场的领导者,致力于推动无线通信、模块化和软件解决方案的持续创新,专注于为客户提供卓越的测量体验。是德科技提供的电子测量仪器、系统、软件及服务广泛应用于电子设备的设计、研发、制造、安装、部署和运营。 此次,是德科技为工程师分享了包括示波器、物联网、5G通信、汽车电子等热门领域的技术知识,手把手教会工程师如何通过示波器行之有效的实现相关领域的专业测试测量。 从朋友得知ST公司在搞活动,可以申请STM32F429的探索套件,作为穷学生一枚的我不免动了心。大二上学期参加完全国大学生电子设计竞赛之后闲来无事,就自己做了一个数字示波器来作为学习STM32的实战检验,前后花费了大概1个月的时间完成了初稿,之后又断断续续的完善我的示波器,并且为它编写了上位机程序和制定了通信协议。使用了前端程控模拟电路和上层电路板,uCOS-II,uCGUI,FFT,SD,上位机等等,现在因为参加老师的项目研发也就暂时闲置了下来,之前就打算在各大论坛开源,但是一直没有时间整理设计文档,恰巧现在ST公司这个活动,所以就全部开源分享给大家吧,一起学习。下一步计划重新设计模拟电路部分使用CPLD和高速AD实现更高的采样率。 硬件平台: 主控器:STM32F103ZET664K RAM 512K ROM 屏幕器:SSD1963 分辨率:480*272 16位色 触摸屏:TSC2046 模拟电路:OP-TL084 OP-U741 SW-CD4051 CMP-LM311 PWR-LM7805 -LM7905 -MC34063 -AMS1117-3.3 DRT-ULN2003 6.继电器:信号继电器 7.电源:DC +12V 注:芯片的数据手册等信息可以在集成电路查询网站搜索https://www.datasheet5.com/ 芯片询价和在线购买链接https://www.bom2buy.com/ 软件平台: 开发环境:RealView MDK-ARM uVision4.10 C编译器:ARMCC ASM编译器:ARMASM 连机器:ARMLINK 实时内核:UC/OS-II 2.9实时操作系统 GUI内核:uC/GUI 3.9图形用户接口 底层驱动:各个外设驱动程序 数字示波器功能: 波形发生器:使用STM32一路DA实现正弦,三角波,方波,白噪声输出。 任意一种波形幅值在0-3.3V任意可调、频率在一定范围任意可调、方波占空比可调。调节选项可以通过触摸屏完成设置。 SD卡存储: SD卡波形存储输出,能够对当前屏幕截屏,以JPG格式存储在SD卡上。能够存储1S内的波形数据,可以随时调用查看。 数据传输:用C#编写上位机,通过串口完成对下位机的控制。(1)实现STOP/RUN功能(2)输出波形电压、时间参数(3)控制截屏(4)控制波形发生器(5)控制完成FFT(6)波形的存储和显示 图形接口: UCGUI 水平扫速: 250 ns*、500ns、1μs、5 μs、10μs、50μs、500 μs、5ms 、50ms 垂直电压灵敏度:10mV/div, 20mV/div, 50mV/div, 0.1V/div, 0,2V/div, 0.5V/div, 1V/div,2V/div, 5V/div 被测信号的各种参数屏幕显示,包括频率、电压峰峰值等。 数字示波器性能参数: 主控: STM32F103ZET6 液晶屏: 4.3寸TFT480×272 65K彩色LCD显示屏 FSMC AD: 12位1MHz采样率 最高实时取样率: 1MSa/s 12Bits 取样缓冲器深度: 5K 垂直灵敏度: 5V,1V,500mV,200mV,100mV,50mV,20mV,10mV; 水平时基范围:2S,1S,500mS,200mS,100mS,50mS,20mS,10mS,5mS,2mS,1mS,500uS,200uS,100uS,50uS,20uS,10uS,5uS,2uS,1uS 输入阻抗: ≥1MΩ 最高输入电压: 30Vpp 耦合方式: AC/DC 触发功能: 实现自动、常规、单次触发方式 ,上升或下降边沿触发 参数计算: 频率、周期、占空比、交流峰-峰值、平均值、光标追踪显示 RUN/STOP 数字示波器实物视频演示: 实物展示: 上位机截图: 设计心得分享: 由于程序量比较大,所以在程序中我完成了大量的注释,详细的说明了具体的实现方法,其中的示波器原理和FFT原理请自己查阅相关教材。我个人认为最值得参考的就是如何制定一个效率较高的通信协议。我查阅了很多资料最后结合实际设计了一套通信协议,这套通信协议效传输速率理论最大可达16000bps,实际采用10666bps速率传输。协议的编码、解码算法的

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 53
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值