This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2541: TASKDONE occasionally never gets set after issuing CMD_TXFIFO_RETRY command

Other Parts Discussed in Thread: CC2541

This probably is very similar to this one:

but with a few additional gotchyas.

Our product has a few external controls on it (buttons, for example) that can trigger an interrupt on Port1.  If the interrupt happens when the device is asleep (PM2), the ISR runs and the device goes back to sleep until the sleep timer expires.  Once the sleep timer expires (once a second), the device runs through its main loop, takes some measurements, and then advertises on all 3 BLE advertising channels using a proprietary radio packet.

Here's the problem:  The advertising code (below) will sometimes hang up on the "while(!(RFIRQF1 & TASKDONE))" statement if the interrupt was fired at some point during the last sleep cycle.  We advertise on 37-38-39 in that order, and it can hang up after advertising on any of them.  The channel does not matter.

If the interrupt did not fire, then it will not hang on "while(!(RFIRQF1 & TASKDONE))".  If the interrupt did fire, then it might hang on the last statement.  If it does hang, it always hangs on the last statement.

We do have the fix (set LLECTRL to 0 before going to sleep) in place, and this problem still pops up.

void advertise(uint8 channel_number)
{
  set_channel(channel_number);

  // Clear the interrupt flags
  S1CON = 0;
  RFERRF = 0;
  RFIRQF0 = 0;
  RFIRQF1 = 0;

  radio_lle_command(CMD_TX);

  // Wait for the radio to finish
  while (!(RFIRQF1 & TXDONE) && !(RFIRQF1 & TASKDONE))
    ;

  // Clear the interrupt flag
  RFIRQF1 = 0;

  // Reuse the FIFO
  radio_lle_command(CMD_TXFIFO_RETRY);

  // Wait for the radio to finish
  while (!(RFIRQF1 & TASKDONE))
    ;
}

  • It's important to note that not all chips exhibit this problem. We have 2 out of 10 that will behave this way.
  • Hi Curtis,

    That is a quite strange issue you are seeing. I have notified our modem guy to take a look at this.

    Cheers,
    Fredrik
  • It is a bit difficult to tell exactly what is going on based on the information here. But I believe the issue could be simply that the interrupt happens between the two while loops and extends the time. It seems to me that the first while loop will normally end due to TXDONE. The command will then end a short time later by TASKDONE. Normally, this will take so much time that TASKDONE occurs after clearing the RFIRQF1 flags, and is then observed as expected. But if the interrupt comes in between, it may delay the clearing of the RFIRQF1 flags so that TASKDONE is cleared before the second while loop.

    I would suggest re-writing as follows to make sure that TASKDONE is not lost:

    void advertise(uint8 channel_number)
    {
      set_channel(channel_number);
    
      // Clear the interrupt flags
      S1CON = 0;
      RFERRF = 0;
      RFIRQF0 = 0;
      RFIRQF1 = 0;
    
      radio_lle_command(CMD_TX);
    
      // Wait for the radio to finish
      while (!(RFIRQF1 & TXDONE) && !(RFIRQF1 & TASKDONE))
        ;
    
      // Clear the TXDONE interrupt flag
      RFIRQF1 = ~TXDONE;
    
      // Reuse the FIFO
      radio_lle_command(CMD_TXFIFO_RETRY);
    
      // Wait for the radio to finish
      while (!(RFIRQF1 & TASKDONE))
        ;
    }
    

    Note that I have assumed that there is no extra radio command being started in interrupt context between the two while loops, so that there should be only one TASKDONE interrupt. From the referenced thread, I assume this is indeed the case.

  • You could probably remove the second write to RFIRQF1 altogether, as I don't think it matters if the TXDONE flag is cleared or not.
  • The interrupt does not happen in between the two while loops. I watched the interrupt causing lines on the scope and they only interrupted while the cc2541 was asleep. I have not tried triggering the interrupts while the cc2541 is awake.

    I set a spare pin to toggle in the while(!(RFIRQF1 & TASKDONE)) loop, and it would toggle all the way up to a watchdog reset.

    And you assume correctly. That code is basically copy-pasted from source.
  • I would still recommend that you try the proposed change. There may be other conditions as well where the TASKDONE interrupt occurs before clearing RFIRQF1. Note that CMD_TXFIFO_RETRY will not produce a TASKDONE interrupt.
  • So the last "while" is pointless then? If CMD_TXFIFO_RETRY does not produce a TASKDONE interrupt, then there's no point in checking for the flag to get set in the RFIRQF1 register? CMD_TXFIFO_RETRY is basically a fire-and-forget command, if I understood correctly.
  • Yes, CMD_TXFIFO_RETRY is a fire-and-forget command which is done in one cycle.

    The second while ensures that the TX command is completely finished and the radio is ready to receive a new command before the function is exited. The first wile will typically end on TXDONE, which comes a short time before TASKDONE. The program will probably work without it, but given the way the first wile is set up, I would recommend to keep the second one as well.