An 8-bit Digital and Analog Design
This project combines a PIC microcontroller, a custom clock source, some simple digital circuitry and selectable output filters. The result is a reprogrammable audio-frequency function generator with resolution from 0.1Hz to 20kHz.
I’ve been an 8-bit developer for decades and designed many of the instruments on my own workbench. One thing I needed was a decent function generator. I have a digital pulse generator, but nothing that could produce analog (non-digital) signals. So, I decided to roll my own and make it another 8-bit development project. Most of my projects deal with relatively low-frequency signals, so I wanted it to cover at least the audio range. I also wanted it to be capable of generating any signal I might need, not just sine and triangle waves—a truly arbitrary function generator.
The result is what I call the Digital Function Generator, or the FGEN1 circuit board. Figure 1 is a picture of the circuit board, which was designed to fit the SIMCO 325XP6 or PacTec CM6-300 case. Figure 2 shows a 1kHz sine wave output from the FGEN1. The FGEN1 can store 14 waveform definitions in onboard EEPROM. All the waveforms can be reprogrammed from simple ASCII text files that are easy to create. The FGEN1 frequency range is 0.1Hz to 20kHz with 0.1Hz resolution. An LCD character display module shows the waveform name and frequency of the FGEN1’s output. Figure 3 is the block diagram of the FGEN1.
Power supply: Figure 4 shows the schematic for the power supply circuitry. An external 9VDC input goes to two 78M05 linear voltage regulators, U10 and U11. I decided to use linear regulators instead of switching regulators to eliminate switching noise. Header SW1 allows the use of a power switch; otherwise a jumper should be placed at SW1. The voltage regulators are positioned on the board so that they can share a common heatsink. U10 supplies power to the digital circuitry while U11 supplies power to the analog circuitry. Analog and digital grounds join at the voltage regulators.
NUMERICALLY CONTROLLED OSCILLATOR
As shown in the schematic in Figure 5, the FGEN1 is controlled by an 8-bit PIC16F18875, from Microchip Technology. The FGEN1 uses several of the PIC’s on-chip peripherals: parallel ports, UART, SPI and—most importantly for this application—the numerically-controlled oscillator (NCO). The NCO operates on the principle of direct digital synthesis (DDS) by repeatedly adding a fixed value, the phase increment, to the phase accumulator (Figure 6). The addition is done every clock cycle. When the phase accumulator overflows it does not restart from zero; the additive remainder is left in the phase accumulator. The PIC’s 20-bit phase accumulator overflow rate is also known as the NCO output rate from the PIC. The NCO output rate is determined by:
Because the PIC’s NCO output feeds an 8-bit counter in the waveform generation circuitry, the effective phase accumulator becomes 28 bits; 20 bits in the PIC’s internal phase accumulator, plus the 8 external bits. The NCO resolution, which is the frequency change when the phase increment equals 1, is:
For a resolution of 0.1Hz this means:
The FGEN1’s custom clock oscillator (Y1) provides the PIC with a 26.8435MHz clock, which means the FGEN’s frequency resolution is approximately 0.1Hz.
The waveform generation circuitry is shown in the schematics in Figure 7. The digital portion of this circuitry is composed of a 74HC4040 ripple counter (U3), a 74HC573 octal transparent latch with 3-state outputs (U4); and a 6116, 2Kx8 static RAM (U5). Only eight bits of the 74HC4040’s 12 outputs are used. This 8-bit count is the phase count; it goes through the 256 equally-spaced phase values in one cycle of the output waveform. The phase count continues to roll over, repeating the periodic output waveform, as long as the NCO output from the PIC is active. Because the 74HC4040 is a ripple counter, its outputs don’t all change simultaneously. The NCO output pulse increments the 74HC4040 and causes the 74HC573 to latch the last phase count while the 74HC4040 output ripples. At the end of the NCO output pulse the new, stable phase count is presented to the 6116 static RAM address lines. Only eight of the 6116’s eleven address lines are used because only 256 memory locations are needed. The 6116 RAM is called the “Output RAM.” Even though the 6116 has more memory than needed, it was used because a smaller parallel I/O static RAM was not available. As the phase count increments, the 256 bytes in the 6116 RAM, which are the voltage values of the waveform, are sequentially output on the RAM’s eight I/O pins. The eight 6116 RAM outputs connect to the R-2R resistor network (RN1) inputs.
During normal operation, the PIC holds the Output RAM in Read mode with output enabled. When a different waveform is loaded into the Output RAM, the PIC disconnects the NCO from the NCO output pin, clears the 74HC4040 ripple counter, and disables the 6116 output. The PIC then changes Port-B (RB0:7) to output and sequentially presents the new 256 waveform values on Port-B while incrementing the 74HC4040 ripple counter and toggling the Output RAM’s Write pin. After the new waveform is written to the Output RAM, Port-B is returned to a high-impedance input, the Output RAM is put back in Read mode with output enabled, and the NCO is reconnected to the NCO output pin.
Converting the Output RAM’s byte-wide output to an analog signal requires a digital-to-analog-converter (DAC). Because the FGEN1 goes to 20kHz with 256 phase values per cycle, the DAC needs to operate at 5.12MHz. Finding an inexpensive, high-speed, single-supply, 8-bit DAC in a DIP package was difficult. The few parts available all used current switches feeding an R-2R network. R-2R networks are fast, reliable, and inexpensive. (For further reading, see the Electronics Tutorials article on Circuit Cellar’s article materials page .) As it turns out, the CMOS outputs of a 6116 static RAM make decent current switches. In fact, the current switches don’t need to be perfect, going from 5V to 0V, as long as all eight span the same range. Thus, it is possible to use the 6116’s outputs as inputs to the R-2R DAC network. The R-2R network is realized as a thick-film resistor network in a 10-pin SIP. The R-2R network feeds inverting amplifier U8.1, which converts the R-2R current to voltage and offsets the signal to the reference voltage supplied by U6.1. This reference voltage (signal ground) is half of the 5V supply used by the single-supply opamps.
Any DAC output requires some lowpass filtering to remove the digital “stair step” appearance of its output. Some waveforms can be heavily filtered because the waveform itself has low harmonic content, such as a sine wave. Other waveforms, such as a sawtooth wave, have high-order harmonics that, if filtered out, would distort the desired waveform. To accommodate different filtering requirements, the FGEN has six second-order Bessel lowpass filters that ratiometrically span the FGEN1’s output range. These are Filter Banks 0 through 5 as shown in Figure 8. The actual circuitry comprising these filters is shown in the schematic in Figure 9. The signal from the waveform generation circuitry feeds all six Filter Banks in parallel. The outputs of Filter Banks 0-5 go to inputs 0-5, respectively, of the 74HC4051 (U9), 8:1 analog multiplexer. The last two inputs to the analog MUX are the unfiltered DAC output, on input 6, and analog ground on input 7. There is a single order lowpass filter (U8.3), known as the Common Filter, on the output of the analog MUX. Table 1 lists the specifications of the filters.
There are three different quad op-amps used in the Filter Banks and other analog circuitry. To reduce costs, the gain-bandwidth product (GBP) of the op-amps was matched to the bandwidth requirements of the circuitry. The MCP6004 (U6), with a GBP of 1MHz, is used for DC voltages and the lowest frequency Filter Bank. The MCP604 (U7), with a GBP of 2.8MHz, is used for the middle-frequency Filter Banks. The MCP6024 (U8), with a GBP of 10MHz, is used for the highest-frequency Filter Bank and all op-amps through which the waveform signal must pass.
The FGEN1 firmware selects the appropriate filter bank based on the filtering scheme specified in the waveform definition file and the output frequency.
CONTROLS AND I/O
The schematic in Figure 10 displays the connections for the manual controls, serial port, and display. Figure 11 shows an example of the wiring between the FGEN1 circuit board and the front panel.
The LCD module is a two-line by 16-character display. It should be mounted so that its connection points are at the top, as in Figure 11. J3 is the connector for the LCD module. The LCD module and J3 both have pin 1 on the right. The 16 pins of J3 correspond with the 16 pins on the LCD module. Note that pins 7, 8, 9, and 10 are unused and do not need to be wired. The FGEN interfaces with the LCD module via a 4-bit interface rather than the usual 8-bit interface. Thus, the four data lines on those pins are unused. Trimmer R30 controls the LCD’s contrast and will need to be adjusted to see the characters on the display.
The front panel switches connect to J5 as shown in Table 2. Because pin 5 is the only Ground on the connector, it must be connected to all the switches.
The FGEN1 is controlled by the front panel switches in Manual Mode. The Line Select switch moves the LCD’s cursor between the waveform line and the frequency line. Figure 12 shows the FGEN1 front panel with the cursor at the ten-thousand Hz digit. The Advance pushbutton moves the LCD cursor to the next frequency digit to the right. The Increment pushbutton increments the digit at the cursor with rollover from 9 to 0. The Filter Bypass switch disables automatic Filter Bank selection and defaults to just the Common Filter.
The Gain and Offset controls are both 10k potentiometers connected at J1 and J7 respectively. Clockwise (CW) and Counter-Clockwise (CCW) connections are shown. The Gain can be adjusted from 0.85 to 2.0. The Offset can shift the output ±0.8V. The output signal is on J6, which should be connected to some type of output connector, such as a BNC, RCA, or audio jack.
The FGEN1 interfaces with a Host via connector J4. Standard RS-232 voltage levels are provided by the MAX232 interface chip (U40). The Host UART should be set to operate at 9600 baud with eight data bits, no parity, and one stop bit (9600:8N1). None of the handshake lines are actively controlled by the FGEN1. DTR is not connected and thus is ignored. DSR and DCD are hard-wired to the ON condition (ON = spacing = +voltage) at all times. RTS is received, buffered, and looped back to the Host as CTS; thus CTS tracks RTS. The PIC monitors RTS to determine when a Host connection is active.
When the PIC detects RTS is active it switches to Host Mode. The Host computer should be running a terminal emulation program with serial connection set to 9600:8N1. In Host Mode, the FGEN1 ignores the front panel switches but keeps the LCD module updated. The FGEN1 sends the menu shown here to the Host:
FUNCTION GENERATOR 1
Firmware 2023.01.18[L]ist waveforms in EEPROM [U]pload waveform to PIC RAM [C]opy PIC RAM waveform to output RAM [S]tore PIC RAM waveform in EEPROM [D]uplicate EEPROM waveform in PIC RAM [E]rase waveform from EEPROM [F]requency
Menu selections are made by typing the character in brackets. To understand the menu options, one must know that there are three memory locations where waveform data can reside:
- EEPROM memory is the 25LC320A EEPROM chip. It holds waveforms 00 to 13.
- PIC RAM is a portion of the PIC’s RAM that temporarily holds waveform data as a bridge between EEPROM and Output memory.
- Output RAM is the 6116 static RAM chip. Waveform data in this memory will be translated into an analog signal at the FGEN1 output.
Waveforms stored in EEPROM are referenced by their waveform number: 00 through 13. The List option will display all the waveforms stored in EEPROM as shown here.
# Name Date Filter
00 SINE 2022-08-27 1
01 SQUARE 2022-08-27 2
02 TRIANGLE 2022-08-27 1
03 +SAWTOOTH 2022-08-27 2
04 -SAWTOOTH 2022-08-27 2
To output a waveform stored in EEPROM in Host Mode you need to select Duplicate, which will copy the waveform data from EEPROM to PIC RAM, then select Copy to copy the waveform data from PIC RAM to the Output RAM.
EEPROM waveform management is done with the Upload, Store, Erase, and Initialize options. Testing of new waveform files can be done with the Upload, Copy, Frequency, and Analog MUX options. In particular, the Frequency and Analog MUX selections allow you to determine the best filtering option for a waveform.
To end Host Mode you must select X, then remove the RS-232 cable from J4.
WAVEFORM DEFINITION FILES
Waveform definition files are ASCII text files that may be created with Notepad or a similar editor. Common waveform files are available for download at the website for Lucid Technologies, my company—see Circuit Cellar’s article materials page for the link . Waveform files should use a TXT file extension. Line Feed (LF = 0x0A) and Carriage Return (CR = 0x0D) are ignored. Other ASCII control characters (<= 0x1F) in the file will cause an error. There are five fields in a waveform file; one is optional and four are required. The fields must appear in the specified order. The four required fields must all begin with an ASCII Asterisk “*” (0x2A). An example file is shown in Listing 1. Each waveform definition file must adhere to this format:
Comment Field: This field is optional. It may contain up to 255 characters. It cannot contain an asterisk.
Name Field: This field is required and must begin with an asterisk. This is the name that will be displayed by the FGEN. It must be less than 16 characters.
Date Field: This field is required and must begin with an asterisk. These ten bytes are the year-month-day; YYYY-MM-DD.
Filter Field: This field is required and must begin with an asterisk. This single character specifies the filtering used for the waveform.
Data Field: This field is required and must begin with an asterisk. This field must contain 512 ASCII characters representing the 256 bytes in one cycle of the waveform. Each byte is stored in ASCII-HEX format as two ASCII characters. Valid characters are 0-9, A-F, and a-f. For example, 0xA7 will be sent as the two ASCII bytes, “A” (0x41) and “7” (0x37). The two ASCII characters representing one byte may not be split between lines. The data field must be terminated by an ASCII Percent “%” (0x25) character. Signal ground is approximately 0x7F. Values greater than signal ground are 0x80-0xFF, while values less than signal ground are 0x00-0x7E.
The FGEN1 is an easy-to-use arbitrary function generator. Its simple programmability makes it versatile and an ideal source of audio frequency signals for experimenters and students. The FGEN1 kit is available on the Lucid Technologies website, a link for which you can find on Circuit Cellar‘s Article Materials and Resources webpage .
 Electronics Tutorial article on R-2R DAC: https://www.electronics-tutorials.ws/combination/r-2r-dac.html
 Common waveform files from Lucid Technologies: https://www.lucidtechnologies.info/software.htm
 FGEN1 Kit from Lucid Technologies: https://www.lucidtechnologies.info/fgen1_11.htm
Microchip Technology | www.microchip.com
PUBLISHED IN CIRCUIT CELLAR MAGAZINE • JULY 2023 #396 – Get a PDF of the issueSponsor this Article
Brian Beard received his BSEE from the U.S. Air Force Academy in 1973, an MBA from UWF in 1989 and MS and PhD. degrees in biomedical engineering from Vanderbilt University in 1993 and 1995, respectively. He flew F-4 phantoms in southeast Asia, Europe and the USA. After flying, he worked for the Air Force designing test instrumentation at Eglin AFB in Florida. He designed analog computer systems, transistor and gate-level logic and worked with early 6800 and Z80 systems. He has been programming, mostly in assembly language, for over 40 years. He currently does medical device safety research at the FDA. One of these days he might actually retire and write more articles.