工程师 - Raspberry Pi Pico程序:读取SPI数据后从串口输出
1,通过Arduino IDE编写代码,并使用Pico C SDK。编译后烧写到Pico板卡中。
使用SPI1,端子号为GPIO 10, 12, 13, 15.
SPI的波特率为400K。
UART设置为500K。
2,第一个版本,将数据以16bit单位的十进制数值形式打印出来
SPI读取单位为2个字节,长度为8.
#include "pico/stdlib.h" #include "hardware/spi.h" #include <stdio.h> #include "hardware/irq.h" #include "hardware/structs/spi.h" #include "hardware/resets.h" void setup() { Serial.begin(500000); gpio_set_function(10, GPIO_FUNC_SPI); gpio_set_function(12, GPIO_FUNC_SPI); gpio_set_function(13, GPIO_FUNC_SPI); gpio_set_function(15, GPIO_FUNC_SPI); spi_init(spi1, 400000); spi_set_format(spi1,16,SPI_CPOL_1,SPI_CPHA_1,SPI_MSB_FIRST); spi_set_slave(spi1, 1); irq_set_exclusive_handler(SPI1_IRQ, my_spi_handler); irq_set_enabled(SPI1_IRQ, true); irq_set_priority(SPI1_IRQ,0); spi1_hw->imsc = 1 << 2; } uint16_t recvBuff[8]; volatile bool recvBuffReady = false; void my_spi_handler() { spi_read16_blocking(spi1, 0, recvBuff, 8); recvBuffReady = true; } void loop() { if (recvBuffReady) { recvBuffReady = false; for (int i = 0; i < 8; i++) { Serial.print(recvBuff[i]); Serial.printf(","); } Serial.println(); } } |
2,修改一下输出格式。
第一个版本的工作情况,master发送是16x2个字节发送一次。Pico板作为slave,是8x2读取一次,把数据存下来。
输入的二进制数据格式,输出时,每2个字节作为一个数值打印成十进制格式,会比较长。
然后loop里面把一次读取的再发出去。读取发送的节奏是可控的,发一次,读两次,正好。
而如果Master连续发送数据的话,这个程序运行状态就不一样了。发送读取节奏不一致了,一次读满8个字节,可能会超时。
而且我们要输出的主要是连续的可打印的字符串,输出每个字符调用write函数即可。
第二个版本的修改,SPI读取单位改为1个字节,长度为8.
串口输出改为write函数。
不过发现有些数据是乱码,或者会丢失。
#include "pico/stdlib.h" #include "hardware/spi.h" #include <stdio.h> #include "hardware/irq.h" #include "hardware/structs/spi.h" #include "hardware/resets.h" void setup() { Serial.begin(500000); gpio_set_function(10, GPIO_FUNC_SPI); gpio_set_function(12, GPIO_FUNC_SPI); gpio_set_function(13, GPIO_FUNC_SPI); gpio_set_function(15, GPIO_FUNC_SPI); spi_init(spi1, 400000); spi_set_format(spi1,8,SPI_CPOL_1,SPI_CPHA_1,SPI_MSB_FIRST); spi_set_slave(spi1, 1); irq_set_exclusive_handler(SPI1_IRQ, my_spi_handler); irq_set_enabled(SPI1_IRQ, true); irq_set_priority(SPI1_IRQ,0); spi1_hw->imsc = 1 << 2; } uint8_t recvBuff[8]; volatile bool recvBuffReady = false; void my_spi_handler() { spi_read_blocking(spi1, 0, recvBuff, 8); recvBuffReady = true; } void loop() { if (recvBuffReady) { recvBuffReady = false; for (int i = 0; i < 8; i++) { Serial.write(recvBuff[i]); } } } |
3,使用中断发送数据。
试验了下,开始传输是OK的,发了几十个字节之后就不好用了。
有可能是在中断里调用串口的写操作会引发不可知的影响。
#include "pico/stdlib.h" #include "hardware/spi.h" #include <stdio.h> #include "hardware/irq.h" #include "hardware/structs/spi.h" #include "hardware/resets.h" void setup() { Serial.begin(500000); gpio_set_function(10, GPIO_FUNC_SPI); gpio_set_function(12, GPIO_FUNC_SPI); gpio_set_function(13, GPIO_FUNC_SPI); gpio_set_function(15, GPIO_FUNC_SPI); spi_init(spi1, 400000); spi_set_format(spi1,8,SPI_CPOL_1,SPI_CPHA_1,SPI_MSB_FIRST); spi_set_slave(spi1, 1); irq_set_exclusive_handler(SPI1_IRQ, my_spi_handler); irq_set_enabled(SPI1_IRQ, true); irq_set_priority(SPI1_IRQ,0); } uint8_t recvBuff[5]; void my_spi_handler() { spi_read16_blocking(spi1, 0, recvBuff, 1); Serial.write(recvBuff[0]); } void loop() { } |
4,直接在loop里通过中断设置flag,或者判断是否数据可读,来收取数据,都没有输出。
这个是因为时序控制不对,中断里你不读数据,到了loop里就来不及了,读不到了把。
#include "pico/stdlib.h" #include "hardware/spi.h" #include <stdio.h> #include "hardware/irq.h" #include "hardware/structs/spi.h" #include "hardware/resets.h" bool flag_readable; uint8_t recvBuff[5]; void setup() { Serial.begin(500000); gpio_set_function(10, GPIO_FUNC_SPI); gpio_set_function(12, GPIO_FUNC_SPI); gpio_set_function(13, GPIO_FUNC_SPI); gpio_set_function(15, GPIO_FUNC_SPI); spi_init(spi1, 400000); spi_set_format(spi1,8,SPI_CPOL_1,SPI_CPHA_1,SPI_MSB_FIRST); spi_set_slave(spi1, 1); irq_set_exclusive_handler(SPI1_IRQ, my_spi_handler); irq_set_enabled(SPI1_IRQ, true); irq_set_priority(SPI1_IRQ,0); spi1_hw->imsc = 1 << 2; flag_readable = false; } void my_spi_handler() { flag_readable = true; } void loop() { if(spi_is_readable(spi1)){ flag_readable = false; spi_read_blocking(spi1, 0, recvBuff, 1); Serial.write(recvBuff[0]); } } |
5,使用ringbuffer函数来读取数据,然后loop里进行发送。
Ring Buffer里的buffer大小是250,这里使用大小100的buffer去读取,然后输出。
#include "pico/stdlib.h" #include "hardware/spi.h" #include <stdio.h> #include "hardware/irq.h" #include "hardware/structs/spi.h" #include "hardware/resets.h" #include "ringbuffer.h" RingBuffer rb; #define BUF_LEN 100 uint8_t buffer[BUF_LEN]; void setup() { Serial.begin(500000); gpio_set_function(10, GPIO_FUNC_SPI); gpio_set_function(12, GPIO_FUNC_SPI); gpio_set_function(13, GPIO_FUNC_SPI); gpio_set_function(15, GPIO_FUNC_SPI); spi_init(spi1, 400000); spi_set_format(spi1,8,SPI_CPOL_1,SPI_CPHA_1,SPI_MSB_FIRST); spi_set_slave(spi1, 1); irq_set_exclusive_handler(SPI1_IRQ, my_spi_handler); irq_set_enabled(SPI1_IRQ, true); irq_set_priority(SPI1_IRQ,0); // Enable receive FIFO half full and receive FIFO read timeout interrupt spi1_hw->imsc = 1 | (1 << 2); RingBuffer_Init(&rb); } void my_spi_handler() { uint8_t data;
// Clear the receive FIFO read timeout interrupt status if(spi1_hw->ris | SPI_SSPRIS_RTRIS_BITS){ spi1_hw->icr = SPI_SSPICR_BITS; } while(spi_is_readable(spi1)){ spi_read_blocking(spi1, 0, &data, 1); RingBuffer_Write(&rb, &data, 1); } } void loop() { uint16_t len = RingBuffer_GetDataLength(&rb); uint8_t data; if (len > 0) { if(len > BUF_LEN) len = BUF_LEN; RingBuffer_Read(&rb, buffer, len); Serial.write(buffer, len);
} else{ /* If receive fifo is not empty, read the data */ if(spi1_hw->sr | SPI_SSPSR_RNE_BITS){ if(spi_is_readable(spi1)){ spi_read_blocking(spi1, 0, &data, 1); RingBuffer_Write(&rb, &data, 1); } } } } |
备注:
1,接SPI信号线来抓取SPI数据,两边的地线最好也能接到一起,不然信号还是有些不稳定的。比如在没有通信时,还是会打印出乱码数据。
就这种:�����