Libary for PM2.

Dependencies:   LSM9DS1 RangeFinder FastPWM

Dependents:   PM2_Example_PES_board PM2_Example_PES_board PM2_Example_PES_board PM2_Example_PES_board ... more

SensorBar.cpp

Committer:
pmic
Date:
2022-06-22
Revision:
32:bb074bb17d39
Parent:
28:6b30640c9e2e

File content as of revision 32:bb074bb17d39:

#include "SensorBar.h"

const float SensorBar::TS = 0.004f;                       // period of 1 ms

const char REG_I_ON[16] = {REG_I_ON_0, REG_I_ON_1, REG_I_ON_2, REG_I_ON_3,
                           REG_I_ON_4, REG_I_ON_5, REG_I_ON_6, REG_I_ON_7,
                           REG_I_ON_8, REG_I_ON_9, REG_I_ON_10, REG_I_ON_11,
                           REG_I_ON_12, REG_I_ON_13, REG_I_ON_14, REG_I_ON_15
                          };

const char REG_T_ON[16] = {REG_T_ON_0, REG_T_ON_1, REG_T_ON_2, REG_T_ON_3,
                           REG_T_ON_4, REG_T_ON_5, REG_T_ON_6, REG_T_ON_7,
                           REG_T_ON_8, REG_T_ON_9, REG_T_ON_10, REG_T_ON_11,
                           REG_T_ON_12, REG_T_ON_13, REG_T_ON_14, REG_T_ON_15
                          };

const char REG_OFF[16] = {REG_OFF_0, REG_OFF_1, REG_OFF_2, REG_OFF_3,
                          REG_OFF_4, REG_OFF_5, REG_OFF_6, REG_OFF_7,
                          REG_OFF_8, REG_OFF_9, REG_OFF_10, REG_OFF_11,
                          REG_OFF_12, REG_OFF_13, REG_OFF_14, REG_OFF_15
                         };

const char REG_T_RISE[16] = {0xFF, 0xFF, 0xFF, 0xFF,
                             REG_T_RISE_4, REG_T_RISE_5, REG_T_RISE_6, REG_T_RISE_7,
                             0xFF, 0xFF, 0xFF, 0xFF,
                             REG_T_RISE_12, REG_T_RISE_13, REG_T_RISE_14, REG_T_RISE_15
                            };

const char REG_T_FALL[16] = {0xFF, 0xFF, 0xFF, 0xFF,
                             REG_T_FALL_4, REG_T_FALL_5, REG_T_FALL_6, REG_T_FALL_7,
                             0xFF, 0xFF, 0xFF, 0xFF,
                             REG_T_FALL_12, REG_T_FALL_13, REG_T_FALL_14, REG_T_FALL_15
                            };

SensorBar::SensorBar(I2C& i2c, float distAxisToSensor) : i2c(i2c), thread(osPriorityAboveNormal, 4096)
{
    // Store the received parameters into member variables
    deviceAddress = 0x3E<<1;
    pinInterrupt = 255;
    pinOscillator = 255;
    pinReset = 255;
    invertBits = 0;
    barStrobe = 0; //Default always on

    this->distAxisToSensor = distAxisToSensor;
    lastBarRawValue = lastBarPositionValue = 0;

    angle = avg_angle = 0;
    nrOfLedsActive = 0;
    avg_filter.setup(10);
    is_first_avg = true;

    clearBarStrobe();  // to illuminate all the time
    clearInvertBits(); // to make the bar look for a dark line on a reflective surface

    // set up thread
    if (begin()) {
        thread.start(callback(this, &SensorBar::update));
        ticker.attach(callback(this, &SensorBar::sendThreadFlag), std::chrono::microseconds{static_cast<long int>(1.0e6f * TS)});
    }
}

SensorBar::~SensorBar()
{
    ticker.detach();
}

// --- Functions pulled from the SX1509 driver

