Welcome! Log In Create A New Profile

Advanced

BV511 & 4213 I2C

Posted by Jeremy 
BV511 & 4213 I2C
January 17, 2013 01:27PM
I'm trying to control a motor using the BV4213, connected to the I2C0 of a BV511.
I currently have an LED on each output Y1,2,3,4 to test the setup, which also use the 5V feed from the 511.
The whole thing is in C++, and based on the Keil example I2C interface for the LPC210x, although I've modified this to correct the changed register names etc for the LPC213x.
I'm programming it in uVision, and using the Philips flash utility to upload the flash.
The processor programs ok, but the I2C never establishes a connection.
When I run uV debug, the I2C0 peripheral outputs the start, address (42), and the first data byte which it then repeats due to nAck (I haven't coded a function to simulate the acknowledges), and so the output seems to be what I would expect.

I've added 10k pullup resistors to the SCK and SDA lines on the 4213, and the clock and data looks clean.
I've set I2C0SCLL and I2C0SCLH to 0x2F0 which gives me a slow 12.5khz SCK (I thought maybe it was struggling with the 10k's but apparently not..)

I've tried forcing the 4213 into Step mode, and it seems to work ok, but I have no way of testing the I2C in any other way.

Anyone got any suggestions I can try???

I can paste the code in here if anyone fancies a stab at it...
Cheers
Jeremy
Re: BV511 & 4213 I2C
January 18, 2013 09:51AM
/************************************************************/
/* PROJECT: I2C test platform for LPC2132 */

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

#include <LPC213x.H>
#include <stdarg.h>

void I2CISR (void) __irq ; //I2C interrupt routine
void I2CTransferByte(unsigned int I2CAddr,unsigned char MemAddr,unsigned char count,...);
//Background call to start master send and receive byte transfers

unsigned char enable1[2] = {0x01,0x01}; //enable command for motor controller outputs Y1 and Y2
unsigned char enable2[2] = {0x02,0x01}; //enable command for motor controller outputs Y3 and Y4
unsigned char turnon1[2] = {0x12,0x01}; //switches output Y2 to 1
unsigned char turnon2[2] = {0x13,0x01}; //switches output Y3 to 1


unsigned char messageIn[4]; //used to read in from swithes - unused
unsigned char *I2CData,
I2Counter,
I2CAddress,
MemAddress,
lock; //Define Function prototypes and Global variables

// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX MAIN XXXXXXXXXXXXXXXXXXXXXXXXXXXXx
int main(void)
{
lock = 0; //Initilise the lock flag


// this block sets up the pins, and the clock rate, it used to be after the VIC setup block
PINSEL0 = 0x50; //Switch GPIO to I2C pins
I2C0CONCLR = 0x000000FF; //Clear all I2C settings - JF added here, but also in transfer byte
I2C0CONSET = 0x00000040; //Enable the I2C interface set I2EN - JF added here, but also in transfer byte
I2C0SCLH = 0x2F0; //Set bit rate 14.7456Mhz/VPBDIV+SCLH+SCLL = 14.7456/4+8+8 = 57.6Khz
I2C0SCLL = 0x2F0; // JF changed from 08 to 2F0 should now be 12.5khz

// Vectored interrupt control setup block

VICVectCntl1 = 0x00000029; //select priority 1 for I2C0 interupt and enable it
VICIntEnable = 0x00000200; //enables the I2C0 interupt
VICVectAddr1 = (unsigned)I2CISR; //pass the address of the IRQ into the VIC slot

// Block writes data to the I2C
I2CTransferByte(0x42,0,2,enable2); //write data to the I2C Memory - write to device 0x42, start at byte 0 of the message, send 2 bytes.
I2CTransferByte(0x42,0,0); //set address to zero

I2CTransferByte(0x42,0,2,turnon2); //write data to the I2C Memory - write to device 0x42, start at byte 0 of the message, send 2 bytes.
I2CTransferByte(0x42,0,0); //set address to zero

while(1)
{
;
}
}

// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Transfer XXXXXXXXXXXXXXXXXXXXXXXXXXXX
void I2CTransferByte(unsigned int I2CAddr,unsigned char MemAddr,unsigned char count,...)
{
va_list ap; // allows variable arguments to be passed in i.e. data volume to be sent
va_start(ap,count);

while(lock == 1) //Wait for interrupt to signal end of I2C activity
{
;
}
lock = 1; //Set I2C bus as active

I2CAddress = I2CAddr; //Place address and data in Globals to be used by the interrupt
if(count >0)
{
I2CData = va_arg(ap,unsigned char *);
}
I2Counter = count;
MemAddress = MemAddr;
I2C0CONCLR = 0x000000FF; //Clear all I2C settings
I2C0CONSET = 0x00000040; //Enable the I2C interface set I2EN
I2C0CONSET = 0x00000020; //Start condition set STA
va_end(ap);
}

// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX I2CISR XXXXXXXXXXXXXXXXXXXXXXXXXXXXx
void I2CISR (void) __irq //I2C interrupt service routine
{

switch (I2C0STAT) //Read Status code and switch to next action
{
case ( 0x08): //Start bit
I2C0CONCLR = 0x20; //Clear start bit
I2C0DAT = I2CAddress; //Send address and write bit

break;

case (0x18): //Slave address+W, ACK
I2C0DAT = MemAddress; //Write Mem,ory start address to tx register
break;

case (0x20): //Salve address +W, Not ACK
I2C0DAT = I2CAddress; //Resend address and write bi
break;

case (0x28):
if(I2Counter-->0) //Data sent, Ack
{
I2C0DAT = *I2CData; //Write data to tx register
I2CData++;
}
else
{
I2C0CONSET = 0x10; //Stop condition
lock = 0; //Signal end of I2C activity
}
break;

case (0x30) : //Data sent, NOT Ack
I2C0DAT = *I2CData; //Write data to tx register
break;


//Receive byte conditions

case (0x40) : //Slave Address +R, ACK
I2C0CONSET = 0x04; //Enable ACK for data byte
break;

case (0x48) : //Slave Address +R, Not Ack
I2C0CONSET = 0x20; //Resend Start condition
break;

case (0x50) : //Data Received, ACK
if(--I2Counter>0)
{
*I2CData = I2C0DAT;
I2CData++;
}
else
{
I2C0CONSET = 0x10; //Stop condition
lock = 0; //Signal end of I2C activity
}
break;

case (0x58): //Data Received, Not Ack
I2C0CONSET = 0x20; // Resend Start condition
break;

default :
break;

}

I2C0CONCLR = 0x08; //Clear I2C interrupt flag
VICVectAddr = 0x00000000; //Clear interrupt in

}
Re: BV511 & 4213 I2C
January 18, 2013 05:06PM
1) The most common fault is lack of pull up resistors but you have that covered, 10k is a bit high but fine

2) Address: The address of the BV4213 is 0x42 but that is the8 bit address, the 7 bit address is 0x21 and which one to use is all dependant on how reading and writing is done on the I2C bus. To make it exactly clear, command 1 on theBV4213 will enable / disable Y1 & Y2, this can be checked with a meter to verify the operation, the enable pins are either pin 1 or pin 9 on the L293 - data sheet is on www.doc.byvac.com website.

<start condition> <0x42> <1> <1> <stop condition>

The action of sending the bytes above WILL effect either pins 1 or 9 as described above. The complication arises because of how the 'send_i2c' works: does it invoke the start condition? Does it send the address? Does it send the address in 7 bit format with the last bit held low? Does it send the stop condition after sending the address? The point is if you can ensure that the above set of bytes are sent then it will work.

3) Clock stretching. The slave device will hold the clock line low if it needs a bit more time to do something, the master 'should' detect this and wait until the clock is released by the slave. Not all slaves behave properly and blindly try and clock another byte even though the clock line is not having any effect because the slave is holding onto the line. The Raspberry Pi does not control the master properly. The easy way round this is to put a delay between sending bytes, not satisfactory for a working system but will indicate a poorly implemented master.

