Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: MAX5719BOB_FunctionGen
Diff: FunctionGenerator.h
- Revision:
- 0:1c31998b91c6
--- /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