Waveform generator based on 51 microcontroller (PCF8591, sine wave, triangle wave, sawtooth wave, square wave)

Sine wave, triangle wave, sawtooth wave and square wave are the four most common waveforms in our daily life, so it is very important to learn how to use a single-chip microcomputer to generate these four waves. But after learning 51 single-chip microcomputer, you know that its IO pin can only output high level or low level. It seems impossible to generate the above waveform by its pin alone, so we need to use another very important chip, DAC The chip converts the digital quantity into an analog quantity, so that it becomes possible to generate the above waveform.

In this design, we switch the output wave mode through the buttons, 1-sine wave, 2-triangular wave, 3-sawtooth wave, 4-square wave, and use the digital tube to display the current output wave mode. The waveform designed this time is a schematic diagram, in which the frequency of sine wave, triangle wave, and sawtooth wave is relatively low. If you want to change the frequency, you can use a timer to achieve it, but the conversion time makes the frequency still relatively low.

At the end of the article, there are complete source code files and simulation files to share

Note that the proteus version used in this simulation is 8.15, which cannot be opened if it is lower than this version. If you find that the waveform is not displayed, please click debug in the control bar and check the following:

(1) PCF8591

pcf8591 is an 8-bit ad/da conversion chip, which uses the IIC communication method and supports a voltage of 2.5-6V. It has four inputs and one output. This design is as follows:

The reference voltage is 5V, and A0, A1, and A2 are grounded. Therefore, the write address is 0x90, and the read address is 0x91.

(2) Sine wave generation

The method of generating a sine wave can be thought of as follows: we discretize a sine curve to obtain a series of points, and these points correspond to the voltage of 0-5V, and then we output these voltages through pcf8591, and at the same time, the Hold, we can get an approximate sinusoidal curve, as long as the interval is short enough, the curve is closer to the sinusoidal curve.

OK, now that we have the theory, we can start to practice it. First of all, let’s think about how to get the sinusoidal curve through the 51 single-chip microcomputer. Write a function yourself? It works, but it’s a bit of a hassle. At this time, we can learn from another commonly used header file, math.h, which contains the sin function we need, but it should be noted that the abscissa of the curve we see every day is an angle, and this function deals with radians. When designing a conversion relationship. That is, 180 degrees = 3.1415……. If we take evenly spaced angles, we can get discretized curves. Second, convert -1 to 1 to a value of 0-255, but PCF8591 will not output negative voltage, so our lowest is 0V, which is sin value * 128 + 128. Finally, the voltage output, we need to write an IC-based voltage output function. The following is the specific implementation code.

1) Discretization and conversion of sine wave

void dac_sine()//sine wave
{
static u16 angle = 0; //angle
static float radian = 0; // radian
angle = angle + 5; // add 5 degrees each time
if(angle == 360) //A cycle of 369 degrees
{
angle = 0;
}
radian = angle * 0.0055; // convert angle to radian
Volt_sine = 128 + (sin(radian * pi) * 128);//scale transformation, 1~-1 conversion to
//0-255
set_volt(volt_sine);//voltage output
}

2) Voltage output

Pay attention to the written 0x40, 4 means to turn on the voltage output, and 0x90 is the voltage output control word, as mentioned above.

void set_volt(u8 value)
{
I2CStart();
I2CSendByte(0x90); //write address
I2CWaitAck();
I2CSendByte(0x40); //Channel selection
I2CWaitAck();
I2CSendByte(value); //Write data 0-255 corresponding to 5V
I2CWaitAck();
I2CStop();
}

With the help of the above code, we can get the following curve:

(3) Triangular wave generation

Generating a triangle wave is much simpler than generating a sine wave. PCF8591 outputs 0-5V, which corresponds to 0-255. We only need to add a variable from 0 to 255, and then drop it from 255 to 0, and the cycle continues, and a triangle wave appears.

1) Program

void dac_triangular()//triangular wave
{
static bit flag = 0; // switch flag
if(!flag) // ascending segment
{
volt_trian = volt_trian + 5;
if(volt_trian == 255)
{
flag = 1;
}
}
else // descending section
{
volt_trian = volt_trian - 5;
if(volt_trian == 0)
{
flag = 0;
}
}
set_volt(volt_trian);//voltage output
}

