NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Project Help and Ideas » MAX038-based Function Generator

May 23, 2012
by JamesFysh
JamesFysh's Avatar

Hi all,

I've been busy for a while now designing a function generator capable of outputting sine/triangle/square waves at approx. 0.1Hz -> 20MHz frequencies. This is mostly based around the Maxim MAX038 IC, but I intend to drive frequency selection, wave-type selection and duty cycle via an ATmega MCU. Further, the MCU will drive a 4x20 or 4x40 LCD display, and take input from 5 push-buttons, allowing the user to change settings.

So far I have sorted out a way to use the MCU to select the correct capacitor based on the intended frequency (a CD4051 lets me switch between 1 of 8 different capacitors, and the MCU drives the A, B and C lines on the CD4051 to do the selection).

I'm still a bit stuck on how to use the MCU for frequency selection (the IIN and FADJ pins on the MAX038) - the IIN pin should be driven by a current-source, between 2uA and 750uA (I'm thinking of using a DAC (SPI or I2C controlled?) + Op-Amp to make a voltage-controlled current source, but have absolutely 0 idea where to begin here). For the FADJ pin, I think a digitally-controlled potentiometer could work, but I'm not sure how feasible this is.

The DADJ (duty-cycle) pin should be supplied with between -2.4 and +2.4V to select a duty-cycle between 15% and 85% (or simply driven to GND to get a 50% duty cycle). I guess here I could use a digitally-controlled DAC again? Not sure if there are DACs that output negative or positive voltages? Or if I can just use the -5V, a voltage divider and a 0-5V ouptut from a DAC to achieve -2.4 - 2.4V.

Finally, I don't think any of this really works unless the MCU can read the current output frequency and adjust the IIN/FADJ outputs accordingly. I'm yet to find a frequency-counter in an IC, or any other reliable way for the MCU to determine the (real) output frequency. Note that the SYNC pin on the MAX038 provides a 50% duty-cycle square-wave at the current frequency, regardless of what DADJ is set to, or the selected output wave-type.

It seems like I will end up running 3-4 DACs off of the I2C and/or SPI pins on the chip, and end up leaving a number of pins on the chip doing nothing -- oh well.

My (in-progress!) design can be seen here. I would love some feedback!

July 03, 2012
by Jer
Jer's Avatar

Hi James

I have designed a simular project. I compleated it in 2010.

If you'd like, I can send you my design and software?

It controls: 1) Wave(sin,sqr,Tri) 2) Freq(.2Hz to 20MHz) 3) Ampl(0 to 5Vpp) 4) Duty(15-85%) 5) DC-Offset(+/- 5V).

It uses the ATmega328P uC to measure the frequency and adjusts the IIN and FADJ inputs to get about 5 digits of frequency control.

All wave parrameters are directly entered via a 16 key pad and viewed by the 4x20 LCD.

Jerry F.

July 04, 2012
by Ralphxyz
Ralphxyz's Avatar

Jer, now that would be a great addition to the Nerdkits community Library.

Ralph

September 13, 2012
by Jer
Jer's Avatar

Waveform Generator Version 3.0

T.O.C.

Specifications 3

Circuit Description 4

Power Supply (Schematic) 5

uC Bus Interface (Schematic) 6

LCD Display (Schematic) 7

Keypad Interface (Schematic) 8

Serial to Parallel Data Bus (Schematic) 9

DAC Registers 8 x 12bit (Schematic) 10

Time-Base (Schematic) 11

Frequency Divider (Schematic) 12

Frequency Counter Mode Select (Schematic) 13

Freq/Period Gate Control (Schematic) 14

Waveform Generator (Schematic) 15

Cf Switch Bank (Schematic) 16

Output Signal Conditioning (Schematic) 17 T.O.C. (continued)

Connector Digital to Analog Board (Schematic) 18

WFG.C Source Code 19

LCD.C (Modifide version) 78

Pictures 87

WFG-03 Specifications

1) .5Hz to 5MHz in 7 ranges. Range: 1 0.5Hz to 5Hz Range: 2 5Hz to 50Hz Range: 3 50Hz to 500Hz Range: 4 500Hz to 5KHz Range: 5 5KHz to 50KHz Range: 6 50KHz to 500KHz Range: 7 500KHz to 5MHz

Frequency is controlled to within 4.5 digits.

2) Three basic waveforms 1) Sine 2) Square 3) Triangle

3) Duty Cycle Adjustment 15% to 85%

4) Amplitude Adjustment 0Vpp to 18Vpp

5) Signal Output Offset -10Vdc to +10Vdc

Circuit Description

The MAX038 IC is at the hart of the waveform generator. This chip uses five signals (If, Vf, Vdc%, WavSel0 and WavSel1) and a timming capacitor (Cf) to set the signal output. Setting the Frequency Setpoint (fs) via the user interface invoces the WFG software to set the signals If, Vf and Cf through the control of four DAC channels DAC[0 – 3] and selecting the timming capacitors needed to achieve the fsetpoint .

the output frequency is calculated by two functions of the MAX038, First is the If is set by the formula If = (fsetpoint * Cf) after setting the If the MAX038’s frequency output si roughly at thedesired frequency and this frequency is measured and stored into the variable (fo). Next (Vf) is calculated by the formula: Vf = (fo - fsetpoint) / (.2915 * fo). I For each range a capacitor from the capacitor bank is ether add or subtracted from Cf. Each range of frequencys are measuered by the uC’s 16bit counter. The ideal counter range is from 5000 to 50000 counts corresponding to the seven ranges:

FREQUENCY RANGE Cf (timming capacitor) 1) .5Hz to 5Hz 111.11uf 2) 5Hz to 50Hz 11.11uf 3) 50Hz to 500Hz 1.11uF
4) 500Hz to 5KHz .111uf
5) 5KHz to 50KHz .011uf 6) 50KHz to 500KHz 1110pf
7) 500Khz to 5MHz 110pf

For the ranges that produce frequencies under 5000 counts per second the circuit is place into period counting mode. In this way the frequency can be calculated by simply dividing the gate clock (25KHz or 250KHz) / the period of the signal being measured.

WFG.C Source Code

// File Name: WFG.c 
// Version: 1.2010.0612
// Author: Jerry Ford
// for NerdKits with ATmega168
// mrobbins@mit.edu
// copy rights: All rights preserved. 
// #define F_CPU 14745600

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"// * The lcd.h and lcd.c have been modified. 
#include "../libnerdkits/io_328p.h"

//#include "../libnerdkits/74c922.h" <<== "I should make this!"

// PIN DEFINITIONS:
//PIN#
//  1 High through resistor.
//  2 PD0 = RS232.Rx
//  3 PD1 = RS232.tx
//  4 PD2 = IRQ_KEY_RDY
//  5 PD3 = IRQ_COUNTER (ISR = INT1_vect)
//  6 PD4 = D_LATCH
//  7 Vcc
//  8 GND
//  9 xtal
// 10 xtal
// 11 PD5 = Fcount
// 12 PD6 = LCD_E(6)
// 13 PD7 = LCD_RS(4)
// 14 PB0 = KEY_D0 & Programing Mode(SW = CLOSED).
// 15 PB1 = KEY_D1
// 16 PB2 = KEY_D2
// 17 PB3 = KEY_D3
// 18 PB4 = KeyP_RD
// 19 PB5 = GATE_RST 
// 20 Vcc
// 21 Vcc
// 22 GND
// 23 PC0 = SDATA       & D0
// 24 PC1 = SCLK        & D1
// 25 PC2 = LCD_D4(11)  & D2
// 26 PC3 = LCD_D5(12)  & D3
// 27 PC4 = LCD_D6(13)  & D4
// 28 PC5 = LCD_D7(14)  & D5

// Rows
//  1) Freq Measured (ANCHOR Row)
//  2) Freq Setpoint
//  3) Range = [1(.5Hz - 5Hz), 2(5Hz - 50Hz), 3(50Hz - 500Hz), 4(500Hz - 5KHz), 
//     5(5KHz - 50KHz), 6(50KHz - 500KHz), 7(500KHz - 500MHz)]
//  4) Wave Type = [SIN, SQR, TRI]
//  5) Amplatude = Vpp 0-10V
//  6) Voffset = +/-10V
//  7) DC% = 15.0 to 85.0%
//  8) FAutoCorrect = On/Off
//  9) FreqErr% = 1.234567% 
// 10) FreqErr = DiffHz
// 11) Ver = 1.2010.0531z

