基于STM32的波形发生器设计

发布时间:2024-01-26  

一、项目介绍

信号发生器在生产实践和科技领域中有着广泛的应用,各种波形曲线均可以用三角函数方程式来表示。能够产生多种波形,如三角波、锯齿波、矩形波(含方波)、正弦波的电路被称为函数信号发生器。函数信号发生器在电路实验和设备检测中具有十分广泛的用途,例如在通信、广播、电视系统中,都需要射频(高频)发射。这里的射频波就是载波,把音频(低频)、视频信号或脉冲信号运载出去,就需要能够产生高频的振荡器。在工业、农业、生物医学等领域内,如高频感应加热、熔炼、淬火、超声诊断、核磁共振成像等,都需要功率或大或小、频率或高或低的振荡器。函数信号发生器是各种测试和实验过程中不可缺少的工具,在通信、测量、雷达、控制、教学等领域应用十分广泛。不论是在生产、科研还是教学上,信号发生器都是电子工程师信号仿真实验的最佳工具。而且,信号发生器的设计方法多,设计技术也越来越先进, 随着我国经济和科技的发展, 对相应的测试仪器和测试手段也提出了更高的要求,信号发生器己成为测试仪器中至关重要的一类,因此开发信号发生器具有重大意义。


二、主程序流程图

对系统运行工作流程进行说明后,给出系统主程序流程。

设计思路:首先将所有配置进行初始化,为了消除干扰和提升仿真效率,我们将系统的工作状态分为两种,一种为调整状态,另一种为波形输出状态,使用一个开关的通断来调整。


系统主程序流程图如图4-1所示。

图片

三、 Proteus软件仿真调试

正弦波

图片

方波

图片

三角波

图片

锯齿波

图片

四、 硬件调试

在实际电路中,我们可以将频率调整的快一点,但是要注意抗干扰问题,毕竟上面的都是理论值,模拟信号非常容易受到其他因素的干扰。所有配置,都必须跟程序里面完全一致,如果波形失真,或者频率不对,还应进行相对应的补偿。

五、 程序清单

#include "stm32f10x.h"

#include "sys.h"

#include "delay.h"

#include "12864.h"

#include "key4_4.h"

#include "timer.h"

#define KEY0 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)//读取按键0

void Delay_Ms(u16 time);

/*************** 配置Switch用到的I/O口*******************/

void Init_GPIO_Switch(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

//关闭jtag,使能SWD,可以用SWD模式调试

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

// 使能PC端口时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

//PC0

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

//IO口速度为50MHz

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

//设置成输入

GPIO_Init(GPIOB, &GPIO_InitStructure);

//初始化PC0

}

/****************************************************************

*功能名称:main

*描述:主程序。

*输入:无

*输出:无

*返回:无

****************************************************************/

int main(void)

{

u8 i=0;

RCC_ClocksTypeDef RCC_Clocks; //初始化程序

RCC_Configuration(RCC_PLLMul_4); //8M*4 == 32M

RCC_GetClocksFreq(&RCC_Clocks); //获取片上时钟

Init_12864(); //初始化12864液晶

Key_Init();

Init_GPIO_Switch();

Init_GPIO_DAC0832();

Data0=25;

TIM3_Int_Init(50+Data0,320);

//频率:32000000/ 320 ==100 000 /100 == 1000 /50==20

LCD_P6x8Str(3,16," Sine Wave ");

LCD_P6x8Str(7,6*2,"Frequency: 15 Hz");

while (1)

{

if(KEY0)

{

if(i!=2)

{

__set_PRIMASK(1);

GPIO_ResetBits(GPIOB, ((uint16_t)0xC000));

}

Key_Test();

i=2;

}

else{

if(i!=5)

{

TIM3_Int_Init(50+Data0,320);

__set_PRIMASK(0); //使能TIMx外设

GPIO_ResetBits(GPIOB, ((uint16_t)0xC000));

}

i=5;

}

}

}

/*************** 定时器*******************/

#include "timer.h"

#include "math.h"

#define ADC_CS_WR(a) if (a)

GPIO_SetBits(GPIOB,GPIO_Pin_14);

else

GPIO_ResetBits(GPIOB,GPIO_Pin_14)

#define ADC_CS_WR2(a) if (a)

