Mark Underwood / FunctionGenerator

Dependents:   MAX5719BOB_FunctionGen

Revision:
0:1c31998b91c6
diff -r 000000000000 -r 1c31998b91c6 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