This time I continue working with Raspberry Pi Foundation's RP2040 MCU for future in audio projects.
Over the last few mornings I created a embedded C library for using SPI peripherals for this MCU.
Breadboard time.... |
I couldn't locate many videos about programming RP2040 SPI using C; one I found covered both MicroPython and C. It's from Shawn Hymel--this guy has good stuff--video here. Also, the YouTube channel "Life with David" has some good RP2040 content (such as an interesting arbitrary waveform generator video how to--here) but so far not a lot about SPI; David concentrates on the RP2040's PIO features, primarily.
Using those videos, as well as the (surprisingly abstract) examples found here, and the SDK SPI documentation from Raspberry Pi Foundation, I ported/rewrote the Atmel 328 SPI library I create a few years ago (here) to code for the RP2040. If you are new to SPI and embedded C, you should read up on SPI first--good article from the Sparkfun folks: here.
After a few mornings, I got it to work.
Proof of concept--1K triangle wave created with the library discussed in this post and an MCP4921 SPI DAC. Code is here. |
There are differences aplenty between Atmel 328 and RP2040 hardware and firmware. For example, the RP2040 supports 16 bit SPI communication; as far as I know, the Atmel 328 natively only supports 8 bits. The RP2040 has 2 SPI peripherals on-chip, the 328P has one.
Pulseview is open source logic analysis software; I used it with an inexpensive Saleae clone to see what was happening with SPI at a low level. A logic analyzer is essential for debugging libraries like this one. |
One of the main differences I found is that the RP2040 requires buffers for SPI data being sent and returned; this is required by the Pico SDK.
This was easily accommodated; I created a suitably sized variable or array, then, when calling a SPI write method from the SDK, I used the address of the buffer to pass the data to the peripheral:
void SPI_TransferTx16_SingleCS(spi_inst_t *spi, const uint16_t data) // cs low, 2 bytes, cs high)
{
gpio_put (cs_pin,0);
spi_write16_blocking(spi,&data, 1);
gpio_put (cs_pin,1);
}
The number 1 you see above indicates how many words occur between the CS down and CS up; so it was easy to create another function where you can argue in as many bytes as needed:
void SPI_TransferTx8_variable_num_words(spi_inst_t *spi, const uint8_t a[0], uint8_t numwords)
// 8bit write, cs down; variable # of bytes before CSup
{
gpio_put (cs_pin,0);
spi_write_blocking(spi,a, numwords);
gpio_put (cs_pin,1);
}
I already had a single method MCP4921.c file I've used for Atmel 328/embedded C (here), but since the RP2040 can accommodate 16 bit words for SPI, I simplified the code for the MCP4921 DAC 12 bit writes to just three lines:
void write4921(uint16_t data4921)
{
// code assumes LDAC (4921 pin 5) tied to ground
data4921 &= ~(1 << 15); // must always be zero
// buffered ref in, gain = ref; no shutdown
data4921 |= (1 << 14) | (1 << 13) | (1 << 12) ;
SPI_TransferTx16_SingleCS(spi0,data4921);
}
To test and troubleshoot, I used PulseView, the amazing and extremely useful open source logic analyzer. Using Pulseview I could quickly see if a given library call worked or not:
I tested sending hex FF, F0, and 03 (so 24 bits) between a single CS down and up. Yes, it worked. However, SPI tests don't reveal useful information without an appropriate peripheral connected to the MCU. So, I tested this write method using an AD5761, which requires 24 bit words for each SPI transmission. |
Among other things, Intellisense allows the user to hover over a method and see details about its use.
OK how did I add Intellsense detail to the 2040 SPI C library? It's easy: in my declarations (specifically: SPI_rp2040.h) I commented immediately above where I defined the function. But I added no comments on the same line as the declaration:
Next, onward to an ADC C library for RP2040. The RP2040 has 3 12 bit ADC's, a built in temperature sensors, and some interesting features like ADC round robin. Update: done, here.
In general, in the coming weeks and months, I will probably port more Atmel 328 libraries I have already written to this new MCU, and try to improve and optimize the code in the process. Geek out, baby!
No comments:
Post a Comment