个人博客:http://www.chenjianqu.com/

原文链接:http://www.chenjianqu.com/show-30.html

设计实现的要求

基本要求

(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个半周期三角波输出)。

运行效果演示

开机动画演示

GUI交互界面

波形输出实例

系统方案

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

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

1.1.     系统总体软件设计

软件架构图如下:

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

1.2.     交互模块

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

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

1.3.     DAC波形发生模块

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

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

1.4.     DDS波形发生模块

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

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

代码实现

数据结构

参数管理器

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;elsepm.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;
}

代码太多这里实在是贴不完,若想获得完整代码,请看我的github:https://github.com/chenjianqu/SignalGenerator

(代码等我有空再放上去)

基于STM32的波形发生器相关推荐

  1. stm32锯齿波_工程师实战:基于STM32的波形发生器设计

    本文摘自21ic论坛,作者:21ic网友 一路向北lm) 为了调动大家的积极性,先开个贴,万一你们都不参加呢!哈哈侥幸心理............... 先发个波让你们看看 ,吓吓你们,快点来参赛!! ...

  2. 基于单片机的波形发生器设计

    单片机可以用来设计各种类型的波形发生器,下面是一种基于单片机的波形发生器设计方案. 所需材料: 单片机:可以选择常见的Atmel AVR单片机,如ATmega328P等. 调制器:可以使用AD9833 ...

  3. stm32超声波扫频_基于STM32的超声波发生器扫频信号源的实现

    基于 STM32 的超声波发生器扫频信号源的实现 张加岭,李善波,侯颖钊,赵 杰 [摘 要] 摘要:扫频信号源采用以 STM32F103 单片机为核心,实现高精度锯 齿波扫频.由于超声波电源换能器串联 ...

  4. 基于单片机的波形发生器,可以发送正弦、三角、锯齿和方波,可以支持调频和调幅,能够提供相关设计说明。

    基于单片机的波形发生器,可以发送正弦.三角.锯齿和方波,可以支持调频和调幅,能够提供相关设计说明. :9740631428454681l***4

  5. 基于单片机信号波形发生器系统设计-毕设课设

    [资源下载]下载地址如下1533: https://docs.qq.com/doc/DTlRSd01BZXNpRUxl 前  言 随着电子测量技术与计算机技术的紧密结合,一种新的信号发生器-----波 ...

  6. 基于8086的波形发生器(汇编语言微机课设)

    目录 一.设计要求 二.设计方案及功能 三.元器件清单 四.原理图 五.代码 一.设计要求 采用8086/8088控制器和8位数/模转换芯片DAC0832设计实现波形发生器,编写软件程序. 基本要求: ...

  7. stm32实现波形发生器

    #include "dac.h" //DAC 通道 1 输出初始化 void Dac1_Init(void) { GPIO_InitTypeDef GPIO_InitStructu ...

  8. 基于单片机智能波形发生器设计

    [资源下载]下载地址如下1557: https://docs.qq.com/doc/DTlRSd01BZXNpRUxl 信号发生器是一种常用的信号源,广泛地应用于电子电路.自动控制系统和教学实验等领域 ...

  9. 基于FPGA的波形发生器设计

    用Spartan6的FPGA开发板实现,外接lcd1602液晶显示和TLC5615 dac数模转换模块.仅通过仿真未在实物上调试过.使用matlab生成波形数据的ROM查找表方法设计. 目录 dds_ ...

最新文章

  1. php 过滤中英文以外,PHP-php过滤重复中英文字符串
  2. java中所有的类都继承于_Java中所有的类都是通过直接或间接地继承()类得到的...
  3. 检测对抗样本_对抗T恤以逃避ML人检测器
  4. 人造流星这种生日礼物,你有过吗?现在国外有了
  5. ANSYS2020R2与Solidworks2019进行关联,但2019安装完后打开出现无法获得下列许可SOLIDWORKS Standard.使用许可文件不支持此版本(-21,126,0)
  6. uniapp怎么解析html字符串,uniapp富文本解析插件的详细使用教程
  7. go语言学习初探(二)基础语法
  8. 【转】Linux下的多线程编程背景知识
  9. PHP中类重写方法,php中重写方法有什么规则
  10. Atitit.atiInputMethod v2词库清理策略工具    q229
  11. Python飞机大战代码
  12. Manjaro 安装xmind 8
  13. 考研英语 - word-list-25
  14. 多线程抢票_多线程抢票系统浅析
  15. 计算机232 数据传输,RS232串口通信的传输格式和接收过程
  16. arcgis如何将16bit栅格数据转换为8bit栅格数据
  17. 计算机硬件综合实验六:CPU组成与机器指令执行实验
  18. 跟着猴博士复试概率论(第二部分)
  19. S7-200SMART PLC的IP更改方法
  20. 【寒江雪】判断一个点是否在网格内

热门文章

  1. 中南大学c语言试题期末考试,2011年中南大学C语言期末试题卷A
  2. 设计模式之(四)抽象工厂模式(Abstract Factory)
  3. AtCoder SoundHound Inc. Programming Contest 2018 E + Graph (soundhound2018_summer_qual_e)
  4. (转载)c/c++笔试面试题(2)
  5. 【ADO.NET--MVC】初学MVC(MVC入门)(1)
  6. 方便的Chrome取色插件ColorPick Eyedropper [设计, FE必备]
  7. Canvas实战---模仿GOOGLE浮动小球效果
  8. Servlet实现文件下载
  9. 老罗Android开发视频教程( android解析json数据 )4集集合
  10. 【转载】水木算法讨论题