Efficient Use of C++ in Embedded Systems

Those of you planning to begin a project in using C++, hopefully using the MPLAB XC32++ compiler or the chipKIT platform, already know about the object-oriented programming concept of polymorphism. Essential to polymorphism is the concept of a virtual method, a method whose behavior can be overridden within an inheriting class by a method with the same signature.

Virtual methods are implemented using a virtual method table (vtable). The compiler creates a vtable for each class. When an object is created, a pointer to this table is added as a hidden member of the object. The compiler also generates code to initialize the vpointers of its objects to the address of the corresponding vtable. When designing your classes for your embedded application, you will want to keep the extra overhead of the vtable in mind.

Along the same lines, as an embedded-systems developer, you may be surprised by the overhead required by a pure virtual method, since it has no implementation and can therefore never be called directly. Well, the compiler generates error-handling code to handle the case where a pure virtual function is called. In the current XC32 v1.20 compiler release, when a pure virtual method is called, an error message is printed and a C++ exception is thrown, bringing in a fairly large amount of code that should never be executed. While the error handling is useful, we plan to look into simplifying it in future compiler releases to reduce the overhead. For now, you will find that avoiding a pure virtual method will help keep your code size down.

A pure virtual method assigns the function the value 0 rather than providing an implementation. For example:

class Base
{
public:
const char* SayHi() { return "Hi"; } // a normal non-virtual method
virtual const char* GetName() { return "Base"; } // a normal virtual method
virtual int GetValue() = 0; // a pure virtual method
};

To avoid the extra overhead, provide a default implementation like this:

class Base
{
public:
const char* SayHi() { return "Hi"; } // a normal non-virtual method
virtual const char* GetName() { return "Base"; } // a normal virtual method
virtual int GetValue() { while(1); /* Error */ } // a normal virtual method
};

Another hidden consumer of Flash space to look out for is Run-Time Type Information (RTTI). This C++ mechanism exposes information about an object’s data type at runtime. The dynamic_cast<> operation and typeid operator are part of RTTI. When this feature is enabled, additional the type information gets stored in Flash and can consume space even when the application does not use it. When you’re not using RTTI, be sure to disable it in your project settings (-fno-rtti). This compiler option can be added to the chipKIT IDE (MPIDE) default compiler option in <install_dir>/hardware/pic32/platforms.txt

C++ exception handling also causes additional code to be generated. Although exceptions provide a way to react to exceptional circumstances (like runtime errors) in an application, they come at a fairly substantial code and data memory cost. When your application doesn’t require exception support, you can disable it in your project settings (-fno-exceptions). This option is specified by default when the chipKIT IDE (MPIDE) invokes the chipKIT compiler.

