This example demonstrates how to use a Lookup Table (LUT) to generate a triangular waveform.
Before running this example, ensure you have:
- An ESP32 development board.
- ESP-IDF development environment set up (version 5.4 or later).
- Familiarity with the ESP-IDF DAC Continuous Signal Generator Example.
- An oscilloscope to visualize the waveform output.
-
Clone the repository:
git clone https://github.com/tinyalg/waveu.git
-
Change to this directory:
cd waveu/examples/triangularAlternatively, open this directory in VSCode with the ESP-IDF extension for easier navigation and editing.
-
Run menuconfig:
In VSCode, open the ESP-IDF Terminal.
idf.py menuconfig
In
menuconfig, configure the following settings under[Component config > Waveu Configuration]:- Select active DAC channels:
- CH0 and CH1: Output to both channels.
- CH0 only: Output to DAC Channel 0.
- CH1 only: Output to DAC Channel 1.
- Select active DAC channels:
-
Flash the example:
idf.py build flash
-
Monitor the output:
idf.py monitor
You should observe messages in the console confirming the start of waveform generation.
I (292) Waveu-ESP32Config: waveformDataOutputTask to on core 0 at priority 10. I (292) Waveu: LEN_DATA_BUFFER=16000, SAMPLE_RATE=1000000, TIMER_PERIOD=16000 I (292) Waveu: Started waveformDataGenerationTask on core 1 at priority 5. I (302) UserWaveConfig: Frequency set to 293.66Hz -
Connect your oscilloscope: Attach the oscilloscope to the DAC output channel as configured in
menuconfig.
The triangular function generates LUT values. It calculates values based on the current phase, amplitude, and offset:
virtual lut_type_t fill_function_triangular(LUTSize size, size_t index, float amplitude, float offset) {
...
// Generate the triangular value
float triangular_value = (phase < 0.5)
? (2 * phase) // Rising edge
: (2 * (1 - phase)); // Falling edge
// Scale and shift to amplitude and offset
return static_cast<lut_type_t>((2 * triangular_value - 1) * amplitude + offset + 0.5);
}The initialize() method sets up the PhaseGenerator and LUT index calculation.
Set the sample rate and create the phase generator.
initializePhaseGenerator(sampleRate);Retrieve a function to map the current phase to an LUT index.
_getIndex = LUTHelper::getIndexFunction(_lutSize);The configure() method prepares waveform generation.
Set the desired frequency and calculate the phase increment.
setPhaseGeneratorFrequency(waveArgs.frequency);Ensure the amplitude and offset values are valid for the DAC.
auto [adjustedAmplitude, adjustedOffset]
= LUTHelper::adjustAmplitudeAndOffset(waveArgs.amplitude, waveArgs.offset);Fill the LUT using the triangular function.
populateLUTs(adjustedAmplitude, adjustedOffset);The nextSample() method generates waveform samples dynamically.
Advance the phase to the next position.
_phaseGenerator->updatePhase();Get the current phase value from the phase generator.
uint32_t currentPhase = _phaseGenerator->getPhase();Calculate the corresponding LUT index.
int lutIndex = _getIndex(currentPhase, PhaseGenerator::N_BITS);Convert the phase or LUT index to a voltage value.
lut_type_t digi_val = GET_LUT_VALUE(_lut, lutIndex);Return the voltage value for output.
return (uint8_t)digi_val;- Configure the DAC output channel via
menuconfig. - Adjust frequency, amplitude, and offset in the
app_main()function to match your testing requirements. - This example demonstrates using an LUT for waveform generation and is ideal for learning signal generation techniques.