I2C Slave sample code

キャンセル
次の結果を表示 
表示  限定  | 次の代わりに検索 
もしかして: 

I2C Slave sample code

ソリューションへジャンプ
11,772件の閲覧回数
intevac_coder
Contributor III

Hello,

 

Is there some sample slave I2C code for MQX available?  There are old threads that talk about it but perhaps those threads are stale?

 

Thank you!

0 件の賞賛
返信
1 解決策
4,939件の閲覧回数
intevac_coder
Contributor III

You're right - I posted incomplete code.  Sorry!

 

I found the file i2c_pol_ki2c.c located in C:\Program Files\Freescale\Freescale MQX 3.7\mqx\source\io\i2c\polled\

has a bug that needs fixing in 2 locations.  Attached is my updated version of the code with fixes.  The 2 fixes are outlined below:

 

About line 818 near the end of _ki2c_polled_rx_tx() in the slave receive section, the i counter is decremented to account for the I2C address coming in.  However, this throws off the to be received byte count by 1, causing the function to always be lagging.  Here's the correction to the polled Freescale driver:

 

io_info_ptr->STATE = I2C_STATE_ADDRESSED_AS_SLAVE_RX;

io_info_ptr->ONTHEWAY = TRUE;

tmp = i2c_ptr->D; /* dummy read to release bus */

// WAM! BUG - DECREMENTING i HERE CAUSES EXTRA BYTE TO ATTEMPT TO BE READ ON I2C SLAVE RECEIVE

// i RESTARTS AT 0. IF LENGTH = 2, THERE ARE 3 BYTE READS INSTEAD OF 2 BYTE READS.

// SINGLE LINE BELOW COMMENTED OUT.

//i--;

 

Making this adjustment requires an adjustment on about line 869 to be made.  The comparison isn't correct any more, either:

 

// WAM! IN ORDER FOR ABOVE CHANGE TO WORK, THE COMPARE MUST NOW BE <= INSTEAD OF <

// if(i < length)

if (i <= length)