//+=====+===============+=======+=======+=======+=======+=======+=======+
//|RANGE| Freq(Min-Max) |SerReg0|       |       |Freq=0 |Slow=0 |SerReg1|                                   
//|     |               |       | DIV10 |DIV100 |Perd=8 |Fast=16| Cfreq |
//+=====+===============+=======+=======+=======+=======+=======+=======+     
//|  0  |  0.50 -    5Hz|    8  |   0   |   0   |   8   |   0   | 252   |       
//|  1  |  5.00 -   50Hz|   24  |   0   |   0   |   8   |   16  | 124   |   
//|  2  | 50.00 -  500Hz|   26  |   2   |   0   |   8   |   16  |  60   |       
//|  3  | 500.0 -   5KHz|   28  |   0   |   4   |   8   |   16  |  28   |       
//|* 4  | 5.00K -  50KHz|   30  |   2   |   4   |   8   |   16  |  12   |       
//|  5  | 50.0K - 500KHz|   16  |   0   |   0   |   0   |   16  |   4   |   
//|  6  |  500K -   5MHz|   18  |   2   |   0   |   0   |   16  |   0   |   
//+=====+===============+=======+=======+=======+=======+=======+=======+

// -1 = no key pressed. Else 0 to 15 key has been pressed.
uint8_t last_key = 0;

// Use first fgate pulse = 0.
// Ignor first N fgate pulses = -1.
int8_t update_f = 0;

                                //"12345678901234567890     Range
const char rangename1[] PROGMEM = "       0.5Hz to 5Hz";//  0
const char rangename2[] PROGMEM = "        5Hz to 50Hz";//  1 
const char rangename3[] PROGMEM = "      50Hz to 500Hz";//  2
const char rangename4[] PROGMEM = "      500Hz to 5KHz";//  3
const char rangename5[] PROGMEM = "      5KHz to 50KHz";//  4
const char rangename6[] PROGMEM = "    50KHz to 500KHz";//  5
const char rangename7[] PROGMEM = "     500KHz to 5MHz";//  6

PGM_P RangeNamesTable[] PROGMEM =
{
    rangename1,
    rangename2,
    rangename3,
    rangename4,
    rangename5,
    rangename6,
    rangename7
};

                           //"12345678901234567890  
const char wave1[] PROGMEM = "SQR"; 
const char wave2[] PROGMEM = "TRI"; 
const char wave3[] PROGMEM = "SIN";

PGM_P WaveTable[] PROGMEM =
{
    wave1,
    wave2,
    wave3
};