[ Thanks to Jason Kajita for this helpful information ]
VN:F [1.9.22_1171]
Rating: 9.3/10 (4 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

NKC ProtoShield Kit for chipKIT Uno32

This article describes an expansion board kit available from NKC Electronics for the chipKIT Uno32 Development Board.

What is a Proto Shield? We all know that the Shields are from the Arduino world and they are named after the little boards that sits on top of the Arduino and compatible boards. A Proto Shield is a combination of a ProtoBoard with a Shield. You usually use a Bread Board to build a prototype, to test the design. Once you have it working and want to house it in a more permanent board, is where a Proto Shield shows its benefits.

The ProtoShield for the chipKIT Uno32 is an exclusive design of NKC Electronics, and it has many nice features:

NKC_protoshield_image_1

The fully assembled ProtoShield board looks like this:

NKC_protoshield_image_2

The kit includes the board and many extra components. What you finally end up installing on the ProtoShield will be based on your requirements. But in general, I recommend soldering all the female headers, as they are needed to bring all the signals and POWER from the chipKIT Uno32 board to the ProtoShield.

NKC_protoshield_image_3

The LED with the associated resistor and filtering capacitor is to add a POWER indicator, meaning you have 5V power from the chipKIT Uno32 board to the ProtoShield.

NKC_protoshield_image_4

The RESET switch is necessary if you want to reset the chipKIT Uno32 board from the top.

NKC_protoshield_image_5

The little 6-pin male header with the 2 jumpers are used to configure how you want to use the 2 power BUSES in the middle of the board. You can send +5V to one of them, GND to the other, or +5 or GND to both of them at the same time.

NKC_protoshield_image_6

If you use the 6-pin male header jumpers to power the BUS, then you can only have +5V.

If you need the full ProtoShield to be 3.3V, then you can select one of the jumpers to GND and the other one open and connect +3.3V directly to the positive BUS. Take a look at the diagram below:

NKC_protoshield_image_7

For more information, visit NKC Electronics Protoshield Kit for chipKIT Uno32

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

Serial Communication – I2C EEPROM using MPLAB X IDE

Overview:

In this project you will learn to use a basic functionality of chipKIT board, which is to use serial communication I2C bus to read and write values from EEPROM memory. I2C bus is a master slave bus, which communicates data from the processor on chipKIT board to other peripherals. More description on I2C communication can be found on the Wikipedia website and on EEPROM can be found here. The configuration is done though MPLAB X IDE.

Hardware Used:

To do the serial communication project, you require the following hardware devices.
  • chipKIT Uno 32
  • chipKIT Basic IO shield
  • PICkit® 3 In-Circuit Debugger/Programmer
  • USB cable
  • 6 pin header to connect chipKIT board and PICkit® 3

Reference:

The reference guide for each board, schematics and other resources are available on their individual homepages:

Procedure:

  1. Connect the hardware devices. The PICkit® 3 should be connected to the chipKIT Uno32 board through a suitable header and use the USB cable to connect the PICkit® 3 to your PC.
  2. Make sure that the jumper settings for JP4 on chipKIT Uno32 board is configured as SPI Slave Select input mode. You can find the jumper settings in chipKIT Uno32 board reference manual from the Digilent website. Also, ensure that the jumpers JP6/JP8 are configured to be used as I2C communication lines.
  3. Place the IO shield on top of the Uno32 board with a bit of a firm press.
  4. Once the hardware setup is made and the device drivers are updated for PICkit® 3 on your computer, launch MPLAB X (Start>All Programs>Microchip>MPLAB X IDE>MPLAB X IDE vx.xx on Window® Machines).
  5. Create a new project through File>New Project. In the pop up new project window, select Standalone Project  under Projects  and click Next. Under Family scroll and select 32-bit MCUs (PIC32). Note the processor number from the chipKIT board’s reference manual and enter it under the Device  option. Click Next.
  6. Select PICkit3 in the Hardware Tools, click Next. Select XC32 (v1.20) under Compiler Toolchains, click Next. Give a project name to create the project.
  7. In the Project tab on the left side of the MPLAB X IDE, right click on Source Files > New > C Main File.. and give a file name for main source code. Once the dot c file opens, select all the codes in the file and replace it with the codes below.
    #include 
    
    #pragma config FPLLMUL = MUL_20, FPLLIDIV = DIV_2, FPLLODIV = DIV_1, FWDTEN = OFF
    #pragma config POSCMOD = HS, FNOSC = PRIPLL, FPBDIV = DIV_1
    #define SYS_FREQ (80000000L)
    
    #define GetSystemClock()           (SYS_FREQ)
    #define GetPeripheralClock()       (SYS_FREQ/1)
    #define GetInstructionClock()      (SYS_FREQ)
    #define I2C_CLOCK_FREQ             5000
    
    // EEPROM Constants
    #define EEPROM_I2C_BUS              I2C1
    #define EEPROM_ADDRESS              0x50        // 0b1010000 Serial EEPROM address
    
    /*******************************************************************************
      Function:
        BOOL StartTransfer( BOOL restart )
    
      Summary:
        Starts (or restarts) a transfer to/from the EEPROM.
    
      Description:
        This routine starts (or restarts) a transfer to/from the EEPROM, waiting (in
        a blocking loop) until the start (or re-start) condition has completed.
    
      Precondition:
        The I2C module must have been initialized.
    
      Parameters:
        restart - If FALSE, send a "Start" condition
                - If TRUE, send a "Restart" condition
    
      Returns:
        TRUE    - If successful
        FALSE   - If a collision occured during Start signaling
    
      Example:
        
        StartTransfer(FALSE);
        
    
      Remarks:
        This is a blocking routine that waits for the bus to be idle and the Start
        (or Restart) signal to complete.
      *****************************************************************************/
    
    BOOL StartTransfer( BOOL restart )
    {
        I2C_STATUS  status;
    
        // Send the Start (or Restart) signal
        if(restart)
        {
            I2CRepeatStart(EEPROM_I2C_BUS);
        }
        else
        {
            // Wait for the bus to be idle, then start the transfer
            while( !I2CBusIsIdle(EEPROM_I2C_BUS) );
    
            if(I2CStart(EEPROM_I2C_BUS) != I2C_SUCCESS)
            {
                DBPRINTF("Error: Bus collision during transfer Start\n");
                return FALSE;
            }
        }
    
        // Wait for the signal to complete
        do
        {
            status = I2CGetStatus(EEPROM_I2C_BUS);
    
        } while ( !(status & I2C_START) );
    
        return TRUE;
    }
    
    /*******************************************************************************
      Function:
        BOOL TransmitOneByte( UINT8 data )
    
      Summary:
        This transmits one byte to the EEPROM.
    
      Description:
        This transmits one byte to the EEPROM, and reports errors for any bus
        collisions.
    
      Precondition:
        The transfer must have been previously started.
    
      Parameters:
        data    - Data byte to transmit
    
      Returns:
        TRUE    - Data was sent successfully
        FALSE   - A bus collision occured
    
      Example:
        
        TransmitOneByte(0xAA);
        
    
      Remarks:
        This is a blocking routine that waits for the transmission to complete.
      *****************************************************************************/
    
    BOOL TransmitOneByte( UINT8 data )
    {
        // Wait for the transmitter to be ready
        while(!I2CTransmitterIsReady(EEPROM_I2C_BUS));
    
        // Transmit the byte
        if(I2CSendByte(EEPROM_I2C_BUS, data) == I2C_MASTER_BUS_COLLISION)
        {
            DBPRINTF("Error: I2C Master Bus Collision\n");
            return FALSE;
        }
    
        // Wait for the transmission to finish
        while(!I2CTransmissionHasCompleted(EEPROM_I2C_BUS));
    
        return TRUE;
    }
    
    /*******************************************************************************
      Function:
        void StopTransfer( void )
    
      Summary:
        Stops a transfer to/from the EEPROM.
    
      Description:
        This routine Stops a transfer to/from the EEPROM, waiting (in a
        blocking loop) until the Stop condition has completed.
    
      Precondition:
        The I2C module must have been initialized & a transfer started.
    
      Parameters:
        None.
    
      Returns:
        None.
    
      Example:
        
        StopTransfer();
        
    
      Remarks:
        This is a blocking routine that waits for the Stop signal to complete.
      *****************************************************************************/
    
    void StopTransfer( void )
    {
        I2C_STATUS  status;
    
        // Send the Stop signal
        I2CStop(EEPROM_I2C_BUS);
    
        // Wait for the signal to complete
        do
        {
            status = I2CGetStatus(EEPROM_I2C_BUS);
    
        } while ( !(status & I2C_STOP) );
    }
    
    // ****************************************************************************
    // ****************************************************************************
    // Application Main Entry Point
    // ****************************************************************************
    // ****************************************************************************
    
    int main(void)
    {
        UINT8               i2cData[10];
        I2C_7_BIT_ADDRESS   SlaveAddress;
        int                 Index;
        int                 DataSz;
        UINT32              actualClock;
        BOOL                Acknowledged;
        BOOL                Success = TRUE;
        UINT8               i2cbyte;
    
        // Initialize debug messages (when supported)
        DBINIT();
    
        // Set the I2C baudrate
        actualClock = I2CSetFrequency(EEPROM_I2C_BUS, GetPeripheralClock(), I2C_CLOCK_FREQ);
        if ( abs(actualClock-I2C_CLOCK_FREQ) > I2C_CLOCK_FREQ/10 )
        {
            DBPRINTF("Error: I2C1 clock frequency (%u) error exceeds 10%%.\n", (unsigned)actualClock);
        }
    
        // Enable the I2C bus
        I2CEnable(EEPROM_I2C_BUS, TRUE);
    
        //
        // Send the data to EEPROM to program one location
        //
    
        // Initialize the data buffer
        I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, EEPROM_ADDRESS, I2C_WRITE);
        i2cData[0] = SlaveAddress.byte;
        i2cData[1] = 0x05;              // EEPROM location to program (high address byte)
        i2cData[2] = 0x40;              // EEPROM location to program (low address byte)
        i2cData[3] = 0xAA;              // Data to write
        DataSz = 4;
    
        // Start the transfer to write data to the EEPROM
        if( !StartTransfer(FALSE) )
        {
            while(1);
        }
    
        // Transmit all data
        Index = 0;
        while( Success && (Index < DataSz) )
        {
            // Transmit a byte
            if (TransmitOneByte(i2cData[Index]))
            {
                // Advance to the next byte
                Index++;
    
                // Verify that the byte was acknowledged
                if(!I2CByteWasAcknowledged(EEPROM_I2C_BUS))
                {
                    DBPRINTF("Error: Sent byte was not acknowledged\n");
                    Success = FALSE;
                }
            }
            else
            {
                Success = FALSE;
            }
        }
    
        // End the transfer (hang here if an error occured)
        StopTransfer();
        if(!Success)
        {
            while(1);
        }
    
        // Wait for EEPROM to complete write process, by polling the ack status.
        Acknowledged = FALSE;
        do
        {
            // Start the transfer to address the EEPROM
            if( !StartTransfer(FALSE) )
            {
                while(1);
            }
    
            // Transmit just the EEPROM's address
            if (TransmitOneByte(SlaveAddress.byte))
            {
                // Check to see if the byte was acknowledged
                Acknowledged = I2CByteWasAcknowledged(EEPROM_I2C_BUS);
            }
            else
            {
                Success = FALSE;
            }
    
            // End the transfer (stop here if an error occured)
            StopTransfer();
            if(!Success)
            {
                while(1);
            }
    
        } while (Acknowledged != TRUE);
    
        //
        // Read the data back from the EEPROM.
        //
    
        // Initialize the data buffer
        I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, EEPROM_ADDRESS, I2C_WRITE);
        i2cData[0] = SlaveAddress.byte;
        i2cData[1] = 0x05;              // EEPROM location to read (high address byte)
        i2cData[2] = 0x40;              // EEPROM location to read (low address byte)
        DataSz = 3;
    
        // Start the transfer to read the EEPROM.
        if( !StartTransfer(FALSE) )
        {
            while(1);
        }
    
        // Address the EEPROM.
        Index = 0;
        while( Success & (Index < DataSz) )
        {
            // Transmit a byte
            if (TransmitOneByte(i2cData[Index]))
            {
                // Advance to the next byte
                Index++;
            }
            else
            {
                Success = FALSE;
            }
    
            // Verify that the byte was acknowledged
            if(!I2CByteWasAcknowledged(EEPROM_I2C_BUS))
            {
                DBPRINTF("Error: Sent byte was not acknowledged\n");
                Success = FALSE;
            }
        }
    
        // Restart and send the EEPROM's internal address to switch to a read transfer
        if(Success)
        {
            // Send a Repeated Started condition
            if( !StartTransfer(TRUE) )
            {
                while(1);
            }
    
            // Transmit the address with the READ bit set
            I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, EEPROM_ADDRESS, I2C_READ);
            if (TransmitOneByte(SlaveAddress.byte))
            {
                // Verify that the byte was acknowledged
                if(!I2CByteWasAcknowledged(EEPROM_I2C_BUS))
                {
                    DBPRINTF("Error: Sent byte was not acknowledged\n");
                    Success = FALSE;
                }
            }
            else
            {
                Success = FALSE;
            }
        }
    
        // Read the data from the desired address
        if(Success)
        {
            if(I2CReceiverEnable(EEPROM_I2C_BUS, TRUE) == I2C_RECEIVE_OVERFLOW)
            {
                DBPRINTF("Error: I2C Receive Overflow\n");
                Success = FALSE;
            }
            else
            {
                while(!I2CReceivedDataIsAvailable(EEPROM_I2C_BUS));
                i2cbyte = I2CGetByte(EEPROM_I2C_BUS);
            }
    
        }
    
        // End the transfer (stop here if an error occured)
        StopTransfer();
        if(!Success)
        {
            while(1);
        }
    
        // Validate the data read
        if( i2cbyte != 0xAA )
        {
            DBPRINTF("Error: Verify failed\n");
        }
        else
        {
            DBPRINTF("Success\n");
        }
    
        // Example complete
        while(1);
    }
  8. In the above program, the EEPROM is being written with a value AA and read from the memory to verify if it written correctly. To verify the result access the watch window from Window > Debugging > Watches or you can use the shortcut key Alt+Shift+2. In the watch window panel that opens on the MPLAB X IDE, enter the new watch as i2cbyte, in the blank <Enter new watch> area.
  9. In the program editor window, put a breakpoint by clicking on the line number  on the while statement near the end of the program.
  10. Click on Debug Project icon shown below,                                                 analogread mplab3
  11. Once the program halts its execution at the line with the breakpoint, click on Watches window below the code area. You can observe the output value being reflected in the i2cbyte variable as shown below.serialcomm1