GPIO_SetBits(GPIOB,GPIO_Pin_15);

else

GPIO_ResetBits(GPIOB,GPIO_Pin_15)

#define PI 3.1415926f //圆周率

u8 mode; //模式:正弦波……

u16 freq; //频率

u8 time; //计次参数

u8 AM; //调幅

/*************** 配置DAC用到的I/O口*******************/

void Init_GPIO_DAC0832(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); // 使能PC端口时钟

GPIO_InitStructure.GPIO_Pin = ((uint16_t)0x03FF); //选择对应的引脚

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz

GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化PC端口

GPIO_SetBits(GPIOC, ((uint16_t)0x00FF)); // 高

GPIO_ResetBits(GPIOC, ((uint16_t)0x00FF)); // 低

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 使能PC端口时钟

GPIO_InitStructure.GPIO_Pin = ((uint16_t)0xC000); //选择对应的引脚

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz

GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PC端口

GPIO_SetBits(GPIOC, ((uint16_t)0xC000)); // 高

GPIO_ResetBits(GPIOC, ((uint16_t)0xC000)); // 低

time=0;

ADC_CS_WR(0);

mode=0; //默认输出正弦波

freq=100; //默认频率

AM=255; //最大幅度

}

void DAC_0832_Data(uint8_t Data)

{

GPIO_ResetBits(GPIOC,~Data);

GPIO_SetBits(GPIOC,Data);

}

void sine_wave(u8 location)//输出正弦波

{

double x=(double)location/50*PI;//把0-100放缩到0-2派(pai,没有那个符号)

u8 y=(sin(x)*(AM/2)+(AM/2));//算出y,并放缩到0-254(因为ADC范围0-AM,芯片落后)

DAC_0832_Data(y);

}

void squ_wave(u8 location)//方……

{

if(location<50)

DAC_0832_Data(AM);

else

DAC_0832_Data(0);//这个简单

}

void tri_wave(u8 location)//三……

{

//为了简化,在单周期输出V字形

u8 y;

if(location<50)

y=(50-location)*AM/50;

else

y=(location-50)*AM/50;

DAC_0832_Data(y);

//偶函数,当然说奇函数也没错

}

void saw_wave(u8 location)//锯……

{

DAC_0832_Data(location*AM/100);

//用(100-location)也以变成反向锯齿

}

//通用定时器中断初始化

//这里时钟选择为APB1的2倍,而APB1为36M

//arr:自动重装值。

//psc:时钟预分频数

//这里使用的是定时器3!

void TIM3_Int_Init(u16 arr,u16 psc)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms

TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率

TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

TIM_ITConfig( //使能或者失能指定的TIM中断

TIM3, //TIM3

TIM_IT_Update ,

ENABLE //使能

);

// TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能

NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

TIM_Cmd(TIM3, ENABLE); //使能TIMx外设

// TIM_Cmd(TIM3, DISABLE);

}

void TIM3_IRQHandler(void) //TIM3中断

{

switch(mode)

{

case W_SINE:sine_wave((u8)(time*freq/100)%100);break;//计算出波的位置

case W_SQU:squ_wave((u8)((time*freq/100)%100));break;

case W_TRI:tri_wave((u8)((time*freq/100)%100));break;

case W_SAW:saw_wave((u8)((time*freq/100)%100));break;

}

time++;

if(time>=100)//计数100次

time=0;

}

#include "key4_4.h"

#include "delay.h"

#include "sys.h"

#include "12864.h"

#include "timer.h"

//8个引脚 4个为行 4个为列

//行输出端口定义

#define X1_GPIO_PORT GPIOB

#define X2_GPIO_PORT GPIOB

#define X3_GPIO_PORT GPIOB

#define X4_GPIO_PORT GPIOB

//列输入端口定义

#define Y1_GPIO_PORT GPIOB

#define Y2_GPIO_PORT GPIOB

#define Y3_GPIO_PORT GPIOB

#define Y4_GPIO_PORT GPIOB

//行输出引脚定义

#define X1_GPIO_PIN GPIO_Pin_0

#define X2_GPIO_PIN GPIO_Pin_1

#define X3_GPIO_PIN GPIO_Pin_2

#define X4_GPIO_PIN GPIO_Pin_3

