Search code examples
cembeddedprintfmsp430contiki

MSP430 printf function executes too slow


I try to use Sky mote (MSP430 F1611 + CC2420) to read the data from I2C sensor with 100Hz sampling frequency and write the data to serial port (USB). I tried a couple of tests and realized that the total output data have a sampling frequency of 78Hz. I used rtimer to profile my sampling and printing code and found the print function slows down the entire process. Here are some profiling outputs:

start 50628
15490,f074,20,3b8c,ffab,49,ffcf,fb70
end 51189
start 51293
15491,f0a8,fff4,3ba4,ffc6,24,ffd8,fb90
end 51880
start 51984
15492,f094,20,3b30,ffa7,5b,fff3,fb70
end 52544
start 52647
15493,f118,bc,3ce0,ffab,70,fffc,fb90
end 53207
start 53311
15494,f030,1b0,3b44,ffa9,1f7,1f,fb80
end 53871

The rtimer has 4098*8 ticks for one second. Here we can clearly see that the printing takes about 560 ticks (17ms). If the sampling frequency is 100Hz, the printing function should be completed within 10ms (327 ticks).

The embedded system I am using is Contiki OS and the baud rate is 115200 (maximum baud rate). The sensor sample contains 112bytes signed int.

Here is my code:

mpu_data_union samples;
int m=mpu_sample_all(&samples);
printf("start %u\n",RTIMER_NOW());
printf("%lu,%x,%x,%x,%x,%x,%x,%x\n",
  counterxx,samples.data.accel_x,samples.data.accel_y,
  samples.data.accel_z,samples.data.gyro_x,
  samples.data.gyro_y,samples.data.gyro_z,
  samples.data.temperature);
printf("end %u\n",RTIMER_NOW());

I hope some people with previous experience on optimizing printf or UART in general could provide some suggestions.

Thanks!


Solution

  • I had a similar-ish problem with the printf function just being far too big for the MSP variant that I was using so I had to write my own version of the printf function as I did not need all of the baggage associated with the format string converters. I wrote a set of little functions that output a value in the required format and send the character output directly to the usart.

    printf("%lu,%x,%x,%x,%x,%x,%x,%x\n", counterxx,samples.data.accel_x,samples.data.accel_y, samples.data.accel_z,samples.data.gyro_x, samples.data.gyro_y,samples.data.gyro_z, samples.data.temperature);

    becomes

    Send_Long_Decimal( counterxx ) ; 
    Send_Hex( samples.data.accel_x ) ; 
    Send_Hex( samples.data.accel_y ) ;
    Send_Hex( samples.data.accel_z ) ;
    Send_Hex( samples.data.gyro_x)  ;
    Send_Hex( samples.data.gyro_y ) ;
    Send_Hex( samples.data.gyro_z ) ;
    Send_Hex( samples.data.temperature ) ;
    Send_Newline() ;
    

    The answer from Morty suggests using the non-blocking, interrupt driven serial port implementation. This is to be encouraged so that the time taken for the serial transfer does not come out of your available processing time.

    Looking at the data sent out in your log, it would appear that the first record is around 64 characters long. Sending 100 of these at 115kbaud will take just over 0.5 seconds. You then have to read the data in from the I2C interface and perform some data processing/storage in the remaining less than 0.5 seconds.