problem with events in multi-threaded code

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

problem with events in multi-threaded code

Jump to solution
1,537 Views
michele_novalia
Contributor III

Dear all,

I am writing a simple app using K40, MQX 3.7, where I have 2 threads.
In one thread (main) I handle capacitive touch and corresponding to which key gets pushed, in a second thread (audio) a different audio file should be played.
The whole point of having threads is that while one audio is playing and a different button is pressed, then the current audio should stop an a new one should start.
I am a beginner with MQX, so it is quite likely that I am doing something silly...

DESIGN:
I thought I could handle this with events, setting an event in main thread, and reading it from the audio thread.
Is this the best way of doing this?

PROBLEM:
when one audio is played, the board does not seem to respond to capacitive touch, so any new key press is ignored until the current audio terminates.
Both of the audios are played properly, but they are not interrupted by the other one.


Some code follows, please help if you notice anything obvious:

TASK_TEMPLATE_STRUCT MQX_template_list[] =
{
   {MAIN_TASK,   MainTask,   1000,  11,   "main",    MQX_AUTO_START_TASK},
   {AUDIO_TASK,  AudioTask,  3000,  10,      "audio",     MQX_AUTO_START_TASK|MQX_TIME_SLICE_TASK},
   {0,           0,           0,     0,   0,      0,                 }
};

/* main.c FILE */
/*****************************************************************/
[..] Main Task calls following:

void MainElCheck(void)
{
    _Electrodes_Check();

    if (ElectrodeFlags & TSS_ELECTRODE_0)
    {       
        _event_set(eventPtr, SOUND1);
    }   
    else if (ElectrodeFlags & TSS_ELECTRODE_1)
    {
        _event_set(eventPtr, SOUND2);
    }

    ElectrodeFlags = 0;   
}


/* audio.c */
/**********************************************************/
volatile BOOL isWriteSample = FALSE;
static BOOL isPlaying = FALSE;
static volatile Sound currentSound = AUDIO_OFF;
static pointer eventPtr = NULL;


/* ******************************************************************** */
void AudioTask(U32 initial_data)
{
    PlayerInit();             // initialization, follows
   
    while ( currentSound != AUDIO_OFF )
    {
        static _mqx_uint eventBits = 0;

        if ( _event_wait_any(eventPtr, ANY_SOUND, 0) != MQX_OK )
        {
            printf("\nERROR! AudioTask, event_wait_any failed!\n");
        }
       
        if ( _event_get_value(eventPtr, &eventBits) != MQX_OK )
        {
            printf("\nERROR! AudioTask, event_get_value failed!\n");
        }
        printf("DBG: Audio - passed wait for any, value: %u\n", eventBits);
   
        switch ( eventBits )
        {
            case SOUND1:
                {
                    currentSound = SOUND1;
                    isPlaying = TRUE;
                    printf("DBG: playing sound 1\n");
                    _event_clear( eventPtr, SOUND1 );
                   
                    while( isPlaying && (currentSound == SOUND1) )
                    {
                        isPlaying = !( PlayWave(SOUND1, array1Len) );
                        _event_get_value(eventPtr, &eventBits);

                        if ( eventBits )
                        {
                            printf("DBG: event changed! new sound is %d\n", eventBits);     ///!!!!! I never see this one
                            currentSound = eventBits;
                            isPlaying = FALSE;
                        }
                    }
                       
                    isPlaying = FALSE;
                }
                break;
            case SOUND2:
                {
                    currentSound = SOUND2;
                    isPlaying = TRUE;
                    printf("DBG: playing sound 2\n");
                    _event_clear( eventPtr, SOUND2 );
                   
                    while( isPlaying && (currentSound == SOUND2) )
                    {
                        isPlaying = !( PlayWave(SOUND2, array2Len) );
                        _event_get_value(eventPtr, &eventBits);

                        if ( eventBits != AUDIO_READY && eventBits != SOUND2 )
                        {
                            printf("DBG: event changed! new sound is %d\n", eventBits);    //!!!!! I never see this one
                            currentSound = eventBits;
                            isPlaying = FALSE;
                        }
                    }

                    isPlaying = FALSE;
                }
                break;

            default:
                currentSound = AUDIO_READY;
                eventBits = AUDIO_READY;
                _event_get_value(eventPtr, &eventBits);
                break;               
        }
    }   
   
    _event_close(eventPtr);
    _mqx_exit(0);
}

