This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Wii Nunchuk [I2C] to Tiva TM4C123G



Greetings, 

I'm currently trying to interface my Tiva C Series with a Wii Nunchuk, to be able to read the inputs from the nunchuk onto the TM4C123G board. Over all the code seems to compile properly, however it seems that the board is unable to update the inputted values. I believe that there may be some small details that I may have missed as it is my first time working with the I2C module of the Tiva boards.

I have followed the procedure (pg 15)  of initializing the nunchuk from the following link: http://www.robotshop.com/media/files/PDF/inex-zx-nunchuck-datasheet.pdf

The following is my code:


#define PART_TM4C1233H6PM
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/i2c.h"
#include "sensorlib/i2cm_drv.h"
#include "inc/tm4c123gh6pm.h"
#include "inc/hw_i2c.h"
#include "driverlib/systick.h"
#include "utils/uartstdio.h"


#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "inc/hw_uart.h"

#ifdef TARGET_IS_BLIZZARD_RB1
#endif
#include "driverlib/rom.h"

//======================================================================================
//Function Declarations
//======================================================================================
void I2CInit(int clockSignalPin);
void I2CWriteData(int peripheral, int address, char* data, int numberOfBytes);
int I2CReadData(int peripheral, int address, char* data, int numberOfBytes);
void nunchukInit();
void nunchuckRecieveRequest();
void nunchuckReadData();
void SysTickHandler();
void UARTInit();

int joyX, joyY;
int accX, accY, accZ;
int btnC, btnZ;

void main(void)
{

    ROM_SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    I2CInit(GPIO_PB2_I2C0SCL);

    // Enable systick - system timer
    ROM_SysTickPeriodSet(SysCtlClockGet() / 10);
    ROM_SysTickEnable();

    UARTInit();

    ROM_UARTCharPutNonBlocking(UART0_BASE, 'H');
    ROM_UARTCharPutNonBlocking(UART0_BASE, 'i');
    ROM_UARTCharPutNonBlocking(UART0_BASE, '\n');

    nunchukInit();

    ROM_SysTickIntEnable();

    while(1)  {


        //nunchuckReadData();
        if(joyY > 160)
        {

            UARTCharPut(UART0_BASE, 'E');
            //setMotor(MOTOR_R, MOTOR_DIR_F, (0x40/(216 - 160)) * (joyY-160));
            //setMotor(MOTOR_L, MOTOR_DIR_F, (0x40/(216 - 160)) * (joyY-160));
        }
        else if( joyY <= 90)
        {
            //setMotor(MOTOR_R, MOTOR_DIR_B, 0x40);
            //setMotor(MOTOR_L, MOTOR_DIR_B, 0x40);
        }
        else if(joyX > 170)
        {
            //setMotor(MOTOR_R, MOTOR_DIR_B, 0x40);
            //setMotor(MOTOR_L, MOTOR_DIR_F, 0x40);
        }
        else if( joyX <= 90)
        {
            //setMotor(MOTOR_R, MOTOR_DIR_F, 0x40);
            //setMotor(MOTOR_L, MOTOR_DIR_B, 0x40);
        }
        else
        {
            //setMotor(MOTOR_R, MOTOR_DIR_F, 0x00);
            //setMotor(MOTOR_L, MOTOR_DIR_F, 0x00);
        }
        /* if(accY > 600)
        {
          UARTCharPut(UART0_BASE, 'E');
            //setMotor(MOTOR_R, MOTOR_DIR_F, 0x40); //(0x40/(750 - 600)) * (accY-600));
            //setMotor(MOTOR_L, MOTOR_DIR_F, 0x40); //(0x40/(750 - 600)) * (accY-600));
        }
        else if( accY <= 400)
        {
          //setMotor(MOTOR_R, MOTOR_DIR_B, 0x40);
            //setMotor(MOTOR_L, MOTOR_DIR_B, 0x40);
        }
        else if(accX > 700)
        {
            //setMotor(MOTOR_R, MOTOR_DIR_B, 0x40);
            //setMotor(MOTOR_L, MOTOR_DIR_F, 0x40);
        }
        else if( accX <= 400)
        {
          //setMotor(MOTOR_R, MOTOR_DIR_F, 0x40);
            //setMotor(MOTOR_L, MOTOR_DIR_B, 0x40);
        }
        else
        {
            //setMotor(MOTOR_R, MOTOR_DIR_F, 0x00);
            //setMotor(MOTOR_L, MOTOR_DIR_F, 0x00);
        }

    } else {
      // setMotor(MOTOR_R, MOTOR_DIR_F, 0x00);
        //setMotor(MOTOR_L, MOTOR_DIR_F, 0x00);*/
    }
}

