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