基于51单片机的波形发生器(PCF8591、正弦波、三角波、锯齿波、方波)

news/2024/11/23 6:51:02/

        正弦波、三角波、锯齿波和方波是我们平时最常见的四种波形,那么学会使用单片机产生这四种波就很重要了。但学过51单片机就知道,其IO引脚只能输出高电平或低电平,单靠其引脚生成以上波形,好像不大可能,因此我们需要配合使用另一个很重要的芯片,DAC芯片,将数字量转换为模拟量,这样,生成以上波形就变得有可能了。

       本次设计,我们通过 按键切换输出波模式,1-正弦波,2-三角波,3-锯齿波,4-方波,使用数码管显示当前输出的波模式。本次设计的波形为示意图,其中正弦波、三角波、锯齿波等频率都比较低,如果想要可变频率,可以通过定时器实现,但转化时间的存在使得频率依旧比较低。

文末有完整源码文件与仿真文件分享

注意本次仿真采取的proteus版本为8.15,低于此版本无法打开。如发现波形不显示,请点击控制栏debug,勾选如下:

 

        (1)PCF8591

        pcf8591是一款8位的ad/da转换芯片,其使用的是IIC通信方式,支持电压2.5-6V。拥有四路输入和一路输出。本次设计如下:

        

         参考电压为5V,A0、A1、A2接地,因此,写地址为0x90,读地址为0x91。

(2)正弦波生成

        生成正弦波的方法我们可以这么想:我们将一个正弦曲线进行离散,可以得到一系列的点,将这些点对应到0-5V的电压,然后我们通过pcf8591输出这些电压,同时在间隔时间内进行保持,我们就可以得到一个近似的正弦曲线,只要这个间隔时间够短,这个曲线就越接近于正弦曲线。

        OK,理论有了,下面就可以开始实践了。首先,我们想想怎么通过51单片机得到正弦曲线。自己写一个函数?也行,但有点麻烦。这时我们可以借鉴另一个常用头文件,math.h,里面就有我们需要的sin函数,但需要注意的是,我们日常看到的曲线横坐标是角度,而这个函数处理的是弧度,这时就设计到一个转换关系。即180度=3.1415......。如果我们取间隔均匀的角度,我们就可以得到离散化的曲线。其次,将-1到1转换为0-255的值,但PCF8591不会输出负压,因此我们最低为0V,即sin值*128+128。最后,电压输出,我们需要写一个I基于IC的电压输出函数。下面是具体的实现代码。

1)正弦波的离散与转换

void dac_sine()//正弦波
{static u16 angle = 0; 		//角度static float radian = 0;	//弧度angle = angle + 5;			//每次加5度if(angle == 360)			//一个周期369度{angle = 0;}radian = angle * 0.0055;	//角度转弧度volt_sine = 128 + (sin(radian * pi) * 128);//标度变换,1~-1转换到//0-255set_volt(volt_sine);//电压输出
}

2)电压输出

注意写入的0x40,4代表开启电压输出,0x90是电压输出控制字,上文有说。

void set_volt(u8 value)					
{I2CStart();I2CSendByte(0x90);						//写地址I2CWaitAck();I2CSendByte(0x40);						//通道选择I2CWaitAck();I2CSendByte(value);						//写入数据0-255对应5VI2CWaitAck();I2CStop();
}

借助以上代码,我们就可以得到如下的曲线:

 (3)三角波生成

        相对于生成正弦波,生成三角波就简单太多了。PCF8591输出0-5V,对应0-255,我们只要让一个变量从0加到255,再从255降到0,不断循环,三角波就出现了。

1)程序

void dac_triangular()//三角波
{static bit flag = 0;    //切换标志if(!flag)				//上升段{volt_trian = volt_trian + 5;if(volt_trian == 255){flag = 1;		}}else					//下降段{volt_trian = volt_trian - 5;if(volt_trian == 0){flag = 0;}}set_volt(volt_trian);//电压输出
}

2)仿真效果

(4)生成锯齿波

        所谓锯齿波,就是三角波的一半。让一个变量从0加到255,再突降到0。重复上述过程,即可得到锯齿波。

