In order to reduce power consumption, I configured the RX pin of the serial port with an interrupt, and when the device was asleep, it was woken up by an interrupt, and then turned off the interrupt to open the serial port to receive data. However, when I configure sense-edge-mask, I interrupt and switch the serial port, and this way does not work properly.
My configuration is as follows
uart20rx0: uart20rx_0 {
gpios = <&gpio1 5 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "Uart20 RX GPIO";
};
&gpio1 {
status = "okay";
sense-edge-mask = <0xffffffff>;
};
/omit-if-no-ref/ uart20_default: uart20_default {
group1 {
psels = <NRF_PSEL(UART_TX, 1, 4)>;
};
group2 {
psels = <NRF_PSEL(UART_RX, 1, 5)>;
bias-pull-up;
};
};
/omit-if-no-ref/ uart20_sleep: uart20_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 1, 4)>,
<NRF_PSEL(UART_RX, 1, 5)>;
low-power-enable;
bias-pull-up;
};
};
My interrupt configuration and serial port configuration code are as follows
#include "app_uart.h"
#include "Uart.h"
#include "app_parser.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(app_uart, LOG_LEVEL_DBG);
namespace app_uart
{
using namespace uart;
K_THREAD_STACK_DEFINE(sRxDataProc, UartRxManager::RX_PROC_STACK_SIZE);
K_MSGQ_DEFINE(sRxDataQueue, sizeof(UartRxManager::UartRxData), UartRxManager::RX_MSGQ_MAX_NUM, 4);
UartRxManager &UartRxManager::instance()
{
static UartRxManager inst;
return inst;
}
int UartRxManager::init()
{
if (is_initialized)
{
LOG_WRN("UART already initialized");
return -1;
}
Uart::Init();
uart_dev = DEVICE_DT_GET(DT_NODELABEL(uart20));
if (!device_is_ready(uart_dev))
{
LOG_ERR("uart20 is not ready");
return -1;
}
uart_config config = {
.baudrate = 9600,
.parity = UART_CFG_PARITY_NONE,
.stop_bits = UART_CFG_STOP_BITS_1,
.data_bits = UART_CFG_DATA_BITS_8,
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE,
};
Uart::Config uart_cfg = {
.dev = uart_dev,
.uartCfg = &config,
.rxCallback = uart_rx_callback,
};
uart_idx = Uart::Register(uart_cfg);
if (uart_idx < 0)
{
LOG_ERR("UART register failed");
return -1;
}
Uart::Enable(uart_idx, false);
k_thread_create(&rx_thread,
sRxDataProc,
K_THREAD_STACK_SIZEOF(sRxDataProc),
rx_proc_thread,
nullptr, nullptr, nullptr,
RX_PROC_STACK_PRIORITY, 0, K_NO_WAIT);
k_thread_name_set(&rx_thread, "UartRxProc");
RxGpioTrigger().init();
is_initialized = true;
return 0;
}
void UartRxManager::send(const uint8_t *data, uint16_t len)
{
if (uart_idx < 0 || !data || len == 0)
{
LOG_WRN("Invalid UART send params");
return;
}
Uart::Transmit(uart_idx, data, len);
}
int UartRxManager::uart_rx_callback(const uint8_t *data, size_t len)
{
if (!data || len == 0)
return -1;
size_t remaining = len;
size_t offset = 0;
while (remaining > 0)
{
size_t chunk = MIN(remaining, RX_LEN_MAX);
auto *buf = static_cast<uint8_t *>(k_malloc(chunk));
if (!buf)
{
LOG_ERR("k_malloc failed");
return -1;
}
memcpy(buf, data + offset, chunk);
UartRxData msg = {
.len = static_cast<uint16_t>(chunk),
.data = buf};
if (k_msgq_put(&sRxDataQueue, &msg, K_NO_WAIT) != 0)
{
LOG_ERR("k_msgq_put failed: dropped %u bytes", msg.len);
k_free(buf);
}
offset += chunk;
remaining -= chunk;
}
// Disable UART RX, re-enable GPIO trigger
Uart::Enable(instance().uart_idx, false);
RxGpioTrigger().enable();
return 0;
}
void UartRxManager::rx_proc_thread(void *p1, void *p2, void *p3)
{
UartRxData rx;
uint8_t raw[UART_BYTE_MAX] = {0};
while (true)
{
if (k_msgq_get(&sRxDataQueue, &rx, K_FOREVER) == 0)
{
if (rx.len > 0 && rx.data)
{
uint16_t copy_len = MIN(rx.len, UART_BYTE_MAX - 1);
memcpy(raw, rx.data, copy_len);
raw[copy_len] = '\0';
LOG_INF("UART RECV: %s", (char *)raw);
if (SensorStatusParser::instance().parse(reinterpret_cast<char *>(raw), copy_len))
{
SensorStatusParser::instance().sendAck();
}
k_free(rx.data);
}
}
}
}
struct gpio_callback RxGpioTrigger::gpio_cb;
void RxGpioTrigger::init()
{
if (!device_is_ready(gpio_spec.port))
{
LOG_ERR("RX GPIO not ready");
return;
}
gpio_pin_configure_dt(&gpio_spec, GPIO_INPUT);
gpio_pin_interrupt_configure_dt(&gpio_spec, GPIO_INT_EDGE_BOTH);
gpio_init_callback(&gpio_cb, gpio_isr_callback, BIT(gpio_spec.pin));
gpio_add_callback(gpio_spec.port, &gpio_cb);
}
void RxGpioTrigger::enable()
{
gpio_pin_configure_dt(&gpio_spec, GPIO_INPUT);
gpio_add_callback(gpio_spec.port, &gpio_cb);
gpio_pin_interrupt_configure_dt(&gpio_spec, GPIO_INT_EDGE_BOTH);
}
void RxGpioTrigger::gpio_isr_callback(const struct device *, struct gpio_callback *, uint32_t)
{
gpio_remove_callback(gpio_spec.port, &gpio_cb);
gpio_pin_interrupt_configure_dt(&gpio_spec, GPIO_INT_DISABLE);
Uart::Enable(UartRxManager::instance().uart_idx, true);
}
} // namespace app_uart
If I remove sense-edge-mask=< 0xffffffff >; Then there is no problem with the way of interrupting and switching between serial ports, but the power consumption will be much higher
Is there something wrong with my configuration?