And "better ways" doesn't include using sprintf()...
Ooohhh that one tickles. But my position is defensible. The majority of my applications are written on PIC24 and PIC32 platforms using a cooperative multitasking approach. I spend a LOT of time waiting in one particular while loop. I can easily afford to sprinkle a ton of sprintf and printf wherever I like.
main.h:
Code:
#define TASK1 0
#define TASK2 1
#define TASK3 2
#define TASK4 3
#define TASK5 4
#define FIRST_TASK TASK1
#define LAST_TASK TASK5
#define MAIN_LOOP_TIME 5 // 5 mS
#define TASK_DELAY_TIME (MAIN_LOOP_DELAY * (LAST_TASK - FIRST_TASK + 1))
main.c:
Code:
void Sync_to_Real_Time( void )
{
// timer 2 is configured to set the T2IF flag every 5mS
if (T2IF)
printf("Sync error\r\n");
while (T2IF == 0)
;
T2IF = CLEAR_INT_FLAG;
}
void main( void )
{
Uint32 static task = FIRST_TASK; // this is from a PIC32, so a 32 bit value won't hurt me
Init_Hardware();
Show_Version();
while (1)
{
Sync_to_Real_Time();
Service_Display();
switch (task)
{
case TASK1:
Service_UI();
Service_Sleep_Timer();
break;
case TASK2:
Service_Temp_Sensors();
Service_Application();
break;
case TASK3:
Service_Debug_Port();
Service_Backlight();
break;
case TASK4:
Service_Sd_Card();
Service_Beeper();
break;
case TASK5:
Service_Rf_Module();
break;
}
task++;
if (task > LAST_TASK)
task = FIRST_TASK;
}
}
Given the structure of main(), I know (as long as I don't see sync errors spitting out of my port) that every task item will be called every 25mS and Service_Dsiplay() will be called every 5mS. The difficulty comes in when things are slooooowwww such as writing to a 320x240 color graphic LCD without a graphics accelerator. So any function that takes longer than 5 mS to execute gets cut down to a state machine. A short example:
Code:
Uint32* last_display_screen; // used to remember what screen is drawn on the display
void Service_Display( void )
{
switch (app_state)
{
case APP_SD_UTILITIES:
Draw_Sd_Utilities_Screen();
break;
case ...:
break;
}
}
void Draw_Sd_Utilities( void )
{
static Uint32 draw_state;
static Uint32 i;
if (last_dispaly_screen != (void*)Draw_Sd_Utilities)
{
draw_state = DRAW_BLANK_SCREEN;
i = 0;
}
switch (draw_state)
{
case DRAW_BLANK_SCREEN:
SetColor(BACKGROUND_COLOR);
Bar(GetMinX(), i, GetMaxX(), i+16);
i+=16;
if (i >= GetMaxY())
draw_state = CREATE_GRAPHIC_OBJECTS;
break;
case CREATE_GRAPHIC_OBJECTS:
...
break;
}
}
That format, while it makes the code more complex to read in the case of servicing the display, allows me to keep the display updated quite well, keep the UI snappy, scan for SD card being inserted/removed, read files on the SD card, communicate over RF, create an RS-232 debug menu, read adc/temperature sensors and digitally filter them with known time constants.
temp_sensor.c
Code:
void Service_Temp_Sensors( void )
{
// Device has two temp sensors, each one takes 380 mS to complete a conversion
// Read one every 200 mS and alternate between the two
#define TEMP_SENSOR_DELAY 200/TASK_DELAY_MS
static Uint32 delay_timer = 0;
static Uint32 sensor_number = 0;
Uint32 temp_sensor_result;
delay_timer++;
if (delay_timer >= TEMP_SENSOR_DELAY)
{
delay_timer = 0;
if (sensor_number == 0)
{
temp_sensor_result = Read_Temp_Sensor( 0 );
Update_Digital_Filter( &temp_sensor_0, temp_sensor_result );
sensor_number = 1;
}
else
{
temp_sensor_result = Read_Temp_Sensor( 1 );
Update_Digital_Filter( &temp_sensor_1, temp_sensor_result );
sensor_number = 0;
}
}
}
My digital filter acts similar to an RC filter...
Code:
void Update_Digital_Filter( filter_type* filter, Uint32 new_val )
{
static Uint32 init_flag = 0;
if (init_flag == 0)
{
filter->output = new_val;
filter->accumulator = new_val << filter->shift_value;
init_flag = 1;
}
else
{
filter->accumulator -= filter->output;
filter->accumulator += new_val;
filter->output = filter->accumulator>>filter->shift_value;
}
}
Since I know how often Update_Digital_Filter() is called, I can calculate and adjust the actual time constant of the filter easily using an Excel spreadsheet to set filter.shift_vaule at uC init.
All of this results in me sitting in my Sync_to_Real_Time() function, so I have
plenty of cycles left over for printf and sprintf
and I can easily time events with a static counter.
Edit: That is a lot of typing to defend my use of sprintf
Edit2: On the handful of PIC12 projects I've done, I don't use this method... and I avoid printf and floats like the plague as well