Problem vTaskSuspend and xTaskResumeFromISR

Hi. I’m using gcc version 4.3.0 and FreeRTOS V5.0.0 for reference. I’m trying to use the vTaskSuspend and xTaskResumeFromISR to debug some servo code that I had modified. Theory of operation is that I have a task that does nothing, until all my ticks of my servo code has gone through and I have all the data, then it gets output onto a serial port using some avrlib stuff. The code is below. What boggles me is that I’ve used the vTaskSuspend and xTaskResumeFromISR in this manner for a I2C command processing with FreeRTOS V 4.3 and it worked wonderfully and so I did a direct port. When it didn’t work, meaning the DebugServo task never runs after it’s first suspension, I started throwing in toggles on a led to see where the code was going and where it wasn’t and I’ve marked which calls to LED_TOGGLE( YELLOW ) don’t work (no flashing light). The only other task running is just changes the servo position ever 50 milliseconds. I’m running this at a really high rate of 1ms ticks, but I don’t think that should be the problem… Any Ideas? /*! file servo_class.c brief Interrupt-driven RC Servo function library. */ //***************************************************************************** // // File Name    : ‘servo.c’ // Title        : Interrupt-driven RC Servo function library // Author       : Pascal Stang – Copyright (C) 2002 // Modified     : Jesse Welling – Copyright (C) 2008 // Created      : 7/31/2002 // Revised      : 4/02/2008 // Version      : 1.1 // Target MCU   : Atmel AVR Series // Editor Tabs  : 3 spaces = 1 tab // // This code is distributed under the GNU Public License //      which can be found at http://www.gnu.org/licenses/gpl.txt // //***************************************************************************** #include <avr/io.h> #include <avr/interrupt.h> #include "FreeRTOS.h" #include "task.h" #include "semphr.h" #include "../avrlib/global.h" #include "../avrlib/uart.h"    // include uart function library #include "../avrlib/rprintf.h" // include printf function library #include "servo.h" #include "../Robostix.h" /*roll over number*/ #define TOP   ((uint16_t)40000u) // handy to think with values #define CENTER_POSITION_MSX100   1500u #define MIN_POSITION_MSX100      1000u #define MAX_POSITION_MSX100      2000u // actual raw values #define CENTER_POSITION_RAW      (CENTER_POSITION_MSX100 * 2)  // 3000 #define MIN_POSITION_RAW         (MIN_POSITION_MSX100 * 2) // 2000 #define MAX_POSITION_RAW         (MAX_POSITION_MSX100 * 2) // 4000 /* Note: Since only 500 millisecondsX100 is between center and min, and likewise center to max, when multiplied *       by 2 to get the raw value that gives 1000 which then can be used as a signed percentX10 from center. *       Nifty! But only for 16Mhz clocks */ // Global variables // servo channel registers static volatile uint16_t ServoPosTics; static volatile uint8_t  ServoChannel; static const uint16_t ServoPeriodTics = TOP; xTaskHandle ServoDebugTaskHandle; typedef struct {     uint8_t  chan;    uint16_t inc;    uint16_t nxt;    uint16_t ocval;    uint16_t srvpos; }TimerDebug_T; TimerDebug_T ServoTimerDebugBuffer[3]; // functions static void DebugServo( void * param); //! initializes software PWM system void servoInit(void) {    uint8_t channel;       //TODO REMOVE DEBUG    uartInit();    uartSetBaudRate(115200);    rprintfInit(uartSendByte);    // disble the timer3 output compare A interrupt    cbi(ETIMSK, OCIE3A);    /* setup timer 3     *     * TCCR3A Bit 7:6 – COMnA1:0: Compare Output Mode for Channel A     * TCCR3A Bit 5:4 – COMnB1:0: Compare Output Mode for Channel B     * TCCR3A Bit 3:2 – COMnC1:0: Compare Output Mode for Channel C     * TCCR3A Bit 1:0 – WGMn1:0: Waveform Generation Mode     *     * TCCR3B Bit 4:3 – WGMn3:2: Waveform Generation Mode     * TCCR3B Bit 2:0 – CSn2:0: Clock Select     *     * TCCR3C Bit 7 – FOCnA: Force Output Compare for Channel A     * TCCR3C Bit 6 – FOCnB: Force Output Compare for Channel B     * TCCR3C Bit 5 – FOCnC: Force Output Compare for Channel C     *     */    TCCR3A |= 0x00;    TCCR3B = 0x02;    TCCR3C |= 0x00;    // enable and clear channels    for(channel=0; channel<NumberOfServoChannels; channel++)    {       // set minimum position as default       ServoChannels[channel]->duty_raw = CENTER_POSITION_RAW;       // set up DDR for each port.       *DDR(ServoChannels[ServoChannel]->port) |= ServoChannels[ServoChannel]->pin_mask;       // turn off current channel to break out of tristate. see page 67 of the data sheet       *(ServoChannels[ServoChannel]->port) &= ~(ServoChannels[ServoChannel]->pin_mask);    }    // set PosTics    ServoPosTics = 0;    // Start at just past the last forcing the intrrupts to start processing the first.    ServoChannel = NumberOfServoChannels + 1;    // set initial interrupt time    uint16_t OCValue = 0;    // read in current value of output compare register OCR3A    //OCValue = OCR3A;      // read OCR3A    OCValue =  inb(OCR3AL);      // read low byte of OCR3A    OCValue += inb(OCR3AH)<<8;   // read high byte of OCR3A    // increment OCR3A value by nextTics    OCValue += ServoPeriodTics;    ServoTimerDebugBuffer[0].ocval = ServoPeriodTics;    ServoTimerDebugBuffer[1].ocval = ServoPeriodTics;    ServoTimerDebugBuffer[2].ocval = ServoPeriodTics;    // set future output compare time to this new value    //OCR3A = OCValue;         // write 16bit    outb(OCR3AH, (OCValue>>8));         // write high byte    outb(OCR3AL, (OCValue & 0x00FF));   // write low byte    // enable the timer3 output compare A interrupt    sbi(ETIMSK, OCIE3A); } //! turns off software PWM system void servoOff(void) {    // disable the timer3 output compare A interrupt    cbi(ETIMSK, OCIE3A); } //! set servo position on channel void servoSetPosition(ServoChannel_T* this, int16_t percentX10) {    // input should be between 0 and SERVO_POSITION_MAX    uint16_t pos_scaled;    percentX10 = MAX(percentX10, -1000);    percentX10 = MIN(percentX10, 1000);       // calculate scaled position    pos_scaled = (uint16_t)( percentX10 + CENTER_POSITION_RAW);    // set position    servoSetPositionRaw(this, pos_scaled); } //! get servo position on channel int16_t servoGetPosition(ServoChannel_T* this) {    return ( (int16_t)servoGetPositionRaw(this) – CENTER_POSITION_RAW ); } //! set servo position on channel (raw unscaled format) void servoSetPositionRaw(ServoChannel_T* this, uint16_t position) {    // bind to limits    position = MAX(position, MIN_POSITION_RAW);    position = MIN(position, MAX_POSITION_RAW);    // set position    this->duty_raw = position;    //TODO REMOVE DEBUG        /*    rprintfu08(this->pin_mask);    rprintfChar(’@’);    rprintfu08(this->port);    rprintfChar(’:’);    rprintfu16(this->duty_raw);    rprintfCRLF();    */    } //! get servo position on channel (raw unscaled format) uint16_t servoGetPositionRaw(ServoChannel_T* this) {    return this->duty_raw; } void PrintServoDebug(void) {    {       rprintfu08(ServoTimerDebugBuffer[0].chan);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[0].inc);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[0].ocval);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[0].nxt);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[0].srvpos);       rprintfCRLF();    }    {       rprintfu08(ServoTimerDebugBuffer[1].chan);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[1].inc);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[1].ocval);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[1].nxt);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[1].srvpos);       rprintfCRLF();    }    {       rprintfu08(ServoTimerDebugBuffer[2].chan);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[2].inc);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[2].ocval);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[2].nxt);       rprintfChar(’ ‘);       rprintfu16(ServoTimerDebugBuffer[2].srvpos);       rprintfCRLF();    } } void StartDebugServoTask(void) {    xTaskCreate (DebugServo, "DbSrv", configMINIMAL_STACK_SIZE, NULL, ( tskIDLE_PRIORITY + 2), &ServoDebugTaskHandle); } static void DebugServo( void * param) {    for(;;)    {       PrintServoDebug();       vTaskSuspend( NULL );       //The toggle does not work here       //LED_TOGGLE( YELLOW );    } } ISR(TIMER3_COMPA_vect) {    uint16_t nextTics;    // schedule next interrupt    uint16_t OCValue;    if(ServoChannel < NumberOfServoChannels)    {       // turn off current channel       *(ServoChannels[ServoChannel]->port) &= ~(ServoChannels[ServoChannel]->pin_mask);    }       // next channel    ServoChannel++;    if(ServoChannel != NumberOfServoChannels)    {       // loop to channel 0 if needed       if(ServoChannel > NumberOfServoChannels)   ServoChannel = 0;       // turn on new channel       *(ServoChannels[ServoChannel]->port) |= ServoChannels[ServoChannel]->pin_mask;       // schedule turn off time       nextTics = ServoChannels[ServoChannel]->duty_raw;    }    else //(Channel == NumberOfServoChannels)    {       // ***we could save time by precalculating this       // schedule end-of-period       nextTics = ServoPeriodTics – ServoPosTics;    }    // read in current value of output compare register OCR3A,    // but within an interrupt so this direct access is safe    OCValue = OCR3A;   // read all 16 bits of OCR3A    //OCValue =  inb(OCR3AL);      // read low byte of OCR3A    //OCValue += inb(OCR3AH)<<8;   // read high byte of OCR3A    //TODO REMOVE DEBUG    ServoTimerDebugBuffer[ServoChannel].chan = ServoChannel;    ServoTimerDebugBuffer[ServoChannel].ocval = OCValue;    ServoTimerDebugBuffer[ServoChannel].inc++;    // increment OCR3A value by nextTics    OCValue += nextTics;    //   OCR3A+=nextTics; // but must protect the register so byte wise shift it in    // set future output compare time to this new value    // but within an interrupt so this direct access is safe    OCR3A = OCValue;    //outb(OCR3AH, (OCValue>>8));         // write high byte    //outb(OCR3AL, (OCValue & 0x00FF));   // write low byte    // set our new tic position    ServoPosTics += nextTics;    //TODO REMOVE DEBUG    ServoTimerDebugBuffer[ServoChannel].nxt = nextTics;    ServoTimerDebugBuffer[ServoChannel].srvpos = ServoPosTics;    if(ServoPosTics >= ServoPeriodTics)    {       ServoPosTics = 0;       //The toggle works here       LED_TOGGLE( YELLOW );       if (xTaskResumeFromISR(ServoDebugTaskHandle) == pdTRUE)       {          //The toggle does not work here          //LED_TOGGLE( YELLOW );          taskYIELD();       }    } }

Problem vTaskSuspend and xTaskResumeFromISR

The ResumeFromISR function has the following statement vTaskResumeFromISR() should not be used to synchronise a task with an interrupt if there is a chance that the interrupt could arrive prior to the task being suspended – as this can lead to interrupts being missed. Use of a semaphore as a synchronisation mechanism would avoid this eventuality. could that be the problem?

Problem vTaskSuspend and xTaskResumeFromISR

That was exactly it… Coincidentally, The example for vTaskResumeFromISR() has a "xSemaphoreCreateBinary()" when it should be a "vSemaphoreCreateBinary()". Had to look at the .h file to figure that one out ;) Thanks a bunch!

Problem vTaskSuspend and xTaskResumeFromISR

Correction: The example for xSemaphoreGiveFromISR() had the error…

Problem vTaskSuspend and xTaskResumeFromISR

>Correction: The example for xSemaphoreGiveFromISR() had the error Fixed – thanks for pointing this out. Regards.