I am a bit blind here... any help appreciated!
Thanks
Mik

0 Kudos
1 Solution
888 Views
MarkP_
Contributor V

Hi,

Maybe the DAC write should be done in interrupt routine.

~Mark

 

View solution in original post

0 Kudos
10 Replies
888 Views
DavidS
NXP Employee
NXP Employee

Hi Mik,

Cool project!

Just guessing that your MainTask is blocked by your AudioTask since the AudioTask is at a higher priority than the MainTask.

You either need to have them both at same priority with MQX_TIME_SLICE_TASK set...this then sets the tasks up in a round robin mode

or in the AudioTask have a blocking call when it isn't doing work to allow the MainTask a chance to run

or setup the TSS to be in an interrupt routine

or also poll the TSS in a FlexTimer interrupt routine.

Events are a good way to do what you are trying to do.

Hope this helps.

Regards,

David

0 Kudos
888 Views
michele_novalia
Contributor III

I amnot sure this is what you meant, but I've tried the following as well:

 

   {MAIN_TASK,   MainTask,   1000,  11,   "main",    MQX_AUTO_START_TASK},
   {AUDIO_TASK,  AudioTask,  3000,  10,      "audio",     MQX_AUTO_START_TASK|MQX_TIME_SLICE_TASK},

 

and then in Audio task:

void _FlexTimer_FTM0_ISR(pointer isr)
{
    static _mqx_uint eventBits = 0;

    _event_get_value(eventPtr, &eventBits);
    
    if ( eventBits )
    {
        printf("DBG: event changed! new sound is %d\n", eventBits);
        currentSound = eventBits;
        isPlaying = FALSE;
    }
    
    isWriteSample = TRUE;
    FTM0_SC &= ~(1 << FTM_SC_TOF_SHIFT);
}

 

But the message "DBG...." is never printed, not even when I touch a key while audio is played (which was what I expected to see...)

 

0 Kudos
888 Views
MarkP_
Contributor V

Hi Mik,

I think the audio task eats all CPU time. What happens if you add a delay:

 

while( isPlaying && (currentSound == SOUND1) )
{
  isPlaying = !( PlayWave(SOUND1, array1Len) );
  _event_get_value(eventPtr, &eventBits);

  if ( eventBits )
  {
     printf("DBG: event changed! new sound is %d\n", eventBits);
     currentSound = eventBits;
     isPlaying = FALSE;
  }

  else

  {

    _time_delay_ticks(2);

  }

}

 

I recommend to use different priorities in different tasks, this ensures that tasks are

running always as their priority determines.

~Mark

 

0 Kudos
888 Views
michele_novalia
Contributor III

Hi Mark,

 

first of all thanks for sharing your ideas.

 

a) I added the dealy you suggested (tried just one as well), but if I add that in the audio task the sound that comes out is a terrible noise rather than a sound.

 

b) I then tried to add this delay in the main task instead, where the capacitive touch is handled, using following tasks configuration:

   {MAIN_TASK,   MainTask,   1000,  11,   "main",    MQX_AUTO_START_TASK},
   {AUDIO_TASK,  AudioTask,  3000,  12,      "audio",     MQX_AUTO_START_TASK|MQX_TIME_SLICE_TASK},

 

The result is that the audio can be interrupted (finally!) but the sound quality is very deteriorated comparing to the original sound. Unfortunately it is not acceptable...

 

c) if I give main task same or lower priority than audio task, then the sounds cannot be interrupted (although quality is Ok)

 

As a conclusion I think the daly is not a valid solution I am afraid :smileysad:

 

Thanks for helping though!

 

Mik

 