//======================================================================================
//FUNCTION DEFINITIONS
//======================================================================================
void I2CInit(int clockSignalPin)
{
    // Initialize I2C module #0 on pins PB2 & PB3
    if(clockSignalPin == GPIO_PB2_I2C0SCL)
    {
        // Turn on I2S0 and reset to a known state
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
        SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);

        // Configure the PortB pins for I2C0
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
        GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);

        // Set correct multiplexed pin
        GPIOPinConfigure(GPIO_PB2_I2C0SCL);
        GPIOPinConfigure(GPIO_PB3_I2C0SDA);

        // Set GPIO Pins for Open-Drain operation
        GPIOPadConfigSet(GPIO_PORTB_BASE, GPIO_PIN_2, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_OD);
        GPIOPadConfigSet(GPIO_PORTB_BASE, GPIO_PIN_3, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_OD);

        // Give control to the I2C Module
        GPIODirModeSet(GPIO_PORTB_BASE, GPIO_PIN_2, GPIO_DIR_MODE_HW);
        GPIODirModeSet(GPIO_PORTB_BASE, GPIO_PIN_3, GPIO_DIR_MODE_HW);

        // Enable and Initalize MASTER/SLAVE
        //I2CMasterEnable(I2C0_BASE);
        I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
    }

}



void I2CWriteData(int peripheral, int address, char* data, int numberOfBytes)
{
    int i;
    int controlWord;

    // Wait for previous transfer
    while(I2CMasterBusBusy(peripheral));

    // Load address and set that we want read (false)
    ROM_I2CMasterSlaveAddrSet(peripheral, address, false);

    // We want burst transfer (more than 1 byte)
    controlWord = I2C_MASTER_CMD_BURST_SEND_START;


    for( i = 0; i < numberOfBytes; i++)
    {
        // The second byte has to be send with CONTINUE control word
        if(i == 1)
            controlWord = I2C_MASTER_CMD_BURST_SEND_CONT;
        // The last byte has to be send with FINISH control word
        if(i == numberOfBytes - 1)
            controlWord = I2C_MASTER_CMD_BURST_SEND_FINISH;
        // If we have only one byte to send, it is not BURST send but SINGLE
        if(numberOfBytes == 1)
            controlWord = I2C_MASTER_CMD_SINGLE_SEND;

        // Set byte to send
        I2CMasterDataPut(peripheral, data[i]);
        // Send byte
        I2CMasterControl(peripheral, controlWord);

        // Wait to send the byte
        while(I2CMasterBusBusy(peripheral));

    }

}


int I2CReadData(int peripheral, int address, char* data, int numberOfBytes)
{

    int i;
    int controlWord;

    // Wait for previous transfer
    while(I2CMasterBusBusy(peripheral));

    // Load address and set that we want write (true)
    I2CMasterSlaveAddrSet(peripheral, address, true);

    // We want burst transfer (more than 1 byte)
    controlWord = I2C_MASTER_CMD_BURST_RECEIVE_START;

    for(i = 0; i < numberOfBytes; i++)
    {
        // The second byte has to be receive with CONTINUE control word
        if(i == 1)
            controlWord = I2C_MASTER_CMD_BURST_RECEIVE_CONT;
        // The last byte has to be receive with FINISH control word
        if(i == numberOfBytes - 1)
            controlWord = I2C_MASTER_CMD_BURST_RECEIVE_FINISH;
        // If we have only one byte to receive, it is not BURST send but SINGLE
        if(numberOfBytes == 1)
            controlWord = I2C_MASTER_CMD_SINGLE_RECEIVE;

        // Read a byte
        I2CMasterControl(peripheral, controlWord);
        // Wait to finish reading
        while(I2CMasterBusBusy(peripheral));

        // Check for errors
        if (I2CMasterErr(peripheral) != I2C_MASTER_ERR_NONE)
            return -1;

        // Move byte from register
        data[i] = I2CMasterDataGet(peripheral);
    }

    // send number of received bytes
    return i;

}

void nunchukInit()
{
    char handShake[] = {0x40, 0x00};

    //I2CInit(GPIO_PB2_I2C0SCL);
    I2CWriteData(I2C0_BASE, 0xA4, handShake, sizeof(handShake));

}
void nunchuckRecieveRequest()
{
    char readValues[] = {0x00};
    I2CWriteData(I2C0_BASE, 0xA4, readValues, sizeof(readValues));
}