//=========================================================================================== //=========================================================================================== //-------------------------------------------------------------------------------------------
ISR(INT0_vect) { //flag that a key has been pressed. last_key = 16; }

//-------------------------------------------------------------------------------------------
ISR(INT1_vect) { //INC update freq data point counter. update_f++;// flag that counting has stopped. }

//------------------------------------------------------------------------------------------- void initz_XIRQ(void) { // initz_IRQ for keypad_IRQ ang Coutner_IRQ

EICRA |= 15;    //ISC11 = 8, ISC10 = 4, ISC01 = 2, ISC00 = 1 (make INT1 a rising edge)

//External Interupt Mask Reg.
EIMSK |= 3; // INT1 = 2, INT0 = 1

//Enable Interupts
//sei();

}

//-------------------------------------------------------------------------------------------

void initz_16bitCounter(void) {
// pin.11, PD5 = Fcount

//turn on pullup resistor
PORTD |= (1<<PD5);

// Initz. the 16bit counter for counting of external pulses. 
TCCR1A = 0;             // COM1A1=128, COM1A0=64,  COM1B1=32, COM1B0=16, r=8    , r=4   , WGM11=2, WGM10=1
TCCR1B = 128+64+4+2+1;  // ICNC1=128 , ICES1=64 ,  r=32     , WGM13=16 , WGM12=8, CS12=4, CS11=2 , CS10=1
TCCR1C = 0;             // FOC1A=128 , FOC1B=64 ,  r=32     , r=16     , r=8    , r=4   , r=2    , r=1

//Timer/Counter1 Interrupt Mask Register
TIMSK1 = 0;//

}

//-------------------------------------------------------------------------------------------

void initz_PinDirections(void) { // This needs to first func called before other hardware setups.

//DDRn 1 = output, 0 = Input
                //      7654 3210
                //      1
                //      2631
                //      8426 8421
DDRB |=0x30;    //(30 = 0011 0000 new)
DDRC |=0x3f;    //(3f = 0011 1111 new)
DDRD |=0xd2;    //(d2 = 1101 0010 new)

//Reset KeyP_RD,GATE_RST pins. (PB4 = H Active Low, PB5 = L Active High)
PORTB = 0x1f;   //xx01 1111

}

//************* int main() { // -- DECLEAR VARIABLES --

//const long F_CPU 14745600;

uint8_t Range = 0; //0, 
uint8_t fDIV[7] = {8,24,26,28,30,16,18}; //
//                     0   1   2  3  4  5 6
uint8_t Cfrange[7] = {252,124,60,28,12,4,0}; //
long fperiod[7] = {25000,250000,250000,250000,250000,0,0}; //
float fmul[7]={1,1,10,100,1000,10,100}; //
float tmul[7]={1,1000,1000,1000,1000000,1000000,1000000000};//
float Cf[7];

float fsetpoint = 16000;
float tempFloat = 0;
float tempFloat2 = 0;

float fuA = 0;          // current used to set 'fo' of the MAX038
float period = 0;

float Vpp = 10;         // Volts pp output setpoint.
float Vofs = 0;         // Volts DC offset output setpoint.

double favg = 0;        // average freqency
double fins = 0;        // instentanious freqency
double freq[10];        // measured frequency samples to be averaged.

float Vdac[8];          // voltage of 
double FaErrFact = 0;   // average freqency error factor.
double FiErrFact = 0;   // instentanious freqency error factor.
double FaErrFact_Last = 0;
double fcorrection = 0;
char tempStr[20] = {"                   "};
char wavename[4]={"SIN"};
char rangeminmax[21];

uint8_t is_number = 0;
uint8_t is_dir = 0;
uint8_t is_dot = 0;
uint8_t is_enter = 0;
uint8_t fn = 10;    // number to avg.
uint8_t fp = 0;     // array pointer.
uint16_t Last_Count = 0;
uint8_t DutyCycle = 50;

uint8_t UpdateDisplay = 1;// Set flag to update the display.

//+=================+
//|WF Type  |SerReg1|
//+=========+=======+
//| SQR     |   0   |
//| TRI     |   1   |
//| SIN     |   2   |
//+=========+=======+    
uint8_t wf_type = 2;
uint8_t waveindex[3] = {2,0,1};

int8_t top_row = 0;

uint8_t cRow = 0; //cursor Row
uint8_t cCol = 0; //cursor Col
uint8_t colleft[33];
uint8_t colright[33];
uint8_t RowMax = 32;

int8_t SetCursor=0;

uint8_t FrCorState = 0;// State macheen for frequency error correction.

//.......................................................................................

// -- INITZ VARIABLES --

// start up the LCD
lcd_init();
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
lcd_home();

//const long F_CPU = 14745600;//xtal freq of uC.

// reset these values?
//fDIV[7] = {8,24,26,28,30,16,18}; 
fDIV[0] = 8;
fDIV[1] = 24;
fDIV[2] = 26;
fDIV[3] = 28;
fDIV[4] = 30;
fDIV[5] = 16;
fDIV[6] = 18;

//uint8_t Cfrange[7] = {252,124,60,28,12,4,0}; 
Cfrange[0] = 252;   //1111 1100
Cfrange[1] = 124;   //0111 1100
Cfrange[2] = 60;    //0011 1100
Cfrange[3] = 28;    //0001 1100
Cfrange[4] = 12;    //0000 1100
Cfrange[5] = 4;     //0000 0100
Cfrange[6] = 0;     //0000 0000

//long fperiod[7] = {25000,250000,250000,250000,250000,0,0}; 
fperiod[0] = 25000;
fperiod[1] = 250000;
fperiod[2] = 250000;
fperiod[3] = 250000;
fperiod[4] = 250000;
fperiod[5] = 1;
fperiod[6] = 1;

//double fmul[7]={1,1,10,100,1000,10,100}; 
fmul[0] = 1;
fmul[1] = 1;
fmul[2] = 10;
fmul[3] = 100;
fmul[4] = 1000;
fmul[5] = 10;
fmul[6] = 100;

//double tmul[7]={1,1000,1000,1000,1000000,1000000,1000000000};
tmul[0]= 1;
tmul[1]= 1000;
tmul[2]= 1000;
tmul[3]= 1000;
tmul[4]= 1000000;
tmul[5]= 1000000;
tmul[6]= 1000000000;

// set the value of Cf for each range.
Cf[0] = .000111111110;  //.5Hz - 5Hz
Cf[1] = .000011111110;  // 5Hz - 50Hz
Cf[2] = .000001111110;  // 50Hz - 500Hz
Cf[3] = .000000111110;  // 500Hz - 5KHz
Cf[4] = .000000011110;  // 5KHz - 50KHz
Cf[5] = .000000001110;  // 50KHz - 500KHz
Cf[6] = .000000000110;  // 500KHz - 5MHz

Vdac[0] = 1.25;
Vdac[1] = 1.25;
Vdac[2] = 1.25;
Vdac[3] = 1.25;
Vdac[4] = 1.25;
Vdac[5] = 1.25;
Vdac[6] = 1.25;
Vdac[7] = 2.25;

waveindex[0] = 2;
waveindex[1] = 0;
waveindex[2] = 1;

// initz left and right valid cols for each row.
colleft[0] = 21;    // fa:
colright[0] = 21;

colleft[1] = 4;     // fs:
colright[1] = 16;

colleft[2] = 11;    // Wave Type: 
colright[2] = 13;

colleft[3] = 7;     // Amplatude Vpp
colright[3] = 11;

colleft[4] = 6;     // Vdc: 2.500 ofs
colright[4] = 9;

colleft[5] = 11;    // DutyCycle= 50 %
colright[5] = 12;   // 01234567890123456789

colleft[6] = 21;    // ta: 1/f
colright[6] = 21;

colleft[7] = 21;    // fi:
colright[7] = 21;

colleft[8] = 21;    // ferr:
colright[8] = 21;

colleft[9] = 21;    // "-- Internal Vars -- "
colright[9] = 21;

colleft[10] = 21;   // "                    "
colright[10] = 21;

colleft[11] = 21;   // Range: number
colright[11] = 21;

colleft[12] = 21;   // Range Min to Max
colright[12] = 21;

colleft[13] = 21;   // Count:
colright[13] = 21;

colleft[14] = 21;   // fn:
colright[14] = 21;

colleft[15] = 21;   // fstate: FrCorState
colright[15] = 21;

colleft[16] = 21;   // Keypad:
colright[16] = 21;

colleft[17] = 21;   // If:
colright[17] = 21;

colleft[18] = 21;   // Cf
colright[18] = 21;

colleft[19] = 21;   // SPDR0: %u "), fDIV[Range]
colright[19] = 21;

colleft[20] = 21;   // SPDR1: %u "), Cfrange[Range] + wf_type
colright[20] = 21;

colleft[21] = 21;   // DAC0:
colright[21] = 21;

colleft[22] = 21;   // DAC1:
colright[22] = 21;

colleft[23] = 21;   // DAC2:
colright[23] = 21;

colleft[24] = 21;   // DAC3:
colright[24] = 21;

colleft[25] = 21;   // DAC4:
colright[25] = 21;

colleft[26] = 21;   // DAC5:
colright[26] = 21;

colleft[27] = 21;   // DAC6:
colright[27] = 21;

colleft[28] = 21;   // DAC7:
colright[28] = 21;

colleft[29] = 21;   // Waveform Generator
colright[29] = 21;

colleft[30] = 21;   // Ver: 1.2010.0706e
colright[30] = 21;

colleft[31] = 21;   // Authour: Jerry Ford 
colright[31] = 21;

colleft[32] = 21;   //   Please Wait...    
colright[32] = 21;

fn = 10;    // number to avg.
fp = 0;     // array pointer.
Last_Count = 0;

// Use first fgate pulse = 0.
// Ignor first N fgate pulses = -1.
update_f =  0;
last_key =  0; // -1 = no key pressed. Else 0 to 15 key has been pressed.
top_row  =  0;
cRow     =  0; //cursor Row
cCol     =  5; //cursor Col

is_number = 0;
is_dir = 0;
is_dot = 0;
is_enter = 0;

//char wf_name[3][5] = {{"SQR"}, {"TRI"},{"SIN"}};
wf_type = 2;

fsetpoint = 32768;

Range = 1;

fuA = .000321587;

//-------------------------------------------------------------------------------------------
void ouputdata(uint8_t data)
{   
    //  6 PD4 = D_LATCH     & D4 16
    // 23 PC0 = SDATA       & D0 1
    // 24 PC1 = SCLK        & D1 2
    // 25 PC2 = LCD_D4(11)  & D2 4 
    // 26 PC3 = LCD_D5(12)  & D3 8
    // 27 PC4 = LCD_D6(13)  & D4 16
    // 28 PC5 = LCD_D7(14)  & D5 32

    // set port value to data.
    PORTC = (data & 63);

    // Let data bus settel.
    delay_us(5);

    // Make D_LATCH go Low.
    PORTD &= 235;   //XXX0 X0XX

    // Hold D_LATCH High for a while.
    delay_us(5);

    // Make D_LATCH go High.
    PORTD |= 16;    //XXX1 XXXX
}

//-------------------------------------------------------------------------------------------

void sd_out(uint8_t address, uint8_t sdata)
{
    uint8_t si = 0;
    //DAT , CLK
    //  L   L = 60
    //  H   L = 61
    //  L   H = 62
    //  H   H = 63

    // Enables the Serial-CLK by makeing SCLK_EN = Low.
    ouputdata(0);

    // Shift out each bit of 8 from (LSB to MSB).
    for( si = 0; si < 8; ++si)
    {
        // Make SCLK Low.
        PORTC &= 61; //xxxx xx0x

        delay_us(20);

        // output the Ser-data bit value.
        if((sdata & 128) > 0)
        {
            PORTC |= 1;  // SET xx xxx1 OR
        }
        else
        {
            PORTC &= 62; // CLR 11 1110 mask
        }

        // Let data settel.
        delay_us(10);

        // Clock SDATA into shift-reg, by makeing SCLK go High.
        PORTC |= 2;

        //Shift sdata Left one bit.
        sdata = (sdata << 1);

        // Let data settel.
        delay_us(10);

    }

    //Make selected address line low.
    ouputdata((address + 1) & 15);

    // Delay a while for SPLTCH(N) = Low.
    delay_us(10);

    //Latch data out, form ser-Par port, by makeing all(1-14) address lines High.
    ouputdata(15);

}

//-------------------------------------------------------------------------------------------

void read_key(void)
{

    /**** Get Key Pressed.****/

    //Reset KeyP_RD (PB4 = H Active Low) 
    //GATE_RST pins (PB5 = L Active Hi)

    //Make the "KeyP_RD" go low. (PB4 = L)
    PORTB = 0x0f;   //0010 1111

    //Wait for the data to be stable.
    delay_us(110);

    // input and store new key pressed.
    last_key = (PINB & 15); //correct value by masking lo nible.

    //Reset KeyP_RD,GATE_RST pins. (PB4 = H, PB5 = L)
    PORTB = 0x1f;   //xx01 0000

    //Wait for the 74C922 to let the port go tri state.
    delay_us(10);

}
//-------------------------------------------------------------------------------------------

void reset_gate(void)
{   
    /* Reset freq counting gate.*/
    /* pin 19, PB5 = GATE_RST   */

    // Set Counter Gate (PB5 = H).
    PORTB = 0x3f;   //xx11 1111

    //keep pin hi for a while.
    delay_us(250);  // change back to 10uS after debugung

    //Make the Counter_Gate go low. (PB5 = L, PB4 = H)
    //Reset KeyP_RD,GATE_RST pins. (PB4 = H, PB5 = L)
    PORTB = 0x1f;   //1101 1111

    // Reset counter count.
    TCNT1 = 0;

    // hold it low for a while.
    //delay_us(50); // change back to 10uS after debugung

    // Reset software flag.
    update_f = 0;

}

//-------------------------------------------------------------------------------------------

void set_dac(uint8_t chnl, uint16_t code, uint8_t ld_bank_now)
{

    // DESCRIPTION: Updates 1 of 8 12bit DAC chnls, split between 2 chips (BANKs 1 and 2).

    // +---+---+---+---+----+---+---+----+
    // |  DAC data bus is at SPLTCH2.    |
    // +---+---+---+---+----+----+---+---+
    // |128| 64| 32| 16|   8|   4|  2|  1|
    // | D7| D6| D5| D4|  D3| D2 | D1| D0|
    // |   |   |   |   | D11| D10| D9| D8|
    // +---+---+---+---+----+----+---+---+

    // +--------------------------------------------------------------------+
    // |  Add & Cntrl is at SPLTCH3                                         |
    // +------+---------+--------+--------+--------+--------+-------+-------+
    // |WFG-OE| DAC_LOAD| DAC_WR1| DAC_WR0| DAC_MSB| DAC_LSB| DAC_A1| DAC_A0|
    // |   128|       64|      32|      16|       8|       4|      2|      1|
    // +------+---------+--------+--------+--------+--------+-------+-------+
    //                                    |

    uint8_t SPLTCH3 = 255;// 01111111 was 127
    uint8_t MSK_LSB = 251;// 11111011 LSB Latch
    uint8_t MSK_MSB = 247;// 11110111 MSB Latch
    uint8_t MSK_WR0 = 111;// 11101111 WR 0-3
    uint8_t MSK_WR1 = 223;// 11011111 WR 4-7
    uint8_t MSK_LD  = 191;// 10111111 LOAD ALL DACs
//  .............................................................

    // Force cannel selection to a valid value (0-7).
    chnl &= 7;

    // Select the DAC chnl[A,B,C,D].
    sd_out(3, SPLTCH3 & (chnl | 0xfc));

    // Set DAC D0-D7 Data is at 2
    sd_out(2 , (code & 255));// Set LSB

    // Select the right DAC chip (first or second).
    if(chnl > 3)
    {
        //chnls 4-7
        sd_out(3, SPLTCH3 & MSK_LSB & (chnl | 0xfc));
        sd_out(3, SPLTCH3 & MSK_LSB & (chnl | 0xfc) & MSK_WR1);
        sd_out(3, SPLTCH3 & MSK_LSB & (chnl | 0xfc));
    }
    else
    {
        //chnls 0-3
        sd_out(3, SPLTCH3 & MSK_LSB & (chnl | 0xfc));
        sd_out(3, SPLTCH3 & MSK_LSB & (chnl | 0xfc) & MSK_WR0);
        sd_out(3, SPLTCH3 & MSK_LSB & (chnl | 0xfc));
    }

    // Latch in LSB to chnl.
    sd_out(3, SPLTCH3 & (chnl | 0xfc));

    //  .............................................................

    // Set DAC D8-D11
    sd_out(2,((code>>8) & 15));

    // Select the right DAC chip (first or second).
    if(chnl > 3)
    {
        //chnls 4-7
        sd_out(3, SPLTCH3 & MSK_MSB & (chnl | 0xfc));
        sd_out(3, SPLTCH3 & MSK_MSB & (chnl | 0xfc) & MSK_WR1);
        sd_out(3, SPLTCH3 & MSK_MSB & (chnl | 0xfc));
    }
    else
    {
        //chnls 0-3
        sd_out(3, SPLTCH3 & MSK_MSB & (chnl | 0xfc));
        sd_out(3, SPLTCH3 & MSK_MSB & (chnl | 0xfc) & MSK_WR0 );
        sd_out(3, SPLTCH3 & MSK_MSB & (chnl | 0xfc));
    }

    // Latch in MSB to chnl.
    sd_out(3, SPLTCH3 & (chnl | 0xfc));

    //  ...................................................

    if(ld_bank_now > 0)
    {
        //update DAC chnls by making DAC_LOAD go LOW.
        sd_out(3, (SPLTCH3 & (chnl | 0xfc) & MSK_LD));

        //complete the DAC update by making DAC_LOAD go HIGH.
        sd_out(3, (SPLTCH3 & (chnl | 0xfc)));
    }

}

//-------------------------------------------------------------------------------------------
void update_Vdac(uint8_t chnl, float Vout)
{
    uint16_t codeval;

    // Calculate codeval 0-4095 (12bit DACs)
    // keep Vout to 0-2.5V
    if(Vout >= 2.5)
    {
        codeval = 4095;
    }
    else if(Vout <= 0)
    {
        codeval = 0;
    }
    else
    {
        codeval = lround(((Vout/2.5) * 4096));
    }

    // set voltage out of dac channel.
    set_dac(chnl,codeval,1);

}

//------------------------------------------------------------
void set_hardware_range(uint8_t new_range)
{

    if(new_range > 6)
    {
        Range = 6;
    }
    else
    {
        // Set new range.
        Range = new_range;
    }

    // Set frequency measureing bits.
    sd_out(1 , Cfrange[Range] + wf_type);

    // Set frequency measureing bits.
    sd_out(0 , fDIV[Range]);

    // copy RangeNamesTable[Range] to rangeminmax.
    strcpy_P(rangeminmax, (PGM_P)pgm_read_word(&(RangeNamesTable[Range])));

    // copy WaveTable[Range] to wavename.
    strcpy_P(wavename, (PGM_P)pgm_read_word(&(WaveTable[wf_type])));

    //Set up fo
    //update_Vdac(0,(fuA-.0000367)/3900);
    update_Vdac(1,1.25);
    update_Vdac(2,1.25);
    update_Vdac(3,1.25);

    delay_ms(100);

    // reset freq counting gate.
    reset_gate();// ??

}

//-------------------------------------------------------------------------------------------

uint8_t rtn_Frange(float new_freqsetp)
{
    uint8_t rng = 0;

    // Calculates the correct range to set the hardware
    // for the desired frequency.

    if (new_freqsetp > 500000)
    {
        rng = 6;    // 110pf
    }
    else if (new_freqsetp>50000)
    {
        rng = 5;    // 1110pf
    }
    else if (new_freqsetp>5000)
    {
        rng = 4;    // .011110uf
    }
    else if (new_freqsetp>500)
    {
        rng = 3;
    }
    else if (new_freqsetp>50)
    {
        rng = 2;
    }
    else if (new_freqsetp>5)
    {
        rng = 1;
    }
    else 
    {
        rng = 0;
    }

    set_hardware_range(rng); // dummy set.

    // Reset frequency Error Correction State macheen.
    FrCorState = 0;

    return rng;
}
//-------------------------------------------------------------------------------------------

void Set_duty_cycle(uint8_t NewDutyCycle)
{
    //Set the new duty cycle.
    // m = (4095 - 0) / (90% - 10%) = 51.18
    // ofs = 4095-(m*85) = -511.875

    //Force limmits of parameter.
    if(NewDutyCycle < 10)
    {
        NewDutyCycle = 10;
    }
    else if(NewDutyCycle > 90)
    {
        NewDutyCycle = 90;
    }

    //Store new duty cycle value.
    DutyCycle = NewDutyCycle;

    // Set DAC for DC%
    set_dac(4,(uint16_t)((DutyCycle * 51.1875)-511.875),1);

}

//-------------------------------------------------------------------------------------------
void Set_Amplitude(float newVampl, float Vampcf)
{
    //Force limmits of parameter.
    if(newVampl < 0)
    {
        newVampl = 0;
    }
    else if(newVampl > 18)
    {
        newVampl = 18;
    }

    // make sure amplitude correction factor is not = to 0.
    if(Vampcf==0)
    {
        Vampcf = 1;
    }

    // Store new value to the parameter.
    Vpp = newVampl;

    // Scale the Parameter to the DAC.
    // Vdac[5] = (Vpp * .25 * Vampcf); Vm is a gain error correction factor.
    Vdac[5] = (Vpp * .125 * Vampcf); //Vdac[5] = (Vpp * .125 * 1.08);

    // Set the DAC with the scalled DAC value.
    update_Vdac(5, Vdac[5]);

}

//-------------------------------------------------------------------------------------------
void Set_Voffset(float newVofs, float Vbias)
{   
    //Force limmits of parameter.
    if(newVofs < -10)
    {
        newVofs = -10;
    }
    else if(newVofs > 10)
    {
        newVofs = 10;
    }

    // Store new value to the parameter.
    Vofs = newVofs;

    // Scale the Parameter to the DAC.
    //Vdac[6] = (((Vofs + Vbias) - 10) * -.125); 
    //Vbias is a Vdc offset error correction.
    Vdac[6] = (((Vofs + Vbias) - 10) * -.125);

    // Set the DAC with the scalled DAC value.
    update_Vdac(6,Vdac[6]);

}
//-------------------------------------------------------------------------------------------
void print_float_to_eng(float floatval)
{

    if (floatval >= 1000000)
    {   
        fprintf_P(&lcd_stream, PSTR("%9.5lf M"), floatval/1000000);
    }
    else if (floatval >= 1000)
    {   
        fprintf_P(&lcd_stream, PSTR("%9.5lf K"), floatval/1000);
    }
    else
    {
        fprintf_P(&lcd_stream, PSTR("%9.5lf "), floatval);
    }

}

//-------------------------------------------------------------------------------------------
void update_line(int8_t line)
{

    switch(line)
    {
        case 0:
            // row[0]
            // favg
            lcd_write_string(PSTR("fa: "));
            print_float_to_eng((float)favg);
            lcd_write_string(PSTR("Hz "));
            break;

        case 1:
            // row[1]
            // fsetpoint
            lcd_write_string(PSTR("fs= "));
            print_float_to_eng(fsetpoint);
            lcd_write_string(PSTR("Hz "));
            break;

        case 2:
            // row[2]
            lcd_write_string(PSTR("Wave Type= "));
            fprintf_P(&lcd_stream, PSTR("%3s"), wavename);
            break;

        case 3:
            // row[3] 
            // Amplatude Vpp                                    
            fprintf_P(&lcd_stream, PSTR("Ampl = %5.2lf Vpp"), Vpp);
            //lcd_write_string(PSTR("Vpp"));
            break;

        case 4:
            // row[4] 
            // Vofs = 
            //lcd_write_string(PSTR("Vdc = 0.0V          "));// row[4]
            fprintf_P(&lcd_stream, PSTR("Vdc = %-06.2lf V"), Vofs);
            break;

        case 5:
            // row[5] 
            // DutyCycle%
            //lcd_write_string(PSTR("DutyCycle = 0.0%    "));// row[5]
            fprintf_P(&lcd_stream, PSTR("DutyCycle= %u"), DutyCycle);
            lcd_write_string(PSTR(" %    "));
            break;

        case 6:
            // row[6]
            // row[19]
            // Calculate 1/f = period.
            period = (double)( 1 / favg);

            fprintf_P(&lcd_stream, PSTR("ta: %.5lf"), (period * tmul[Range]));

            if(Range == 0)
            {
                lcd_write_string(PSTR("S "));
            }
            else if(Range < 3)
            {                       
                lcd_write_string(PSTR("mS "));
            }   
            else if(Range < 6)
            {
                lcd_write_string(PSTR("uS "));
            }
            else    
            {   
                lcd_write_string(PSTR("nS "));
            }

            break;

        case 7:
            // row[7]                         //"12345678901234567890"
            lcd_write_string(PSTR("fi: "));
            print_float_to_eng(fins);
            lcd_write_string(PSTR("Hz "));
            break;

        case 8:
            // row[8] 
            fprintf_P(&lcd_stream, PSTR("ferr: %-9.5lf%%"), (100 * FiErrFact)) ;
            break;

        case 9:
            // row[9]
            lcd_write_string(PSTR("-- Internal Vars -- "));
            break;

        case 10:
            // row[10]               //012345678901234567890
            lcd_write_string(PSTR("                    "));
            break;

        case 11:
            // row[11]
            fprintf_P(&lcd_stream, PSTR("Range: %u          "), Range);
            break;

        case 12:
            // row[12]
            //Range fmin Hz to fmax Hz
            fprintf_P(&lcd_stream, PSTR("%20s"), rangeminmax);
            break;

        case 13:
            // row[13]
            fprintf_P(&lcd_stream, PSTR("Counter: %u     "), Last_Count);
            break;

        case 14:
            // row[14]
            fprintf_P(&lcd_stream, PSTR("n: %u"), fn);
            break;

        case 15:
            // row[15]
            fprintf_P(&lcd_stream, PSTR("s: %u "),FrCorState);
            break;

        case 16:
            // row[16]

            if (last_key < 16)
            {
                fprintf_P(&lcd_stream, PSTR("Key: %u "),last_key);
            }

            break;

        case 17:
            // row[17]
            fprintf_P(&lcd_stream, PSTR("If = %.4lf uA"), fuA * 1000000);
            break;

        case 18:
            // row[18]

            switch (Range)
            {
                case 0:
                    fprintf_P(&lcd_stream, PSTR("cf: %.3lf"), Cf[Range] * 1000000);
                    lcd_write_string(PSTR("uF "));
                    break;
                case 1:
                    fprintf_P(&lcd_stream, PSTR("cf: %.3lf"), Cf[Range] * 1000000);
                    lcd_write_string(PSTR("uF "));
                    break;
                case 2:
                    fprintf_P(&lcd_stream, PSTR("cf: %.3lf"), Cf[Range] * 1000000);
                    lcd_write_string(PSTR("uF "));
                    break;
                case 3:
                    fprintf_P(&lcd_stream, PSTR("cf: %.3lf"), Cf[Range] * 1000000);
                    lcd_write_string(PSTR("uF "));
                    break;
                case 4:
                    fprintf_P(&lcd_stream, PSTR("cf: %.3lf"), Cf[Range] * 1000000);
                    lcd_write_string(PSTR("uF "));
                    break;
                case 5:
                    fprintf_P(&lcd_stream, PSTR("cf: %.3lf"), Cf[Range] * 1e12);
                    lcd_write_string(PSTR("pF "));
                    break;
                case 6:
                    fprintf_P(&lcd_stream, PSTR("cf: %.3lf"), Cf[Range] * 1e12);
                    lcd_write_string(PSTR("pF "));
                    break;
            }

            break;

        case 19:
            // row[19]
            fprintf_P(&lcd_stream, PSTR("SPDR0: %u "), fDIV[Range]);
            break;

        case 20:
            // row[20]
            fprintf_P(&lcd_stream, PSTR("SPDR1: %u "), Cfrange[Range] + wf_type);
            break;

        case 21:
            // row[21]
            fprintf_P(&lcd_stream, PSTR("DAC0 = %.4lf"), Vdac[0]);
            lcd_write_string(PSTR("V"));
            break;

        case 22:
            // row[22]
            fprintf_P(&lcd_stream, PSTR("DAC1 = %.4lf"), Vdac[1]);
            lcd_write_string(PSTR("V"));
            break;

        case 23:
            // row[23]
            fprintf_P(&lcd_stream, PSTR("DAC2 = %.4lf"), Vdac[2]);
            lcd_write_string(PSTR("V"));
            break;

        case 24:
            // row[24]
            fprintf_P(&lcd_stream, PSTR("DAC3 = %.4lf"), Vdac[3]);
            lcd_write_string(PSTR("V"));
            break;

        case 25:
            // row[25]
            fprintf_P(&lcd_stream, PSTR("DAC4 = %.4lf"), Vdac[4]);
            lcd_write_string(PSTR("V"));
            break;

        case 26:
            // row[26]
            fprintf_P(&lcd_stream, PSTR("DAC5 = %.4lf"), Vdac[5]);
            lcd_write_string(PSTR("V"));
            break;

        case 27:
            // row[27]
            fprintf_P(&lcd_stream, PSTR("DAC6 = %.4lf"), Vdac[6]);
            lcd_write_string(PSTR("V"));
            break;

        case 28:
            // row[28]
            fprintf_P(&lcd_stream, PSTR("DAC7 = %.4lf"), Vdac[7]);
            lcd_write_string(PSTR("V"));
            break;

        case 29:
            // row[29]
            lcd_write_string(PSTR(" Waveform Generator "));
            break;

        case 30:
            // row[30]
            lcd_write_string(PSTR("Ver: 1.2010.0706l   "));
            break;

        case 31:
            lcd_write_string(PSTR("Authour: Jerry Ford "));
            break;

        case 32:
            lcd_write_string(PSTR("  Please Wait...    "));
            break;

    }

}

//-------------------------------------------------------------------------------

void update_wfg_display(int8_t toprow,int8_t anchor, int8_t newCursor,int8_t clr)
{
    if (UpdateDisplay > 0)
    {

        // CLR update display flag.
        UpdateDisplay = 0;

        //SetCursor 0 = Hide, Blink-Off
        //SetCursor 1 = Hide, Blink-On
        //SetCursor 2 = Show, Blink-Off
        //SetCursor 3 = Show, Blink-On

        //..............................................................
        // Check if the whole display should be cleard first.
        if(clr > 0) 
        {
            // Clear Display
            lcd_write_byte(0x01);
            delay_ms(5);
        }

        //..............................................................
        // * Update the First Row.
        lcd_line_one();

        if(anchor < 0)
        {
            update_line(toprow);
        }
        else
        {   // The first row can be made to keep the same data.
            update_line(anchor);
        }

        //..............................................................        
        // check if the next row is going to go passed
        // valid displayed rows and reset to 0 if so. 
        if((toprow + 1) > RowMax)
        {
            toprow = -1;
        }

        // Update the Second Row.
        lcd_line_two();
        update_line(++toprow);

        //..............................................................
        // check if the next row is going to go passed
        // valid displayed rows and reset to 0 if so. 
        if((toprow + 1) > RowMax)
        {
            toprow = -1;
        }

        lcd_line_three();
        update_line(++toprow);

        //..............................................................
        // check if the next row is going to go passed
        // max_rows and reset to 0 if so. 
        if((toprow + 1) > RowMax)
        {
            toprow = -1;
        }

        lcd_line_four();
        update_line(++toprow);

        // Position Cursor at current (R,C) and set cursor properties Show and Blink.
        lcd_cursor_position((cRow - top_row),cCol, (newCursor && 2),(newCursor && 1));

    }
}

//-------------------------------------------------------------------------------------------

void Regulate_fout(void)
{
    // Measure and Regulate frequency.

    if ((update_f == 1) && (TCNT1 > 1))
    {   
        //          RANGE =      0,      1,      2,      3,      4,  5, 6  
        //long fperiod[7] = {25000, 250000, 250000, 250000, 250000,  0, 0}; 
        //double  fmul[7] = {    1,      1,     10,    100,   1000, 10, 100};

        uint8_t x = 0;
        uint8_t fn_max = 10;

        // Store curent counter value.
        Last_Count = TCNT1;

        //Calculate New frequncy.
        if (Range < 5)
        {
            // count is invers proportinal to frequency.            
            fins =((double)((double)fperiod[Range]/Last_Count) * fmul[Range]);
        }
        else
        {
            // count is proportinal to frequency.
            fins =  (double)(Last_Count *  fmul[Range]);
        }

        // Store new freq. value at the next point in the array.
        freq[fp] = fins;

        //INC number of freq points to be averged, up to max n count.
        if (fn < fn_max)
        {
            fn++;
        }

        //INC fp "freq-pointer" from 0 to (MAX_N -1) then reset fp.
        if (fp < (fn_max-1))
        {
            // INC fp.
            fp++;
        }
        else
        {
            // Reset fp.
            fp = 0;
        }

        if( fn > 0 )
        {

            // Calc sum of freq set.
            for(x = 0; (x < fn ) ; x++)
            {
                favg = (double)(favg + freq[x]);
            }

            // Calculate frequency avg.
            favg = (double)(favg / ( fn + 1));

        }
        else
        {
            favg = fins;
        }

        // Calculate instentanious Frequency Error Factor.
        FiErrFact = (double)((fins - fsetpoint) / fsetpoint);

        // Calculate average Frequency Error Factor.
        FaErrFact = (double)((favg - fsetpoint) / fsetpoint);

        // Error Correction State mechean.
        switch (FrCorState)
        {
        case 0:// wait for 10 samples STATE = 1.

            // Reset avg calculator.
            fn = 0;
            fp = 0;

            // Reset fine tunning DACs. 
            // This sets up fo.
            Vdac[1] = 1.25;
            Vdac[2] = 1.25;
            Vdac[3] = 1.25;
            fcorrection = 1.25;
            update_Vdac(1,Vdac[1]);
            update_Vdac(2,Vdac[1]);
            update_Vdac(3,Vdac[1]);

            // Calculate If = (FreqSetpoint * Cf)
            fuA = (float)(fsetpoint * Cf[Range]);

            // Calculate VDAV-0 = [(If - (2.5V/-68K)) * 3.9K]   // "fuA = If"
            Vdac[0] = ((fuA -.0000365) * 3900);

            //Set up fo.
            update_Vdac(0,Vdac[0]);

            if (fabs(FiErrFact) > .66 )
            {
                // Wait to let the MAX038 to catch up.
                delay_ms(100);
            }

            // goto next state.             
            FrCorState += 1;

            break;

        case 1:// wait for another 10 samples STATE = 1.

            // Set "If" to get closer to the fsetpoint.
            //NOTE: Vdac[0] controls the If signal of the MAX038.
            Vdac[0] *= (1 - FiErrFact);

            // Correct  up fo Error.
            update_Vdac(0,Vdac[0]);

            if (fabs(FiErrFact) < .5 )
            {
                // Start: Frequency Error Correction STATE.
                if (fp == 0)
                {                   
                    if (fabs(FaErrFact_Last - FaErrFact) < .1 )
                    {
                        // goto next state.
                        FrCorState += 1;
                    }
                }
            }

            break;

        case 2:
            // Correct fo's error by controlling "Vf1"
            // Vdac[1] = ((FaErrFact * 2.066265) + 1.251389
            // Adjust for fo's error by adding just 1/10th  
            // of the correction factor to Vf1.
            Vdac[1] += (FiErrFact * 2);

            // Correct fo's Error.
            update_Vdac(1,Vdac[1]);

            if (fabs(FiErrFact) <.05)
            {
                // Start: Frequency Error Correction STATE.
                if (fp == 0)
                {

                    if (fabs(FaErrFact_Last - FaErrFact) < .005 )
                    {
                        // goto next state.
                        FrCorState += 1;
                    }
                    else if (fabs(FaErrFact_Last - FaErrFact) > .1 )
                    {
                        // Reset DAC1 to 1.25V "Vf1 = 0V"
                        Vdac[1] = 1.25;

                        // Correct  up fo Error.
                        update_Vdac(1,Vdac[1]);

                        // goto back one state.
                        FrCorState -= 1;
                    }
                }
            }

            break;

        case 3:

            // Correct fo's error by controlling "Vf2"
            // Vdac[2] = ((FiErrFact * 20.66265) + 1.251389
            // Adjust for fo's error by adding just 1/100th  
            // of the correction factor to Vf2.
            Vdac[2] += (FiErrFact * 2.1);

            // Correct  up fo Error.
            update_Vdac(2,Vdac[2]);

            if (fabs(FiErrFact) <.005)
            {
                // Start: Frequency Error Correction STATE.
                if (fp == 0)
                {
                    if (fabs(FaErrFact_Last - FaErrFact) < .0005 )
                    {
                        // goto next state.
                        FrCorState += 1;
                    }
                    else if (fabs(FaErrFact_Last - FaErrFact) > .01 )
                    {
                        // Reset DAC1 to 1.25V "Vf1 = 0V"
                        Vdac[2] = 1.25;

                        // Correct  up fo Error.
                        update_Vdac(2,Vdac[2]);

                        // goto next state.
                        FrCorState -= 1;
                    }
                }
            }

            break;

        case 4:

            // Correct fo's error by controlling "Vf3"
            // Vdac[3] = ((FiErrFact * 206.6265) + 1.251389
            // Adjust for fo's error by adding just 1/10th  
            // of the correction factor to Vf2.
            //Vdac[3] += (FiErrFact * 8) + (FaErrFact * 2); // very good! 9
            //Vdac[3] += (FiErrFact * 8) + (FaErrFact * 3); // very good!
            fcorrection += (FiErrFact * 3) + (FaErrFact * .5); // very good!

            Vdac[3] = (float)fcorrection;

            // Correct  up fo Error.
            update_Vdac(3,Vdac[3]);

            // Start: Frequency Error Correction STATE.
            if (fp == 0)
            {

                if ((Vdac[3] < .1)  || (Vdac[3] > 2.4))
                {
                    fcorrection = 1.25;
                    // Reset DAC1 to 1.25V "Vf1 = 0V"
                    Vdac[3] = fcorrection;

                    // Correct  up fo Error.
                    update_Vdac(3,Vdac[3]);

                    // go back one state.
                    FrCorState -= 1;
                }
            }

            break;

        }

        // Set flag to update the display.
        UpdateDisplay = 1;

        if(fsetpoint > 100)
        {
            // wait before resetting gate.
            delay_ms(1);
        }

        // Reset freq counting gate.
        reset_gate();

    }

}//End of Regulate_fout()

//-------------------------------------------------------------------------------

void check_Usrer_input(void)
{   //check if the user has pressed a key and 
    // preform appriate action.

    is_number = 0;
    is_dir = 0;
    is_dot = 0;
    is_enter = 0;

    char Keypad = '\0';

    // Declear DAC Channel variable.
    //uint8_t dach = 0; used to manuly set the DACs.

    if(last_key >= 0)
    {
        // Need to add a beep to the hardware.

        // Determin type of key.
        switch(last_key)
        {
            case 0: // 'keypad 1'
                is_number = 1;
                Keypad = '1';
                break;

            case 1: // '2'
                is_number = 1;
                Keypad = '2';
                break;

            case 2: // '3'
                is_number = 1;
                Keypad = '3';
                break;

            case 3: // 'Move Cursor Up ^'

                is_dir = 1;

                //set current row position value up.
                if(cRow < 1)
                {
                    cRow = RowMax;
                    top_row = RowMax;
                }
                else
                {
                    cRow -= 1;

                    if (cRow < top_row)
                    {
                        top_row = cRow;
                    }

                }

                break;

            case 4: // '4'
                is_number = 1;
                Keypad = '4';
                break;

            case 5: // '5'
                is_number = 1;
                Keypad = '5';
                break;

            case 6: // '6'
                is_number = 1;
                Keypad = '6';
                break;

            case 7: // 'Move Cursor Left <'

                is_dir = 1;

                if(cCol > 0)
                {
                    cCol -=1;
                }
                else
                {
                    cCol = 0;
                }

                break;

            case 8: // '7'
                is_number = 1;
                Keypad = '7';
                break;

            case 9: // '8'
                is_number = 1;
                Keypad = '8';
                break;

            case 10: // '9'
                is_number = 1;
                Keypad = '9';
                break;

            case 11: // 'Move Cursor Right >'

                is_dir = 1;

                if(cCol<19)
                {
                    cCol +=1;
                }

                break;

            case 12: // '.'
                is_dot = 1;
                is_number = 1;
                Keypad = '.';
                break;

            case 13: // '0'
                is_number = 1;
                Keypad = '0';
                break;

            case 14: // 'Enter'

                if(cRow == 4)
                {
                    is_number = 1;
                    Keypad = '-';
                }
                else
                {
                    is_enter = 1;

                }

                break;

            case 15: // 'Move Cursor Down v'

                is_dir = 1;

                // set current row position value up.
                if(cRow > (RowMax - 1))
                {
                    cRow = 0;
                    top_row = 0;
                }
                else
                {
                    cRow += 1;

                    if (cRow > (top_row + 3))
                    {
                        top_row = (cRow - 3);
                    }

                }

                break;
        }

        //.......................................................................

        // Perform user entered request.
        if(is_number > 0 )
        {
            switch (cRow)
            {
                case 0: // favg
                    break;

                case 1:// fsetpoint

                    //012345678901234567890
                    //fs= ###.##### MHz

                    if(fsetpoint >= 1000000)
                    {
                        tempFloat = 1000000;
                    }
                    else if(fsetpoint >= 1000)
                    {
                        tempFloat = 1000;
                    }
                    else
                    {
                        tempFloat = 1;
                    }

                    // Store the current the value as a string.
                    sprintf_P(tempStr,PSTR("%9.5f"),(fsetpoint/tempFloat));

                    //colleft[1] = 4;   
                    //colright[1] = 16;
                    //012345678901234567890
                    //fs= ###.##### MHz
                    if((cCol >= colleft[cRow]) && (cCol < (colright[cRow] - 3)))
                    {
                        // Change chr at col position.
                        tempStr[cCol-colleft[cRow]] = Keypad;

                        // convert the string back to a voltage value (aka float). 
                        sscanf_P(tempStr, PSTR("%f"), &tempFloat2);

                        //
                        fsetpoint = (tempFloat2 * tempFloat);

                        // INC cCol. Because this is a numeric fieled.
                        //if(cCol<12)
                        if(cCol < (colright[cRow] - 3))
                        {
                            cCol++;
                        }

                    }
                    else if((cCol >= (colleft[cRow]+9)) && (cCol < (colright[cRow] )))
                    {   //  - 5432109876543210 -
                        //  + 0123456789012345 +
                        //01234567890123456789 ABS pos
                        //fs= ###.##### KHz

                        if(Keypad >='1' && Keypad <'4')
                        {

                            // Convert Setpoint to Hz.
                            if(fsetpoint >= 1000000)
                            {
                                tempFloat = (fsetpoint/1000000);
                            }
                            else if(fsetpoint >= 1000)
                            {
                                tempFloat = (fsetpoint/1000);
                            }
                            else
                            {
                                tempFloat = fsetpoint;
                            }

                            // set freq setpoint with new mul(1 = Hz, 1000 = KHz or 1000000 MHz)
                            if(Keypad == '2')
                            {
                                tempFloat *= 1000;
                            }
                            else if(Keypad == '3')
                            {
                                tempFloat *= 1000000;
                            }

                            // Check Freq setpoint limmits.
                            if(tempFloat > 5000000)
                            {
                                fsetpoint = 5000000;
                            }
                            else if(tempFloat < .5)
                            {
                                fsetpoint = .5;
                            }
                            else
                            {
                                fsetpoint = tempFloat;
                            }

                        }
                    }

                    // Setup hardware for measureing the desired freq out.
                    Range = rtn_Frange(fsetpoint);

                    break;

                case 2:// Wave Type

                    //+=================+
                    //|WF Type  |SerReg1|
                    //+=========+=======+
                    //| SQR     |   0   |
                    //| TRI     |   1   |
                    //| SIN     |   2   |
                    //+=========+=======+    
                    // uint8_t wf_type = 2;

                    if((Keypad == '1') || (Keypad == '2') || (Keypad == '3'))
                    {                           
                        // Keypad 1 = Sine Wave MAX038.WaveSel[2]
                        // Keypad 2 = Square Wave MAX038.WaveSel[0]
                        // Keypad 3 = Triangle Wave MAX038.WaveSel[1]
                        wf_type = waveindex[last_key];

                        // Set frequency measureing bits with the wave select bits.
                        sd_out(1 , Cfrange[Range] + wf_type);

                        // copy WaveTable[Range] to wavename.
                        strcpy_P(wavename, (PGM_P)pgm_read_word(&(WaveTable[wf_type])));
                    }

                    break;

                case 3:// Amplatude = 0 - 10 Vpp.

                    if((cCol >= colleft[cRow]) && (cCol <= colright[cRow]))
                    {
                        // Store the current parameters value as a string.
                        sprintf_P(tempStr,PSTR("%5.2f"),Vpp);

                        // Change chr at col position.
                        tempStr[cCol - colleft[cRow]] = Keypad;

                        // convert the string back to a voltage value (aka float). 
                        sscanf_P(tempStr, PSTR("%f"), &tempFloat);

                        // Set new amplitude of signal.
                        Set_Amplitude(tempFloat, 1.075);

                        // INC col.
                        if(cCol < colright[cRow])
                        {
                            cCol += 1;

                            //Skip over dp "."
                            if(cCol == colleft[cRow] + 2)
                            {
                                cCol += 1;
                            }

                        }
                        else
                        {
                            cCol = colleft[cRow];
                        }

                    }

                    break;

                case 4:// Vofs =

                    if((cCol >= colleft[cRow]) && (cCol <= colright[cRow]))
                    {

                        // Store the current parameters value as a string.
                        sprintf_P(tempStr,PSTR("%-06.2f"), Vofs);

                        if (last_key == 14) // the Enter Key is also the "-/+" key
                        {
                            tempFloat *= -1;
                        }
                        else
                        {
                            // Change chr at col position.
                            tempStr[cCol - colleft[cRow]] = Keypad;

                            // convert the string back to a voltage value (aka float). 
                            sscanf_P(tempStr, PSTR("%f"), &tempFloat);
                        }

                        // Set signals Vdc offset.  
                        Set_Voffset(tempFloat, .394);

                        // INC col.
                        if(cCol < colright[cRow])
                        {
                            cCol += 1;

                            // Skip over dp "."
                            if(cCol == colleft[cRow] + 3)
                            {
                                cCol += 1;
                            }

                        }
                        else
                        {
                            cCol = colleft[cRow];
                        }

                    }

                    break;

                case 5:// DutyCycle = 
                        //01234567890123456789
                        //DutyCycle= 15% to 85%

                        if((cCol >= colleft[cRow]) && (cCol <= colright[cRow]))
                        {
                            // Store the current DAC voltage value as a string.
                            sprintf_P(tempStr,PSTR("%u"),DutyCycle);

                            // Change chr at col position.
                            tempStr[cCol-colleft[cRow]] = Keypad;

                            if(cCol == colleft[cRow])
                            {
                                cCol = colright[cRow];
                            }
                            else
                            {
                                cCol = colleft[cRow];
                            }

                            // convert the string back to a voltage value (aka float). 
                            sscanf_P(tempStr, PSTR("%u"), &DutyCycle);

                            // Set new duty cycle value.
                            Set_duty_cycle(DutyCycle);
                        }

                    break;

                case 6:// ta: 1/f
                    break;
                case 7:// fi:
                    break;
                case 8://Ferr:
                    break;
                case 9:// "-- Internal Vars -- "
                    break;
                case 10:// "                    "    
                    break;
                case 11:// Range:                    
                    break;
                case 12:// Range Min to Max          
                    break;
                case 13:// Count:                    
                    break;
                case 14:// fn:                       
                    break;
                case 15:// fstate: FrCorState        
                    break;
                case 16:// Keypad:                   
                    break;
                case 17:// If:                       
                    break;
                case 18:// Cf
                    break;
                case 19:// SPDR0: %u "), fDIV[Range]
                    break;
                case 20:// SPDR1: %u "), Cfrange[Range] + wf_type
                    break;
                case 21:// DAC0 
                case 22:// DAC1 
                case 23:// DAC2 
                case 24:// DAC3 
                case 25:// DAC4
                case 26:// DAC5
                case 27:// DAC6
                case 28:// DAC7

                    /* Disable the editing of the dac values.

                    //if((cCol > 6) && (cCol < 13) )
                    if((cCol >= colleft[cRow]) && (cRow <= colright[cRow]))
                    {   
                        // set the DAC Channel based on the current row.
                        dach = (cRow - 10);

                        // Store the current DAC voltage value as a string.
                        sprintf_P(tempStr,PSTR("%6.4f"),Vdac[dach]);

                        if(cCol == 8)
                        {
                            cCol = 9;
                        }
                        else
                        {
                            // Change chr at col position.
                            tempStr[cCol-7] = Keypad;

                            // convert the string back to a voltage value (aka float). 
                            sscanf_P(tempStr, PSTR("%f"), &Vdac[dach]);

                            // Set the DAC[ch]
                            //set_dac(dach,(uint16_t)((Vdac[dach]/2.5)*4096),1);
                            update_Vdac(dach,Vdac[dach]);

                            // INC cCol. Because this is a numeric fieled.
                            if(cCol==7)
                            {
                                // skip over the position of the dec-point '.'
                                cCol+=2;
                            }
                            else if(cCol<19)
                            {
                                cCol +=1;
                            }

                        }

                    }
                    */

                    break;

                case 29://  
                    //lcd_write_string(PSTR(" Waveform Generator "));
                    break;

                case 30:// 
                    //lcd_write_string(PSTR("Ver: 1.2010.0623a   "));
                    break;

                case 31://  
                    //lcd_write_string(PSTR("Authour: Jerry Ford "));
                    break;

                case 32://  
                    //lcd_write_string(PSTR("  Please Wait...    "));
                    break;
            }

        }

        // Keep cursor in valid location for that row.
        if (colleft[cRow] > 20)
        {
            // if left col position is > 20 then 
            // turn off the cursor.
            SetCursor = 2; 
            cCol = 19;
        }
        else
        {
            SetCursor = 3;

            if(cCol < colleft[cRow])
            {
                cCol = colleft[cRow];
            }
            else if(cCol > colright[cRow])
            {
                cCol = colright[cRow];
            }
        }

        // Set flag to update the display.
        UpdateDisplay = 1;

    }

}

//-------------------------------------------------------------------------------------------

void update_key_pressed(void)
{
    if(last_key > 15)
    {
        // input and read key from the 74C922.
        read_key();

        // check key for corresponding function to preform.
        check_Usrer_input();

    }
}

//-------------------------------------------------------------------------------------------
// INITZ PROGRAM.

//set up pin directions for the ports .
initz_PinDirections();//needs to be first.

// INITZ the IRQs for the Keypad and Frequency Counter.
initz_XIRQ();

// INITZ the internal 16bit counter.
initz_16bitCounter();

// INITZ IO Pin Direction.
initz_PinDirections();

// Setup hardware for measureing the desired freq out.
Range = rtn_Frange(fsetpoint);

// Turn off cursor.
SetCursor = 0;

// Show Scrolling splash screen.
for(top_row=0; top_row < 30 ; top_row++)
{   
    // Set flag to update the display.
    UpdateDisplay = 1;

    // update display.
    update_wfg_display(top_row,-1,SetCursor,1);

    // delay a bit to let the user see all rows.
    delay_ms(200);
}

// Hold display to let user see the version.
delay_ms(1200);

// Set current row.
cRow = 0;
top_row = cRow;

// void lcd_display(uint8_t display_on,uint8_t cursor_show, uint8_t cursor_blink)
SetCursor = 3;// 0 = off, 2 = show not blinking, 3 = Show Blinking.

// Set flag to update the display.
UpdateDisplay = 1;

// Show Top_Row, No-ANCOR,Blink-state,CLR-First
update_wfg_display(top_row,-1,SetCursor,1);

//Set duty cycle.
Set_duty_cycle(50);

// Set Vpp of signal.
Set_Amplitude(1, 1.075);

// Set signals Vdc offset.  
Set_Voffset(0, .394);

// set unused DAC.
set_dac(7,(uint16_t)((Vdac[7] / 2.5) * 4096),1);

//Enable Interupts
sei();

// Reset freq counting gate.
reset_gate();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~  MAIN LOOP  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
while(1) 
{
    // Check if user has pressed a key.
    update_key_pressed();

    // Measure and Regulate frequency.
    Regulate_fout();

    // Update Display.
    update_wfg_display(top_row,-1,SetCursor,1);

}// End of main Loop. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

return 0;

} //END of Main() //************* //===========================================================================================