2) Simulation effect

(4) Generate a sawtooth wave

The so-called sawtooth wave is half of the triangle wave. Let a variable increase from 0 to 255, and then suddenly drop to 0. Repeat the above process to get a sawtooth wave.

1) Program

void dac_zigzag()//sawtooth wave
{
volt_zigzag = volt_zigzag + 5;
if(volt_zigzag == 255)
{
volt_zigzag = 0;
}
set_volt(volt_zigzag);
}

2) Effect

(5) square wave

As for the square wave, we can use the IO port to achieve it. If you use PCF8591, then switch to 0V after outputting 5V for a period of time, and repeat.

1) Program

void Delay10ms() //@12.000MHz
{
unsigned char i, j;

i = 20;
j = 113;
do
{
while (--j);
} while (--i);
}
void dac_square()//square wave
{
volt_aquare = 255;
set_volt(volt_aquare);
Delay10ms();//delay wait
volt_aquare = 0;
set_volt(volt_aquare);//voltage output
Delay10ms();
}

2) Effect

OK, OK, the explanation is over, the following is all the code

(1) mian.c

/********************************************
Author: sakura
Time: 2023/5/16
funcation: Use 8-bit AD/DA converter PCF8591 to generate sine wave
, triangular wave, sawtooth wave, and square wave are four commonly used waveforms.
Switch the waveform output by pressing the button, and the digital tube displays at the same time
Current waveform mode, 0-sine wave, 1-triangular wave,
2-sawtooth wave, 3-square wave.
copyright: All rights reserved, please specify for reference
*************************************************/
#include "main.h"
#include "iic.h"
#include "dacout.h"
#include "switchover.h"
/************************************************
Function name: main
Function: main function entry
Parameters: none
Return value: None
***************************************************/
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

/************************************************
Function name: I2C_Delay
Function: iic delay function, 15 machine cycles
Parameters: n, delay n*15
Return value: None
***************************************************/
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--);
}

/************************************************
Function name: I2CStart
Function: iic start signal
Parameters: None
Return value: None
***************************************************/
void I2CStart(void)
{
    sda = 1;
    scl = 1;
I2C_Delay(DELAY_TIME);
    sda = 0;
I2C_Delay(DELAY_TIME);
    scl = 0;
}

/************************************************
Function name: I2CStop
Function: iic termination signal
Parameters: None
Return value: None
***************************************************/
void I2CStop(void)
{
    sda = 0;
    scl = 1;
I2C_Delay(DELAY_TIME);
    sda = 1;
I2C_Delay(DELAY_TIME);
}

/************************************************
Function name: I2CSendByte
Function: iic write byte
Parameters: byt, the data to be written
Return value: None
***************************************************/
void I2CSendByte(unsigned char byte)
{
    unsigned char i;
\t
    for(i=0; i<8; i ++ ){
        scl = 0;
I2C_Delay(DELAY_TIME);
        if(byte & 0x80){
            sda = 1;
        }
        else {
            sda = 0;
        }
I2C_Delay(DELAY_TIME);
        scl = 1;
        byte <<= 1;
I2C_Delay(DELAY_TIME);
    }
\t
    scl = 0;
}

/************************************************
Function name: I2CReceiveByte
Function: iic read byte
Parameters: none
Return value: da, the read data
***************************************************/
/*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;
}*/

/************************************************
Function name: I2CWaitAck
Function: The host waits for the response signal
Parameters: none
Return value: ackbit, 0-acknowledgement, 1-non-acknowledgement
***************************************************/
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
\t
    scl = 1;
I2C_Delay(DELAY_TIME);
    ackbit = sda;
    scl = 0;
I2C_Delay(DELAY_TIME);
\t
return ackbit;
}