This verifies the operation of writing into and reading from EEPROM memory through the I2C serial communication. To get more information on working of I2C serial communication visit the wikipedia website by clicking here. In our application EEPROM on the IOshield is the slave device and PIC controller on chipKIT Uno32 board acts as the master device. 
VN:F [1.9.22_1171]
Rating: 3.6/10 (8 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

Line Following Robot with Arduino Shield

OVERVIEW:

Here is a fairly basic robot application that implements a line following algorithm. This application uses off the shelf components along with a very basic chassis built from common materials. This could be a good weekend project. The Line-Follower chassis for this Demo was constructed with 2½” wide by ¼” thick poplar project wood purchased at a local home improvement center. Note that a box was also created to house the battery holder.

HARDWARE USED

CONSTRUCTION

The Arduino Motor Shield is mounted to a chipKIT Uno32 Development Platform with all component connections made to the shield as indicated in labels next to component.  

LOGIC OVERVIEW

The line following robot is programmed to basically avoid the color black. If both sensors see black, then the robot will back up until it no longer sees black. If one sensor sees black, then the robot will turn in the direction of that sensor until it no longer sees black. Other than that, if the robot doesn’t see black on either sensor, it will simply move for- ward. In this application, the black line is created using common electrical tape. The logic for this algorithm is shown below along with a graphical representation of the front of the line following bot for reference.  

 SKETCH

 
/*
 9/10/2012
 Marc McComb

The line following robot is programmed to basically avoid the color black. If both sensors see black, 
then the robot will back up until it no longer sees black. If one sensor sees black, then the robot will
turn in the direction of that sensor until it no longer see black. Other than that, if the robot doesn’t 
see black on either sensor, it will simply move forward. In this application, the black line is created 
using common electrical tape. 

This application uses the following hardware:
chipKITâ„¢ Uno32 available at http://www.digilentinc.com/chipkit 
Arduino Motor Shield available at: http://arduino.cc/en/Main/ArduinoMotorShieldR3 
Sparkfun QRE1113 Line Sensor Board- Analog(Item #: ROB-09453) https://www.sparkfun.com/products/9453 

Software used:
The Aruino Motor shield is designed for a PWM on pin 11. Since the Uno32 doesn't have a hardware PWM 
on that pin, no worries, we just use Brian Schmaltzs SoftPWMServo library:
http://www.chipkit.org/wiki/index.php?title=Library_Status#SoftPWMServo_library 

You will need to make sure that you add the library. To do this:
1) Make sure that you have a folder in your Documents:/MPIDE folder called Libraries
2) Copy the SoftPWMServo library folder from C:\mpide-0023-windows-20120903\hardware\pic32\libraries 
    to the Libraries folder above
3) If MPIDE is open, close and reopen. 
4) Now all you have to do is go to the MPIDE toolbar and select Sketch>Import Library>SoftPWMServo

More info on libraries at:
http://arduino.cc/en/Reference/Libraries

*/

#include <SoftPWMServo.h>

//Setting up the Hardware pins
// First the line following (IR) sensors
const int irLeft = 2; //Left line sensor is on pin A2
const int irRight = 3; //Right line sensor is on pin A3

//Setting up the Arduino Motor Shield
const int leftDIR = 12; 
const int rightDIR = 13;
const int leftPWM = 3;
const int rightPWM = 11;
const int leftBrake = 9;
const int rightBrake = 8;
const char bothSpeed = 100; //sets how fast the motors will spin (0 to 255)

//Here we set up variable that will hold the ADC value representing the line sensor values
int leftSees = 0; //A2 ADC value (0 to 1023)
int rightSees = 0; //A3 ADC value (0 to 1023)

void setup() 
{ 

  //Make sure to set all of our control signal pins as output
  pinMode(leftDIR, OUTPUT); 
  pinMode(rightDIR, OUTPUT); 
  pinMode(leftBrake, OUTPUT); 
  pinMode(rightBrake, OUTPUT); 

  //Next we make sure our brake signals are set LOW
  digitalWrite(leftBrake, LOW);
  digitalWrite(rightBrake, LOW);
} 

void loop() 
{ 
    //Start by reading the left sensor on A2
  int leftEye = analogRead(irLeft);

  //delay a little bit
  delay(5);

  //next read the right sensor connected A3
  int rightEye = analogRead(irRight);

  //Next, we run the motors based on the sensor reading

  //If both sensors see black (ADC value greater than 1000), then back up
  if ((leftEye >= 1000)&&(rightEye >= 1000)) reverse();

  //Otherwise, if only the left sensor sees black, then turn off the left motor
  //so the robot veer to the left
  else if ((leftEye >= 1000)&&(rightEye < 1000)) turnLeft();

  //Otherwise, if only the right sensor sees black, then turn off the right motor
  //so the robot veer to the right
  else if ((leftEye < 1000)&&(rightEye >= 1000)) turnRight();

  //Otherwise, move forward
  else forward();

}

//Turn right by turning off the right motor 
//i.e disable the PWM to that wheel
void turnRight(void)
{
  digitalWrite(leftDIR, HIGH); 
  digitalWrite(rightDIR, HIGH);
  SoftPWMServoPWMWrite(leftPWM, bothSpeed);
  SoftPWMServoPWMWrite(rightPWM, 0);
}

//Turn left by turning off the left motor
//i.e disable the PWM to that wheel
void turnLeft(void)
{
  digitalWrite(leftDIR, HIGH); 
  digitalWrite(rightDIR, HIGH);
  SoftPWMServoPWMWrite(leftPWM, 0);
  SoftPWMServoPWMWrite(rightPWM, bothSpeed);
}

//Move forward by enabling both wheels
void forward(void)
{
  digitalWrite(leftDIR, HIGH); 
  digitalWrite(rightDIR, HIGH);
  SoftPWMServoPWMWrite(leftPWM, bothSpeed);
  SoftPWMServoPWMWrite(rightPWM, bothSpeed);
}

//Reverse by enabling both wheels 
void reverse(void)
{
  digitalWrite(leftDIR, LOW); 
  digitalWrite(rightDIR, LOW);
  SoftPWMServoPWMWrite(leftPWM, bothSpeed);
  SoftPWMServoPWMWrite(rightPWM, bothSpeed);
}
 
VN:F [1.9.22_1171]
Rating: 6.9/10 (27 votes cast)
VN:F [1.9.22_1171]
Rating: +2 (from 2 votes)