LCD.C (Modifide version)

//=============================================================================================== // lcd.c // for NerdKits // mrobbins@mit.edu // // PIN DEFINITIONS: // // // PD2-5 -- LCD DB4-7 (pins 11-14) (was PB3-6) // PD6 -- LCD E (pin 6) (was PA5) // PD7 -- LCD RS (pin 4) (was PA4) // Last Modified By: Jerry Ford for WFG project. // Last Mod. Date: 6/2/2010 //==================================================================

include <avr/io.h>

include <avr/pgmspace.h>

include <inttypes.h>

include <stdio.h>

include "lcd.h"

include "delay.h"

//================================================================== // lcd_set_type_data() void lcd_set_type_data() { PORTD |= (1<<PD7); }

//==================================================================

// lcd_set_type_command() void lcd_set_type_command() { PORTD &= ~(1<<PD7); }

//==================================================================

// lcd_write_nibble(...) void lcd_write_nibble(char c) {

// NOTE: only 2 or 3 work in the delays here.

// set data
//PORTD &= ~(0x0f << 2);    // nerd kits way.
//PORTD |= (c&0x0f) << 2;   // nerd kits way.
PORTC &= ~(0x0f << 2);      // new way 20100503
PORTC |= (c&0x0f) << 2;     // new way 20100503

