Realtime AM and FM Modulator Using DDS on ATmega328P

ATmega328PC/C++CommunicationsSignal Processing
GitHub

Project Overview

This project implements a real-time amplitude and frequency modulation system based on Direct Digital Synthesis (DDS) on an ATmega328P microcontroller. The system generates digitally synthesized carrier signals and performs AM or FM modulation on the message signal received from the built-in ADC, and produces an analog output via PWM followed by an analog reconstruction filter.

Pulse width is sampled at 20 kHz and is modulated at the required width to generate the desired analog signal. This output is then filtered using an RC low-pass filter with an adjustable cutoff frequency to remove high-frequency PWM component and construct the analog signal. Since the ATmega328P, a digital device, doesn’t have the capability of generating a negative voltage, the generated sine wave has a constant 2.5 V DC offset. The user can control the carrier frequency and the modulation type by a rotary encoder or UART interface. Feedback on the system’s current state is shown on a 16x2 LCD display.

Circuit schematic of the AM/FM modulator
Circuit schematic of the AM/FM modulator
AM/FM modulator device
AM/FM modulator device

System Implementation

Signal Generation with Direct Digital Synthesis

An interrupt timer set at 20 kHz is used to process the samples incoming from the ADC at 125 kHz sampling frequency. This interrupt service routine is also responsible for modifying a second PWM timer to produce the desired output signal. With the timer having a resolution of 8-bits, ADC is matched to this rate to eliminate the overhead of downsampling. With the internal sampling frequency and the filter design considered, the maximum carrier frequency is limited to 5 kHz.

The output signal is generated by traversing a 32-bit lookup table of sine wave values, indexed by a phase accumulator. The accumulated phase is the sum of the previous phase increment values, which is calculated based on the desired carrier frequency and the sampling frequency.

For amplitude modulation, variable in is simply set to the carrier frequency, and the output signal is scaled by the message signal from the ADC.

Time domain output of the AM signal with a 700 Hz carrier frequency
Time domain output of the AM signal (700 Hz carrier)
FFT of the AM signal with a 2.5 kHz carrier frequency
FFT of the AM signal (2.5 kHz carrier)

For frequency modulation, variable in is adjusted with respect to the message signal from the ADC. System has a fixed modulation index with 1 Hz / 2 mV. The ADC input is offset by 128 (2.5 V) and the frequency deviation is calculated at each ISR call, which then the phase increment is adjusted accordingly.

Time domain output of the FM signal with a 500 Hz carrier frequency
Time domain output of the FM signal (500 Hz carrier)
FFT of the FM signal with a 2.5 kHz carrier frequency
FFT of the FM signal (2.5 kHz carrier)

While significant noise and a number of spurs are visible in the frequency domain outputs, it is clear that the carrier and message signal components are the prominent features of the spectrum.

For efficient runtime, the sine LUT is stored in the EEPROM and loaded onto the RAM at boot time. If the EEPROM data is invalid or the generation routine is triggered, the LUT is regenerated and written to the EEPROM. For generating the LUT samples, a single sample is calculated and the rest is generated using trigonometric sum identities for efficient computation.

User Interface and Peripherals

For the smooth operation and control of the device, a robust peripheral routine that places sampling calls and exchanges information between UART and physical interfaces is implemented. Each registered update processes the command and updates global state variables that are used by the DDS and peripheral routines. This process handles different states covering boot, DDS parameter updates, invalid states, and user feedback.

A system counter matched to the sampling frequency is also implemented for scheduling tasks in a pseudo-OS manner. Different routines may be called with respect to this counter and time-critical peripheral events such as debouncing also relies on this counter. The LCD feedback system is implemented using a custom I2C library.