波形发生器是一种在电子实验、教学以及工程测试中不可或缺的设备,用于产生各种电信号以激励电路或模拟信号。本设计旨在利用价格低廉、应用广泛的MCS-51系列单片机作为核心控制单元,结合模块化的外围电路,在Proteus仿真平台上一款简易实用的函数信号发生器。

一、系统设计

1、51单片机

        51单片机是指兼容Intel 8051指令系统的一系列单片机型号(如AT89C51、STC89C52等),是本次波形发生器设计的"大脑"。采用8位中央处理器,拥有32个可编程I/O口、3个16位定时器/计数器和8个中断源。通常包含4K-8K字节的Flash程序存储器和128-256字节的RAM。常用12MHz晶振,此时机器周期为1μs,指令执行速度适中。

2、PCF8591

        PCF8591是NXP公司生产的一款单芯片、低功耗8位CMOS数据采集器件,它集成了A/D和D/A转换功能于一体。采用I²C串行总线通信,仅需SDA(数据线)和SCL(时钟线)两根线与单片机连接。4路模拟输入(可编程为单端或差分输入),1路模拟输出,8位分辨率(即数字量0-255对应模拟电压0-VREF),电源电压2.5V-6V宽范围,典型值5V,3个地址引脚(A0、A1、A2)允许同一I²C总线上挂接最多8个PCF8591。

3、按键

        按键(按钮)是一种通过触点的通断来控制电路的人工控制主令电器。在单片机系统中,按键是最基础、最常用的人机交互工具。未按下时,按键的常开触点断开,I/O口通过上拉电阻保持高电平;按下后触点闭合,I/O口被拉到低电平。单片机通过检测I/O口电平的变化来判断按键是否被按下。

二、软件设计

1、PCF8591

        PCF8591使用IIC协议进行通信,支持adc输入和dac输出。本次设计主要使用他的一个输出功能。参考代码如下:

#include "iic.h"
 
//本代码加入了读取ACK问题,使用了goto语句
 
//AD转换,读取com通道的8位数据,0x00/0x40表示打开通道AIN0	0x01/0x41表示打开通道AIN1	0x02/0x42表示打开通道AIN2	0x03/0x43表示打开通带AIN3
unsigned char read_AD_data(unsigned char com)
{
	unsigned char dat = 0;
	unsigned char i = 0;
	read_ad_data:
	START_IIC();
	write_1_byte_IIC(0x90);//准备向pcf8591器件写入数据,此器件的地址为1001 A0 A1 A2 R/W
	if (!read_ACK_IIC())//若接收到ACK则与器件握手成功
	{
		write_1_byte_IIC(com);//向器件写入使用哪个AD通到开始采集数据,0x00/0x40表示通道AIN0	0x01/0x41表示通道AIN1	0x02/0x42表示通道AIN2	0x03/0x43表示通带AIN3
		if (!read_ACK_IIC())
		{
			START_IIC();//和at24c02 读取数据有点类似   马来币之前没有写 手册英文没有看到这个地方如何读取的,参考别人写的代码
			write_1_byte_IIC(0x91);//准备读取pcf8591发来的数据
			if (!read_ACK_IIC())
			{
				dat = read_1_byte_IIC();
				write_ACK_IIC(1);//主机接收到数据向从设备不发送应答信号
				STOP_IIC();	//结束本次通讯
				return dat;
			}
			else
			{
				STOP_IIC();
				goto read_ad_data;
			}
		}
		else
		{
			STOP_IIC();
			goto read_ad_data;
		}
	}
	else
	{
		i++;
		if (i == 255)
		{
			return i;//如果握手失败255次则直接结束本函数,并向函数返回运行结果255;
		}
		STOP_IIC();
		goto read_ad_data;
	}
		
}
 
//DA转换,输出模拟量通过芯片的AOUT端口输出
unsigned char write_DA_data(unsigned char dat)//0~255对应0V~5V
{
	unsigned char i = 0;
	write_da_data:
	START_IIC();
	write_1_byte_IIC(0x90);
	if (read_ACK_IIC() == 0)
	{
		write_1_byte_IIC(0x40);//允许器件模拟输出,并且不自动增加输出端口
		if (!read_ACK_IIC())
		{
			write_1_byte_IIC(dat);
			if (!read_ACK_IIC())
			{
				STOP_IIC();
				return i;//如果握手失败255次则直接结束本函数,并向函数返回运行结果255;
			}
			else
			{
				STOP_IIC();
				goto write_da_data;
				
			}
				
		}
	 else
	 {
			STOP_IIC();
			goto write_da_data;
	 }
	}
	else
	{
		i++;
		if (i == 255)
		{
			return i;//如果握手失败255次则直接结束本函数,并向函数返回运行结果255;
		}
		STOP_IIC();
		goto write_da_data;
	}
}
//数字量转换成模拟电压公式如下
unsigned char convert_data(unsigned char DA)
{
	unsigned char i = 0;
	i = (unsigned char)(DA * 5 / 256.0 + 0.0005);//pcf8591内部数字量转化为模拟量的计算公式
	return i;
}
#ifndef __PCF8591_H__
#define __PCF8591_H__
 
