个人博客: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) 按键模块采用独立按键的方式设置输出波形的类型、幅值和频率等数据。
软件架构图如下:
整个系统的交互采用按键和LCD显示屏实现。为了交互的更加便捷,本系统设计了多级的菜单界面,按键通过由菜单管理器切换菜单界面。菜单界面的底层是参数界面,参数界面用于设置和显示参数。设置的参数通过被放入参数管理器,可用于设置波形的频率和幅值。我们的设想是低频波形用单片机上的DAC实现,高频信号用AD8951也就是直接数字信号合成器合成产生波形。但是为了选择的多样性,无论高频还是低频,我们的DAC和DDS都会同时工作。
本系统的交互模块有需要用到LCD显示屏和按键。LCD显示模块主要由菜单显示模块和参数显示模块组成,分别由菜单管理器和参数管理器控制,通过按键设置菜单管理器和参数管理器的参数,可以设置显示不同的界面。参数管理器也是连接波形发生模块的桥梁。
下图为交互模块的程序框图:
本系统使用的是STM32F103里面自带的DAC。为了提高DAC转换的速度,使用DMA传输波形数据,DMA的触发传输使用的是定时器的中断。本系统使用的正弦波波形数据是通过C语言数学库math.h里面的函数sin()计算得到的数组,三角波通过自定义函数计算波形数据,这两个波形一个周期内采样了512个数据点;而方波的则不同,方波每个周期只改变两次DAC的值。当用户通过交互界面改变幅值参数时,会重新计算波形数据数组。当用户改变频率参数时,会重新计算DMA定时器的周期。波形的输出和关闭则是通过改变DMA定时器中断使能和DMA传输使能实现。
下图为DAC波形发生模块的程序框图:
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;
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;
}
代码太多这里实在是贴不完,若想获得完整代码,请看我的github:https://github.com/chenjianqu/SignalGenerator
(代码等我有空再放上去)