0 Kudos
888 Views
DavidS
NXP Employee
NXP Employee

Hi Mik,

I think you are on the right trail.

Try making the MainTask the highest priority task (so get rid of the time slice stuff) and add in the _time_delay(100) (the 100 is in milliseconds) to the MainTask.  That way the audio task runs most of the time and you are polling the touch 10 times a second.  Since that loop in main is short you should affect the audio quality.

Regards,

David

0 Kudos
888 Views
michele_novalia
Contributor III

Hi David,

 

thanks for your suggestion, IMO it makes sense.

 

However this is what I tried:

   {MAIN_TASK,   MainTask,   1000,  10,   "main",    MQX_AUTO_START_TASK},
   {AUDIO_TASK,  AudioTask,  3000,  12,      "audio",     MQX_AUTO_START_TASK},

 

Then adding in MainElectrodeCheck() a delay as it follows:

a)     _time_delay(100);

=> the audio is not interruptable

 

b)     _time_delay(10);

=> the audio is not interruptable & low quality

 

c)     _time_delay(1);

=> the audio is interruptable but low quality

 

I'm a bit puzzled here... it seems an exercise of fine tuning.

 

Mark, I'll give it a go at your idea as well, I just need to reorganize the code so that in the interrupt routine it is clear which sound should be played.

 

Thanks for your interest guys!

Mik

0 Kudos
889 Views
MarkP_
Contributor V

Hi,

Maybe the DAC write should be done in interrupt routine.

~Mark

 

0 Kudos
888 Views
michele_novalia
Contributor III

Mark,

 

bingo!

that was the trick, it seems the audio is both: interruptable and good quality when played inside the interrupt routine!

 

Thank you very much for this!

(Now I wish to know why is so :smileyhappy:

 

Mik

0 Kudos
888 Views
MarkP_
Contributor V

Hi Mik,

Nice to hear that sound is OK.

One more improvement left:

If there are other (heavy) interrupts running or there are long _int_disable().._int_enable() code blocks, the DAC write may be delayed.

The improvement is to use DAC-buffer and watermark interrupt / event. When this watermark position is reached the

next buffer data is written in task or in interrupt.

The watermark is set in Processor Expert code driver by calling DA1_SetBufferWatermark(), or when using DAC driver by calling DAC_SetBufferWatermark(DAC_DevicePtr, DAC_Watermark);

(Example in ..\Freescale MQX 3.7\mqx\examples\dac)

I haven't used the DAC feature in my project, cannot help more.

Maybe David knows this feature better.

~Mark

 

0 Kudos
888 Views
michele_novalia
Contributor III

Hello David,

 

thanks for your reply.

I have tried the following two:


a)
   {MAIN_TASK,   MainTask,   1000,  11,   "main",    MQX_AUTO_START_TASK},
   {AUDIO_TASK,  AudioTask,  3000,  11,      "audio",     MQX_AUTO_START_TASK|MQX_TIME_SLICE_TASK},

b)
   {MAIN_TASK,   MainTask,   1000,  11,   "main",    MQX_AUTO_START_TASK|MQX_TIME_SLICE_TASK},
   {AUDIO_TASK,  AudioTask,  3000,  11,      "audio",     MQX_AUTO_START_TASK|MQX_TIME_SLICE_TASK},

 

In both of the cases no audio is ever played, as it seems that the Touching event is not caught from Audio...

it seems the audio task never goes after this:

        if ( _event_wait_any(eventPtr, ANY_SOUND, 0) != MQX_OK )

 

About this:

"or in the AudioTask have a blocking call when it isn't doing work to allow the MainTask a chance to run"

 

I think this shouldn't work as while one audio is playing (ie audio is doing work) and a key is pressed it wont be noticed (if I understood it correctly).

 

Finally, please forgive my MQX ignorance:

 

"or setup the TSS to be in an interrupt routine or also poll the TSS in a FlexTimer interrupt routine."

 

Would you mind to elaborate these concepts please?

 

Thanks,

Mik

 

0 Kudos