/*
void SensorBar::debounceConfig(uint8_t configValue)
{
    // First make sure clock is configured
    uint8_t tempuint8_t = readByte(REG_MISC);
    if ((tempuint8_t & 0x70) == 0) {
        tempuint8_t |= (1 << 4);    // Just default to no divider if not set
        writeByte(REG_MISC, tempuint8_t);
    }
    tempuint8_t = readByte(REG_CLOCK);
    if ((tempuint8_t & 0x60) == 0) {
        tempuint8_t |= (1 << 6);    // default to internal osc.
        writeByte(REG_CLOCK, tempuint8_t);
    }

    configValue &= 0b111; // 3-bit value
    writeByte(REG_DEBOUNCE_CONFIG, configValue);
}

void SensorBar::debounceEnable(uint8_t pin)
{
    unsigned int debounceEnable = readWord(REG_DEBOUNCE_ENABLE_B);
    debounceEnable |= (1 << pin);
    writeWord(REG_DEBOUNCE_ENABLE_B, debounceEnable);
}

unsigned int SensorBar::interruptSource(void)
{
    unsigned int intSource = readWord(REG_INTERRUPT_SOURCE_B);
    writeWord(REG_INTERRUPT_SOURCE_B, 0xFFFF);    // Clear interrupts
    return intSource;
}

void SensorBar::configClock(uint8_t oscSource, uint8_t oscPinFunction, uint8_t oscFreqOut, uint8_t oscDivider)
{
    // RegClock constructed as follows:
    //    6:5 - Oscillator frequency souce
    //        00: off, 01: external input, 10: internal 2MHz, 1: reserved
    //    4 - OSCIO pin function
    //        0: input, 1 ouptut
    //    3:0 - Frequency of oscout pin
    //        0: LOW, 0xF: high, else fOSCOUT = FoSC/(2^(RegClock[3:0]-1))
    oscSource = (oscSource & 0b11) << 5;      // 2-bit value, bits 6:5
    oscPinFunction = (oscPinFunction & 1) << 4;   // 1-bit value bit 4
    oscFreqOut = (oscFreqOut & 0b1111);   // 4-bit value, bits 3:0
    uint8_t regClock = oscSource | oscPinFunction | oscFreqOut;
    writeByte(REG_CLOCK, regClock);

    // Config RegMisc[6:4] with oscDivider
    // 0: off, else ClkX = fOSC / (2^(RegMisc[6:4] -1))
    oscDivider = (oscDivider & 0b111) << 4;   // 3-bit value, bits 6:4
    uint8_t regMisc = readByte(REG_MISC);
    regMisc &= ~(0b111 << 4);
    regMisc |= oscDivider;
    writeByte(REG_MISC, regMisc);
}
*/

//Call .setBarStrobing(); to only illuminate while reading line
void SensorBar::setBarStrobe()
{
    barStrobe = 1; //Do strobe
}

//Call .clearBarStrobing(); to illuminate all the time
void SensorBar::clearBarStrobe()
{
    barStrobe = 0; //Always on
}

// .setInvertBits(); to make the bar functions look for a white line on dark surface
void SensorBar::setInvertBits()
{
    invertBits = 1; //Do strobe
}

// .clearInvertBits(); to make the bar look for a dark line on a reflective surface
void SensorBar::clearInvertBits()
{
    invertBits = 0; //Always on
}

//****************************************************************************//
//
//  Bar functions
//
//****************************************************************************//

uint8_t SensorBar::getRaw()
{
    return lastBarRawValue;
}

int8_t SensorBar::getBinaryPosition()
{
    return -lastBarPositionValue;
}

float SensorBar::getAngleRad()
{
    return angle;
}

float SensorBar::getAvgAngleRad()
{
    return avg_angle;
}

uint8_t SensorBar::getNrOfLedsActive()
{
    return nrOfLedsActive;
}

bool SensorBar::isAnyLedActive()
{
    bool retval = false;
    if(nrOfLedsActive != 0) {
        retval =  true;
    }
    return retval;
}

//****************************************************************************//
//
//  Utilities
//
//****************************************************************************//

//Run this once during initialization to configure the SX1509 as a sensor bar
//Returns 1 for success
bool SensorBar::begin(void)
{
    bool returnVar = false;

    // Reset the SX1509
    reset();

    // Communication test. We'll read from two registers with different
    // default values to verify communication.
    unsigned int testRegisters = 0;
    testRegisters = readWord(REG_INTERRUPT_MASK_A);   // This should return 0xFF00
    // Then read a uint8_t that should be 0x00
    if (testRegisters == 0xFF00) {
        //Success!  Configure the device.
        writeByte(REG_DIR_A, 0xFF);
        writeByte(REG_DIR_B, 0xFC);
        writeByte(REG_DATA_B, 0x01);

        returnVar = true;
    }

    return returnVar;
}

// Do a software reset
void SensorBar::reset()
{
    // No hardware option, try software reset
    writeByte(REG_RESET, 0x12);
    writeByte(REG_RESET, 0x34);
}