// E high
PORTD |= (1<<PD6);
delay_us(1);

// E low
PORTD &= ~(1<<PD6);
delay_us(1);

}

//==================================================================

void lcd_write_byte(char c) { lcd_write_nibble( (c >> 4) & 0x0f ); lcd_write_nibble( c & 0x0f ); delay_us(80); }

//==================================================================

void lcd_clear_and_home() { lcd_set_type_command(); lcd_write_byte(0x01); delay_ms(50); lcd_write_byte(0x02); delay_ms(50); }

//==================================================================

void lcd_home() { lcd_set_type_command(); lcd_write_byte(0x02); delay_ms(50); }

//==================================================================

void lcd_write_data(char c) { lcd_set_type_data(); lcd_write_byte(c); }

//==================================================================

// lcd_write_int16 void lcd_write_int16(int16_t in) { uint8_t started = 0;

uint16_t pow = 10000;

if(in < 0) 
{
    lcd_write_data('-');
    in = -in;
}

while(pow >= 1) 
{
    if(in / pow > 0 || started || pow==1) 
    {
        lcd_write_data((uint8_t) (in/pow) + '0');
        started = 1;
        in = in % pow;
    }

    pow = pow / 10;
}

}

//==================================================================

// lcd_write_int16_centi // assumes that its measured in centi-whatevers void lcd_write_int16_centi(int16_t in) { uint8_t started = 0;

