SparkFun Line Follower Array Library. based on https://github.com/sparkfun/SparkFun_Line_Follower_Array_Arduino_Library

Dependents:   WRS_mechanamu_test

SensorBar.cpp

Committer:
sgrsn
Date:
2018-08-31
Revision:
0:c1d649c7b904

File content as of revision 0:c1d649c7b904:

#include "SensorBar.h"
#include "stdint.h"
#include "sx1509_registers.h"

/*example***************************************************

#include "mbed.h"
#include "SensorBar.h"

const uint8_t SX1509_ADDRESS = 0x3E<<1;  // SX1509 I2C address (00)

int main()
{
    I2C i2c(p28, p27);
    SensorBar mySensorBar(&i2c, SX1509_ADDRESS);
    mySensorBar.setBarStrobe();
    mySensorBar.clearInvertBits();
    uint8_t returnStatus = mySensorBar.begin();
    printf("next, %d\r\n", returnStatus);
    if(returnStatus)
    {
        printf("sx1509 IC communication OK");
    }
    else
    {
        printf("i2c failed");
        wait_ms(10);
        while(1);
    }
    
    while(1)
    {
        uint8_t rawValue = mySensorBar.getRaw();

        for( int i = 7; i >= 0; i-- )
        {
            printf("%d", (rawValue >> i) & 0x01);
        }
        printf("b, ");
        printf("%d\r\n", mySensorBar.getPosition());
        wait_ms(10);
    }
}

***********************************************************/


SensorBar::SensorBar(I2C *i2c, uint8_t address, uint8_t resetPin, uint8_t interruptPin, uint8_t oscillatorPin)
{
    // Store the received parameters into member variables
    _i2c = i2c;
    deviceAddress = address;
    pinInterrupt = interruptPin;
    pinOscillator = oscillatorPin;
    pinReset = resetPin;
    invertBits = 0;
    barStrobe = 0; //Default always on
}

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);
}

//Run this once during initialization to configure the SX1509 as a sensor bar
//Returns 1 for success
uint8_t SensorBar::begin(void)
{
    uint8_t returnVar = 0;
    
    // 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 = 1;
    }
    else
    {
        returnVar = 0;
    }
 
    return returnVar;
}

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

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( void )
{
    barStrobe = 1; //Do strobe
}

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

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

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

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

uint8_t SensorBar::getRaw( void )
{
    //Get the information from the wire, stores in lastBarRawValue
    scan();
    
    return lastBarRawValue;
}

int8_t SensorBar::getPosition( void )
{
    //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
    scan();
    
    //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;
    }
    
    return lastBarPositionValue;
}

uint8_t SensorBar::getDensity( void )
{
    uint8_t bitsCounted = 0;
    uint8_t i;
    
    //get input from the I2C machine
    scan();
    
    //count bits
    for ( i = 0; i < 8; i++ )
    {
        if ( ((lastBarRawValue >> i) & 0x01) == 1 )
        {
            bitsCounted++;
        }
    }
    return bitsCounted;
}


//****************************************************************************//
//
//  Utilities
//
//****************************************************************************//
void SensorBar::scan( void )
{
    if( barStrobe == 1 )
    {
        writeByte(REG_DATA_B, 0x02); //Turn on IR
        wait_ms(2);  //Additional delay required after IR is turned on to allow LEDs to achieve full brightness
        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
    }
    //delay(8);
}

// 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);
}


//****************************************************************************//
//
//  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( void )
{
    return cBufferElementsUsed;
}

}