DACFunctionGenerator class is a Function Generator for DAC output
Dependents: MAX5719BOB_FunctionGen
Revision 0:1c31998b91c6, committed 2021-01-03
- Comitter:
- whismanoid
- Date:
- Sun Jan 03 06:15:34 2021 +0000
- Commit message:
- Initial commit class DACFunctionGenerator
Changed in this revision
FunctionGenerator.cpp | Show annotated file Show diff for this revision Revisions of this file |
FunctionGenerator.h | Show annotated file Show diff for this revision Revisions of this file |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FunctionGenerator.cpp Sun Jan 03 06:15:34 2021 +0000 @@ -0,0 +1,3 @@ +#include "FunctionGenerator.h" + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FunctionGenerator.h Sun Jan 03 06:15:34 2021 +0000 @@ -0,0 +1,457 @@ +// /******************************************************************************* +// * Copyright (C) 2021 Maxim Integrated Products, Inc., All Rights Reserved. +// * +// * Permission is hereby granted, free of charge, to any person obtaining a +// * copy of this software and associated documentation files (the "Software"), +// * to deal in the Software without restriction, including without limitation +// * the rights to use, copy, modify, merge, publish, distribute, sublicense, +// * and/or sell copies of the Software, and to permit persons to whom the +// * Software is furnished to do so, subject to the following conditions: +// * +// * The above copyright notice and this permission notice shall be included +// * in all copies or substantial portions of the Software. +// * +// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES +// * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// * OTHER DEALINGS IN THE SOFTWARE. +// * +// * Except as contained in this notice, the name of Maxim Integrated +// * Products, Inc. shall not be used except as stated in the Maxim Integrated +// * Products, Inc. Branding Policy. +// * +// * The mere transfer of this software does not imply any licenses +// * of trade secrets, proprietary technology, copyrights, patents, +// * trademarks, maskwork rights, or any other form of intellectual +// * property whatsoever. Maxim Integrated Products, Inc. retains all +// * ownership rights. +// ******************************************************************************* +// */ +// ********************************************************************* +// @file FunctionGenerator.h +// ********************************************************************* + +// Prevent multiple declaration +#ifndef __FunctionGenerator_H__ +#define __FunctionGenerator_H__ + +//-------------------------------------------------- +// Option to support Sine waveform; requires floating point +// +// Triangle Ramp: Ramp Up then Ramp Down +#ifndef USE_FunctionGenerator_TriangleRampUpDown +#define USE_FunctionGenerator_TriangleRampUpDown 0 +#endif // USE_FunctionGenerator_TriangleRampUpDown +// +// Ramp Up from m_code_limit_L to m_code_limit_H by m_code_increment +#ifndef USE_FunctionGenerator_RampUp +#define USE_FunctionGenerator_RampUp 0 +#endif // USE_FunctionGenerator_RampUp +// +// Ramp Down from m_code_limit_H to m_code_limit_L by m_code_increment +#ifndef USE_FunctionGenerator_RampDown +#define USE_FunctionGenerator_RampDown 0 +#endif // USE_FunctionGenerator_RampDown +// +// Computed Sinusoid (Sine wave) m_code_amplitude * sin(m_phase_accumulator) + m_code_offset; m_phase_increment determines frequency +#ifndef USE_FunctionGenerator_Sine +#define USE_FunctionGenerator_Sine 0 +#endif // USE_FunctionGenerator_Sine +// +// Arbitrary Waveform Generation by table lookup; m_table_index_increment determines frequency +#ifndef USE_FunctionGenerator_Table +#define USE_FunctionGenerator_Table 0 +#endif // USE_FunctionGenerator_Table +// +// Sinusoid using Arbitrary Waveform Generation table lookup +#ifndef USE_FunctionGenerator_Sine_Table +#define USE_FunctionGenerator_Sine_Table 1 +#endif // USE_FunctionGenerator_Sine_Table +// + +// standard include for target platform +#include "mbed.h" +// workaround for error 'M_PI' was not declared +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/** + @brief DACFunctionGenerator class is a Function Generator for DAC output + */ +// +class DACFunctionGenerator +{ +public: + enum shape_t { + // Constant DC output + Constant = 0, + // +#if USE_FunctionGenerator_TriangleRampUpDown + // Triangle Ramp: Ramp Up then Ramp Down + TriangleRampUpDown, +#endif // USE_FunctionGenerator_TriangleRampUpDown + // +#if USE_FunctionGenerator_RampUp + // Ramp Up from m_code_limit_L to m_code_limit_H by m_code_increment + RampUp, +#endif // USE_FunctionGenerator_RampUp + // +#if USE_FunctionGenerator_RampDown + // Ramp Down from m_code_limit_H to m_code_limit_L by m_code_increment + RampDown, +#endif // USE_FunctionGenerator_RampDown + // +#if USE_FunctionGenerator_Sine + // Computed Sinusoid (Sine wave) m_code_amplitude * sin(m_phase_accumulator) + m_code_offset; m_phase_increment determines frequency + Sine, +#endif // USE_FunctionGenerator_Sine + // +#if (USE_FunctionGenerator_Table) || (USE_FunctionGenerator_Sine_Table) + // Arbitrary Waveform Generation by table lookup; m_table_index_increment determines frequency + Table, +#endif // USE_FunctionGenerator_Table + // + } m_shape; //!< shape of the generated waveform +private: + uint32_t m_code; //!< DAC output code value (unsigned value) +public: + uint32_t m_code_limit_H; //!< high limit of code (unsigned value) + uint32_t m_code_limit_L; //!< low limit of code (unsigned value) +#if (USE_FunctionGenerator_TriangleRampUpDown) || (USE_FunctionGenerator_RampUp) || (USE_FunctionGenerator_RampDown) + int32_t m_code_increment; //!< amount to be added to code (signed value) (Ramp) +#endif +#if (USE_FunctionGenerator_Sine) || (USE_FunctionGenerator_Sine_Table) + uint32_t m_code_offset; //!< (Sine) + uint32_t m_code_amplitude; //!< (Sine) +#endif // USE_FunctionGenerator_Sine +#if (USE_FunctionGenerator_Sine) + float m_phase_accumulator; //!< phase angle in radians (Sine) + float m_phase_increment; //!< increment in phase angle in radians, per sample period (Sine) + const float m_phase_accumulator_limit_H = (2 * M_PI); //!< high limit of phase angle in radians (Sine) +#endif // USE_FunctionGenerator_Sine +#if (USE_FunctionGenerator_Table) || (USE_FunctionGenerator_Sine_Table) + uint32_t* m_table_data; //!< (Table) + uint32_t m_table_index; //!< (Table) + int32_t m_table_index_increment; //!< (Table) + uint32_t m_table_length; //!< (Table) +#endif // USE_FunctionGenerator_Table +public: + DACFunctionGenerator() //!< #ctor + { // ............0xFFFFF for 20-bit + m_code_limit_H = 0xFFFFF; + m_code_limit_L = 0x00000; +#if (USE_FunctionGenerator_TriangleRampUpDown) || (USE_FunctionGenerator_RampUp) || (USE_FunctionGenerator_RampDown) + m_code_increment = 0x00001; +#endif + m_code = (m_code_limit_L/4 + m_code_limit_H/4) * 2; // approx (code_limit_H+code_limit_L)/2 but avoid overflow +#if USE_FunctionGenerator_TriangleRampUpDown + m_shape = TriangleRampUpDown; +#else // USE_FunctionGenerator_TriangleRampUpDown + m_shape = Constant; +#endif // USE_FunctionGenerator_TriangleRampUpDown + }; + void Configure_Constant(uint32_t code = 0x5555) + { + m_code = code; + m_shape = Constant; + } +#if USE_FunctionGenerator_TriangleRampUpDown + void Configure_TriangleRampUpDown( + uint32_t code_limit_L = 0x00000, + uint32_t code_limit_H = 0xFFFFF, + int32_t increment = 0x00001 + ) + { + m_code_limit_H = code_limit_H; + m_code_limit_L = code_limit_L; + m_code_increment = increment; + m_code = (m_code_limit_L/4 + m_code_limit_H/4) * 2; // approx (code_limit_H+code_limit_L)/2 but avoid overflow + m_shape = TriangleRampUpDown; + } +#endif // USE_FunctionGenerator_TriangleRampUpDown +#if USE_FunctionGenerator_RampUp + void Configure_RampUp( + uint32_t code_limit_L = 0x00000, + uint32_t code_limit_H = 0xFFFFF, + int32_t increment = 0x00001 + ) + { + m_code_limit_H = code_limit_H; + m_code_limit_L = code_limit_L; + m_code_increment = increment; + m_code = code_limit_L; + m_shape = RampUp; + } +#endif // USE_FunctionGenerator_RampUp +#if USE_FunctionGenerator_RampDown + void Configure_RampDown( + uint32_t code_limit_L = 0x00000, + uint32_t code_limit_H = 0xFFFFF, + int32_t increment = 0x00001 + ) + { + m_code_limit_H = code_limit_H; + m_code_limit_L = code_limit_L; + m_code_increment = increment; + m_code = code_limit_H; + m_shape = RampDown; + } +#endif // USE_FunctionGenerator_RampDown +#if USE_FunctionGenerator_Sine + void Configure_Sine( // TODO WIP Sine + uint32_t code_limit_L = 0x00000, + uint32_t code_limit_H = 0xFFFFF, + float num_samples_per_tone_cycle = 10.0 + ) + { + m_code_limit_H = code_limit_H; + m_code_limit_L = code_limit_L; + //~ m_code_increment = increment; + m_code = (m_code_limit_L/4 + m_code_limit_H/4) * 2; // approx (code_limit_H+code_limit_L)/2 but avoid overflow + m_code_offset = (m_code_limit_L/4 + m_code_limit_H/4) * 2; // approx (code_limit_H+code_limit_L)/2 but avoid overflow + m_code_amplitude = m_code_limit_H - m_code_offset; + m_phase_accumulator = 0; + //~ m_phase_accumulator_limit_H = (2 * M_PI); + m_phase_increment = m_phase_accumulator_limit_H / num_samples_per_tone_cycle; + //~ m_phase_increment = (2 * M_PI) / num_samples_per_tone_cycle; + m_shape = Sine; + } +#endif // USE_FunctionGenerator_Sine +#if USE_FunctionGenerator_Table + void Configure_Table( // TODO WIP Table + uint32_t* table_data, + uint32_t table_length, + int32_t increment = 0x00001 + ) + { + m_table_data = table_data; + m_table_length = table_length; + m_code = table_data[0]; + //~ m_phase_accumulator = 0; + //~ m_phase_increment = (2 * M_PI) / num_samples_per_tone_cycle; + m_table_index_increment = increment; + m_shape = Table; + } +#endif // USE_FunctionGenerator_Table +#if USE_FunctionGenerator_Sine_Table + // TODO: implement Configure_Sine_Table(uint32_t code_limit_L, uint32_t code_limit_H, float num_samples_per_tone_cycle, uint32_t* table_data_out, uint32_t table_length) + void Configure_Sine_Table( + uint32_t* table_data_buffer, + uint32_t table_length, + uint32_t code_limit_L = 0x00000, + uint32_t code_limit_H = 0xFFFFF, + float num_tone_cycles_in_table = 7 + ) + { + m_code_limit_H = code_limit_H; + m_code_limit_L = code_limit_L; + //~ m_code_increment = increment; + m_code = (m_code_limit_L/4 + m_code_limit_H/4) * 2; // approx (code_limit_H+code_limit_L)/2 but avoid overflow + m_code_offset = (m_code_limit_L/4 + m_code_limit_H/4) * 2; // approx (code_limit_H+code_limit_L)/2 but avoid overflow + m_code_amplitude = m_code_limit_H - m_code_offset; + m_table_data = table_data_buffer; + m_table_length = table_length; + // + // DIAGNOSTIC: initialize table to midscale + //~ for (m_table_index = 0; m_table_index < m_table_length; m_table_index++) + //~ { + //~ m_table_data[m_table_index] = m_code_offset - 0.75 * m_code_amplitude; + //~ } + //~ m_code_amplitude = 0.5 * m_code_amplitude; // DIAGNOSTIC + //~ m_table_data[0] = m_code_limit_L; // DIAGNOSTIC + //~ m_table_data[1] = m_code_limit_H; + //~ m_table_data[3] = m_code_limit_L; + //~ m_table_data[m_table_length-1] = m_code_limit_H; + // + // write a sine wave table into caller-provided buffer table_data_buffer[table_length] + // float num_tone_cycles_in_table = 7; // num_samples_per_tone_cycle; // ???? + // TODO: is m_table_data[m_table_length-1] not being initialized? + for (m_table_index = 0; m_table_index < /* DIAGNOSTIC */ m_table_length+0; m_table_index++) + { + // calculate phase angle from m_table_index + // m_table_index = m_table_length * m_phase_accumulator / (2 * M_PI); + // m_phase_accumulator = m_phase_accumulator + m_phase_increment + // m_phase_increment = m_phase_accumulator_limit_H / num_samples_per_tone_cycle; + float phase_accumulator = num_tone_cycles_in_table * ((((float)m_table_index + 0.5) / (float)m_table_length) * (2.0 * M_PI)); + // + // calculate code from sine(phase angle) + float code = m_code_amplitude * sin(phase_accumulator) + m_code_offset; + m_code = code; + if (code > m_code_limit_H) { + m_code = m_code_limit_H; + } + if (code < m_code_limit_L) { + m_code = m_code_limit_L; + } + //~ m_code = m_code_offset - m_code_amplitude + m_table_index * 0.1 * m_code_amplitude; // DIAGNOSITC override sine with a ramp + // + // store code into table_data[m_table_index] + m_table_data[m_table_index] = m_code; + // + } + // + m_table_index = 0; + m_code = m_table_data[m_table_index]; + //~ m_phase_accumulator = 0; + //~ m_phase_increment = (2 * M_PI) / num_samples_per_tone_cycle; + m_table_index_increment = 1; + m_shape = Table; + } +#endif // USE_FunctionGenerator_Sine_Table +public: + uint32_t Code() const { return m_code; }; //!< get DAC output code value +public: + uint32_t Next() //!< determine next code value + { + switch(m_shape) + { + // Constant DC output + case Constant: + break; +#if USE_FunctionGenerator_TriangleRampUpDown + // Triangle Ramp: Ramp Up then Ramp Down + case TriangleRampUpDown: + if (m_code_increment >= 0) { + // increment is positive or zero: rising ramp + // avoid (code + increment) overflow maxint + // avoid (code + increment) > code_limit_H + if ((m_code + m_code_increment) < m_code) { // arithmetic overflow + m_code_increment = -m_code_increment; // change the slope + m_code = m_code + m_code_increment; // note: increment is negative + } + else if ((m_code + m_code_increment) > m_code_limit_H) { + m_code_increment = -m_code_increment; // change the slope + m_code = m_code + m_code_increment; // note: increment is negative + } + else { + m_code = m_code + m_code_increment; // note: increment is positive + } + } + else { + // increment is negative: falling ramp + // avoid (code + increment) underflow minint + // avoid (code + increment) < code_limit_L which might be 0U + if (m_code < (m_code + m_code_increment)) { // arithmetic underflow + m_code_increment = -m_code_increment; // change the slope + m_code = m_code + m_code_increment; // note: increment is positive + } + else if (m_code_limit_L > (m_code + m_code_increment)) { + m_code_increment = -m_code_increment; // change the slope + m_code = m_code + m_code_increment; // note: increment is positive + } + else { + m_code = m_code + m_code_increment; // note: increment is negative + } + } + break; // case TriangleRampUpDown +#endif // TriangleRampUpDown +#if USE_FunctionGenerator_RampUp + // Ramp Up from m_code_limit_L to m_code_limit_H by m_code_increment + case RampUp: + // increment must be positive or zero: rising ramp + if (m_code_increment < 0) { + m_code_increment = -m_code_increment; // change the slope + } + // increment is positive or zero: rising ramp + // avoid (code + increment) overflow maxint + // avoid (code + increment) > code_limit_H + if ((m_code + m_code_increment) < m_code) { // arithmetic overflow + m_code = m_code + m_code_increment; // note: increment is negative + } + else if ((m_code + m_code_increment) > m_code_limit_H) { + m_code = m_code_limit_L; + } + else { + m_code = m_code + m_code_increment; // note: increment is positive + } + break; // case RampUp +#endif // USE_FunctionGenerator_RampUp +#if USE_FunctionGenerator_RampDown + // Ramp Down from m_code_limit_H to m_code_limit_L by m_code_increment + case RampDown: + // increment must be negative: falling ramp + if (m_code_increment >= 0) { + m_code_increment = -m_code_increment; // change the slope + } + // increment is negative: falling ramp + // avoid (code + increment) underflow minint + // avoid (code + increment) < code_limit_L which might be 0U + if (m_code < (m_code + m_code_increment)) { // arithmetic underflow + m_code = m_code + m_code_increment; // note: increment is positive + } + else if (m_code_limit_L > (m_code + m_code_increment)) { + m_code = m_code_limit_H; + } + else { + m_code = m_code + m_code_increment; // note: increment is negative + } + break; // case RampDown +#endif // RampDown +#if USE_FunctionGenerator_Sine + // Computed Sinusoid (Sine wave) m_code_amplitude * sin(m_phase_accumulator) + m_code_offset; m_phase_increment determines frequency + case Sine: + { + // + // DIAGNOSTIC: scope trigger + // ARDUINO + //~ pinMode (8, OUTPUT); + //~ digitalWrite(8, 0); // output logic low + // ARDUINO + // + // DIAGNOSTIC: this line is likely compute heavy on soft float systems + // ARDUINO UNO 16MHz: this line takes 132.0 to 154.0us, limiting sample update rate + float code = m_code_amplitude * sin(m_phase_accumulator) + m_code_offset; + // + // DIAGNOSTIC: scope trigger + // ARDUINO + //~ pinMode (8, OUTPUT); + //~ digitalWrite(8, 1); // output logic high -- initial value in constructor + // ARDUINO + // + m_code = code; + if (code > m_code_limit_H) { + m_code = m_code_limit_H; + } + if (code < m_code_limit_L) { + m_code = m_code_limit_L; + } + m_phase_accumulator = m_phase_accumulator + m_phase_increment; + if (m_phase_accumulator > m_phase_accumulator_limit_H) { + m_phase_accumulator = m_phase_accumulator - m_phase_accumulator_limit_H; + } + } + break; // case Sine +#endif // USE_FunctionGenerator_Sine +#if (USE_FunctionGenerator_Table) || (USE_FunctionGenerator_Sine_Table) + // Arbitrary Waveform Generation by table lookup; m_phase_increment determines frequency + case Table: + if (m_table_index >= m_table_length) { + m_table_index = m_table_length; + } + m_code = m_table_data[m_table_index]; + //~ m_phase_accumulator = m_phase_accumulator + m_phase_increment; + //~ if (m_phase_accumulator > m_phase_accumulator_limit_H) { + //~ m_phase_accumulator = m_phase_accumulator - m_phase_accumulator_limit_H; + //~ } + //~ m_table_index = m_table_length * m_phase_accumulator / m_phase_accumulator_limit_H; + m_table_index = m_table_index + m_table_index_increment; + // if (m_table_index < 0) { // m_table_index is unsigned so no underflow + // m_table_index = 0; + // } + if (m_table_index >= m_table_length) { + m_table_index = 0; // wrap to 0, assuming increment is 1 + } + break; // case Table +#endif // USE_FunctionGenerator_Table + } // switch(shape) + return m_code; + }; +}; + +#endif // __FunctionGenerator_H__ + +// End of file