uint16_t pow = 10000;

if(in < 0) 
{
    lcd_write_data('-');
    in = -in;
}

while(pow >= 1) 
{
    if(in / pow > 0 || started || pow==1) 
    {
        lcd_write_data((uint8_t) (in/pow) + '0');
        started = 1;
        in = in % pow;
    }

    if(pow == 100) 
    {
        if(!started) 
        {
            lcd_write_data('0');
        }

        lcd_write_data('.');
        started = 1;
    }

    pow = pow / 10;
}

}

//==================================================================

void lcd_write_string(const char *x) { // assumes x is in program memory while(pgm_read_byte(x) != 0x00) { lcd_write_data(pgm_read_byte(x++)); } }

//==================================================================

void lcd_goto_position(uint8_t row, uint8_t col) { lcd_set_type_command();

// 20x4 LCD: offsets 0, 0x40, 20, 0x40+20
uint8_t row_offset = 0;

switch(row) 
{
    case 0: row_offset = 0; break;
    case 1: row_offset = 0x40; break;
    case 2: row_offset = 20; break;
    case 3: row_offset = 0x40+20; break;
}

lcd_write_byte(0x80 | (row_offset + col));

}

//==================================================================

void lcd_line_one() { lcd_goto_position(0, 0); } void lcd_line_two() { lcd_goto_position(1, 0); } void lcd_line_three() { lcd_goto_position(2, 0); } void lcd_line_four() { lcd_goto_position(3, 0); }

//==================================================================

// lcd_init() void lcd_init() { // set pin driver directions // (output on PD7,PD6, and PD3-6) //DDRD |= 0xfc;// nerd-kits way DDRC |=0x3c; //DDRD |=0xc0; DDRD |=0xd2; //(d2 = 1101 0010 new) // wait 100msec delay_ms(100); lcd_set_type_command();

// do reset
lcd_write_nibble(0x03);
delay_ms(6);
lcd_write_nibble(0x03);
delay_us(250);
lcd_write_nibble(0x03);
delay_us(250);

// write 0010 (data length 4 bits)
lcd_write_nibble(0x02);

// set to 2 lines, font 5x8
lcd_write_byte(0x28);

// disable LCD
//lcd_write_byte(0x08);

// enable LCD
lcd_write_byte(0x0c);//was lcd_write_byte(0x0c);

// clear display
lcd_write_byte(0x01);
delay_ms(5);

// enable LCD
lcd_write_byte(0x0c);//was lcd_write_byte(0x0c);

// set entry mode
lcd_write_byte(0x06);

// set cursor/display shift
lcd_write_byte(0x14);

// clear and home
lcd_clear_and_home();

}

