MSP430F5529 FreeRTOS port i2c example

Hi,

I am able to port freeRTOS onto the MSP430F5529 LP using code composer studio.

The source code provided below is an example I’m following.

I modified the example to try and add I2C functionality. The I2C code works well in a while(1) loop, but when I add an “i2c_readword()” to try and read 2 bytes from the same register in the RTOS Task 1 function, I start getting issues on the USCI_B0 peripheral. It looks like the I2C bus is getting out of sync when I put a Saleae logic analyzer on the transaction. I even removed one task and just went to a single task, but it still doesn’t work.

I tried to add a mutex block, not successful, still looks out of sync.

What could be going wrong? Is it my implementation?

My I2C driver does not use any low power modes, but I do trigger interrupts and when I’m waiting for the interrupts, I’m polling for a complete flag.

Sample Code Below.

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "queue.h"

static void prvSetupHardware( void )
{
	taskDISABLE_INTERRUPTS();

	WDTCTL = WDTPW + WDTHOLD;            // Stop watchdog timer
        
        //P1.0,LED1 for indicating sensors are gathering data
	P1DIR |= BIT0;
	P4DIR |= BIT7;//P4.7,LED2 for sending data to PC
}

void vBlink1(void * pvParameters)
{
	const TickType_t xDelay = 100 / portTICK_PERIOD_MS;
	for(;;)
	{
		P1OUT^=BIT0;
		//P4OUT^=BIT7;
		vTaskDelay( xDelay );
	}

}

void vBlink2(void * pvParameters)
{
	const TickType_t xDelay = 500 / portTICK_PERIOD_MS;
	for(;;)
	{
		P4OUT^=BIT7;
		//Tried to add I2C below
                //I2C_ReadWord();


		vTaskDelay( xDelay );
	}

}

int main( void )
{
	 /* Perform any hardware setup necessary. */
	 prvSetupHardware();
	 /* --- APPLICATION TASKS CAN BE CREATED HERE --- */
	 /* Start the created tasks running. */

	 xTaskCreate( vBlink1, "Blink1", 
                      configMINIMAL_STACK_SIZE, NULL, 1, NULL );
	 xTaskCreate( vBlink2, "Blink2",
                      configMINIMAL_STACK_SIZE, NULL, 1, NULL );
	 vTaskStartScheduler();
	 /* Execution will only reach here if there was 
            insufficient heap to start the scheduler. */
	 for( ;; );
	 return 0;
}


/*-----------------------------------------------------------*/

/* The MSP430X port uses this callback function to configure its tick interrupt.This allows the application to choose the tick interrupt source. configTICK_VECTOR must also be set in FreeRTOSConfig.h to the correct interrupt vector for the chosen tick interrupt source.  This implementation of vApplicationSetupTimerInterrupt() generates the tick from timer A0, so in this case configTICK_VECTOR is set to TIMER0_A0_VECTOR. */
void vApplicationSetupTimerInterrupt( void )
{
const unsigned short usACLK_Frequency_Hz = 32768;

	/* Ensure the timer is stopped. */
	TA0CTL = 0;

	/* Run the timer from the ACLK. */
	TA0CTL = TASSEL_1;

	/* Clear everything to start with. */
	TA0CTL |= TACLR;

	/* Set the compare match value according to 
           the tick rate we want. */
	TA0CCR0 = usACLK_Frequency_Hz / configTICK_RATE_HZ;

	/* Enable the interrupts. */
	TA0CCTL0 = CCIE;

	/* Start up clean. */
	TA0CTL |= TACLR;

	/* Up mode. */
	TA0CTL |= MC_1;
}
/*-----------------------------------------------------------*/

Does the code work as expected without the I2C in use (as per the code you posted)?

My first thought is the driver designed to work in FreeRTOS, using interrupts, or is it a ‘bare-metal’ driver that assumes it has total control of the system? Bare-metal drivers can have problems when just used blindly under an OS. Drivers like this also definably need a mutex in them or around them if they can be called by multiple tasks.

Hello Rtel,

Yes, it does work without the I2C_ReadWord();

The LEDs blink as expected in the given intervals.

I noticed there are different types of processes.

I’m using something in Blocking until a timer expires.’

However, is it better to go to something in a suspend state until the I2C reads are completed?

So in task 2, run the i2c_readword();

then xTaskResume(process 1)?

However, if both processes need to be timing accurate, is that still possible?

My tick rate is 1000 Hz.

Another thing I tried is just using a while(1) loop with the I2C driver. It reads out the data correctly and consistently.

