8 years, 9 months ago.

BLE nRF - High frequency SPI writes with ticker locks up when characteristic added to service.

I’ve got a couple questions here as I’ve been battling some strange behavior and hard faults for a while and while I think I’ve found a strange issue regarding the number of characteristics in a service, its also quite possible my approach to using a quick ticker to write to SPI is not the best way to tackle the problem in general.

I’m working with an ARCH BLE board and in short I’m trying to update some LEDs via SPI as quickly as possible while maintaining the ability to have the occasional short BLE packet written from an app to change things like color or speed of the LEDs. After quite a bit of experimentation I’m doing this by setting up a ticker in the main loop of the program which consistently calls the LEDupdate function that sends LED data via SPI. In my BLE onDataWrittenCallback the very first line uses a pointer to my ticker to disable the ticker. Then I parse the data written, put it in the correct spots so that it can change whatever LED settings it needs to change and set a flag that my ticker needs to be turned back on. Once I hit the main BLE wait loop again the ticker is re attached, the ticker flag is set to false since the ticker is now active and my LEDs resume updating.

void onDataWrittenCallback(const GattWriteCallbackParams *params) {
    
    ptrTicker->detach();
    
    if (params->handle == ledServicePtr->getColorHandle()) {
        currentSettings.color[0].r = params->data[0];
        currentSettings.color[0].g = params->data[1];
        currentSettings.color[0].b = params->data[2];
    } else if (params->handle == ledServicePtr->getColorRawHandle()) {
        currentSettings.color[0].r = params->data[0];
        currentSettings.color[0].g = params->data[1];
        currentSettings.color[0].b = params->data[2];    
    }
    OLEDdirty = true; 
    TICKERdirty = true;
}

Thats the gist of my callback and below is my main loop

    Ticker ticker;
    ptrTicker = &ticker;     
   
    while (true) {
        if (OLEDdirty) {redrawOled();}
        if (LEDdirty) {
            LEDdirty = false;
            LEDs.update();
            }
        if (TICKERdirty) {    
             TICKERdirty = false;
             ticker.attach_us(LEDupdate, 8000);
           }  
        ble.waitForEvent();
    } 
}

You can see that on data written the very first thing I do is detach the ticker and then the very last thing I do is reattach it. I’ve found that a ticker that is too fast would cause the BLE to lockup when I tried to write, and sometimes the writes would go through but then the third or fourth write might lock up the BLE and cause the chip to fault. Interestingly I was able to go much faster with a ticker.attach_us instead of a simple ticker.attach, not sure why. In the end this approach seems to work well if I don't set my ticker to too short an interval.

The next part is a bit strange. I had this setup working and then I tried to add one more simple single uint8_t characteristic to my Service, which already has three characteristics. It made the chip default on any write, even though nothing else changed in the code. After chasing the problem for a while I made my ticker interval longer and once again things started working. I had to bump a ticker.attach_us(LEDupdate, 6000); to ticker.attach_us(LEDupdate, 12000); when I added the new characteristic. I’m not sure why the BLE soft device would be so much slower and more prone to locking up with just another characteristic added. Beyond that I’m surprised its locking up at all since the ticker is detached on the write and then reattached at the end, unless there is still some soft device work going on in the background that the ticker is bumping into. If that is the case and the soft device is still doing things behind the scenes is there a good way to delay the re attach of my ticker until I know the soft device is settled?

I realize there are a couple of questions rolled up in this, but for clarity’s sake I’ll summarize here.

1) I’m trying to get high frequency writes to an SPI peripheral and I’m using a ticker_us to call my function which performs the writes. Is this the best way to do this or is there another approach I should try?

2) Whenever anything is written to the BLE I am in the first line of my onDataWritten function detaching my ticker and then on the very last line in the main loop before giving things back to ble.waitForEvent(); I am re-attaching my ticker. Sometimes I still lock up the chip if the ticker interval is too short, is the soft device still working behind the scenes and I’m stepping on it by trying to reattach my ticker too soon? Is there a better way to know when its safe to reattach my ticker?

3) Everything was stable and working using the fast ticker method until I added a single one byte characteristic to my existing service which already had three characteristics. I had to almost double the length of my ticker interval to get things stable again despite the fact that nothing was calling this new characteristic and it was not being written too or referenced anywhere beyond its initialization. Does it make sense that more characteristics would slow the soft device down like this?

I might have similar issues with my code. Did you solve yours?

posted by Frits Jan van Kempen 23 Feb 2016
Be the first to answer this question.