//================================================================== int lcd_putchar(char c, FILE *stream) { lcd_write_data(c); return 0; }

//==================================================================

void lcd_cursor_position(uint8_t row, uint8_t col, uint8_t cursor_show, uint8_t cursor_blink) { // Move to (Row,Col) lcd_goto_position(row, col);

// sets: show and/or blink cursor.
lcd_display(1, cursor_show, cursor_blink);

}

//==================================================================

void lcd_display(uint8_t display_on,uint8_t cursor_show, uint8_t cursor_blink) { // sets display on/off, show and/or blink cursor.

if(display_on > 0)
{
    display_on = 4;
}

if(cursor_show > 0)
{
    cursor_show = 2;
}

if(cursor_blink > 0)
{
    cursor_blink = 1;
}

// set cursor/display shift
lcd_write_byte(8 + display_on + cursor_show + cursor_blink);

}

//==================================================================

Pictures

Power Supply Top View

Digital Board

Analog Board

User Interface Set Wave type, Amplitude, Vdc and Duty Dycle%

User Interface fa: Freqency Average, fi: Freqency Instantanious, ferr: freq. Error and fs: freq Setpoint

The whole project: Power Supply, Digital Board, User Interface, Analog Board

Sine Wave at 12.456Hz

Sine Wave at 32.768KHz

Square Wave at 32.768KHz

Sine Wave at 123.45Hz

Ramp Wave at 32.768KHz

September 13, 2012
by Ralphxyz
Ralphxyz's Avatar

Wow Jer, that is a lot of work.

Now just what I need another project to play, oh er I mean to experiment with!

Can't wait to see the Library article with the pictures etc.

Ralph

Post a Reply

Please log in to post a reply.

Did you know that Morse code is a compact way to transmit human-readable text over binary channels? Learn more...