I think something in the OS is executing during the i2c reads that perhaps times out the i2c engine. Does this mean I need to add some blocking mutex inside the i2c driver?

Thanks Richard,

I tried to create a Mutex similar to the following example.

It’s using global variables, however, what I noticed was that when the OS is wrapped around the I2C reads, it somehow goes out of sync.

The I2C driver is bare driver where it tries to access the HW peripheral registers. I do not have any MUTEX inside the I2C driver when it directly accesses the HW peripherals, but I didn’t think I needed any MUTEX because I’m calling the I2C_readword() only in one process and not in the other.

I eliminated the other task as well, since it was just blinking an LED at 100 ms intervals and just used the second task only. It still did not give consistent readings. Sometimes the readings would be correct, but at other times, it will not be correct.

/*

  • FreeRTOS includes
    */
    #include “FreeRTOS.h”
    #include “task.h”
    #include “timers.h”
    #include “queue.h”
    #include “semphr.h”

/*

  • Sensors includes
    */
    #include “SHT21.h”

float temperature[10]={0};
float humidity[10]={0};

/*

  • Semaphore for read and write I2C,only one sensor should have the access to
  • I2C at one time.
    */
    SemaphoreHandle_t I2C_mutex;

static void prvInitSensors()
{
initSHT21();
__no_operation();
}

static void prvSetupHardware( void )
{
taskDISABLE_INTERRUPTS();

WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
P1DIR |= BIT0;//P1.0,LED1 for indicating sensors are gathering data
P4DIR |= BIT7;//P4.7,LED2 for sending data to PC
clearI2CPort();
initI2C();
prvInitSensors();

}

void vMeasure1(void * pvParameters)
{
const TickType_t xDelay = 500 / portTICK_PERIOD_MS;
char pointer=0;
float temp;
for(;:wink:
{
P1OUT^=BIT0;
//P4OUT^=BIT7;

    /*
     * take mutex,wait for I2C to be free
     */
    xSemaphoreTake(I2C_mutex,portMAX_DELAY);

    /*
     * give back mutex
     */
    xSemaphoreGive(I2C_mutex);

    vTaskDelay( xDelay );
}

}

void vMeasure2(void * pvParameters)
{
const TickType_t xDelay = 500 / portTICK_PERIOD_MS;
char pointer=0;
float temp;
for(;:wink:
{
P4OUT^=BIT7;
//P1OUT^=BIT0;

    /*
     * take mutex,wait for I2C to be free
     */
    xSemaphoreTake(I2C_mutex,portMAX_DELAY);

    temp=getSHT21HUM();
    humidity[pointer]=temp;
    pointer=(pointer+1)%10;

    /*
     * give back mutex
     */
    xSemaphoreGive(I2C_mutex);
    vTaskDelay( xDelay );
}

}

int main( void )
{
/* Perform any hardware setup necessary. /
prvSetupHardware();
/
— APPLICATION TASKS CAN BE CREATED HERE — /
/
Start the created tasks running. */

 I2C_mutex=xSemaphoreCreateMutex();

 xTaskCreate( vMeasure1, "test I2C read/write", configMINIMAL_STACK_SIZE, NULL, 1, NULL );
 xTaskCreate( vMeasure2, "test I2C read/write", configMINIMAL_STACK_SIZE, NULL, 1, NULL );
 vTaskStartScheduler();
 /* Execution will only reach here if there 
   was insufficient heap to start the scheduler. */
 for( ;; );
 return 0;

}

void vApplicationSetupTimerInterrupt( void )
{
//code omitted
}
/-----------------------------------------------------------/

Small point, there isn’t much reason to take a mutex and then immediately give it. If you aren’t going to access the resource the mutex is guarding, you don’t need to take it. All that does is pause Measure1 if it runs while Measure2 is doing the I2C (which may throw off its timing)

The big issue with bare-metal drivers is that they assume they have total control over the PC, they will have spin loops checking for events (which use CPU time that other tasks could otherwise use) and assume that they will be responding quickly after the event occurs, which might not happen if the tasks gets switched away, which might cause hardware timeout errors.

You really want to write an interrupt based I2C driver designed for FreeRTOS.

Thanks Richard,

I do have interrupts getting triggered inside the i2c driver, but while waiting for the interrupt, I do use while loops which are using up CPU instructions.

Do you know if there are any UART examples for the MSP430s that I can reference?

I personally haven’t worked with MSP430s enough to have an personal example, but there are some in the Demo directory, that probably are doing it in a way that works with FreeRTOS.

Ok, thank you. I think I found a serial.h and serial.c example.

I will check those. It does look like it’s implemented a bit differently than what I have.