This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

SPI & Bluetooth best practices

Hi All,

I'm a little rusty on my embedded C and was wondering if I could get a few broad pointers on the best way to organize this research system I'm putting together.

The system is basic: I have an NRF52840 that talks to a custom IC via SPI and then sends whatever it reads out to an NRF52 dongle via bluetooth. If this was one way then it would be especially straight forward but unfortunately, the NRF52840 can also receive commands from the dongle (over bluetooth) that may or may not trigger specific SPI transactions. It seems to me that the best way to organize the NRF52840 (that's talking to the custom IC) as the peripheral and the dongle as the central device. This way the peripheral can use an interrupt to query the IC and then notify the central device with a new packet.

I'm having a little more trouble with the other main function though (dongle sending commands to the peripheral) though since it's really important that this bluetooth connection runs as fast as possible without dropping any packets.

Is it fair to have a main while loop on all my devices that spins (just checking if there's been an incoming command) when they're in their main interrupt-driven operating mode. Then if there has been a command, they break out of their interrupt-driven mode and work on whatever the command is? I worry that this constant spinning may burn unnecessary power and also may slow down the main BLE operation (which needs to run as fast as possible!). Are there other alternatives that may allow the bluetooth and spi to operate as fast as possible in parallel?

Thanks so much for any advice/help. If anything was unclear please let me know and I can clarify. I tried to distill everything to not be too confusing but I may have distilled too much.

- Ryan

Parents
  • Hello Ryan,

    Thank you for your patience with this.

    If this was one way then it would be especially straight forward but unfortunately, the NRF52840 can also receive commands from the dongle (over bluetooth) that may or may not trigger specific SPI transactions. It seems to me that the best way to organize the NRF52840 (that's talking to the custom IC) as the peripheral and the dongle as the central device. This way the peripheral can use an interrupt to query the IC and then notify the central device with a new packet.

    Fortunately, it does not have to be complicated just because it is two way! :) 

    I'm having a little more trouble with the other main function though (dongle sending commands to the peripheral) though since it's really important that this bluetooth connection runs as fast as possible without dropping any packets.

    The BLE protocol is lossless, so you can be certain that no packets will be lost in the link. The only way packets may be lost is if they are not handled correctly when queued to be transferred by the SoftDevice on one side (such as if the queue command returns an error, and the program proceeds as though nothing had happened - dropping the packet that failed to be queued), or if the packet is discarded after reception on the central side.
    You will also not have to worry about bogging down the speed of your BLE transfers with your application, since the SoftDevice takes priority over any application-layer task. This way, the SoftDevice will always be guaranteed to meet its BLE timing-critical deadlines, since it knows that it can control the CPU whenever it needs to.
    So you may configure your connection parameters to whatever you would like, and the SoftDevice will make sure it is met - it is rather the application that must adjust to the possibility that the SoftDevice will take control of the CPU at any time in the program.

    Is it fair to have a main while loop on all my devices that spins (just checking if there's been an incoming command) when they're in their main interrupt-driven operating mode.

    Are you here referring to an incoming BLE event, or SPI transaction containing some command? I assume the former, and in this case you will not need to have a spinning while loop that checks for received commands, since the SoftDevice will take control of the CPU (even wake it if in SYSTEM_ON sleep) to meet the connection event and receive the command.

    Then if there has been a command, they break out of their interrupt-driven mode and work on whatever the command is?

    This also happens as part of the ble_evt_handler or specific service event handler you've implemented. When the SoftDevice has completed a connection event it will forward the data received (like a write to a command characteristic) to the the appropriate event handler in the application layer. Provided that the particular event handler has a high enough priority it can then be executed right away.
    You can see how this in done in most of the BLE peripheral examples. To be specific, you could take a look at the BLE UART peripheral example where the NUS events are forwarded to the nus event handler and processed immediately upon the application resuming control over the CPU.

    I worry that this constant spinning may burn unnecessary power and also may slow down the main BLE operation (which needs to run as fast as possible!).

    As mentioned the BLE operation will happen at the configured rate regardless of what the application is doing. The CPU might very well be in the low power SYSTEM_ON sleep state for most of the time, ready to receive BLE commands in the next connection event. All of our BLE peripheral examples demonstrate this - the idle_state_handler which is running in the main while loop places the CPU in SYSTEM_ON sleep whenever there is nothing else that needs doing. The CPU will then be ready to quickly wake up as soon as there is an event generated.

    Are there other alternatives that may allow the bluetooth and spi to operate as fast as possible in parallel?

    Yes, this is a good question - how to proceed in order to operate the SPI when the CPU might be claimed by the SoftDevice at any time.
    The answer to this is to make use of the nRF52840's easyDMA feature through using the SPIM peripheral (rather than the SPI), since this utilizes easyDMA.
    easyDMA lets a peripheral access RAM without needing CPU intervention. You could therefore both receive or send (if queued in advanced) data while the CPU is busy with the SoftDevice. To achieve this totally without CPU intervention you would need to also make use of the PPI peripheral, which lets you connect an event to a task trigger - i.e that when a specific event occurs, you would have it trigger a task (such as TASKS_START) without needing to involve the CPU.
    You could see the PPI peripheral demonstrated in the SAADC peripheral example where it is used to connect a TIMER CC event to the SAADC's TASKS_SAMPLE - enabling the SAADC to sample periodically without concern that the CPU might not be available at the particular time the sampling should happen.

    Please do not hesitate to ask if any part of this should be unclear, or if you encounter any other issues or questions!

    Best regards,
    Karl

Reply
  • Hello Ryan,

    Thank you for your patience with this.

    If this was one way then it would be especially straight forward but unfortunately, the NRF52840 can also receive commands from the dongle (over bluetooth) that may or may not trigger specific SPI transactions. It seems to me that the best way to organize the NRF52840 (that's talking to the custom IC) as the peripheral and the dongle as the central device. This way the peripheral can use an interrupt to query the IC and then notify the central device with a new packet.

    Fortunately, it does not have to be complicated just because it is two way! :) 

    I'm having a little more trouble with the other main function though (dongle sending commands to the peripheral) though since it's really important that this bluetooth connection runs as fast as possible without dropping any packets.

    The BLE protocol is lossless, so you can be certain that no packets will be lost in the link. The only way packets may be lost is if they are not handled correctly when queued to be transferred by the SoftDevice on one side (such as if the queue command returns an error, and the program proceeds as though nothing had happened - dropping the packet that failed to be queued), or if the packet is discarded after reception on the central side.
    You will also not have to worry about bogging down the speed of your BLE transfers with your application, since the SoftDevice takes priority over any application-layer task. This way, the SoftDevice will always be guaranteed to meet its BLE timing-critical deadlines, since it knows that it can control the CPU whenever it needs to.
    So you may configure your connection parameters to whatever you would like, and the SoftDevice will make sure it is met - it is rather the application that must adjust to the possibility that the SoftDevice will take control of the CPU at any time in the program.

    Is it fair to have a main while loop on all my devices that spins (just checking if there's been an incoming command) when they're in their main interrupt-driven operating mode.

    Are you here referring to an incoming BLE event, or SPI transaction containing some command? I assume the former, and in this case you will not need to have a spinning while loop that checks for received commands, since the SoftDevice will take control of the CPU (even wake it if in SYSTEM_ON sleep) to meet the connection event and receive the command.

    Then if there has been a command, they break out of their interrupt-driven mode and work on whatever the command is?

    This also happens as part of the ble_evt_handler or specific service event handler you've implemented. When the SoftDevice has completed a connection event it will forward the data received (like a write to a command characteristic) to the the appropriate event handler in the application layer. Provided that the particular event handler has a high enough priority it can then be executed right away.
    You can see how this in done in most of the BLE peripheral examples. To be specific, you could take a look at the BLE UART peripheral example where the NUS events are forwarded to the nus event handler and processed immediately upon the application resuming control over the CPU.

    I worry that this constant spinning may burn unnecessary power and also may slow down the main BLE operation (which needs to run as fast as possible!).

    As mentioned the BLE operation will happen at the configured rate regardless of what the application is doing. The CPU might very well be in the low power SYSTEM_ON sleep state for most of the time, ready to receive BLE commands in the next connection event. All of our BLE peripheral examples demonstrate this - the idle_state_handler which is running in the main while loop places the CPU in SYSTEM_ON sleep whenever there is nothing else that needs doing. The CPU will then be ready to quickly wake up as soon as there is an event generated.

    Are there other alternatives that may allow the bluetooth and spi to operate as fast as possible in parallel?

    Yes, this is a good question - how to proceed in order to operate the SPI when the CPU might be claimed by the SoftDevice at any time.
    The answer to this is to make use of the nRF52840's easyDMA feature through using the SPIM peripheral (rather than the SPI), since this utilizes easyDMA.
    easyDMA lets a peripheral access RAM without needing CPU intervention. You could therefore both receive or send (if queued in advanced) data while the CPU is busy with the SoftDevice. To achieve this totally without CPU intervention you would need to also make use of the PPI peripheral, which lets you connect an event to a task trigger - i.e that when a specific event occurs, you would have it trigger a task (such as TASKS_START) without needing to involve the CPU.
    You could see the PPI peripheral demonstrated in the SAADC peripheral example where it is used to connect a TIMER CC event to the SAADC's TASKS_SAMPLE - enabling the SAADC to sample periodically without concern that the CPU might not be available at the particular time the sampling should happen.

    Please do not hesitate to ask if any part of this should be unclear, or if you encounter any other issues or questions!

    Best regards,
    Karl

Children
No Data
Related