WARNING:
Here is some code that I abandon because it was too slow.
It is an excerpt and incomplete for a PIC16F877A.
It uses a MC1408 D/A converter. Basically a R2R ladder network.
With a 20MHz MCU clock, I seem to remember being able cover the audio frequencies.
It will generate both a sine and triangle waveform.
Edit- It looks like it also does a frequency sweep and the input is the sine period instead of frequency.
#include <xc.h>
#include <stdint.h>
uint8_t buffer[80];
typedef union {
uint8_t b[2];
uint16_t i;
} bits_t;
uint8_t S_Count;
uint8_t S_Phase;
uint8_t S_PhaseRate;
uint8_t S_TimeRate;
uint8_t S_Triangle;
bits_t S_Time1;
bits_t S_Time2;
const uint8_t cosine[256] =
{
254, 254, 254, 254, 254, 254, 253, 253, 252, 251, 251, 250, 249, 248, 247, 246,
245, 244, 242, 241, 239, 238, 236, 235, 233, 231, 229, 228, 226, 224, 221, 219,
217, 215, 213, 210, 208, 205, 203, 200, 198, 195, 193, 190, 187, 184, 182, 179,
176, 173, 170, 167, 164, 161, 158, 155, 152, 149, 146, 143, 139, 136, 133, 130,
127, 124, 121, 118, 115, 111, 108, 105, 102, 99, 96, 93, 90, 87, 84, 81,
78, 75, 72, 70, 67, 64, 61, 59, 56, 54, 51, 49, 46, 44, 41, 39,
37, 35, 33, 30, 28, 26, 25, 23, 21, 19, 18, 16, 15, 13, 12, 10,
9, 8, 7, 6, 5, 4, 3, 3, 2, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 6, 7, 8,
9, 10, 12, 13, 15, 16, 18, 19, 21, 23, 25, 26, 28, 30, 33, 35,
37, 39, 41, 44, 46, 49, 51, 54, 56, 59, 61, 64, 67, 70, 72, 75,
78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 115, 118, 121, 124,
127, 130, 133, 136, 139, 143, 146, 149, 152, 155, 158, 161, 164, 167, 170, 173,
176, 179, 182, 184, 187, 190, 193, 195, 198, 200, 203, 205, 208, 210, 213, 215,
217, 219, 221, 224, 226, 228, 229, 231, 233, 235, 236, 238, 239, 241, 242, 244,
245, 246, 247, 248, 249, 250, 251, 251, 252, 253, 253, 254, 254, 254, 254, 254,
};
//============================================================
void main(void)
{
// MAIN LOOP
while (1) {
// Get USB packet
// Byte 0 Buffer length (command token + data bytes)
// Byte 1 Command token
// Byte 2..n-1 Data bytes
// Byte n Checksum
ReceivePacket();
switch ( buffer[1] ) {
// Sine wave generation token...
// MC1408 8-Bit Multiplying Digital-to-Analog Converter
// buffer[2] == Time1 MSB
// buffer[3] == Time1 LSB
// buffer[4] == Time2 MSB
// buffer[5] == Time2 LSB
// buffer[6] == Time rate
// buffer[7] == Phase rate
// buffer[8] == Triangle flag
// buffer[9] == Voltage reference
case FT_WAVGEN:
S_Time1.b[1]=buffer[2];
S_Time1.b[0]=buffer[3];
S_Time2.b[1]=buffer[4];
S_Time2.b[0]=buffer[5];
S_TimeRate=buffer[6];
S_Time.i=S_Time1.i;
S_PhaseRate=buffer[7];
S_Triangle=buffer[8];
S_Phase=0;
S_Count=0;
TRISC=0x00; // set port C as output
// Setup voltage ref configuration
TRISAbits.TRISA2=1;
CVRCON=buffer[9];
// Setup CCP1 configuration
CCPR1H=S_Time.b[1];
CCPR1L=S_Time.b[0];
CCP1CON=0x0B; // Compare mode, trigger special event
// Setup Timer1 configuration
TMR1H=0;
TMR1L=0;
T1CON=0x05; // 1:1 Prescale | TMR1CS | TMR1ON bits
// Enable CCP1 interrupt
PIR1bits.CCP1IF=0; // CCP1 Interrupt Flag bit
PIE1bits.CCP1IE=1; // CCP1 Interrupt Enable bit
INTCON=0xC0; // GIE and PEIE interrupts
break;
}
}
}
//============================================================
void __interrupt() isr(void)
{
if (PIE1bits.CCP1IE && PIR1bits.CCP1IF) {
PIR1bits.CCP1IF=0; // clear CCP1 Interrupt Flag
if (S_Triangle) {
if (S_Phase < 128)
PORTC=S_Phase*2;
else
PORTC=(255-S_Phase)*2;
} else {
PORTC=cosine[S_Phase];
}
S_Phase+=S_PhaseRate;
if (S_TimeRate > 0) {
if (S_Count == 0) {
if (S_Time2.i > S_Time1.i) {
S_Time.i+=S_TimeRate;
if (S_Time.i > S_Time2.i) S_Time.i=S_Time1.i;
}
if (S_Time2.i < S_Time1.i) {
S_Time.i-=S_TimeRate;
if (S_Time.i < S_Time2.i) S_Time.i=S_Time1.i;
}
CCPR1L=S_Time.b[0];
CCPR1H=S_Time.b[1];
}
S_Count++;
}
}
}