MCU User Forum
  USB to I2C request

Post New Topic  Post A Reply
profile | register | preferences | faq | search

UBBFriend: Email This Page to Someone! next newest topic | next oldest topic
Author Topic:   USB to I2C request
ymmace
New Member
posted May 11, 2010 10:28 PM     Click Here to See the Profile for ymmace     Edit/Delete Message
Hello all:

I just got the C8051F343 for development these days.
The target is to develop a firmware that supports:
PC <-> USB I/F <-> C8051F343 <-> I2C

Because I'm not familiar with any USB interface programming.
Could anyone tell me how to kick-off through the 3 USB example program provided by Silicon Labs.
And which part of the program could I embed my I2C sub-routine in?

Thanks for your supporting in advance.

IP: Logged

Scotty
Member
posted May 13, 2010 05:05 AM     Click Here to See the Profile for Scotty     Edit/Delete Message
Hi ymmace,

you didn't specify how do you want to communicate with the USB interface. VirtualCOMPort, etc.

I think the best way for you as a USB beginner is as follows:
- read through the USB examples
- also read the USBXpress documentation
- specify if you want to communicate with the MCU via a VCP implementation or via direct access (e.g.with USBXpress or alternative drivers)

From my sight, I think USBXpress implementation is the best choice for you because it takes most of the USB implementation away from you, so you can concentrate on the project itself. On the host side you must communicate with USBXpress DLL.

Regards,

Scotty

IP: Logged

ymmace
New Member
posted May 17, 2010 03:34 AM     Click Here to See the Profile for ymmace     Edit/Delete Message
Hi Scotty:

Thanks a lot for your reply.

After studying, I would like to apply by HID or Bulk mode.
I would not communicate with VirtualCOMPort because of communicate speed and previous bad experience.
I had applied 8051 with CP2012 before.
However, the baudrate is not quick enough and stability is not good enough.

If I apply bulk mode, how to I design my firmware flow?
Here is my assumption:
1. PC(USB Host) bulk out the command to C8051F343(USB device).
2. C8051F343 parsing the command. (Read or Write? Word or Page?)
3. C8051F343(I2C Host) send I2C signal to Device(I2C Slave).
4. Device(I2C Slave) feedback information to C8051F343(I2C Host).
5. C8051F343(USB device) bulk in the information to PC(USB Host).

However, I have no idea when to handle item-2 and item-5.
Watch certain flag or something?

IP: Logged

Scotty
Member
posted May 17, 2010 05:34 AM     Click Here to See the Profile for Scotty     Edit/Delete Message
Hi ymmace,

I would not communicate with VirtualCOMPort because of communicate speed and previous bad experience.
I had applied 8051 with CP2012 before.
However, the baudrate is not quick enough and stability is not good enough.

Don't consider MCU + USB-UART-Converter and USB-MCU + VCP implementation as the same.
The first approach has USB<->UART<->UART<->MCU, whereas the second is USB<->MCU, therefore the speed is always the same on the second approach because USB has fixed data rate, but there's no UART between the USB and the MCU. So MCU could implement VCP and therefore is recognized as serial port from the host, but the MCU can bridge the data to SPI/I2C/etc. anyway without any knowledge from host. So, the VCP could be recognized from any software already using serial communications, but the MCU controls how the data is processed. Sure there's some overhead, but speed should be much faster than MCU + USB-UART-bridge.

HID implementation is not very fast if I'm right, and bulk transfer needs deep driver development I think. So to answer your questions I suggest you tell us what you want to do exactly and then we can give better help.

Regards,

Scotty

IP: Logged

ymmace
New Member
posted May 17, 2010 05:56 AM     Click Here to See the Profile for ymmace     Edit/Delete Message
Hi Soccty:

It sounds good of your reply.
I will try to do with USBXpress first.

All I want is almost written in the original post.
I want to make a communication(bridge) board to program my target device.(ISP to 8051)
Something like this:
PC <-> USB <-> C8051F343 <-> I2C <-> Target device(8051 like)
The flash space of target device is about 64KB. And I want to finish the programming within 1 second.

How do you suggest?