// readByte(uint8_t registerAddress)
//  This function reads a single uint8_t located at the registerAddress register.
//  - deviceAddress should already be set by the constructor.
//  - Return value is the uint8_t read from registerAddress
//
//  Currently returns 0 if communication has timed out
//
uint8_t SensorBar::readByte(uint8_t registerAddress)
{
    char readValue;
    char data[2] = {registerAddress, 0};
    i2c.write(deviceAddress, data, 1);
    uint8_t val = i2c.read(deviceAddress, &readValue, 1);

    return readValue;
}

// readWord(uint8_t registerAddress)
//  This function will read a two-uint8_t word beginning at registerAddress
//  - A 16-bit unsigned int will be returned.
//      - The msb of the return value will contain the value read from registerAddress
//      - The lsb of the return value will contain the value read from registerAddress + 1
unsigned int SensorBar::readWord(uint8_t registerAddress)
{
    unsigned int readValue;
    unsigned int msb, lsb;
    //unsigned int timeout = RECEIVE_TIMEOUT_VALUE * 2;
    char data[2] = {registerAddress, 0};
    char r_data[2];
    uint8_t val = i2c.write(deviceAddress, data, 1);
    val = i2c.read(deviceAddress, r_data, 2);
    msb = ((unsigned int)r_data[0] & 0x00FF) << 8;
    lsb = ((unsigned int)r_data[1] & 0x00FF);
    readValue = msb | lsb;

    return readValue;
}

// readBytes(uint8_t firstRegisterAddress, uint8_t * destination, uint8_t length)
//  This function reads a series of uint8_ts incrementing from a given address
//  - firstRegsiterAddress is the first address to be read
//  - destination is an array of uint8_ts where the read values will be stored into
//  - length is the number of uint8_ts to be read
//  - No return value.
void SensorBar::readBytes(uint8_t firstRegisterAddress, char * destination, uint8_t length)
{
    char data[2] = {firstRegisterAddress, 0};
    i2c.write(deviceAddress, data, 1);
    uint8_t val = i2c.read(deviceAddress, destination, length);
}

// writeByte(uint8_t registerAddress, uint8_t writeValue)
//  This function writes a single uint8_t to a single register on the SX509.
//  - writeValue is written to registerAddress
//  - deviceAddres should already be set from the constructor
//  - No return value.
void SensorBar::writeByte(uint8_t registerAddress, uint8_t writeValue)
{
    char data[2] = {registerAddress, writeValue};
    i2c.write(deviceAddress, data, 2);
}

// writeWord(uint8_t registerAddress, ungisnged int writeValue)
//  This function writes a two-uint8_t word to registerAddress and registerAddress + 1
//  - the upper uint8_t of writeValue is written to registerAddress
//      - the lower uint8_t of writeValue is written to registerAddress + 1
//  - No return value.
void SensorBar::writeWord(uint8_t registerAddress, unsigned int writeValue)
{
    uint8_t msb, lsb;
    msb = ((writeValue & 0xFF00) >> 8);
    lsb = (writeValue & 0x00FF);
    char data[3] = {registerAddress, msb, lsb};
    i2c.write(deviceAddress, data, 3);
}

// writeBytes(uint8_t firstRegisterAddress, uint8_t * writeArray, uint8_t length)
//  This function writes an array of uint8_ts, beggining at a specific adddress
//  - firstRegisterAddress is the initial register to be written.
//      - All writes following will be at incremental register addresses.
//  - writeArray should be an array of uint8_t values to be written.
//  - length should be the number of uint8_ts to be written.
//  - no return value.
void SensorBar::writeBytes(uint8_t firstRegisterAddress, uint8_t * writeArray, uint8_t length)
{
    char data[10] = {};
    data[0] = firstRegisterAddress;
    for(int i = 0; i < length; i++) {
        data[1+i] = writeArray[i];
    }
    i2c.write(deviceAddress, data, length+1);
}

