Provides a simple way to generate complex square wave signals on any available pin. In addition the SignalGenerator can generate a carrier wave which is useful when generating IR signals to control electronic devices like a TV etc. The signal generation can be carried out either synchronously or asynchronously. In the case of synchronous signal generation all interrupts can optionally be disabled to improve timing accuracy.

SignalGenerator.cpp

Committer:
taylorza
Date:
2014-09-14
Revision:
4:64d2d834341b
Parent:
3:f30dcc6e8e70

File content as of revision 4:64d2d834341b:

///////////////////////////////////////////////////////////////////////////////
// Signal Generator
// Author: Chris Taylor (taylorza)
#include "mbed.h"
#include "SignalGenerator.h"

SignalGenerator::SignalGenerator(PinName pin) 
    : _pin(pin),
      _pTicker(NULL), 
      _pInternalTimingBuffer(NULL)
{
    
}

SignalGenerator::~SignalGenerator()
{
    stopAsync();
    
    if (_pTicker != NULL) 
    {
        delete _pTicker;
    }
}
          
void SignalGenerator::set(bool pinState)
{
    stopAsync();
    _pin = pinState ? 1 : 0;
}

void SignalGenerator::set(
    bool initialState, 
    uint32_t timingBuffer[], 
    uint16_t bufferCount, 
    bool disableInterrupts,
    uint32_t lastStateHoldTime, 
    int32_t carrierFrequency)
{
    stopAsync();
    
    if (timingBuffer == NULL || bufferCount == 0)
    {
        return;
    }
    
    int alreadyDisabledIrq = 1;    
    if (disableInterrupts)
    {
        alreadyDisabledIrq = __disable_irq();
    }
    
    Timer transitionTimer;
    uint16_t lastTiming = bufferCount - 1;
    
    if (carrierFrequency > 0)
    {        
        int timingIndex = 0;                     
        bool pinState = initialState;
        bool carrierState = true;        
        uint32_t carrierHalfPeriod = (500000 / carrierFrequency);
        uint32_t compare = timingBuffer[timingIndex++];
        uint32_t carrierCompare = carrierHalfPeriod;       
                
        transitionTimer.start();
        while(true)
        {    
            bool stateChange = false;
            if (transitionTimer.read_us() >= compare)
            {                
                pinState = !pinState;                  
                stateChange = true;                                              
                if (timingIndex == lastTiming) break;
                compare += timingBuffer[timingIndex++];                
            }
            
            if (transitionTimer.read_us() >= carrierCompare)
            {
                carrierState = !carrierState;                
                carrierCompare += carrierHalfPeriod;                
                stateChange = true;
            }
            
            if (stateChange) _pin = pinState & carrierState ? 1 : 0;
        }

        if (lastStateHoldTime > 0)
        {
            compare += lastStateHoldTime;
            while (transitionTimer.read_us() < compare)
            {
                if (transitionTimer.read_us() >= carrierCompare)
                {
                    carrierState = !carrierState;
                    carrierCompare += carrierHalfPeriod;
                    _pin = pinState & carrierState ? 1 : 0;
                }                    
            }
        }                            
    }
    else
    {   
        int timingIndex = 0;                     
        uint32_t compare = timingBuffer[timingIndex++];
        
        transitionTimer.start();              
        _pin = initialState ? 1 : 0;                
        while(true)
        {                     
            if (transitionTimer.read_us() >= compare)
            {   
                _pin = !_pin;                                                             
                if (timingIndex == lastTiming) break;
                compare += timingBuffer[timingIndex++];
            }
        }
        if (lastStateHoldTime > 0)
        {
            compare += lastStateHoldTime;
            while (transitionTimer.read_us() < compare)
            {
                ;
            }
        }        
    }  
    
    if (!alreadyDisabledIrq) __enable_irq();
}

void SignalGenerator::asyncStep()
{
    _pin = !_pin;
    
    _bufferIndex++;
    if (_bufferIndex < _bufferCount)
    {
        _pTicker->attach_us(this, &SignalGenerator::asyncStep, _pInternalTimingBuffer[_bufferIndex]);
    }
    else if (_repeat)
    {
        _bufferIndex = 0;
        _pTicker->attach_us(this, &SignalGenerator::asyncStep, _pInternalTimingBuffer[_bufferIndex]);        
    }
    else
    {
        stopAsync();
    }
}

void SignalGenerator::setAsync(
        bool initialState, 
        uint32_t timingBuffer[], 
        uint16_t bufferCount, 
        bool repeat)
{
    stopAsync();
    
    if (_pTicker == NULL)
    {
        _pTicker = new Timeout;
    }
    
    _pInternalTimingBuffer = new uint32_t[bufferCount];
    _bufferCount = bufferCount;
    _repeat = repeat;
    _bufferIndex = 0;
    
    for(int i = 0; i < bufferCount; ++i)
    {
        _pInternalTimingBuffer[i] =  timingBuffer[i] > 15 ? timingBuffer[i] - 10 : 5;
    }
        
    _pin = initialState ? 1 : 0;    
    _pTicker->attach_us(this, &SignalGenerator::asyncStep, _pInternalTimingBuffer[_bufferIndex]);
}

void SignalGenerator::stopAsync()
{
    if (_pTicker != NULL)
    {
        _pTicker->detach(); 
    }
    
    if (_pInternalTimingBuffer != NULL)
    {
        delete[] _pInternalTimingBuffer;
    }
}