Signal Generator

Dependencies:   IniManager RA8875 Watchdog mbed-rtos mbed

Fork of speaker_demo_Analog by jim hamblen

SignalGenDAC.cpp

Committer:
WiredHome
Date:
2017-05-20
Revision:
6:1f48212fbaf9
Parent:
5:49dd0c647a40

File content as of revision 6:1f48212fbaf9:

//
// Signal Generate DAC Driver
//
// Derived from AN10917: Memory to DAC data transfers using the LPC1700's DMA
// 

#include "SignalGenDAC.h"

#define PI 3.14159      // for the sine-wave

/// The linked list structure used to control the DMA transfer
///
typedef struct {
    uint32_t  source;     /// start of source area
    uint32_t  destination;/// start of destination area
    uint32_t  next;       /// address of next strLLI in chain
    uint32_t  control;    /// DMACCxControl register
} LinkListItem_t;    

/// The DACsignal memory, which is DMA sent to the DAC to play the waveform,
/// produced by scaling the VoltSignal array
///
float VoltSignal[SIGNAL_MEM_ENTRIES] __attribute__ ((section("AHBSRAM0")));
signed long DACsignal[SIGNAL_MEM_ENTRIES] __attribute__ ((section("AHBSRAM0")));

/// The linked list item record, used by the DMA engine to decide how to play
///
LinkListItem_t llio __attribute__ ((section("AHBSRAM0")));


SignalGenDAC::SignalGenDAC(PinName _aout, float _minV, float _maxV) :
    minV(_minV), maxV(_maxV) {
    aout = new AnalogOut(_aout);
}

SignalGenDAC::~SignalGenDAC() {
}

void SignalGenDAC::Start(bool oneShot) {
    printf("Start(%d) w/%d samples\r\n", oneShot ? 1 : 0, numSamples);
    isOn = (oneShot) ? false : true;
    
    llio.source = (uint32_t)DACsignal;
    llio.destination = (uint32_t)&LPC_DAC->DACR;
    llio.next = (uint32_t)&llio;
    llio.control = (1<<26) | (2<<21) | (2<<18) | numSamples;

    LPC_SC->PCONP |= (1<<29);

    /* Enable GPDMA  and sync logic */
    LPC_GPDMA->DMACConfig        = 1;
    LPC_GPDMA->DMACSync          = (1<<6); 

    /* Load DMA Channel0 */
    LPC_GPDMACH0->DMACCSrcAddr   = (uint32_t)DACsignal;
    LPC_GPDMACH0->DMACCDestAddr  = (uint32_t)&LPC_DAC->DACR;
    LPC_GPDMACH0->DMACCLLI       = (oneShot) ? 0 : (uint32_t)&llio;

    int playSampleCount = numSamples + oneShot;
    LPC_GPDMACH0->DMACCControl = playSampleCount  // transfer size (0 - 11) = 64
                  | (0 << 12)         // source burst size (12 - 14) = 1
                  | (0 << 15)         // destination burst size (15 - 17) = 1
                  | (2 << 18)         // source width (18 - 20) = 32 bit
                  | (2 << 21)         // destination width (21 - 23) = 32 bit
                  | (0 << 24)         // source AHB select (24) = AHB 0
                  | (0 << 25)         // destination AHB select (25) = AHB 0
                  | (1 << 26)         // source increment (26) = increment
                  | (0 << 27)         // destination increment (27) = no increment
                  | (0 << 28)         // mode select (28) = access in user mode
                  | (0 << 29)         // (29) = access not bufferable
                  | (0 << 30)         // (30) = access not cacheable
                  | (0 << 31);        // terminal count interrupt disabled

    LPC_GPDMACH0->DMACCConfig = 1
                  | (0 << 1)          // source peripheral (1 - 5) = none
                  | (7 << 6)          // destination peripheral (6 - 10) = DAC
                  | (1 << 11)         // flow control (11 - 13) = mem to per
                  | (0 << 14)         // (14) = mask out error interrupt
                  | (0 << 15)         // (15) = mask out terminal count interrupt
                  | (0 << 16)         // (16) = no locked transfers
                  | (0 << 18);        // (27) = no HALT

    /* DACclk = 25 MHz, so 10 usec interval */
    LPC_DAC->DACCNTVAL = 18;               // 16-bit reload value
    /* DMA, timer running, dbuff */
    LPC_DAC->DACCTRL = 
          1<<3          // DMA_ENA dma burst is enabled
        | 1<<2          // CNT_ENA Timeout couner is enabled
        | 1<<1;         // DBLBUF_ENA double-buffering enabled
}