void SensorBar::update()
{
    while(true) {
        ThisThread::flags_wait_any(threadFlag);

        //Assign values to each bit, -127 to 127, sum, and divide
        int16_t accumulator = 0;
        uint8_t bitsCounted = 0;
        int16_t i;

        //Get the information from the wire, stores in lastBarRawValue
        if( barStrobe == 1 ) {
            writeByte(REG_DATA_B, 0x02); //Turn on IR
            thread_sleep_for(2); // wait_us(2000);
            writeByte(REG_DATA_B, 0x00); //Turn on feedback
        } else {
            writeByte(REG_DATA_B, 0x00); //make sure both IR and indicators are on
        }
        //Operate the I2C machine
        lastBarRawValue = readByte( REG_DATA_A );  //Peel the data off port A

        if( invertBits == 1 ) { //Invert the bits if needed
            lastBarRawValue ^= 0xFF;
        }

        if( barStrobe == 1 ) {
            writeByte(REG_DATA_B, 0x03); //Turn off IR and feedback when done
        }

        //count bits
        for ( i = 0; i < 8; i++ ) {
            if ( ((lastBarRawValue >> i) & 0x01) == 1 ) {
                bitsCounted++;
            }
        }

        //Find the vector value of each positive bit and sum
        for ( i = 7; i > 3; i-- ) { //iterate negative side bits
            if ( ((lastBarRawValue >> i) & 0x01) == 1 ) {
                accumulator += ((-32 * (i - 3)) + 1);
            }
        }
        for ( i = 0; i < 4; i++ ) { //iterate positive side bits
            if ( ((lastBarRawValue >> i) & 0x01) == 1 ) {
                accumulator += ((32 * (4 - i)) - 1);
            }
        }

        if ( bitsCounted > 0 ) {
            lastBarPositionValue = accumulator / bitsCounted;
        } else {
            lastBarPositionValue = 0;
        }

        //Update member variables
        angle = updateAngleRad();
        nrOfLedsActive = updateNrOfLedsActive();

        if(nrOfLedsActive == 0) {
            if(!is_first_avg) {
                avg_filter.reset();
                is_first_avg = true;
            }
        } else {
            if(is_first_avg) {
                is_first_avg = false;
                avg_filter.reset(angle);
            }
            avg_angle = avg_filter.update(angle);
        }
    }
}

float SensorBar::updateAngleRad()
{
    int8_t binaryPosition  = getBinaryPosition();
    float position = static_cast<float>(binaryPosition) / 127.0f * 0.0445f; // 0.0445 m is half of sensor length
    return atan2f(position, distAxisToSensor);
}

uint8_t SensorBar::updateNrOfLedsActive()
{
    uint8_t bitsCounted = 0;
    uint8_t i;

    //count bits
    for ( i = 0; i < 8; i++ ) {
        if ( ((lastBarRawValue >> i) & 0x01) == 1 ) {
            bitsCounted++;
        }
    }
    return bitsCounted;
}

void SensorBar::sendThreadFlag()
{
    thread.flags_set(threadFlag);
}

//****************************************************************************//
//
//  Circular buffer
//
//****************************************************************************//
/*
//Construct a CircularBuffer type with arguments
//  uint16_t inputSize: number of elements

//mbed has CircularBuffer
namespace name
{

CircularBuffer::CircularBuffer(uint16_t inputSize)
{
    cBufferData = new int16_t[inputSize];
    cBufferLastPtr = 0;
    cBufferElementsUsed = 0;
    cBufferSize = inputSize;
}

CircularBuffer::~CircularBuffer()
{
    delete[] cBufferData;
}

//Get an element at some depth into the circular buffer
//zero is the push location.  Max is cBufferSize - 1
//
//Arguments:
//  uint16_t elementNum: number of element in
//
int16_t CircularBuffer::getElement( uint16_t elementNum )
{
    //Translate elementNum into terms of cBufferLastPtr.
    int16_t virtualElementNum;
    virtualElementNum = cBufferLastPtr - elementNum;
    if( virtualElementNum < 0 ) {
        virtualElementNum += cBufferSize;
    }

    //Output the value
    return cBufferData[virtualElementNum];
}

//Put a new element into the buffer.
//This also expands the size up to the max size
//Arguments:
//
//  int16_t elementVal: value of new element
//
void CircularBuffer::pushElement( int16_t elementVal )
{
    //inc. the pointer
    cBufferLastPtr++;

    //deal with roll
    if( cBufferLastPtr >= cBufferSize ) {
        cBufferLastPtr = 0;
    }

    //write data
    cBufferData[cBufferLastPtr] = elementVal;

    //increase length up to cBufferSize
    if( cBufferElementsUsed < cBufferSize ) {
        cBufferElementsUsed++;
    }
}

//Averages the last n numbers and provides that.  Discards fractions
int16_t CircularBuffer::averageLast( uint16_t numElements )
{
    //Add up all the elements
    int32_t accumulator = 0;
    int8_t i;
    for( i = 0; i < numElements; i++ ) {
        accumulator += getElement( i );
    }
    //Divide by number of elements
    accumulator /= numElements;
    return accumulator;
}

//Returns the current size of the buffer
uint16_t CircularBuffer::recordLength()
{
    return cBufferElementsUsed;
}

}
*/