Raspberry Pi Pico (RP2040) SPI Example with MicroPython and C/C++ | Digi-Key Electronics

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Being able to talk to various peripherals and sensors is one of the most useful things a microcontroller can do and the RP2040 is no exception. Over the next couple of episodes, I'm gonna show you how to talk to a sensor using a few of these protocols. Specifically, I'm gonna start with I2C and SPI. And I'm gonna use them to talk to the same sensor so you can get an idea of how those two protocols differ. Let's dive in. (upbeat music) SPI stands for serial peripheral interface and Analog Devices has a great tutorial that breaks down how the protocol works if you're interested. It uses four IO lines to send data between a controller and a peripheral. One line is the clock, which is used to synchronize the reading of data. Another line is master out, slave in, which you'll also sometimes see as controller out, peripheral in. Data comes out of the main controller on this line and is read by the peripheral or sensor. There's also master in, slave out. Data is sent by the peripheral to the controller on this line. Finally, the chip select line allows us to control which peripheral is being communicated with at any given time. You have four clocking modes that you need to pay attention to. You'll most often find that SPI devices use mode zero, where the clock idle's low and data is sampled on the rising edge. SPI is a bust that allows you to attach multiple peripherals as shown in this top image. You will need a CS line for each peripheral. The controller should drive a specific CS line low when it wants to talk to the associated peripheral. Some devices, like LED controllers, can be daisy chained together and only use one chip select line. We'll only need one peripheral, so we won't need to worry about this for now. There is no speed limit set for SPI as it all depends on how fast you can switch your clock line and if your wires or traces will cleanly carry the signal. However, many peripheral devices you buy will have some kind of limit, so make sure you read the data sheet. You'll generally see SPI clocked at one or five megahertz with some devices going up to 50 megahertz. Let's connect a SPI sensor to our Raspberry Pi Pico to see how this works. I'll be using an ADXL343 breakout board from Adafruit. This is a three axis accelerometer that can communicate over SPI or I2C, which is perfect for seeing how both protocols work. Here is how we'll connect the breakout board to the Pico. We'll need to give it 3.3 volts from the Pico and also connect the ground pins. Note that the silk screen on the breakout board mostly labels the pins for I2C usage. The SPI clock line should go to SEL and the chip select line should go to CS. Note that you can really any pin on the Pico for chip select as we'll be controlling it manually in code. Controller out, peripheral in is labeled as SPI TX on the Pico and should be connected to SDA on the breakout board. Controller in, peripheral out is labeled as SPI RX on the Pico and that one should go to SDO on the sensor. Note that these pins are part of the SPI zero peripheral on the Pico. Here is how I connected my boards. If you do not already have MicroPython running on the Pico, you will want to put the Pico into bootloader mode by holding the boot select button while plugging in the USB cable. This is a trick I learned recently about how to quickly load MicroPython onto the Pico. Click on the board button on the bottom right of Thonny and make sure you have the Raspberry Pi Pico selected. Click configure interpreter. Click install or update firmware and click install in the pop-up window. This will download and install the latest firmware for MicroPython. You should be presented with the REPL prompt in the shell. Whenever you're working with raw data from a sensor, I always recommend reading the data sheet. We'll first look at the SPI communication section to see how to send data to and from the sensor. The ADXL343 uses polarity in phase one, which is mode 3. That's different from the default so we need to make a note of that. The first byte consists of a read or write bit, followed by a multiple bytes bit, and then the address of the desired register. We can write data to that register or read data from it, depending on how the read or write bit was set. For a read, data is sent out on the SDO line after the first byte is received. We can also read or write multiple bytes if we set the multiple byte bit. Each subsequent read or write after the first byte will be from subsequent registers. So if we read from register 30 and continue reading with the multiple byte bit set, we'll read from register 31, then 32 and so on. The other thing we care about is the register map. If it's available, I like to read the device ID register first to make sure we can communicate with the sensor. This register should return hex E5. Then we'll need to set the measure bit to one in the power control register, in order to tell the sensor to start taking accelerometer readings. By default, the sensor measures acceleration in the plus or minus 2G range. You can increase that range to plus or minus 16G by setting a few bits in the data format register. However, we'll leave it at the 2G range default for now. X, Y, and Z accelerometer data is held in the data registers. This paragraph is important as it tells us how the data is configured. The data for one access is 16 bits spread across two registers, with the least significant byte being read first. So we'll need to read the bites, swap the order and change it to a signed integer using two's compliment. Let's show this in MicroPython first . At the top import machine, utime, ustruct and sys, then we'll want to define some constants to help us read and compute the accelerometer data. We only care about the device ID power control and the start of the data registers. The device ID should read hex E5 and we'll need to do some math to convert the raw readings into meters per second squared. We'll control the chip select pin manually, so we'll set it as an output. Next we'll initialize the SPI object. We'll use a one megahertz baudrate to start. Feel free to try increasing this baudrate later, but know that you might start running into issues with breadboards and long jumper wires, the higher you go. We set the polarity in phase to one, as we saw on the data sheet, we'll leave it at eight bits and most significant bit first, and we define our clock MOSI and MISO pins. Next, we write a couple of functions to help us communicate with the sensor. The first simply sends out two bytes over the SPI bus. The first byte contains the register address and the second is the data we want to write to that register. Note that we manually set the CS pin to low during the whole operation, and then set it back to high, when we're done. Next, we create a function that reads from one or more registers. It sets the read bit high and sets the multiple byte bit high, if the color wants more than one bite. The registered address is the last six bits of that first byte. We then send out the first byte and read a number of bytes as specified by the color. All of that data is returned in a byte array. In the main part of the program, we set the CS pin high initially. There's a slight bug in MicroPython, the clock line idols low at first, when we set polarity to one. We perform a read that does nothing to get it to start idling high. Hopefully this will be fixed in the future. Next we read the device ID register and compare the result with the known good ID we got from the data sheet. If we don't get that ID, we know we can't communicate with the chip, so we throw an error and exit. Next, we read the contents of the power control register and set just the measure bit high, which is bit three. By oaring a one in bit three with the contents of the data register. We can just affect that bit rather than overwrite everything. We then write that result back to the power control register. I like to print out the contents of that register before and after the write to make sure the right function works. I'll wait two seconds to give the user time to read the output before continuing. In my forever loop, I read the six consecutive data registers and use ustruct to convert these sets of two byte arrays to 16 bit signed integers. Then I do some math to convert the raw readings into meters per second squared. Finally, I print the results to the serial terminal and sleep for a bit. Let's save this program somewhere on the computer and then run it on the Pico. If it all goes well, it should print out the contents of the power control register twice to show us that the writing worked and then begin giving us accelerometer data. If I move the board around, you can see how the X, Y, and Z measurements change. Now let's do this in C. I recommend following my guide on Maker.io to set up VS Code and the tool chain for the Pico C SDK. Navigate to wherever you keep your Pico projects. In a previous video, I showed you how to create a simple blink program. And that blink program is a very useful template for making new C programs for the Pico. So let's copy that folder and rename it to something like ADXL343_SPI. You will want to delete the build folder that's in there as we will run, CMake from scratch after making some adjustments. Open VS Code and open that new folder we just made. In CMake lists change the project name from blink to ADXL343_SPI. We'll also need to include the C, SDK SPI library, so add hardware_SPI in target link libraries. If you are using the Pico probe for debugging, you can leave the STDIO_uart as one, but if not, you'll want to change it to zero and change STDIO_ USB to one. Save this file. If this does not automatically run CMake, you will need to run, CMake from the console, or if you have the C make extension installed like I do, you can click the kit button at the bottom and click the Arm GCC compiler from the dropdown list. Then click the CMake button and click the debug configuration. This will cause CMake to run and create a new build folder. In main.c delete everything. At the top, include the STDIO, Pico STD lib and hardware SPI libraries. We'll also want to define some constants with the register addresses we care about along with the device ID and some math Constance. While it's not really necessary for this demo, I'll go ahead and declare my register read and write functions, I'll define them next. Writing to the sensor is pretty simple. We construct the two byte message and then send it out over SPI. Note that we set the CS pin to low first and set it to high, after the transmission is done. Reading is a little more complicated. We figure out if the multiple byte bit should be set and then construct our message with the read bit set. This message is sent out using the SPI, right blocking command, after dropping the CS line low. We then immediately read for a number of bytes, specified by the color before setting the CS line back to high. In main, we set our pins and SPI port, which is SPI zero for the pins we chose. We then initialize the serial port. We set our CS pin as output and set it to a default of high. Then we configure our SPI port at one megahertz and set the polarity and phase each to one. We also tell the RP2040 to connect the data and clock pins to the SPI functions. Just like we did in MicroPython, we need to perform one useless read in order to force the clock pin to idle high. Then we read the device ID and compare it to the known good ID value. We get the value of the power control register and set the measure bit to high before writing it back to the power control register. I like to print out the value of the register before and after the write, to make sure it was set properly. We wait for two seconds and start taking readings from the accelerometer. We start at the data X zero register. We read it and the five registers after it to get all three axis. We do a little math to convert the reading of each axis to a 16 bit signed value, and then convert the raw readings into meters per second squared. Finally, we print the results and sleep for a 10th of a second before repeating the process. Save this file. I'll build the project with make, if you have the Pico probe connected with the debugging tools installed, as I showed in a previous video, you can simply start the debugging process to run your code. If not, you'll want to put your Pico into bootloader mode and copy the .UF2 file over to the RPI-RP2 drive. You'll find the .UF2 file inside the build folder of your project directory. When it's done uploading the Pico should automatically reset and start running your program. Open whatever serial program you like to use and connect it to the Pico with a baud rate of 115,200, you should see the X, Y, and Z accelerometer data flying across the screen. If it's working properly, you should be able to hold the breadboard steady and see the effects of gravity. If you have one access perpendicular to the ground, it should read plus or minus 9.8 meters per second squared. I hope this helps give you a good start using SPI on the Raspberry PI Pico. On the next episode, I'm gonna show you how to do the exact same thing, but using I2C instead. Stay tuned and happy hacking. (upbeat music)
Info
Channel: DigiKey
Views: 44,431
Rating: undefined out of 5
Keywords: Raspberry pi, raspberry pi pico, micropython, c++, sensor, spi
Id: jdCnqiov6es
Channel Id: undefined
Length: 14min 54sec (894 seconds)
Published: Mon Jul 12 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.