使用带有 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_putc
和uart_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 发送的命令是(例如):
- 设置电机速度
- 获取当前传感器数据
另一方应响应这些命令并执行所需的操作或return所需的数据。
两个处理器可能需要有相似的服务循环和 ISR。
Rx ISR 仅检查 Rx 环队列中可用的 space。如果 space 可用,它会从 uart 获取一个字符并将其排入队列。如果 uart 中没有可用的 Rx 字符,则 ISR 可能会退出。
基础级代码服务循环应该:
检查 uart Tx 是否可以接受另一个字符(通过 uart_is_writable
),如果可以,它可以从 Tx 环形队列 [如果可用] 中取出一个字符并发送它(通过 uart_putc
)。它可以在此循环以保持 uart 发送器忙碌。
检查是否从另一端接收到足够的字符以形成 packet/message。如果有 是 这样的数据包,它可以为其中包含的“请求”提供服务 [使字符出列以在 Rx 环队列中制造更多 space]。
如果底层需要发送一条消息,它应该入队到Tx环队列。它将在前面的步骤中[最终]发送。
一些额外的想法...
链接代码仅启用Rx中断并在轮询模式下运行Tx。这可能就足够了。但是,为了获得最大吞吐量和最低延迟,您可能还希望驱动 Tx 中断。
您可能还希望在 uart 中启用 FIFO,这样您就可以对多个字符进行排队。这可以减少对 ISR 的调用次数并提供更好的 throughput/latency 因为服务循环不必如此频繁地轮询。
我很忐忑
我正在制作一台遥控机器,使用 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_putc
和uart_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 发送的命令是(例如):
- 设置电机速度
- 获取当前传感器数据
另一方应响应这些命令并执行所需的操作或return所需的数据。
两个处理器可能需要有相似的服务循环和 ISR。
Rx ISR 仅检查 Rx 环队列中可用的 space。如果 space 可用,它会从 uart 获取一个字符并将其排入队列。如果 uart 中没有可用的 Rx 字符,则 ISR 可能会退出。
基础级代码服务循环应该:
检查 uart Tx 是否可以接受另一个字符(通过
uart_is_writable
),如果可以,它可以从 Tx 环形队列 [如果可用] 中取出一个字符并发送它(通过uart_putc
)。它可以在此循环以保持 uart 发送器忙碌。检查是否从另一端接收到足够的字符以形成 packet/message。如果有 是 这样的数据包,它可以为其中包含的“请求”提供服务 [使字符出列以在 Rx 环队列中制造更多 space]。
如果底层需要发送一条消息,它应该入队到Tx环队列。它将在前面的步骤中[最终]发送。
一些额外的想法...
链接代码仅启用Rx中断并在轮询模式下运行Tx。这可能就足够了。但是,为了获得最大吞吐量和最低延迟,您可能还希望驱动 Tx 中断。
您可能还希望在 uart 中启用 FIFO,这样您就可以对多个字符进行排队。这可以减少对 ISR 的调用次数并提供更好的 throughput/latency 因为服务循环不必如此频繁地轮询。