Best regards
ymmace

IP: Logged

Scotty
Member
posted May 17, 2010 07:16 AM     Click Here to See the Profile for Scotty     Edit/Delete Message
Hi ymmace,

The flash space of target device is about 64KB. And I want to finish the programming within 1 second.
Hmm, if I'm correct maximum HID speed would be 64kB per USB endpoint, and USBXpress builds up on HID (not sure about it). So this wouldn't be the problem, but I don't think that your target could be programmed within one second with a serial programming interface. If I assume high-speed I2C interface with 1MBit/s it takes 0.5sec alone for the data transfer in best case (one adress set and 64kB program data) and I doubt that your target flash could be programmed within the remaining 7.5µs per byte (don't know if page write is supported and if yes how long it would take). I assume it would only be possible with either USB high-speed interface or parallel programming for your target, but this kicks of In-System-Programming capability.

So you must rethink about your specification/requirements.

Regards,

Scotty

IP: Logged

erikm
Member
posted May 17, 2010 08:53 AM     Click Here to See the Profile for erikm   Click Here to Email erikm     Edit/Delete Message
PC <-> USB <-> C8051F343 <-> I2C <-> Target device(8051 like)
The flash space of target device is about 64KB. And I want to finish the programming within 1 second.

since you do not specify what you want to program [Target device(8051 like] I will guess, without even going into the transmission time (see Scotty's post above), I doubt you can achieve 1 second, just the erase time will, probably, make that impossible.

Erik

IP: Logged

ymmace
New Member
posted May 17, 2010 05:19 PM     Click Here to See the Profile for ymmace     Edit/Delete Message
Hi Scotty:

Truly, the bottle neck of my project would be "Flash programming" and "I2C communication" after my applying USB communication between PC and bridge-board.
All I want to do first is to apply USB communication interface and make it NOT the bottle neck.
I will re-start my project with USBXpress according to your suggestion.

Thank you.

Hi erikm:

I work my project with an ASIC. The transmission time of I2C and programming time of Flash may not so ideal.
My first step is to improve the transmission time between PC <-> bridge board. The UART interface is applied in our previous bridge board.
Thanks to your reminding anyway.

Best regards
ymmace

IP: Logged

Talex
New Member
posted August 12, 2010 09:11 PM     Click Here to See the Profile for Talex     Edit/Delete Message
If you are still trying to get your USB to I2C code working I have some code examples I can provide, using both USBXpress and HID to I2C.

I have coded something very similar, using a C8051F340 that interfaces to a number of IC's on the I2C/SMBUS (a serial EEPROM, a S/PDIF receiver, a PLL chip, a voltage/current monitoring chip, and a digital amplifier IC) and records data which is read by a PC and displayed in a GUI written in VB6.

Originally we had used a Devasys USB to I2C bridge (a C8051F340 on a nice little development board that comes preloaded with USB to I2C firmware and has a windows API) with its original fw until I started writing my own and reflashing them, and modifying the windows GUI from the Devasys drivers to USBXpress, later HID...

IP: Logged

hjsteger
New Member
posted November 30, 2010 08:56 PM     Click Here to See the Profile for hjsteger   Click Here to Email hjsteger     Edit/Delete Message
Talex, I am interested in the USB-to-I2C code that you've developed. I've been working on developing code to run on C8051F340D that implements a USB (HID) interface to I2C Master. This function will be used in a system that runs a Linux operating system (so the HID driver must work under Linux). In addition, would you by chance have a bootloader for this as well?

IP: Logged

t_alex
New Member
posted January 06, 2011 01:19 PM     Click Here to See the Profile for t_alex   Click Here to Email t_alex     Edit/Delete Message
I'm not sure how to attach files here, but I can e-mail you what I've been working on.

I have a bootloader for the F340, it's based on the AN200 usbxpress bootloader but I modified it so I can use HID in my main firmware instead of usbxpress (usbxpress is still used for bootloading only).

My main firmware is based heavily on the HIDtoUART example, I modified the reporthandler function to decode my packets (I2C read or write, how many bytes, 8 or 16 bit sub-address, etc.), repackage each I2C request into a struct, then send a pointer to the struct to SMB_Read() or SMB_Write().

IP: Logged

t_alex
New Member
posted January 06, 2011 01:21 PM     Click Here to See the Profile for t_alex   Click Here to Email t_alex     Edit/Delete Message
this might get you started though:

//-----------------------------------------------------------------------------
// SMBus Interrupt Service Routine (ISR)
//-----------------------------------------------------------------------------
//
// Note: This is a high-priority ISR (it can interrupt other interrupts,
// most importantly the USB ISR, this is necessary because USB ISR
// calls SMB_Read_ISR() and SMB_Write_ISR() and then SMBUS ISR must
// fire for the SMBUS read/write functions to work properly)
//
void SMB0_ISR (void) interrupt 7 using 3 // switch register banks (faster)
{
static bit bSend_Address = 0;
static bit bSet_Read_Bit = 0;
static bit bWrite_Done = 0;
static bit bRetry = 0;

static unsigned char Byte_Counter;

switch (SMB0CN & 0xF0) // Status vector
{
case SMB_MTSTA: // 0xE0 -> (MT) start transmitted

if (bSMB_RST){
bSend_Address = 0;
bSet_Read_Bit = 0;
bRetry = 0;
bWrite_Done = 0;
bSMB_RST = 0;
}

if (bSet_Read_Bit)
{ // already wrote register address to read, set read bit (repeated start)
SMB0DAT = (pStructAddr->TARGET | 0x01);
}
else{
SMB0DAT = pStructAddr->TARGET;// Load address of the target slave
if (SMBUS_CTRL & 0xC0){
bSend_Address = 1; // Send internal address next after device ID
}
}

STA = 0; // Manually clear START bit
Byte_Counter = 0; // Reset the counter

EIE1 |= 0x02; // Re-enable USB0 Interrupts
break;

case SMB_MTDB: //0xC0 -> (MT) data byte transmitted

if (ACK){ // Slave ACK?
if (bSend_Address){
// Send register address next
if (SMBUS_CTRL & I2C_16ADR){
// If 16 bit sub-address mode
SMBUS_CTRL &= 0x7F;
SMB0DAT = pStructAddr->ADDRESS_16;
}
else{
bSend_Address = 0;
SMB0DAT = pStructAddr->ADDRESS;
}
}
else if (bSMB_RW)
{
// If transfer is a READ
if (!bSet_Read_Bit)
{
bSet_Read_Bit = 1; // Set read bit flag
STA = 1; // Send repeated START
}
}
else // If transfer is a WRITE
{
if (!bWrite_Done)
{ // send data byte
SMB0DAT = pStructAddr->DATA[Byte_Counter];
// check if write completed
if (Byte_Counter == (pStructAddr->COUNT)) bWrite_Done = 1;
// increment counter
++Byte_Counter;
}
else
{
STO = 1; // Set STO to terminate transfer
bSMB_BUSY = 0; // And free SMBus interface
bSet_Read_Bit = 0; // Make sure this flag is cleared (but it shouldn't be set)
bRetry = 0; // Reset the retry flag
bWrite_Done = 0; // Clear the write finished flag
}
}

}
else // If slave NACK
{
STO = 1; // Send STOP condition, abort
if (bRetry || (SMBUS_CTRL & 0x10))
{
bSMB_BUSY = 0;
if (pStructAddr == &I2C_Main) bSMB_FAIL = 1;
else bSMB_FAIL_ISR = 1;
bSMB_RST = 1; // set RST flag (resets all other SMBUS flags
// at start of next transfer)
}
else
{
ERROR_COUNT++; // indicate an error took place
bRetry = 1;
bSet_Read_Bit = 0;
bSend_Address = 0;
bWrite_Done = 0;
STA = 1;
}
}
break;

case SMB_MRDB: // 0x80 -> (MR) data byte received

pStructAddr->DATA[Byte_Counter] = SMB0DAT; // Store received byte
if (Byte_Counter == (pStructAddr->COUNT)){ // Check if read is finished
ACK = 0;
STO = 1; // Send STOP to terminate transfer
bSMB_BUSY = 0; // SMB_BUSY = 0
bSet_Read_Bit = 0;
bRetry = 0;
}
else {
++Byte_Counter;
ACK = 1; // Send ACK to indicate byte received
}
break;

default:

SMB0CF &= ~0x80; // Reset communication
SMB0CF |= 0x80;
STA = 0;
STO = 0;
ACK = 0;
bSend_Address = 0;
bSet_Read_Bit = 0;
bWrite_Done = 0;
bSMB_BUSY = 0;
bRetry = 0;
if (pStructAddr == &I2C_Main) bSMB_FAIL = 1;
else bSMB_FAIL_ISR = 1;
break;

} // end switch

PCA0CPH4 = 0x00; // clear watchdog timer
SI = 0; // Clear interrupt flag
}

IP: Logged

t_alex
New Member
posted January 06, 2011 01:30 PM     Click Here to See the Profile for t_alex   Click Here to Email t_alex     Edit/Delete Message
along with the above ISR goes this code:

//-----------------------------------------------------------------------------
// Global CONSTANTS
//-----------------------------------------------------------------------------

#define SMB_FREQUENCY 100000 // Target SCL clock rate (100kHz)

#define WRITE 0 // SMBus WRITE command
#define READ 1 // SMBus READ command
#define SMBUS_BUFFER_SIZE 256 // array length for read/write buffers

// Status vector - top 4 bits only
#define SMB_MTSTA 0xE0 // (MT) start transmitted
#define SMB_MTDB 0xC0 // (MT) data byte transmitted
#define SMB_MRDB 0x80 // (MR) data byte received
#define SMB_SRADD 0x20 // (SR) slave address received
// (also could be a lost
// arbitration)
#define SMB_SRSTO 0x10 // (SR) STOP detected while SR or ST,
// or lost arbitration
#define SMB_SRDB 0x00 // (SR) data byte received, or
// lost arbitration
#define SMB_STDB 0x40 // (ST) data byte transmitted
#define SMB_STSTO 0x50 // (ST) STOP detected during a
// transaction; bus error
// End status vector definition

// I2C transfer structure definition
struct I2CTrans
{
unsigned char TARGET; // Target SMBus slave ID
unsigned char ADDRESS_16; // Target SMBus slave address (16 bit)
unsigned char ADDRESS; // Target SMBus slave address (8, 16 bit)
unsigned char TRANSMODE; // Addressing mode
unsigned char COUNT; // Read/write byte count
unsigned char DATA[SMBUS_BUFFER_SIZE];
};

// I2C TRANSMODE status vector (sub-addressing mode)
#define I2C_NOADR 0x00
#define I2C_8ADR 0x40
#define I2C_16ADR 0x80

// ----------------------------------------------------------------------------
// Local Definitions
// ----------------------------------------------------------------------------

static volatile bit bSMB_RW; // SMBUS Read/write bit
static volatile bit bSMB_FAIL; // SMBUS critical error flag (Main)
static volatile bit bSMB_FAIL_ISR; // SMBUS critical error flag (ISR)
static volatile bit bSMB_BUSY; // SMBUS busy flag
static volatile bit bSMB_RST; // SMBUS reset flag

struct I2CTrans xdata * data pStructAddr; // SMBUS packet pointer

xdata volatile struct I2CTrans I2C_Main; // SMBUS packet (Main)
xdata volatile struct I2CTrans I2C_ISR; // SMBUS packet (ISR)

//-----------------------------------------------------------------------------
// Timer3 Interrupt Service Routine (ISR)
//-----------------------------------------------------------------------------
// A Timer3 interrupt indicates an SMBus SCL low timeout.
// The SMBus is disabled and re-enabled here
//
void Timer3_ISR (void) interrupt 14
{
SMB0CF &= ~0x80; // Disable SMBus
SMB0CF |= 0x80; // Re-enable SMBus
STA = 0;
STO = 0;
ACK = 0;
bSMB_RST = 1;
bSMB_BUSY = 0; // SMB_BUSY = 0

TMR3CN &= ~0x80; // Clear Timer3 interrupt-pending flag
}

//-----------------------------------------------------------------------------
// Free_SDA
//-----------------------------------------------------------------------------
//
void Free_SDA (void)
{
unsigned char i;

// If slave is holding SDA low because of an improper SMBus reset or error
while(!SDA)
{
// Provide clock pulses to allow the slave to advance out
// of its current state. This will allow it to release SDA.
// Timed to pulse SCL at approximately 100kHz @ sysclk = 48mHz

XBR1 |= 0x40; // Enable Crossbar
SCL = 0; // Drive the clock low
for(i = 0; i < 48; i++); // Hold the clock low
SCL = 1; // Release the clock
while(!SCL); // Wait for open-drain
// clock output to rise
for(i = 0; i < 64; i++); // Hold the clock high
PCA0CPH4 = 0x00; // clear watchdog timer
}
}

//-----------------------------------------------------------------------------
// SMB_Write
//-----------------------------------------------------------------------------
//
bit SMB_Write (void)
{
if (!(SMBUS_CTRL & 0x20)){ // if not Master Inhibit
EIE1 &= 0xFD; // Disable USB0 Interrupts

SMBUS_CTRL &= 0x3F;
SMBUS_CTRL |= I2C_Main.TRANSMODE;

bSMB_FAIL = 0; // set FAIL to 0
bSMB_RW = WRITE; // Mark this transfer as a WRITE
pStructAddr = &I2C_Main; // set struct address
bSMB_BUSY = 1; // claim bus as BUSY
STA = 1; // Start transfer
while (bSMB_BUSY); // Wait for transfer to complete
}
else bSMB_FAIL = 1;

if (bSMB_FAIL){ // if transfer failed
ERROR_COUNT++;
return 1; // return FAIL
}
else return 0; // return success
}

//-----------------------------------------------------------------------------
// SMB_Write_ISR
//-----------------------------------------------------------------------------
//
bit SMB_Write_ISR (struct I2CTrans xdata * buf)
{
if (!(SMBUS_CTRL & 0x20)){ // if not Master Inhibit
while (bSMB_BUSY); // Wait for SMBus to be free

SMBUS_CTRL &= 0x3F;
SMBUS_CTRL |= buf->TRANSMODE;

bSMB_FAIL_ISR = 0; // set FAIL to 0
bSMB_RW = WRITE; // Mark this transfer as a WRITE
pStructAddr = buf; // set struct address
bSMB_BUSY = 1; // claim bus as BUSY
STA = 1; // Start transfer
while (bSMB_BUSY); // Wait for transfer to complete
}
else bSMB_FAIL_ISR = 1;

if (bSMB_FAIL_ISR){ // if transfer failed
ERROR_COUNT++;
LED1 = 0; // set error LED
return 1; // return FAIL
}
else return 0; // return success
}

//-----------------------------------------------------------------------------
// SMB_Read
//-----------------------------------------------------------------------------
//
bit SMB_Read (void)
{
unsigned char i; // counter

if (!(SMBUS_CTRL & 0x20)) // if not Master Inhibit
{
EIE1 &= 0xFD; // Disable USB0 Interrupts

SMBUS_CTRL &= 0x3F;
SMBUS_CTRL |= I2C_Main.TRANSMODE;

bSMB_FAIL = 0; // set FAIL to 0
bSMB_RW = READ; // Mark this transfer as a READ
pStructAddr = &I2C_Main; // set struct address
bSMB_BUSY = 1; // claim bus as BUSY
STA = 1; // Start transfer
while (bSMB_BUSY); // Wait for transfer to complete
}
else return 1; // return fail

if (bSMB_FAIL) // if transfer failed
{
for (i=I2C_Main.COUNT; i; i--){
I2C_Main.DATA[i] = 0; // zero out data
}
I2C_Main.DATA[0] = 0; // zero out data
++ERROR_COUNT;
}
return bSMB_FAIL; // return status
}

//-----------------------------------------------------------------------------
// SMB_Read_ISR
//-----------------------------------------------------------------------------
//
bit SMB_Read_ISR (struct I2CTrans xdata * buf)
{
unsigned char i; // counter

if (!(SMBUS_CTRL & 0x20)) // if not Master Inhibit
{
while (bSMB_BUSY); // Wait for SMBus to be free

SMBUS_CTRL &= 0x3F;
SMBUS_CTRL |= buf->TRANSMODE;

bSMB_FAIL_ISR = 0; // set FAIL to 0
bSMB_RW = READ; // Mark this transfer as a READ
pStructAddr = buf; // set struct address
bSMB_BUSY = 1; // claim bus as BUSY
STA = 1; // Start transfer
while (bSMB_BUSY); // Wait for transfer to complete
}
else bSMB_FAIL_ISR = 1;

if (bSMB_FAIL_ISR) // if transfer failed
{
for (i=buf->COUNT; i; i--){
buf->DATA[i] = 0; // zero out data
}
buf->DATA[0] = 0; // zero out data
ERROR_COUNT++;
LED1 = 0; // set error LED
return 1; // return FAIL
}
else return 0; // return success
}

//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------

IP: Logged

t_alex
New Member
posted January 06, 2011 01:42 PM     Click Here to See the Profile for t_alex   Click Here to Email t_alex     Edit/Delete Message
and this goes in my Init.c:

//-----------------------------------------------------------------------------
// SMBus_Init
//-----------------------------------------------------------------------------
//
void SMBus_Init (void)
{
SMB0CF = 0x5D; // Use Timer1 overflows as SMBus clock
// source;
// Enable setup & hold time
// extensions;
// Enable SMBus Free timeout detect;
// Enable SCL low timeout detect;
// Slave mode inhibited (bit 6 set)
SMB0CF |= 0x80; // Enable SMBus;
EIE1 |= 0x01; // Enable the SMBus interrupt
EIP1 |= 0x01; // SMBus interrupt high priority
}

//-----------------------------------------------------------------------------
// Timer1_Init
//-----------------------------------------------------------------------------
//
void Timer1_Init (void)
{

// Make sure the Timer can produce the appropriate frequency in 8-bit mode
// Supported SMBus Frequencies range from 10kHz to 100kHz. The CKCON register
// settings may need to change for frequencies outside this range.

CKCON |= 0x08; // Timer1 clock source = SYSCLK

TMOD = 0x20; // Timer1 in 8-bit auto-reload mode

// Timer1 configured to overflow at 1/3 the rate defined by SMB_FREQUENCY
TH1 = -(SYSCLK/SMB_FREQUENCY/3);

TL1 = TH1; // Init Timer1

TR1 = 1; // Timer1 enabled
}

//-----------------------------------------------------------------------------
// Timer3_Init
//-----------------------------------------------------------------------------
//
// Timer 3 is the SMBUS SCL low timeout detector
//
void Timer3_Init (void)
{
TMR3CN = 0x00; // Timer3 configured for 16-bit auto-
// reload, low-byte interrupt disabled

CKCON &= ~0x40; // Timer3 uses SYSCLK/12

TMR3RL = -(SYSCLK/12/40); // Timer3 configured to overflow after
TMR3 = TMR3RL; // ~25ms (for SMBus low timeout detect):
// 1/.025 = 40Hz

EIE1 |= 0x80; // Timer3 interrupt enable
TMR3CN |= 0x04; // Start Timer3
}


And you may have noticed a reference to a variable called SMBUS_CTRL. This is what it does:

unsigned char SMBUS_CTRL = 0;
// bit 7: 1: 16 bit addressing mode (cleared after each transfer)
// bit 6: 1: 8 bit addressing mode
// bit 5: 1: Disable I2C master
// bit 4: 1: Disable retry on NACK
// bit 3: unused
// bit 2: unused
// bit 1: unused
// bit 0: unused
// END SMBUS_CTRL description

IP: Logged

All times are CT (US)

next newest topic | next oldest topic

Administrative Options: Close Topic | Archive/Move | Delete Topic
Post New Topic  Post A Reply
Hop to:

Contact Us | MCU User Forum

Have you seen our MCU Knowledge Base?


Ultimate Bulletin Board 5.47b