使用带有 c sdk 的 raspberry pi pico uart 很奇怪

Weirdness using the raspberry pi pico uart with the c sdk

我很忐忑

我正在制作一台遥控机器,使用 pi pico 驱动电机并读取一些传感器,以及 raspberry pi 4 通过串口向 pi pico 发送命令并托管网络界面。

下面的代码似乎可以工作...但是...如果我用 uart_is_writable 编码删除 if 及其内容,它将不起作用。有人知道为什么吗?

#include <stdlib.h>
#include <string.h>

#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "hardware/irq.h"

//DEFINES
#define UART_ID uart0
#define BAUD_RATE 19200
#define DATA_BITS 8
#define STOP_BITS 1
#define PARITY    UART_PARITY_NONE
#define UART_TX_PIN 0
#define UART_RX_PIN 1
#define LED_PIN PICO_DEFAULT_LED_PIN

static int chars_rxed = 0;

volatile char uCommand[32] = {0, 0};

void on_uart_rx(void) {
   char tmp_string[] = {0, 0};
   new_command = true;
   while (uart_is_readable(UART_ID)) {
       uint8_t ch = uart_getc(UART_ID);
       tmp_string[0] = ch;
       strcat(uCommand, tmp_string);
       if(uart_is_writable(UART_ID)){
         uart_putc(UART_ID, '-');
         uart_puts(UART_ID, uCommand);
         uart_putc(UART_ID, '-');
       }
       chars_rxed++;
   }
}

int main(){

 uart_init(UART_ID, BAUD_RATE);

 gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
 gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);

 uart_set_hw_flow(UART_ID, false, false);

 uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY);

 uart_set_fifo_enabled(UART_ID, false);

 int UART_IRQ = UART_ID == uart0 ? UART0_IRQ : UART1_IRQ;

 irq_set_exclusive_handler(UART_IRQ, on_uart_rx);
 irq_set_enabled(UART_IRQ, true);

 uart_set_irq_enables(UART_ID, true, false);

 uart_puts(UART_ID, "\nOK\n");

   while (1){
       tight_loop_contents();
       if(uCommand[0] != 0){
         uart_putc(UART_ID, '/');
         uart_puts(UART_ID, uCommand);
         memset(uCommand, 0, sizeof(uCommand));
       }
     }

}

您链接到的示例代码用于简单的 tty/echo 实现。

您需要根据您的用例对其进行调整。

因为禁用了 Tx 中断,所以必须轮询发送器的所有输出 I/O。此外,uart 中的 FIFO 被 禁用 ,因此仅使用单个字符 I/O。

因此,uart_is_writable检查一个字符是否可以被传输。


链接代码...

在 Rx ISR 中回显接收到的字符。所以,它需要调用上面的函数。请注意,如果 Tx not 就绪(即已满),则字符 not 回显并且 dropped .

我不知道uart_putcuart_puts是否以这种方式检查是否可以发送。

所以,我假设他们不会。 这意味着如果您调用 uart_putc/uart_puts 并且 Tx 已满,则 uart 中的 current/pending 字符可能会得到 trashed/corrupted.

因此,应调用 uart_is_writable 以发送 any/each 个字符。

或者 ... uart_putc/uart_puts do 检查并将 阻止 直到 space 在 uart 中可用Tx 先进先出。对于您的用例,这种阻塞是 可取的。


你想要什么...

旁注:我在 RPi 上完成了类似的 [product/commercial 级] 编程,通过 uart 进行电机控制,所以其中一些来自我自己的经验.

对于您的用例,您不想回显接收到的字符。您想将其附加到接收缓冲区。

要实现这一点,您可能需要使用环形队列:一个用于接收字符,一个用于传输字符。

我假设您已经[或已经]为send/receive命令设计了某种简单的数据包协议。 Rpi 发送的命令是(例如):

  1. 设置电机速度
  2. 获取当前传感器数据

另一方应响应这些命令并执行所需的操作或return所需的数据。

两个处理器可能需要有相似的服务循环和 ISR。

Rx ISR 仅检查 Rx 环队列中可用的 space。如果 space 可用,它会从 uart 获取一个字符并将其排入队列。如果 uart 中没有可用的 Rx 字符,则 ISR 可能会退出。

基础级代码服务循环应该:

  1. 检查 uart Tx 是否可以接受另一个字符(通过 uart_is_writable),如果可以,它可以从 Tx 环形队列 [如果可用] 中取出一个字符并发送它(通过 uart_putc)。它可以在此循环以保持 uart 发送器忙碌。

  2. 检查是否从另一端接收到足够的字符以形成 packet/message。如果有 这样的数据包,它可以为其中包含的“请求”提供服务 [使字符出列以在 Rx 环队列中制造更多 space]。

  3. 如果底层需要发送一条消息,它应该入队到Tx环队列。它将在前面的步骤中[最终]发送。


一些额外的想法...

链接代码启用Rx中断并在轮询模式下运行Tx。这可能就足够了。但是,为了获得最大吞吐量和最低延迟,您可能还希望驱动 Tx 中断。

您可能还希望在 uart 中启用 FIFO,这样您就可以对多个字符进行排队。这可以减少对 ISR 的调用次数并提供更好的 throughput/latency 因为服务循环不必如此频繁地轮询。