{

   i2c_ptr->S |= I2C_S_IICIF_MASK; /* clear IICIF */

 

Below is the corrected application code.  The below example acts like a slave device reciving a register and making 2 different replies.  It runs in polling mode.  Unfortunately this consumes the CPU and effectively blocks tasks with a lower priority.  After doing some experimenting, I think the slave interrupt driver has a bug as well.  :smileysad:  Maybe I can get a consulting fee?  :smileyindifferent:

 

Exactly the code I have to make registered responses as slave work.  The semaphore was created in main.c to manage access to the I2C0 device.

 

 

void task_i2c_slave(uint_32 initial_data)

{

   I2C_STATISTICS_STRUCT stats;

   initialize_i2c0_slave();

   while(TRUE)

   {

      i2c0_reg_reply();

   _time_delay(DELAY_I2C0SLAVE_TASK);

   }

}

 

void initialize_i2c0_slave(void)

{

   fd_i2c_slave = fopen ("i2c0:", NULL); // Polled version

   if (fd_i2c_slave == NULL)

   {

      printf ("Failed to open the I2C0 slave driver!\n");

      _time_delay (200L);

      _mqx_exit (1L);

   }

   // Set I2C into slave mode & set the slave address

   if( I2C_OK == ioctl (fd_i2c_slave, IO_IOCTL_I2C_SET_SLAVE_MODE, NULL) )

   { printf("I2C0 slave mode GOOD\n"); }

   else

   { printf("I2C0 slave mode ERROR\n"); }

}

 

#define REGRD0 0X01

#define REGRD1 0x02

 

void i2c0_reg_reply(void)

{

   static uint_8 buffer[2] = {0x92, 0x15}; // Data to send to master request

   uint_32 result;

   uint_32 param;

   uint_8 recv_buffer[2] = {0, 0};

   uint_8 send1[4] = {0xca, 0x19, 0x83, 0xf5}; // Pretend register data

   uint_8 send2[5] = {0x83, 0x02, 0xb6, 0xb7, 0x2b}; // pretend register data

   uint_8 bytecount = 1;

   uint_8 switchval;

 

   result = _lwsem_wait(&lwsem_i2c0); // Capture the semaphore

   // This if statement must be in here, otherwise the I2C doesn't ACK correctly

   if(result != MQX_OK)

   { printf("\ni2c0_reg_reply lwsem_wait N2 failed 0x%X\n", result); }

   param = I2C0_SLAVE_ADDR;

   //result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);

   result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_DESTINATION_ADDRESS, &param);

 

   if(result != MQX_OK)

   { printf("I2C0 slave mode address ERROR\nCP>"); }

 

   param = I2C0_SLAVE_ADDR;

   bytecount = 1;

   result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);

   ioctl (fd_i2c_slave, IO_IOCTL_I2C_SET_RX_REQUEST, &bytecount);

   result = fread (recv_buffer, 1, bytecount, fd_i2c_slave);

   ioctl(fd_i2c_slave, IO_IOCTL_I2C_STOP, NULL); // Get driver to original state

 

   // Take the received register value and act on it

   switchval = recv_buffer[0]; // Capture the desired register address

   switch(switchval)

   {

   case REGRD0:

      bytecount = 4;

      param = I2C0_SLAVE_ADDR;

      result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);

      fwrite (&send1, 1, bytecount, fd_i2c_slave);

      ioctl (fd_i2c_slave, IO_IOCTL_I2C_STOP, NULL);

      break;

   case REGRD1:

      bytecount = 5;

      param = I2C0_SLAVE_ADDR;

      result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);

      fwrite (&send2, 1, bytecount, fd_i2c_slave);

      ioctl (fd_i2c_slave, IO_IOCTL_I2C_STOP, NULL);

   break;

   default:

   printf("\n%s\n%s", INVALID_I2C_REGISTER, COMMAND_PROMPT);

   }

   result = _lwsem_post(&lwsem_i2c0); // Release the semaphore

}

 

元の投稿で解決策を見る

0 件の賞賛
返信
6 返答(返信)
4,939件の閲覧回数
intevac_coder
Contributor III

I've got the I2C slave driver "half" working.  The unit receives bytes on the I2C0 port.  However, there is what I call a lag on the data coming in.  It seems as though there has to be a junk byte on the end of reading as a slave in order for things to work correctly.  When I want to receive 2 bytes, I have to transmit 3 bytes.  Interestingly, the 3rd byte is always dumped and never shows up.  Writing as a slave to the master works just fine.

 

The tough part is that reading as a master in IO_IOCTL_I2C_SET_MASTER_MODE works just fine.  The number of bytes matches and everything syncs up.  But in slave, as far as I've gotten, there has to be the junk byte on the end from the master to the K40 as a slave.

 

 Here is my code for i2c slave:

 

// Globals

FILE_PTR fd_i2c_slave;

#define I2C0_SLAVE_ADDR 0x50

 

void init_function(void)

{

// Open the I2C driver, and assign a I2C device handler

fd_i2c_slave = fopen ("i2c0:", NULL);

 if(fd_i2c_slave == NULL)

{

printf ("Failed to open the I2C0 slave driver!\n");

}

// Set I2C into slave mode & set the slave address

ioctl (fd_i2c_slave, IO_IOCTL_I2C_SET_SLAVE_MODE, NULL) ;

}

 

// Receive 2 bytes as a slave

void receive_function(void)

{

uint_8 recv_buffer[1] = {0};

uint_32 result;

uint_32 param;

 

// Set the slave address

param = I2C0_SLAVE_ADDR;

 result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);

if(result != MQX_OK)

{

printf("I2C0 slave mode address ERROR\nCP>");

}

 

//Set how many bytes to read

result = ioctl (fd_i2c_slave, IO_IOCTL_I2C_SET_RX_REQUEST, &bytestoread);

