| 1 | # Raspberry Pi Pico SDK (for the RP2040) |
| 2 | |
| 3 | @tableofcontents |
| 4 | |
| 5 | <!-- markdownlint-disable MD031 MD046 --> |
| 6 | Just to be very clear and concise: The following instructions are |
| 7 | **not required to use the Arduino IDE** with any RP2040-based boards. |
| 8 | These instructions pertain to using only [Raspberry Pi's Pico SDK](https://github.com/raspberrypi/pico-sdk) |
| 9 | for RP2040 based boards. |
| 10 | |
| 11 | ## Prerequisite |
| 12 | |
| 13 | Follow Raspberry Pi's |
| 14 | ["Getting Started with Pico" document](https://rptl.io/pico-get-started) to |
| 15 | setup a proper development environment on your host PC (the machine that |
| 16 | will build your project). Notice that the setup instructions are a little |
| 17 | different for building on a Windows PC. |
| 18 | |
| 19 | Either set an environment variable named `PICO_SDK_PATH` that points to your |
| 20 | local clone of the pico-sdk or put the pico-sdk next to the RF24 folder or |
| 21 | next to the folder containing your project using the RF24 library: |
| 22 | |
| 23 | path/to/github/repos/ |
| 24 | pico-sdk/ |
| 25 | RF24/ |
| 26 | my_rf24_project/ |
| 27 | |
| 28 | Alternatively, the RF24 repository (and optionally the RF24Network and RF24Mesh |
| 29 | repositories) can be included into your project's "lib" folder as copies or |
| 30 | git submodules. For more detail, see the below instructions to incorporate |
| 31 | RF24 libs into your project. |
| 32 | |
| 33 | ## Building the RF24 examples for the Pico SDK |
| 34 | |
| 35 | Each example imports a set of constants (`CE_PIN`, `CSN_PIN`, `IRQ_PIN`) from the |
| 36 | examples_pico/defaultPins.h file. Default values for SPI pins (SCK, MOSI, MISO) |
| 37 | come from the Pico SDK repository's |
| 38 | [pico-sdk/src/boards/include/boards/\<board_name>.h files](https://github.com/raspberrypi/pico-sdk/tree/master/src/boards/include/boards) |
| 39 | |
| 40 | 1. Create a "build" directory in the RF24 repository's root directory and |
| 41 | navigate to it: |
| 42 | ```sh |
| 43 | cd RF24 |
| 44 | mkdir build |
| 45 | cd build |
| 46 | ``` |
| 47 | 2. Configure CMake for your desired build type and specific RP2040-based board |
| 48 | ```sh |
| 49 | cmake ../examples_pico -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico |
| 50 | ``` |
| 51 | Or if building on a Windows PC: |
| 52 | ```sh |
| 53 | cmake -G "NMake Makefiles" ../examples_pico -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico |
| 54 | ``` |
| 55 | The supported RP2040-based boards are listed in header files in the Pico SDK |
| 56 | repository's [pico-sdk/src/boards/include/boards/\<board_name>.h files](https://github.com/raspberrypi/pico-sdk/tree/master/src/boards/include/boards). |
| 57 | If the `-DPICO_BOARD` option is not specified, then the Pico SDK will default to building for the Raspberry Pi Pico board. |
| 58 | 3. Build the examples using the CMakeLists.txt file located in the |
| 59 | RF24/examples_pico directory. |
| 60 | ```sh |
| 61 | cmake --build . --config Release |
| 62 | ``` |
| 63 | Notice we specified the build type again using the `--config` option. |
| 64 | |
| 65 | @note If you see an error stating "'PICO_DEFAULT_SPI_SCK_PIN' was not declared in this scope", |
| 66 | then it means the board you selected with the `-DPICO_BOARD` option (in step 2) does not have a |
| 67 | default set of SPI pins defined for it. To workaround this error, see the below instructions to |
| 68 | use different pins for the SPI bus. |
| 69 | |
| 70 | @warning If doing consecutive build attempts, it is strongly encouraged to delete all files in the build |
| 71 | directory before re-attempting to build the project. |
| 72 | |
| 73 | ## Incorporating RF24 libs into your project |
| 74 | |
| 75 | In order to use the RF24 libraries in your RP2040 based project: |
| 76 | |
| 77 | 1. Make a copy of the RF24 library (and optionally RF24Network and RF24Mesh |
| 78 | libraries) in a "lib" directory located in your project's root directory. |
| 79 | |
| 80 | path/to/my/project/ |
| 81 | lib/ |
| 82 | RF24/ |
| 83 | RF24Network/ |
| 84 | RF24Mesh/ |
| 85 | src/ |
| 86 | CMakeLists.txt |
| 87 | ... |
| 88 | |
| 89 | Alternatively you can add the RF24\* repositories as [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). |
| 90 | 2. Include their CMakeLists.txt files from the RF24 libraries in your project's top-level |
| 91 | CMakeLists.txt file (usually located in the "src" directory). The following snippet |
| 92 | assumes that your project's "src" directory is on the same level as the previously |
| 93 | mentioned "lib" directory. |
| 94 | ```cmake |
| 95 | include(../lib/RF24/CMakeLists.txt) |
| 96 | include(../lib/RF24Network/CMakeLists.txt) |
| 97 | include(../lib/RF24Mesh/CMakeLists.txt) |
| 98 | ``` |
| 99 | 3. In the same CMakeLists.txt file from step 2, add the RF24 libraries into the |
| 100 | `target_link_libraries` configuration: |
| 101 | ```cmake |
| 102 | target_link_libraries(${CMAKE_PROJECT_NAME} |
| 103 | # ... Your project's other libraries ... |
| 104 | RF24 |
| 105 | RF24Network |
| 106 | RF24Mesh |
| 107 | ) |
| 108 | ``` |
| 109 | If you are using tinyUSB, this line (or similar) should already exist: |
| 110 | ```cmake |
| 111 | target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) |
| 112 | ``` |
| 113 | 4. Finally, remember to include the necessary RF24\* libraries' header files in your |
| 114 | project's source code where applicable. |
| 115 | ```cpp |
| 116 | #include <RF24.h> |
| 117 | #include <RF24Network.h> |
| 118 | #include <RF24Mesh.h> |
| 119 | ``` |
| 120 | @note Any of the project's source file(s) that includes any of RF24\* libraries must be a C++ file. |
| 121 | It is highly recommended to use the `.cpp` file extension for such project source files. |
| 122 | |
| 123 | ## Using different pins for the SPI bus |
| 124 | |
| 125 | Initially (without modification), the SPI bus uses the default pins defined in the |
| 126 | Pico SDK repository's [pico-sdk/src/boards/include/boards/\<board_name>.h files](https://github.com/raspberrypi/pico-sdk/tree/master/src/boards/include/boards). |
| 127 | However, there may be some boards that do not define the necessary pins to use as defaults. This can |
| 128 | be remedied using either project source code or build-time configuration. |
| 129 | |
| 130 | @warning There is no support for software driven SPI on RP2040 based boards at this time. |
| 131 | If someone is so inclined to implement this using the Pico SDK's PIO (Programmable Input |
| 132 | Output) feature, please submit an issue or pull request to the |
| 133 | [RF24 repository](http://github.com/nRF24/RF24). |
| 134 | |
| 135 | @note Before deciding what pins to use for the SPI bus, review the |
| 136 | [GPIO pins' "Function Select Table" in the Pico SDK documentation](https://raspberrypi.github.io/pico-sdk-doxygen/group__hardware__gpio.html#details). |
| 137 | There are essentially 2 SPI buses with multiple pin options for each. |
| 138 | |
| 139 | ### Project Source code option |
| 140 | |
| 141 | This option is the most reliable and flexible. It involves calling `SPI.begin()` and then passing the `SPI` object to `RF24::begin(_SPI*)`. |
| 142 | |
| 143 | ```cpp |
| 144 | #include <RF24.h> |
| 145 | |
| 146 | RF24 radio(7, 8); // pin numbers connected to the radio's CE and CSN pins (respectively) |
| 147 | SPI spi; |
| 148 | |
| 149 | int main() |
| 150 | { |
| 151 | // again please review the GPIO pins' "Function Select Table" in the Pico SDK docs |
| 152 | spi.begin(spi0, 2, 3, 4); // spi0 or spi1 bus, SCK, TX, RX |
| 153 | |
| 154 | if (!radio.begin(&spi)) { |
| 155 | printf("Radio hardware is not responding!\n"); |
| 156 | } |
| 157 | // continue with program as normal ... |
| 158 | } |
| 159 | ``` |
| 160 | |
| 161 | ### Build-time configuration option |
| 162 | |
| 163 | To specify the default SPI pins used at build time, you can use either: |
| 164 | |
| 165 | 1. declare these pins in the CMakeLists.txt file |
| 166 | ```cmake |
| 167 | target_compile_definitions(${CMAKE_PROJECT_NAME} |
| 168 | PUBLIC PICO_DEFAULT_SPI=0 # can only be 0 or 1 (as in `spi0` or `spi1`) |
| 169 | PUBLIC PICO_DEFAULT_SPI_SCK_PIN=2 # depends on which SPI bus (0 or 1) is being used |
| 170 | PUBLIC PICO_DEFAULT_SPI_TX_PIN=3 # depends on which SPI bus (0 or 1) is being used |
| 171 | PUBLIC PICO_DEFAULT_SPI_RX_PIN=4 # depends on which SPI bus (0 or 1) is being used |
| 172 | ) |
| 173 | ``` |
| 174 | 2. additional command line arguments |
| 175 | ```shell |
| 176 | cmake --build . --config Release -DPICO_DEFAULT_SPI=0 -DPICO_DEFAULT_SPI_SCK_PIN=2 -DPICO_DEFAULT_SPI_TX_PIN=3 -DPICO_DEFAULT_SPI_RX_PIN=4 |
| 177 | ``` |
| 178 | |
| 179 | ## Using Multiple Radios |
| 180 | |
| 181 | It is possible to drive multiple nRF24L01 transceivers on a single board. To do this each radio needs dedicated digital output pins for the CE and CSN pins. |
| 182 | |
| 183 | @warning The RPi Pico board's 3v regulator is typically insufficient to power more than 1 radio. |
| 184 | It is also worth mentioning that the RPi Pico board uses a switching regulator which inherently |
| 185 | produces electrical noise (a not steady 3v signal otherwise referred to as "power instability"). |
| 186 | |
| 187 | If you want to drive each radio with a separate SPI bus, then the following example will demonstrate how to do that. |
| 188 | |
| 189 | ```cpp |
| 190 | #include <RF24.h> |
| 191 | |
| 192 | // Declare the pin numbers connected to the radios' CE and CSN pins (respectively) |
| 193 | RF24 radio0(8, 5); // first radio object |
| 194 | RF24 radio1(14, 13); // second radio object |
| 195 | |
| 196 | // By default, one SPI bus instance is created by the RF24 lib. We'll use this |
| 197 | // default instance of the `spi0` interface for our first radio, but we want a |
| 198 | // different SPI bus for the second radio. |
| 199 | // |
| 200 | // So, here we declare a second SPI bus instance: |
| 201 | SPI my_spi; // we specify the `spi1` bus interface below |
| 202 | |
| 203 | bool setupRadios() |
| 204 | { |
| 205 | // Initialize the first radio using the default SPI instance |
| 206 | if (!radio0.begin()) { |
| 207 | printf("Radio0 hardware is not responding!\n"); |
| 208 | return false; |
| 209 | } |
| 210 | // first radio object initialized successfully |
| 211 | |
| 212 | // specify the the second SPI bus interface and corresponding GPIO pins |
| 213 | my_spi.begin(spi1, 10, 11, 12); // spi1 bus, SCK, TX, RX |
| 214 | if (!radio1.begin(&my_spi)) { |
| 215 | printf("Radio1 hardware is not responding!\n"); |
| 216 | return false; |
| 217 | } |
| 218 | // second radio object initialized successfully |
| 219 | |
| 220 | return true; |
| 221 | } |
| 222 | |
| 223 | int main() |
| 224 | { |
| 225 | stdio_init_all(); // init necessary IO for the RP2040 |
| 226 | |
| 227 | while (!setupRadios()) { // if either radioX.begin() failed |
| 228 | sleep_ms(1000); // add 1 second delay for console readability |
| 229 | // hold program in infinite attempts to initialize the radios |
| 230 | } |
| 231 | |
| 232 | // continue with program as normal ... |
| 233 | } |
| 234 | ``` |