void SignalGenDAC::Stop(void) {
    printf("Stop()\r\n");
    LPC_GPDMACH0->DMACCLLI       = 0;
    while (LPC_GPDMACH0->DMACCConfig & 1)
        wait_ms(1);
    aout->write(offset / maxV);
    isOn = false;
}


void SignalGenDAC::PrepareWaveform(SG_Waveform mode, float _frequency, float _dutycycle, float _voltage, float _offset) {
    int x, dcCount, firstQtr, lastQtr;
    frequency = _frequency;
    dutycycle = _dutycycle;
    voltage = _voltage;
    offset = _offset;
    float upp = rangelimit(offset + voltage/2, minV, maxV);
    float mid = rangelimit(offset, minV, maxV);
    float low = rangelimit(offset - voltage/2, minV, maxV);
    float v;
    numSamples = 128;    // Ideally, compute this based on the frequency for good resolution
    dcCount = dutycycle/100.0 * numSamples;
    firstQtr = dcCount / 2;
    lastQtr = dcCount + (numSamples - dcCount)/2;
    
    // Set the timebased based on the frequency
    if (isOn) {
        Stop();
    }
    printf("Generate wave for mode: %d\r\n", mode);
    switch (mode) {
        case SG_SINE:
            for (x=0; x<numSamples; x++) {
                if (x < dcCount) {
                    v = offset + voltage/2 * sin(x * 1 * PI / dcCount);
                } else {
                    v = offset - voltage/2 * sin((x - dcCount) * 1 * PI / (numSamples-dcCount));
                }
                v = rangelimit(v, minV, maxV);
                VoltSignal[x] = v;
            }
            VoltSignal[numSamples] = rangelimit(offset, minV, maxV);
            break;
        case SG_SQUARE:
            for (x=0; x<numSamples; x++) {
                if (0 && x == 0) {
                    v = rangelimit(offset, minV, maxV);                    
                } else if (x < dcCount) {
                    v = rangelimit(offset + voltage/2, minV, maxV);
                } else {
                    v = rangelimit(offset - voltage/2, minV, maxV);
                }
                VoltSignal[x] = v;
            }
            VoltSignal[numSamples] = rangelimit(offset, minV, maxV);
            break;
        case SG_TRIANGLE:
            for (x=0; x<numSamples; x++) {
                if (x < firstQtr) {
                    v = voltage/2 * (float)x/(firstQtr);
                    v += offset;
                } else if (x < dcCount) {
                    v = voltage/2 * (float)x/(firstQtr);
                    v = voltage - (v - offset);
                } else if (x < lastQtr) {
                    v = voltage * (float)(x - dcCount)/(numSamples - dcCount);
                    v = offset - v;
                } else {
                    v = voltage * (float)(x - dcCount)/(numSamples - dcCount);
                    v = v + offset - voltage;
                }
                VoltSignal[x] = v;
            }
            VoltSignal[numSamples] = rangelimit(offset, minV, maxV);
            break;
        case SG_SAWTOOTH:
            for (x=0; x<numSamples; x++) {
                if (x < dcCount) {
                    v = offset - voltage/2 + (float)x/dcCount * voltage/2;
                } else {
                    v = offset + (float)(x - dcCount)/(numSamples - dcCount) *  voltage/2;
                }
                v = rangelimit(v, minV, maxV);
                VoltSignal[x] = v;
            }
            VoltSignal[numSamples] = rangelimit(offset, minV, maxV);
            break;
        case SG_USER:
            break;
    }
    //printf("DAC Data %3.2f %3.2f\r\n", voltage, offset);
    for (x=0; x<=numSamples; x++) {
        DACsignal[x] = ((uint16_t)(VoltSignal[x]/maxV * 1023) << 6);
        printf("%3d, %5.3f, %d\r\n", x, VoltSignal[x], DACsignal[x]);
    }
    if (!isOn) {
        aout->write(offset / maxV);
    }
}

float SignalGenDAC::rangelimit(float value, float min, float max) {
    if (value < min)
        return min;
    else if (value > max)
        return max;
    else
        return value;
}