//buffer to read data into, size of unit, # of bytes to read, I2C device handler

result = fread (&recv_buffer, 1, bytestoread, fd_i2c_slave);

//Wait for completion

fflush (fd_i2c_slave);

printf("I2C0 received 0:%x 1:%x ", recv_buffer[0], recv_buffer[1]);

}

 

0 件の賞賛
返信
4,939件の閲覧回数
trailman
Contributor V

Thanks very much for the time spent to make it work !

I would like to help on the topic, but the board and Codewarrior (with its license) on which I work is at work and I can not currently work on it (i'm on some other projects).

 

However I have some remarks on your code :

 

uint_8 recv_buffer[1] = {0};  defines a buffer of only one byte, with first byte explicitely inited to 0. For your test you need at least 2 bytes, so to be safe I think you should use : uint_8 recv_buffer[3] = {0,0,0};

 

If I'm right, you set bytestoread to 2 and then "result = fread (&recv_buffer, 1, bytestoread, fd_i2c_slave);" returns when 3 bytes have been sent by master. In this case, what is the value in "result" ? 

 

Thanks again.

0 件の賞賛
返信
4,940件の閲覧回数
intevac_coder
Contributor III

You're right - I posted incomplete code.  Sorry!

 

I found the file i2c_pol_ki2c.c located in C:\Program Files\Freescale\Freescale MQX 3.7\mqx\source\io\i2c\polled\

has a bug that needs fixing in 2 locations.  Attached is my updated version of the code with fixes.  The 2 fixes are outlined below:

 

About line 818 near the end of _ki2c_polled_rx_tx() in the slave receive section, the i counter is decremented to account for the I2C address coming in.  However, this throws off the to be received byte count by 1, causing the function to always be lagging.  Here's the correction to the polled Freescale driver:

 

io_info_ptr->STATE = I2C_STATE_ADDRESSED_AS_SLAVE_RX;

io_info_ptr->ONTHEWAY = TRUE;

tmp = i2c_ptr->D; /* dummy read to release bus */

// WAM! BUG - DECREMENTING i HERE CAUSES EXTRA BYTE TO ATTEMPT TO BE READ ON I2C SLAVE RECEIVE

// i RESTARTS AT 0. IF LENGTH = 2, THERE ARE 3 BYTE READS INSTEAD OF 2 BYTE READS.

// SINGLE LINE BELOW COMMENTED OUT.

//i--;

 

Making this adjustment requires an adjustment on about line 869 to be made.  The comparison isn't correct any more, either:

 

// WAM! IN ORDER FOR ABOVE CHANGE TO WORK, THE COMPARE MUST NOW BE <= INSTEAD OF <

// if(i < length)

if (i <= length)

{

   i2c_ptr->S |= I2C_S_IICIF_MASK; /* clear IICIF */

 

Below is the corrected application code.  The below example acts like a slave device reciving a register and making 2 different replies.  It runs in polling mode.  Unfortunately this consumes the CPU and effectively blocks tasks with a lower priority.  After doing some experimenting, I think the slave interrupt driver has a bug as well.  :smileysad:  Maybe I can get a consulting fee?  :smileyindifferent:

 

Exactly the code I have to make registered responses as slave work.  The semaphore was created in main.c to manage access to the I2C0 device.

 

 

void task_i2c_slave(uint_32 initial_data)

{

   I2C_STATISTICS_STRUCT stats;

   initialize_i2c0_slave();

   while(TRUE)

   {

      i2c0_reg_reply();

   _time_delay(DELAY_I2C0SLAVE_TASK);

   }

}

 

void initialize_i2c0_slave(void)

{

   fd_i2c_slave = fopen ("i2c0:", NULL); // Polled version

   if (fd_i2c_slave == NULL)

   {

      printf ("Failed to open the I2C0 slave driver!\n");

      _time_delay (200L);

      _mqx_exit (1L);

   }

   // Set I2C into slave mode & set the slave address

   if( I2C_OK == ioctl (fd_i2c_slave, IO_IOCTL_I2C_SET_SLAVE_MODE, NULL) )

   { printf("I2C0 slave mode GOOD\n"); }

   else

   { printf("I2C0 slave mode ERROR\n"); }

}

 

#define REGRD0 0X01

#define REGRD1 0x02

 

void i2c0_reg_reply(void)

{

   static uint_8 buffer[2] = {0x92, 0x15}; // Data to send to master request

   uint_32 result;

   uint_32 param;

   uint_8 recv_buffer[2] = {0, 0};

   uint_8 send1[4] = {0xca, 0x19, 0x83, 0xf5}; // Pretend register data

   uint_8 send2[5] = {0x83, 0x02, 0xb6, 0xb7, 0x2b}; // pretend register data

   uint_8 bytecount = 1;

   uint_8 switchval;

 

   result = _lwsem_wait(&lwsem_i2c0); // Capture the semaphore

   // This if statement must be in here, otherwise the I2C doesn't ACK correctly

   if(result != MQX_OK)

   { printf("\ni2c0_reg_reply lwsem_wait N2 failed 0x%X\n", result); }

   param = I2C0_SLAVE_ADDR;

   //result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);

   result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_DESTINATION_ADDRESS, &param);

 

   if(result != MQX_OK)

   { printf("I2C0 slave mode address ERROR\nCP>"); }

 

   param = I2C0_SLAVE_ADDR;

   bytecount = 1;

   result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);

   ioctl (fd_i2c_slave, IO_IOCTL_I2C_SET_RX_REQUEST, &bytecount);

   result = fread (recv_buffer, 1, bytecount, fd_i2c_slave);

   ioctl(fd_i2c_slave, IO_IOCTL_I2C_STOP, NULL); // Get driver to original state

 

   // Take the received register value and act on it

   switchval = recv_buffer[0]; // Capture the desired register address

   switch(switchval)

   {

   case REGRD0:

      bytecount = 4;

      param = I2C0_SLAVE_ADDR;

      result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);

      fwrite (&send1, 1, bytecount, fd_i2c_slave);

      ioctl (fd_i2c_slave, IO_IOCTL_I2C_STOP, NULL);

      break;

   case REGRD1:

      bytecount = 5;

      param = I2C0_SLAVE_ADDR;

      result = ioctl(fd_i2c_slave, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);

      fwrite (&send2, 1, bytecount, fd_i2c_slave);

      ioctl (fd_i2c_slave, IO_IOCTL_I2C_STOP, NULL);

   break;

   default:

   printf("\n%s\n%s", INVALID_I2C_REGISTER, COMMAND_PROMPT);

   }

   result = _lwsem_post(&lwsem_i2c0); // Release the semaphore

}

 

0 件の賞賛
返信
4,939件の閲覧回数
trailman
Contributor V

Wow ! Thank you.

 

This is the first time I see a functional slave mode I2C example for MQX (with read+write as slave).

 

In addition to that, now I know that if I need to make it work with the interrupt driven I2C driver (the one I use for my MQX application), I'll have to spend some time to debug the MQX driver (this was not expected).

But as I will probably need a both master and slave interface, I guess it won't be easy ...

 

0 件の賞賛
返信
4,939件の閲覧回数
trailman
Contributor V

Welcome to the club. If you speed about the thread "how to implement a SLAVE-mode MQX i2c driver??", you're right, it is old (started one year ago), but it is still alive and we still searching/waiting for the slave I2C sample code.

There's some code in this thread but it's a very basic usage of slave mode I2C that is not enough for a real world usage.

0 件の賞賛
返信
4,939件の閲覧回数
intevac_coder
Contributor III

I tried to use the code in that example but it doesn't work on the tower system on I2C0.  No luck.  I'm going to try it on I2C1 after I rewire things.  Hopefully I'll come up with something & I'll let you know.

0 件の賞賛
返信