SAME54 I2C Driver Issue Solved

If you are reading this post that means you are using ATMEL or Microchip controller. We are using ATSAME54 micro from microchip, it is ARM based cortex-M4 series controller. In our application we have around eight I2C ( Inter-integrated Circuit) slave devices connected. I2C slave devices includes, RTC, onboard temperature sensor, FRAM, LED drivers etc. Well as you know all I2C slave devices can be addressed using their addresses.

Well as most of guys does we too have downloaded sample code from start.atmel.com and tested. We have SAME54 explained pro and we brought PCA9685 LED driver. This is simple I2C based LED driver and is easily available, its schematic and design also available from adafruit. With sample code and its default address it works like charm, meaning, in first go it starts communicating with LED driver.

Now fun part starts, after verifying this we have incorporated same design in our hardware. After we receive hardware, LED driver is not at all responding, it was not coming out of sleep mode. We have tried all the options, but no luck. Now when we are running same code on explained pro it is working. So only difference was the address. With PCA9685 module connected with explained pro as I2C master, we were using default address ie 0x40. Now when we change this address by shorting address lines, it stops working. We are getting “RXNACK” bit set on (received not acknowledged). Strange thing is if we revert back its address to default address ie 0x40 it works.

When we connected oscilloscope to SDA line we were getting data, ie address then registers.

I2C without driver change

Solution

Well after lot of googling and spending almost two weeks, we found solution, that was issue with ATMEL I2C driver source code generated from start.atmel.com. The solution is very simple once you found out the root cause :-). Yes it is bug in microchip’s I2C driver and its very unique.

I2C is very simple and we suspected hardware. Started with pull up registers for SDA and SCL lines, After trying out with various combinations of pull up registers (4K, 10K, 2.2k) we diverted our attention to I2C drivers. Yes, The culprit is – ‘i2c_m_sync_cmd_read()’ and ‘i2c_m_sync_cmd_write()’ functions from ‘hal\src\hal_i2c_m_sync.c’. The present code it is sending two bytes, but it is expected is to send three bytes. We have modified these read/write commands and it is working. Please note this change is necessary if you are using I2C and code downloaded from atmel start.

Follow below steps to fix this issue,

Open – ‘hal\src\hal_i2c_m_sync.c’

In function ‘i2c_m_sync_cmd_read()’ and ‘i2c_m_sync_cmd_write()’ comment out following code, so after commenting out the functions should look as below,

	struct _i2c_m_msg msg;
int32_t           ret;
#if 0
msg.addr   = i2c->slave_addr;
msg.len    = 1;
msg.flags  = 0;
msg.buffer = ®
ret = _i2c_m_sync_transfer(&i2c->device, &msg);
if (ret != 0) {
/* error occurred */
return ret;
}
#endif
msg.addr   = i2c->slave_addr;
msg.flags  = I2C_M_STOP;
msg.buffer = buffer;
msg.len    = length;
ret = _i2c_m_sync_transfer(&i2c->device, &msg);
if (ret != 0) {
/* error occurred */
return ret;
}
return ERR_NONE;

Now connect oscilloscope and check SDA line, it gives results as expected.

I2C after change in driver

If your I2C slave devices are configured with default addresses ie 0x00, then this change is not required, but remember to make this change every time you download I2C driver code from start.atmel.com. Share your findings on I2C issues while using ATMEL SAME54P20A controller.

Modbus Master Stack On ATSAME54 With RTOS

In previous post we discussed about how to port modbus slave stack on ATSAME54. This post we will discuss on how to port modbus master stack on SAME microcontroller. You can download example freemodbus master stack for reference from FreeModbus. We have referred the ‘AT91SAM7X_FREETROS’ example project. You can follow the steps from previous post for creating directory of project files. Instead of modbus slave stack, add modbus master stack. In addition, We are using FreeRTOS in our project. So In top level directory we have added FreeRTOS V9.0.0 . Please refer to modifications in port specific files described in previous post.

So lets begin with issues while port modbus master stack on SAME, Without an operating systems there are normally no queues available. In case of modbus stack without using RTOS, the queue sending and receiving functions are implemented using a static variable which is set to TRUE when an event is posted. The receive function checks the variable and if TRUE returns the previously stored event. But in case of an RTOS the functions should use a real event queue. We Changed the function xMBPortEventPost() to post an event to a queue. This function will be called from an ISR so we have used xMBPortEventPostFromISR() API for that. This is the interrupt safe version of xMBPortEventPost() provided in FreeRTOS.

We changd the function xMBPortEventGet( ) to retrieve an event from that queue . The function should block until an event has been posted to the queue.

Like this we are calling xQueueCreateStatic() API from vQueueCreate and vQueueDelete() API from vMBPEventDelete .

Next step is to define the functions vMBPEnterCritical( ) and vMBPExitCritical( ) in file portother.c. A switch to another task cannot occur between the calls to vMBPEnterCritical and vMBPExitCritical . Only interrupts whose logical priority is above the value assigned to the configMAX_SYSCALL_INTERRUPT_PRIORITY constant can execute in critical section and interrupts are not permitted to call FreeRTOS API functions. In FreeRTOS there are API’s portENTER_CRITICAL() and portEXIT_CRITICAL() which we have used respectively in above two functions.

Note that if we are calling portENTER_CRITICAL() and portEXIT_CRITICAL() functions from ISR, the following assertion will get fail: configASSERT((portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK) == 0);

If you are also stuck here, means you are calling these APIs from ISR. In FreeRTOS there are interrupt safe APIs which can be used inside ISR. So here in this case we found following two interrupt safe versions for above functions:

portSET_INTERRUPT_MASK_FROM_ISR() and portCLEAR_INTERRUPT_MASK_FROM_ISR()

Use thses APIs where the function is called from ISR:

In main function, we created two tasks using xTaskCreateStatic() API from FreeRTOS , one is application task and another is for modbus task .

TESTING ON SIMULATOR:

We have now created the modbus master workpace. For testing it, we have to run the modbus slave program on PC side. We are developing the stack on linux system. For this system we found out the Diagslave Modbus Slave Simulator . Install it in your system then execute ‘linux_x86-64/diagslave -h’ It will show the options you can configure. We executed simulator by giving the following command:

After running modbus master program, the simulator showing this output:

Happy to help you during your port modbus master stack on SAME controllers.