unsigned char read_AD_data(unsigned char com);//直接读取的就是芯片转换后的数字信号
 
//DA转换,输出模拟量通过芯片的AOUT端口输出
unsigned char write_DA_data(unsigned char dat);//芯片直接接收数字信号后自动转换为模式信号并输出
 
//数字量转换成模拟电压公式如下
unsigned char convert_data(unsigned char DA);
 
#endif // !__PCF8591_H__
 

2、波形生成相关

        本次设计使用数组加定时器的方式生成波形,由于单片机性能的原因,做不到高频,但是一般情况下够用,参考代码如下:

#include <reg51.h>   //51头文件
#include "PCF8591.h"

#define uchar unsigned char
#define uint unsigned int


sbit s1=P3^2;
sbit s2=P3^3;
uchar k=0,p=0,delay=0;
int bxxz=0;pinglv=0;
uchar a=1,b=0,c=0,d=0;

//正弦波数据
uchar code sin[64]={
135,145,158,167,176,188,199,209,218,226,234,240,245,249,252,254,254,253,251,247,243,237,230,222,213,204,193,182,170,158,
146,133,121,108,96,84,72,61,50,41,32,24,17,11,7,3,1,0,0,2,5,9,14,20,28,36,45,55,66,78,90,102,114,128
};
//方波数据报表
uchar code juxing[64]={
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

//锯齿波数据
uchar code juchi[64]={
0,4,8,12,16,20,24,28,32,36,40,45,49,53,57,61,65,69,73,77,81,85,89,93,97,101,105,109,113,117,121,125,130,134,138,142,
146,150,154,158,162,166,170,174,178,182,186,190,194,198,202,206,210,215,219,223,227,231,235,239,243,247,251,255
};

//三角波数据
uchar code sanjiao[64]={
0,8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128,136,144,152,160,168,176,184,192,200,208,216,224,232,240,248,
248,240,232,224,216,208,200,192,184,176,168,160,152,144,136,128,120,112,104,96,88,80,72,64,56,48,40,32,24,16,8,0
};

void delay1()		  //延时  ms级 
{
	int a,b;
	for(a=1;a>0;a--)
		for(b=122;b>0;b--);
}

void int0() interrupt 0	  //外部中断0  P3.2  中断处理,用于波形切换
{	
	EX0=0;
	delay1();

	if(s1==0)
	{		
		bxxz++;
		if(bxxz==4)
		bxxz=0;
		switch(bxxz)
		{
			case 0 :
				{a=1,b=0,c=0,d=0;}		//正弦波
				break;
			case 1 :
				{a=0,b=1,c=0,d=0;}		//方波
				break;
			case 2 :					
				{a=0,b=0,c=1,d=0;}	  	//锯齿波
				break;	
			case 3 :
				{a=0,b=0,c=0,d=1;}	  	//三角波
				break;
		}
		delay1();
		while(!s1);	
	}

	while(!s1);
	EX0=1;	  
}

void int1() interrupt 2		//外部中断1  P3.3  中断处理,用于频率调节 
{
	EX1=0;	
	delay1();
	if(s2==0){
		pinglv += 15;
		pinglv %= 500;
	}
	while(!s2);	
	EX1=1;	//开中断
}

void main()
{

	EA=1;
	IT0=1;
	EX0=1;
	IT1=1;
	EX1=1;
	pinglv=15;

	while(1)
	{
	    
		while(a)	       //产生正弦波
		{
 			delay=pinglv; //获取周期数据
			write_DA_data(sin[k]);
			
			k++;
			if(k==64)	  //一个周期取64点波数据
			k=0;

			while(delay)  //延时处理          
		    delay--;
		}
		while(b)		   //产生方波
		{
 			delay=pinglv;
			write_DA_data(juxing[k]);
			k++;
			if(k==64)
			k=0;
			while(delay)           
		    delay--;
		}
		while(c)		   //产生锯齿波
		{
 			delay=pinglv;
			write_DA_data(juchi[k]);
			k++;
			if(k==64)
			k=0;
			while(delay)            
		    	delay--;
		}
		while(d)			 //产生三角波     
		{
 			delay=pinglv;
			write_DA_data(sanjiao[k]);
			k++;
			if(k==64)
			k=0;
			while(delay)            
		    delay--;
		}
	}
}

三、项目演示

(1)上电后单片机默认是正弦波,可以看到下图:

(2)按下第一个按键,可以切换波形,如下:

(3)按下第三个按键,可以进行频率调节。

四、项目总结

        本项目基于51单片机和PCF8591设计并实现了一款简易多波形信号发生器。系统以AT89C51为控制核心,利用其定时功能产生精确的时间基准,通过I²C总线控制PCF8591进行数模转换,配合按键调节,在Proteus仿真环境中成功实现了正弦波、方波、三角波和锯齿波四种波形的输出,并能实时调节频率。关注本人微信公众号“小念电子设计”回复“波形发生”即可获取完整源码。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