/************************************************
Function name: I2CSendAck
Function: Slave sends response signal
Parameters: ackbit, 0-acknowledgement, 1-non-acknowledgement
Return value: None
***************************************************/
/*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;//data line
sbit scl = P2^0;//clock line

static void I2C_Delay(unsigned char n);
void I2CStart(void);
void I2CStop(void);
void I2CSendByte(unsigned char byte);
//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; //sine wave
u8 volt_trian = 0; //triangular wave
u8 volt_zigzag = 0; //Sawtooth wave
u8 volt_aquare = 0; //square wave

extern u8 mode; //output mode
/************************************************
Function name: dac_sine
Function: output sine wave
Parameters: None
Return value: None
***************************************************/
void dac_sine()//sine wave
{
static u16 angle = 0; //angle
static float radian = 0; // radian
angle = angle + 5; // add 5 degrees each time
if(angle == 360) //A cycle of 369 degrees
{
angle = 0;
}
radian = angle * 0.0055; // convert angle to radian
Volt_sine = 128 + (sin(radian * pi) * 128);//scale transformation, 1~-1 conversion to
//0-255
set_volt(volt_sine);//voltage output
}
/************************************************
Function name: dac_triangular
Function: output triangle wave
Parameters: none
Return value: None
***************************************************/
void dac_triangular()//triangular wave
{
static bit flag = 0; // switch flag
if(!flag) // ascending segment
{
volt_trian = volt_trian + 5;
if(volt_trian == 255)
{
flag = 1;
}
}
else // descending section
{
volt_trian = volt_trian - 5;
if(volt_trian == 0)
{
flag = 0;
}
}
set_volt(volt_trian);//voltage output
}
/************************************************
Function name: dac_zigzag
Function: output sawtooth wave
Parameters: None
Return value: None
***************************************************/
void dac_zigzag()//sawtooth wave
{
volt_zigzag = volt_zigzag + 5;
if(volt_zigzag == 255)
{
volt_zigzag = 0;
}
set_volt(volt_zigzag);
}
/************************************************
Function name: Delay10ms
Function: 10ms delay
Parameters: None
Return value: None
***************************************************/
void Delay10ms() //@12.000MHz
{
unsigned char i, j;

i = 20;
j = 113;
do
{
while (--j);
} while (--i);
}
/************************************************
Function name: dac_square
Function: output square wave
Parameters: none
Return value: None
***************************************************/
void dac_square()//square wave
{
volt_aquare = 255;
set_volt(volt_aquare);
Delay10ms();//delay wait
volt_aquare = 0;
set_volt(volt_aquare);//voltage output
Delay10ms();
}
/************************************************
Function name: waveform_out
Function: Waveform output selection
Parameters: none
Return value: None
***************************************************/
void waveform_out()
{
switch(mode)
{
case 0:dac_sine();break; //mode 0, sine wave
case 1:dac_triangular();break; //mode 1, sine wave
case 2:dac_zigzag();break; //Mode 2, sine wave
case 3:dac_square();break; //mode 3, sine wave
default: break;
}
}
/************************************************
Function name: set_volt
Function: Use pcf8591 to output voltage
Parameters: value 0-255
Return value: None
***************************************************/
void set_volt(u8 value)
{
I2CStart();
I2CSendByte(0x90); //write address
I2CWaitAck();
I2CSendByte(0x40); //Channel selection
I2CWaitAck();
I2CSendByte(value); //Write data 0-255 corresponding to 5V
I2CWaitAck();
I2CStop();
}

dacout.h

#ifndef __DACOUT_H
#define __DACOUT_H
#include "main.h"

void dac_sine();//sine wave
void dac_triangular();//triangular wave
void dac_zigzag();//Sawtooth wave
void Delay10ms(); //@12.000MHz
void dac_square();//square wave
void waveform_out();
void set_volt(u8 value);

#endif

(4) switchover.c

#include "switchover.h"
//Common cathode digital tube segment code 0-9
u8 code SMG_NODOT[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//No decimal point
u8 mode = 0;//output mode
/************************************************
Function name: key_pron
Function: switch output wave mode, 1-sine wave, 2-triangular wave, 3-sawtooth wave
4-square wave
Parameters: none
Return value: None
***************************************************/
void key_pron()
{
if(!key)
{
Delay10ms();
if(!key)
{
while(!key);
mode + + ;
if(mode == 4)
{
mode = 0;
}
}
}
}
/************************************************
Function name: seg_pron
Function: digital tube display output wave mode
Parameters: None
Return value: None
***************************************************/
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.000MHz

void key_pron();
void seg_pron();

#endif

6. Source code and simulation resource download

Link: https://pan.baidu.com/s/1_ug6Ih4NxAT39CVCHRjFCg?pwd=pmuo
Extract code: pmuo