1)程序

void dac_zigzag()//锯齿波
{volt_zigzag = volt_zigzag + 5;if(volt_zigzag == 255){volt_zigzag = 0;}set_volt(volt_zigzag);
}

2)效果

 

(5)方波

至于方波,我们用IO口就可以实现。用PCF8591的话,那就输出一段时间5V后切换0V,重复。

1)程序

void Delay10ms()		//@12.000MHz
{unsigned char i, j;i = 20;j = 113;do{while (--j);} while (--i);
}
void dac_square()//方波
{volt_aquare = 255;set_volt(volt_aquare);Delay10ms();//延时等待volt_aquare = 0;set_volt(volt_aquare);//电压输出Delay10ms();
}

2)效果

 

OK,OK,讲解完毕,以下为全部代码

(1)mian.c

/*********************************************
Author:sakura
Time:2023/5/16
funcation:利用8位的AD/DA转换器PCF8591产生正弦波、三角波、锯齿波、方波四种常用波形。通过按键切换波形输出,同时数码管显示当前波形模式,0-正弦波,1-三角波、2-锯齿波,3-方波。
copyright:版权所有,借鉴请注明
*********************************************/
#include "main.h"
#include "iic.h"
#include "dacout.h"
#include "switchover.h"
/************************************************
函数名:main
功能:主函数入口
参数:无
返回值:无
************************************************/
void main()
{while(1){waveform_out();key_pron();seg_pron();}
}

main.h

#ifndef __MAIN_H
#define __MAIN_H#include <reg51.h>
#include <intrins.h>
#include <math.h>#define u8 unsigned char 
#define u16 unsigned int 
#define pi 3.14159//Π#endif

(2)iic.c

#include "iic.h"#define DELAY_TIME	5/************************************************
函数名:I2C_Delay
功能:iic延时函数,15个机器周期
参数:n,延时n*15
返回值:无
************************************************/
static void I2C_Delay(unsigned char n)
{do{_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();		}while(n--);      	
}/************************************************
函数名:I2CStart
功能:iic起始信号
参数:无
返回值:无
************************************************/
void I2CStart(void)
{sda = 1;scl = 1;I2C_Delay(DELAY_TIME);sda = 0;I2C_Delay(DELAY_TIME);scl = 0;    
}/************************************************
函数名:I2CStop
功能:iic终止信号
参数:无
返回值:无
************************************************/
void I2CStop(void)
{sda = 0;scl = 1;I2C_Delay(DELAY_TIME);sda = 1;I2C_Delay(DELAY_TIME);
}/************************************************
函数名:I2CSendByte
功能:iic写字节
参数:byt,写入的数据
返回值:无
************************************************/
void I2CSendByte(unsigned char byt)
{unsigned char i;for(i=0; i<8; i++){scl = 0;I2C_Delay(DELAY_TIME);if(byt & 0x80){sda = 1;}else{sda = 0;}I2C_Delay(DELAY_TIME);scl = 1;byt <<= 1;I2C_Delay(DELAY_TIME);}scl = 0;  
}/************************************************
函数名:I2CReceiveByte
功能:iic读字节
参数:无
返回值:da,读出的数据
************************************************/
/*unsigned char I2CReceiveByte(void)
{unsigned char da;unsigned char i;for(i=0;i<8;i++){   scl = 1;I2C_Delay(DELAY_TIME);da <<= 1;if(sda) da |= 0x01;scl = 0;I2C_Delay(DELAY_TIME);}return da;    
}*//************************************************
函数名:I2CWaitAck
功能:主机等待应答信号
参数:无
返回值:ackbit,0-应答,1-非应答
************************************************/
unsigned char I2CWaitAck(void)
{unsigned char ackbit;scl = 1;I2C_Delay(DELAY_TIME);ackbit = sda; scl = 0;I2C_Delay(DELAY_TIME);return ackbit;
}/************************************************
函数名:I2CSendAck
功能:从机发送应答信号
参数:ackbit,0-应答,1-非应答
返回值:无
************************************************/
/*void I2CSendAck(unsigned char ackbit)
{scl = 0;sda = ackbit; I2C_Delay(DELAY_TIME);scl = 1;I2C_Delay(DELAY_TIME);scl = 0; sda = 1;I2C_Delay(DELAY_TIME);
}*/