Have a look at this site ForthLib i2c and i2c.flb not C++ but it is a working version of I2C using the hardware on the ARM, it may shed some light on the problem.
Re: BV511 & 4213 I2C
January 22, 2013 01:09PM
Hi Jimeer;
Thanks for the feedback, but so far I'm no closer to resolving the problem...
1) On closer inspection, the BV511 also uses 10k pull-ups, so I actually have 10k on each end of the 15cm cable (5k total on each line) so should be no problem, especially at this speed.

2A) The address I'm using is 0x42 which is passed to the I2C0DAT register on the LPC2132 which is 8 bits (7 plus the direction bit). This shows up in the debug window as actual data (0x42) and as address (0x21), so I'm pretty sure this is handled correctly.

2b) After the <start> <0x42> I'm sending two bytes of data . These are <0x02><0x01> to enable Y3 and Y4. A second message then switches on Y3 ( <start><0x42><0x13><0x01> ).
Needless to say, Y3 does not switch on, and indeed Y3 and Y4 do not become enabled (by measuring pin 9 on the L293).

3) I've tried adding a delay in case clock stretching is the issue, but with no effect - especially at this SCK speed.

Assuming the device is functioning correctly and has the address 0x42 (much more likely that I'm doing something dumb), then I have an error in my code somewhere. I'll see if I can catch the data on my ancient scope and maybe glean something from that.....

Cheers
Jeremy
Re: BV511 & 4213 I2C
January 23, 2013 10:20AM
Right, I have finally fixed this problem, so here is the corrected code for anyone who wants to try and get started with this pair of boards using C++.
The main error was down in the Case statement where the contents of the address pointer were mistakenly passed to the data out, meaning that a byte of zero was sent first. This code is not very helpful in it's current format, and needs restructuring to make it do anything useful, but maybe it helps someone get started. In it's current form, I am using the 10k pull ups on the BV511, and I've added two more on the 4213 end also. I have about 15cm of unscreened wire 4off ~19/0.2 and I'm running test LEDs out of each output (Y1/2/3/4) on the same 5V supply, with ~330R resistors.
The code enables all outputs, and switches on Y2 and Y3 (High), Y1 and Y4 remain off (low). Good Luck. Jeremy
/************************************************************/
/* PROJECT: I2C test platform */

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

#include <LPC213x.H>
#include <stdarg.h>

void I2CISR (void) __irq ; //I2C interrupt routine
void I2CTransferByte(unsigned int I2CAddr,unsigned char MemAddr,unsigned char count,...);
//Background call to start master send and receive byte transfers

unsigned char enable1[2] = {0x01,0x01}; //enable command for motor controller outputs Y1 and Y2
unsigned char enable2[2] = {0x02,0x01}; //enable command for motor controller outputs Y3 and Y4
unsigned char turnon1[2] = {0x12,0x01}; //switches output Y2 to 1
unsigned char turnon2[2] = {0x13,0x01}; //switches output Y3 to 1
unsigned char step1[1] = {0x20}; //continuous step

unsigned char messageIn[4]; //used to read in from swithes - unused
unsigned char *I2CData,
I2Counter,
I2CAddress,
MemAddress,
lock; //Define Function prototypes and Global variables

// delay added for between command and data bytes needed by BV4213
void delay (void)
{
unsigned int count = 0;
for ( count = 0; count <= 0xFFF; count++){
}
return;
}

// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX MAIN XXXXXXXXXXXXXXXXXXXXXXXXXXXXx
int main(void)
{
lock = 0; //Initilise the lock flag


// this block sets up the pins, and the clock rate, it used to be after the VIC setup block
PINSEL0 = 0x50; //Switch GPIO to I2C pins
I2C0CONCLR = 0x0000002C; //Clear all I2C settings - JF added here, but also in transfer byte
I2C0CONSET = 0x00000040; //Enable the I2C interface set I2EN - JF added here, but also in transfer byte
I2C0SCLH = 0x1F0; //Set bit rate 14.7456Mhz/VPBDIV+SCLH+SCLL = 14.7456/4+8+8 = 57.6Khz
I2C0SCLL = 0x1F0; // Changed to 1F0 ~24khz, seems to be speed limited, poss due to 10k pull ups

// Vectored interrupt control setup block

VICVectCntl1 = 0x00000029; //select priority 1 for I2C0 interupt and enable it
VICIntEnable = 0x00000200; //enables the I2C0 interupt
VICVectAddr1 = (unsigned)I2CISR; //pass the address of the IRQ into the VIC slot

// Block writes data to the I2C
I2CTransferByte(0x42,0,2,enable1); //write data to the I2C Memory - write to device 0x42, start at byte 0 of the message, send 2 bytes.
I2CTransferByte(0x42,0,2,enable2); //write data to the I2C Memory - write to device 0x42, start at byte 0 of the message, send 2 bytes.
//I2CTransferByte(0x42,0,0); //set address to zero - doesn't seem to do anything..?
//delay (); //delay 1
I2CTransferByte(0x42,0,2,turnon2); //write data to the I2C Memory - write to device 0x42, start at byte 0 of the message, send 2 bytes.
I2CTransferByte(0x42,0,2,turnon1); //write data to the I2C Memory - write to device 0x42, start at byte 0 of the message, send 2 bytes.
//I2CTransferByte(0x42,0,0); //set address to zero

while(1)
{
;
}
}

// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Transfer XXXXXXXXXXXXXXXXXXXXXXXXXXXX
void I2CTransferByte(unsigned int I2CAddr,unsigned char MemAddr,unsigned char count,...)
{
va_list ap; // allows variable arguments to be passed in i.e. data volume to be sent
va_start(ap,count);

while(lock == 1) //Wait for interrupt to signal end of I2C activity
{
;
}
lock = 1; //Set I2C bus as active

I2CAddress = I2CAddr; //Place address and data in Globals to be used by the interrupt
if(count >0)
{
I2CData = va_arg(ap,unsigned char *);

}
I2Counter = count;
MemAddress = MemAddr;
I2C0CONCLR = 0x0000002C; //Clear all I2C settings - was FF but user manual reserves some bits
I2C0CONSET = 0x00000040; //Enable the I2C interface set I2EN
I2C0CONSET = 0x00000020; //Start condition set STA
va_end(ap);
}

// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX I2CISR XXXXXXXXXXXXXXXXXXXXXXXXXXXXx
void I2CISR (void) __irq //I2C interrupt service routine
{


switch (I2C0STAT) //Read Status code and switch to next action
{
case ( 0x08): //Start Condition has been sent
I2C0CONCLR = 0x20; //Clear start bit
I2C0DAT = I2CAddress; //Now send the Target address and write bit
break;


case (0x18): //Target address and write bit were sent and acknowledged by target
I2C0DAT = *I2CData; //Send the first byte of data
I2Counter--;
delay (); //This delay seems to be needed by the BV4213 - not sure why yet...
break;


case (0x20): //Target address was sent but Not acknowledged by target
I2C0DAT = I2CAddress; //Resend the target address and write bit
break;


case (0x28):
if(I2Counter-->0) //Data was sent, and acknowledged by target
{
I2CData++;
I2C0DAT = *I2CData; //Write data to tx register
}
else
{
I2C0CONSET = 0x10; //Stop condition
lock = 0; //Signal end of I2C activity
}
break;


case (0x30) : //Data was sent, but NOT Ack
I2C0DAT = *I2CData; //Send same data to tx register
break;


//Receive byte conditions

case (0x40) : //Slave Address +R, ACK
I2C0CONSET = 0x04; //Enable ACK for data byte
break;

case (0x48) : //Slave Address +R, Not Ack
I2C0CONSET = 0x20; //Resend Start condition
break;

case (0x50) : //Data Received, ACK
if(--I2Counter>0)
{
*I2CData = I2C0DAT;
I2CData++;
}
else
{
I2C0CONSET = 0x10; //Stop condition
lock = 0; //Signal end of I2C activity
}
break;

case (0x58): //Data Received, Not Ack
I2C0CONSET = 0x20; // Resend Start condition
break;

default :
break;

}

I2C0CONCLR = 0x08; //Clear I2C interrupt flag
VICVectAddr = 0x00000000; //Clear interrupt in

}
Re: BV511 & 4213 I2C
January 23, 2013 11:11AM
Jeremy,
that is a useful bit of code and should help with any I2C board on the BV511, good work.
Sorry, only registered users may post in this forum.

Click here to login