void nunchuckReadData()
{

    char data[6];
    int i;

    // Read 6 bytes
    I2CReadData(I2C0_BASE, 0xA4, data , 6);

    // Necessary for original Nunchuk
    for(i=0; i < 6; i++)
        data[i] = (data[i] ^ 0x17) + 0x17;


    // Decode read bytes
    joyX = data[0];
    joyY = data[1];

    accX = data[2] << 2 | ((data[5] >> 2) & 0x03);
    accY = data[3] << 2 | ((data[5] >> 4) & 0x03) ;
    accZ = data[4] << 2 | ((data[5] >> 6) & 0x03) ;

    btnZ = (data[5] & 0x01) == 0;
    btnC = ((data[5] >> 1) & 0x01) == 0;


    // Debug values
    //UARTprintf("%u, %u, %u, %u, %u, %c, %c\n", joyX, joyY, accX, accY, accZ, btnC ? '1' : '0',  btnZ ? '1' : '0' );

    // Send request for new values for future reading
    nunchuckRecieveRequest();

}

void SysTickHandler()
{
    // Invert pin4
    //GPIO_PORTF_DATA_R ^= 0x01 << 4;

    nunchuckReadData();

}

void UARTInit()
{
    //
    // Enable the peripherals used by this example.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Set GPIO A0 and A1 as UART pins.
    //
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Configure the UART for 115,200, 8-N-1 operation.
    //
    ROM_UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));

    //UARTStdioInit(2);

}
//=============================================================//======================================================================================                             

END OF CODE

//=============================================================//======================================================================================

The following is a photo of my debug window from the CCS software that I'm using to run my Tiva board. It clearly enters the main loop but cannot updates the values of my variables:

 

//======================================================================================
  • Rafiul Islam said:
    however it seems that the board is unable to update the inputted values

    Hello Rafiul,

    Can you explain a bit more what you mean by this statement? Are you able to see any I2C communications at all? If so, do you mean that you are not able to modify the received data from the external device? Are you certain the data you have is from the external source and not simply the initial values in the variables? Are the values stored in global or local variables?

    Finally, are you able to use a scope to see the signals on the SDA line to see what messages are being sent. Here you might be able to see if your messages to update any data in the external device are being NACK'd by the slave.

  • Rafiul Islam said:
    may be some small details that I may have missed as it is my first time working with the I2C module of the Tiva boards.

    That's telling - is it not?  

    KISS dictates that you (first) build your skill via SIMPLER project.   Often - several here suggest a small I2C-based EEProm - as far more suitable means to build your understanding & (later) mastery.

    The device you're attaching is far more complex - your odds of success dramatically lessen - there is a far easier/faster - superior path.   KISS!

  • While running the code the variables are initialized to "46" for some odd reason. To be quite honest, I'm not fully sure if any data is being received from the external device. However I was successfully about to interface the nunchuk using an arduino board. From that i have compared a multitude of I2C TM4C123G source code examples along with code for other stellaris boards which were successfully able to interface with the nunchuk. At this point..i have no idea how to go about this.

    I appreciate your time and effort, thanks.
  • I'm sorry, im not too familiar with "KISS". I'm assuming that its an abbreviation for doing things in simpler steps. I agree that the device i connected is a bit more complicated than a regular I2C device. But from my analysis of I2C code for other devices, it only varies from the nunchuk, requiring an initialization process. However I was successfully able to interface the nunchuk using an arduino board. From that i have compared a multitude of I2C TM4C123G source code examples along with code for other stellaris boards which were successfully able to interface with the nunchuk. At this point..i have no idea how to go about this.
  • If you have I2C code from other Stellaris boards, the I2C module has changed little over time and the source should be a good starting point. You may need to adjust the port/pins being used but the basics should work fine. I also think there is a loopback test included in the examples in TivaWare (C:\ti\TivaWare_C_Series-2.1.1.71\examples\peripherals\I2C\) .

    The first step and probably the most critical is to get I2C working so perhaps set it up as a loopback test as noted in the provided examples. This can be done by using one of the I2C modules on chip as master and the second as slave. Once you have some assurance your I2C is working, then try hooking up to the nunchuk. Once you reach this step, try sending a single message and assure you are getting a successful transmit. Move on incrementally from there.

    As I don't know anything about the nunchuk or what the interface entails. Be sure to check if the proper pull ups are present on SCL and SDA lines since they are needed to assure the signals are pulled high when released by the master and slave. I2C only drives low and floats high.

    In regard to the '46' phenomenon, be sure to initialize your variables to a know value. Uninitialized variables are troublesome since you can't predict what they will be when going through a reset or powerup.
  • Raiful,

    If you are migrating code from an older Sellaris board to TM4C, this document might prove helpful as well.

    www.ti.com/.../spma050
  • Chuck Davenport said:
    using one of the I2C modules on chip as master and the second as slave.

    May I (highly) disagree?   Now this new user must master not only Master I2C but Slave I2C - as well.   And Slave operation has proved difficult - repeatedly - for many (most) here.   And - time/effort spent "massaging the Slave into operation" provides no benefit towards user's "Master Only" application.

    KISS (simplicity) dictates far simpler, small, I2C based EEProm.   No Slave operation is required - and ALL users have a much higher success rate w/this "KISS Compliant" method...