iic.h

#ifndef __IIC_H
#define __IIC_H#include "main.h"sbit sda = P2^1;//数据线
sbit scl = P2^0;//时钟线static void I2C_Delay(unsigned char n);
void I2CStart(void);
void I2CStop(void);
void I2CSendByte(unsigned char byt);
//unsigned char I2CReceiveByte(void);
unsigned char I2CWaitAck(void);
//void I2CSendAck(unsigned char ackbit);
#endif

(3)dacout.c

#include "dacout.h"
#include "iic.h"
u8 volt_sine = 0;    	//正弦波
u8 volt_trian = 0;		//三角波
u8 volt_zigzag = 0;		//锯齿波
u8 volt_aquare = 0;		//方波extern u8 mode;			//输出模式
/************************************************
函数名:dac_sine
功能:输出正弦波
参数:无
返回值:无
************************************************/
void dac_sine()//正弦波
{static u16 angle = 0; 		//角度static float radian = 0;	//弧度angle = angle + 5;			//每次加5度if(angle == 360)			//一个周期369度{angle = 0;}radian = angle * 0.0055;	//角度转弧度volt_sine = 128 + (sin(radian * pi) * 128);//标度变换,1~-1转换到//0-255set_volt(volt_sine);//电压输出
}
/************************************************
函数名:dac_triangular
功能:输出三角波
参数:无
返回值:无
************************************************/
void dac_triangular()//三角波
{static bit flag = 0;    //切换标志if(!flag)				//上升段{volt_trian = volt_trian + 5;if(volt_trian == 255){flag = 1;		}}else					//下降段{volt_trian = volt_trian - 5;if(volt_trian == 0){flag = 0;}}set_volt(volt_trian);//电压输出
}
/************************************************
函数名:dac_zigzag
功能:输出锯齿波
参数:无
返回值:无
************************************************/
void dac_zigzag()//锯齿波
{volt_zigzag = volt_zigzag + 5;if(volt_zigzag == 255){volt_zigzag = 0;}set_volt(volt_zigzag);
}
/************************************************
函数名:Delay10ms
功能:10ms延时
参数:无
返回值:无
************************************************/
void Delay10ms()		//@12.000MHz
{unsigned char i, j;i = 20;j = 113;do{while (--j);} while (--i);
}
/************************************************
函数名:dac_square
功能:输出方波
参数:无
返回值:无
************************************************/
void dac_square()//方波
{volt_aquare = 255;set_volt(volt_aquare);Delay10ms();//延时等待volt_aquare = 0;set_volt(volt_aquare);//电压输出Delay10ms();
}
/************************************************
函数名:waveform_out
功能:波形输出选择
参数:无
返回值:无
************************************************/
void waveform_out()
{switch(mode){case 0:dac_sine();break;		//模式0,正弦波case 1:dac_triangular();break;	//模式1,正弦波case 2:dac_zigzag();break;		//模式2,正弦波case 3:dac_square();break;		//模式3,正弦波default:break;}
}
/************************************************
函数名:set_volt
功能:使用pcf8591输出电压
参数:value 0-255
返回值:无
************************************************/
void set_volt(u8 value)					
{I2CStart();I2CSendByte(0x90);						//写地址I2CWaitAck();I2CSendByte(0x40);						//通道选择I2CWaitAck();I2CSendByte(value);						//写入数据0-255对应5VI2CWaitAck();I2CStop();
}

dacout.h

#ifndef __DACOUT_H
#define __DACOUT_H
#include "main.h"void dac_sine();//正弦波
void dac_triangular();//三角波
void dac_zigzag();//锯齿波
void Delay10ms();		//@12.000MHz
void dac_square();//方波
void waveform_out();
void set_volt(u8 value);#endif

(4)switchover.c

#include "switchover.h"
//共阴数码管段码0-9
u8 code SMG_NODOT[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//无小数点
u8 mode = 0;//输出模式
/************************************************
函数名:key_pron
功能:切换输出波模式,1-正弦波,2-三角波,3-锯齿波4-方波
参数:无
返回值:无
************************************************/
void key_pron()
{if(!key){Delay10ms();if(!key){while(!key);mode++;if(mode == 4){mode = 0;}}}
}
/************************************************
函数名:seg_pron
功能:数码管显示输出波模式
参数:无
返回值:无
************************************************/
void seg_pron()
{P1 = SMG_NODOT[mode];
}

switch.h

#ifndef __SWITCHOVER_H
#define __SWITCHOVER_H
#include "main.h"sbit key = P2^7;extern void Delay10ms();		//@12.000MHzvoid key_pron();
void seg_pron();#endif

6.源码及仿真资源下载

链接:https://pan.baidu.com/s/1_ug6Ih4NxAT39CVCHRjFCg?pwd=pmuo 
提取码:pmuo


http://www.ppmy.cn/news/781479.html

相关文章

Gradio,我们可以为我们的模型创建Web界面

Gradio是一个Python库&#xff0c;允许我们快速为机器学习模型创建可定制的接口。 使用Gradio&#xff0c;我们可以为我们的模型创建Web界面&#xff0c;而无需编写任何HTML&#xff0c;CSS或JavaScript。 Gradio旨在与广泛的机器学习框架配合使用&#xff0c;包括TensorFlow&a…

信号隔离安全栅与信号隔离器的区别

http://www.360doc.com/content/19/0216/22/52711050_815423007.shtml

win10 音频设备图形隔离 占用CPU

这几天工作很烦&#xff01;每次上班开机我的电脑就超级卡&#xff0c;自我感觉i7处理器加上8g内存应该杠杠的&#xff0c;打开任务管理器发现就不对了&#xff0c; 有“windows 音频设备图形隔离 ”这个进程吃了我20%的cpu&#xff0c;电脑的风扇也特别的响。后面各种百度&…

AUX音频隔离

音响噪声产生的原理 音响的基本原理是左手定律&#xff0c;将一根通电导线放入磁场中&#xff0c;导线所受安培力与磁场方向、电流方向的关系遵循左手定则&#xff0c;即&#xff1a; 动圈式音响的基本原理是通电导线中电流的变化导致所受安培力的变化&#xff0c;从而带动偏振…

隔离技术之端口隔离

端口隔离技术 端口隔离主要应用在同一个vlan内&#xff0c;不同的用户之间不可互相访问 好处&#xff1a; 可以避免广播风暴&#xff0c;节约了vlan的资源&#xff0c;提高了用户之间的安全性 比如一个用户的电脑中病毒了&#xff0c;不会影响到其他用户 端口隔离是基于端口&…

什么是空间音频?它与双耳音频有什么关系?

什么是空间音频&#xff1f;它与双耳音频有什么关系&#xff1f; 在此之前&#xff0c;您可能已经听到一些关于空间音频的信息&#xff0c;但空间音频究竟是什么&#xff1f;这一功能到底有什么魅力&#xff0c;能让 Google、Apple 和 Samsung 等大公司纷纷将它集成到自己的产…

音频输出设备是如何决定的

1. 既然是分析音频输出设备&#xff0c;我们首先需要知道当前手机支持的音频输出设备有哪些 adb shell dumpsys media.audio_policy > /home/jon/audio_policy.txt 我们关注如下字段&#xff1a; - Available output devices:Device 1:- id: 1- tag name: Earpiece- type:…

语音信号处理:分帧、加窗

原文链接&#xff0c;在原文的基础上加入了自己的一些学习过程中遇到的一些问题而插入的其他链接的知识点&#xff0c;有错误请大家指正&#xff0c; 多多交流python对语音信号读取、分帧、加窗_YAOHAIPI的博客-CSDN博客用python做语音信号处理一、读入音频信号语音信号有三个重…