//列输入引脚定义

#define Y1_GPIO_PIN GPIO_Pin_4

#define Y2_GPIO_PIN GPIO_Pin_5

#define Y3_GPIO_PIN GPIO_Pin_6

#define Y4_GPIO_PIN GPIO_Pin_7

//行输出时钟定义

#define X1_RCC RCC_APB2Periph_GPIOB

#define X2_RCC RCC_APB2Periph_GPIOB

#define X3_RCC RCC_APB2Periph_GPIOB

#define X4_RCC RCC_APB2Periph_GPIOB

//列输入时钟定义

#define Y1_RCC RCC_APB2Periph_GPIOB

#define Y2_RCC RCC_APB2Periph_GPIOB

#define Y3_RCC RCC_APB2Periph_GPIOB

#define Y4_RCC RCC_APB2Periph_GPIOB

//移植代码只需要修改上面的端口和引脚和时钟即可,下面的代码不用修改。

//矩阵键盘所用的8个引脚可连续可不连续,看实际需要和个人爱好自己定义。

unsigned char Y1,Y2,Y3,Y4;

void Key_Init(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(X1_RCC|X2_RCC|X3_RCC|X4_RCC|Y1_RCC|Y2_RCC|Y3_RCC|Y4_RCC|RCC_APB2Periph_AFIO, ENABLE);

GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

/*******************4行输出 *****************************/

GPIO_InitStructure.GPIO_Pin = X1_GPIO_PIN ;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_Init(X1_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = X2_GPIO_PIN ;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_Init(X2_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = X3_GPIO_PIN ;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_Init(X3_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_InitStructure.GPIO_Pin = X4_GPIO_PIN ;

GPIO_Init(X4_GPIO_PORT, &GPIO_InitStructure);

/******************* 4列输入 *******************/

GPIO_InitStructure.GPIO_Pin = Y1_GPIO_PIN ;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_Init(Y1_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = Y2_GPIO_PIN ;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_Init(Y2_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = Y3_GPIO_PIN ;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_Init(Y3_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_InitStructure.GPIO_Pin = Y4_GPIO_PIN;

GPIO_Init(Y4_GPIO_PORT, &GPIO_InitStructure);

}

int Key_Scan(void)

{

uchar KeyVal;

GPIO_SetBits(X1_GPIO_PORT,X1_GPIO_PIN); //先让X1输出高

GPIO_SetBits(X2_GPIO_PORT,X2_GPIO_PIN); //先让X2输出高

GPIO_SetBits(X3_GPIO_PORT,X3_GPIO_PIN); //先让X3输出高

GPIO_SetBits(X4_GPIO_PORT,X4_GPIO_PIN); //先让X4输出高

if((GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN)|GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN)|GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN)|GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN))==0x0000)

return -1; //如果X1到X4全为零则没有按键按下

else

{

Delay_Ms(5); //延时5ms去抖动

if((GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN)|GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN)|GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN)|GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN))==0x0000)

return -1;

}

GPIO_ResetBits(X1_GPIO_PORT,X1_GPIO_PIN);

GPIO_ResetBits(X2_GPIO_PORT,X2_GPIO_PIN);

GPIO_ResetBits(X3_GPIO_PORT,X3_GPIO_PIN);

GPIO_SetBits(X4_GPIO_PORT,X4_GPIO_PIN);

Y1=GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN);Y2=GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN);

Y3=GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN);Y4=GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN);

if(Y1==1&&Y2==0&&Y3==0&&Y4==0)

KeyVal='*';

if(Y1==0&&Y2==1&&Y3==0&&Y4==0)

KeyVal=0;

if(Y1==0&&Y2==0&&Y3==0&&Y4==1)

KeyVal='D';

if(Y1==0&&Y2==0&&Y3==1&&Y4==0)

KeyVal='#';

while(((GPIO_ReadInputDataBit(Y1_GPIO_PORT,Y1_GPIO_PIN))|(GPIO_ReadInputDataBit(Y2_GPIO_PORT,Y2_GPIO_PIN))|(GPIO_ReadInputDataBit(Y3_GPIO_PORT,Y3_GPIO_PIN))|(GPIO_ReadInputDataBit(Y4_GPIO_PORT,Y4_GPIO_PIN))) > 0);

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>