Developer Home Contents Search Contact Us Support Intel(r)

Generating Sine Waves With The 80c196kc/Kd's Pwm Generator

(#3351) Generating Sine Waves With The 80c196kc/Kd's Pwm Generator


The 80C196KC family's on-chip peripheral set includes a Pulse Width Modulator (PWM) for generating a pulse train of varying width, which can be filtered to generate analog voltages. This can be used to generate sine waves and even more complex waveforms by periodically changing the PWM value.

Note that different members of the 8096 family may have 1, 2, or 3 PWM generators, and may implement them in different manners. This application note is specific to the PWM generator in the 8096 and 80C196KC/KD families of parts, not the HSI/O module's PWM mode, or the PWM generator of the 80C196MC, although similar applications may be possible with these parts.

This article describes the software and hardware required to generate sine waves, including examples using interrupts only, and examples using the PTS (Peripheral Transaction Server) for comparison. Several example files are available in a zipped file, SINGEN96.exe, which is available in the MCS-96/Tools category on the World Wide Web.

Some of the possible uses for this technique include alarm generation, DTMF or other signaling tones for telephony, multi-part music generation, sound effects, motor drive, or precision sine-wave generation.


This technique is based on a frequency synthesis technique known as Direct Digital Synthesis (DDS). In DDS, a counter is used to step through a sine look-up table, which is then converted to an analog voltage. However, the counter can count by any increment, including fractional, and is called a phase accumulator.

In this program, the phase accumulator is a 16-bit value called PHASE_ACC. For each output sample, it has the value in PHASE_INC added to it, and the most-significant byte is used as an index into the 256 entry sine table. This value is then output to the PWM channel, which is updated on the next full cycle. This value could be multiplied by a gain factor for amplitude control.

These variables can be regarded as having an integer portion and a fractional portion thusly: iiiiiiii.ffffffff, where the integer portion is in increments of 1/256 of a circle. This integer portion is used to index into the sine table.

The sine table was generated by the program SINTAB.BAS which generated the file SINTAB.A51, which was then merged into the source file (and edited to change the DB statements to DCBs). The PWM can be programmed for a frequency of either Fxtal/512 or Fxtal/1024. The examples presented here run the PWM in its fast mode, to give a higher upper frequency limit.

The periodic interrupts were generated using the '196's HSIO timer function. In order to gauge the performance of the PTS, several different programs were written, including one with the calculations in a loop, and several with calculations unrolled to eliminate loop overhead. Due to the speed of the PWM output, only about 3 samples can be calculated between interrupts. This is not a large enough queue to overcome the extra overhead of using the PTS, and so for longer queues it was necessary to enable the PTS interrupts while still in the interrupt service routine, thereby emptying the queue while it is being filled.


To yield a sine wave, the PWM's pulse output must be run through a lowpass filter. A simple RC filter may be adequate for non-critical applications, but an active filter is generally preferable, due to its flatter response and sharper cutoff. Figure 1 shows one possible second order active filter. Two of these cascaded generally gives adequate performance.

Another possibility is a monolithic switched-cap filter, such as the Maxim MAX290 family of 8th-order lowpass filters, or the Intel 2912A switched-cap CODEC (telecommunications) filter.

The maximum output frequency is limited to about 1/4 of the PWM sample frequency due to large, low frequency modulation components which are difficult to filter out. Therefore the corner frequency of the filter should be set at 1/4 to 1/5 of the PWM frequency.

All code was run, and measurements were made, on an ICE196KDHX running with a 12-MHz crystal.


Several example files are included in the zipped file, SINGEN96.exe, which is available in the MCS-96/Tools category on the on the World Wide Web, these include:

PWM.A96straight interrupt driven routine
DTMF.A96two tone version of above
PWM_PTS1.A963 sample queue, looped instructions
PWM_PTS3.A963 sample queue, inline instructions
PWM_PTS4.A968 samples, inline, PTS active during interrupt
PWM_PTS5.A9616 samples, looped, PTS active during interrupt
SINTAB.BASused to generate the sine table
VECTORS.A96Include file defining interrupt vectors

The following is a code fragment from PWM.A96 which demonstrates the technique of generating a single sine wave.

; xorb ioport1,#01h ; debug instruction.
add phase_acc,phase_inc
ldb indx,phase_acc+1
ldb pwm_control,SinTab[indx]

Specifications and Performance

An 80C196 running at 12 MHz will have a maximum PWM frequency of 12 MHz/512, or 23437.5 Hz, producing a pulse every 42.6 uS. This is sufficient to generate sine waves of 4 to 6 KHz. Running at 16 MHz or 20 MHz will of course allow higher frequencies to be generated.

The output frequency, Fo, is calculated from the sample frequency Fs, and the value of PHASE_INC by:

Fo = Fs * ( Phase_inc / 65536 )

where 65536 = the maximum value of Phase_Acc + 1


Phase_inc = Fo * ( 65536 / Fs )

for a 16-MHz crystal, this reduces to:

Phase_inc = Fo * 2.09715

for 12 MHz:

Phase_inc = Fo * 2.7962

for 20 MHz:

Phase_inc = Fo * 1.6777

The time spent in the interrupt is shown in Table 1 for various configurations. This time was measured by how long it took for 10000 iterations of the Interrupt Service Routine (ISR). For the PTS, the time spent in the ISR is only part of the total time used in servicing the PWM, so another method must be used to calculate total overhead.

The PTS is a hardwired Interrupt/DMA controller which relieves the processor of the task of servicing routine peripheral device interrupts. It will perform a simple I/O operation on each interrupt, stealing processor cycles to do so, and generate an end-of-PTS interrupt, which will be serviced in software, when its data is exhausted.

The foreground time was measured by how long it took the microcontroller to execute 50000 empty loops in the foreground. The shorter the time, the less time is being used by the background tasks (PTS and ISR). The table shows that for small numbers of samples in the PTS queue, the straight interrupt approach out-performs the PTS. This is due partly to the extra processing needed to maintain a queue of output values for the PTS, and partly due to the cycles stolen by the PTS to do its thing. Most of the examples place the PTS queue in external RAM, where its operation is visible to the ICE system trace function, although this slows operation down a bit.

All times were measured with the ICE196KDHX's event timer function.

 Table 1.
 Interrupt Foreground % Available 
 Time Time to Foreground
 PWM Foreground only .05832 Sec 100%
 PWM 8.835 uS .07983 Sec 73.0%
 DTMF 12.0 uS .08866 Sec 65.7%
 PWM_PTS1 32.3 .09517 Sec 61.3%
 PWM_PTS3 27.66 .08563 Sec 68.1%
 PWM_PTS4 54.75 .07674 Sec 76.0%
 PWM_4I* 50.50 .07450 Sec 78.3%
 PWM_PTS5 67.18 .08065 Sec 72.3%
 PWM_PTS5** 119.9 .07788 Sec 74.9%

  • Same as PWM_PTS4 but with queue in register RAM rather than external RAM, for improved speed.
  • * With 16-byte queue instead of 8 bytes.

    Notice that the foreground time for the PTS version doesn't fall below (improve upon) the plain interrupt routine until we get to around an 8-byte queue for the inline version, or 16 bytes for the looped version. Clearly, this is one time when the PTS cannot be assumed to offer better performance than the straight interrupt driven method.

    U1 is an LM6482 or similar CMOS op-amp with rail-to-rail input and output range on a single +5V power supply. This will allow a sine wave output of 0.0 to 5.0V. For different corner frequencies, scale the resistors and/or capacitors accordingly. The component values shown are for a Butterworth (flattest amplitude) response curve.


    The 80C196KC/KD have 3 PWM outputs, other family members may have only one or 2. The 80C196KC/KD has enough power to generate 2 or even 3 sines on each of its 3 channels.

    The PTS is a powerful feature of the 'C196 family, but careful evaluation must be performed to assure that its use improves, rather than degrades, overall performance.


    Intel Embedded Microcontroller Handbook, order number 270645
    Intel Embedded Applications Handbook, order number 270648
    Intel Application Note AP-445
    8XC196KR Peripherals: A Users Point of View, order number 270873
    Intel Connectivity Handbook, order number 231658

    * Legal Information © 1999 Intel Corporation