ESP32自定义UART驱动中断异常:写入使能寄存器后ISR未触发
UART TX_FIFO_EMPTY 中断无法触发的问题
我编写了一个UART发送函数,原本应触发TX_FIFO_EMPTY中断对应的ISR,但功能完全失效。调试发现:无论FIFO为空还是有数据,ISR都不会触发;直接写入FIFO寄存器可在串口输出字符,但无法通过ISR完成发送操作。
相关代码
静态变量与发送函数
static UART_INPUT uart_input_ = { .UART_NUM = 0, .data = NULL, .length = 0 }; void uart_send(uint8_t UART_NUM, char* data) { // 修改要发送的数据参数 uart_input_.UART_NUM = UART_NUM; uart_input_.length = strlen(data); uart_input_.data = data; // 清空寄存器并触发TX FIFO写入中断 WRITE_REG(UARTS_ENB_INT[UART_NUM], 0); WRITE_REG(UARTS_ENB_INT[UART_NUM], (1 << 1)); }
寄存器定义数组
#define UART0_INTERRUPT_ENABLE (UART0_BASE + 0x0C) #define UART1_INTERRUPT_ENABLE (UART1_BASE + 0x0C) #define UART2_INTERRUPT_ENABLE (UART2_BASE + 0x0C) #define UART0_INTERRUPT_DISABLE (UART0_BASE + 0x10) #define UART1_INTERRUPT_DISABLE (UART1_BASE + 0x10) #define UART2_INTERRUPT_DISABLE (UART2_BASE + 0x10) const uint32_t UARTS_FIFO[] = { UART0_FIFO, UART1_FIFO, UART2_FIFO }; const uint32_t UARTS_DIS_INT[] = { UART0_INTERRUPT_DISABLE, UART1_INTERRUPT_DISABLE, UART2_INTERRUPT_DISABLE }; const uint32_t ETS_UARTS[] = { ETS_UART0_INTR_SOURCE, ETS_UART1_INTR_SOURCE, ETS_UART2_INTR_SOURCE }; const uint32_t UARTS_ENB_INT[] = { UART0_INTERRUPT_ENABLE, UART1_INTERRUPT_ENABLE, UART2_INTERRUPT_ENABLE }; const uint32_t UARTS_INT_ST[] = { UART0_INT_ST, UART1_INT_ST, UART2_INT_ST }; static RingBuffer *ring_buffer; static uint8_t *buffer = NULL;
ISR实现
void IRAM_ATTR uart_isr(void* arg) { // ESP32每个UART只有一个中断,这里同时处理读写中断 UART_INPUT* uart_input = (UART_INPUT*)arg; // 从参数获取UART编号 uint8_t UART_NUM = uart_input->UART_NUM; // 读取中断状态,判断是读还是写中断 uint32_t status = READ_REG(UARTS_INT_ST[UART_NUM]); // 如果是RX FIFO满中断 if (status & (1 << 0)) { while (is_data_available_uart(UART_NUM)) { // 读取寄存器数据并清除保留位 uint8_t data = READ_REG(UARTS_FIFO[UART_NUM]) & 0xFF; // 将数据写入环形缓冲区供后续读取 write_ring_buffer(ring_buffer, data); } } // 如果是TX FIFO空中断 if (status & (1 << 1)) { // 获取要发送的数据 char* data = uart_input_.data; // 通过UART发送用户数据 // 检查中断是否仍处于使能状态 while (uart_input_.length > 0 && (READ_REG(UARTS_ENB_INT[UART_NUM]) & (1 << 1))) { // 遍历数据并写入FIFO寄存器 WRITE_REG(UARTS_FIFO[UART_NUM], *data); // 发送前必须解引用数据 uart_input_.length--; data++; } if (uart_input_.length == 0) { WRITE_REG(UARTS_DIS_INT[UART_NUM], (1 << 1)); } } // 清除中断,避免重复触发 WRITE_REG(UARTS_DIS_INT[UART_NUM], 1 >> 0); WRITE_REG(UARTS_DIS_INT[UART_NUM], (1 << 1)); }
中断注册代码
esp_err_t err = esp_intr_alloc(ETS_UARTS[UART_NUM], ESP_INTR_FLAG_IRAM, uart_isr, (void*) &uart_input_, NULL);
问题分析与修复方案
1. 中断使能寄存器操作错误
ESP32的UART中断使能寄存器是位掩码寄存器,直接写入0会关闭所有中断,且直接赋值(1 << 1)会覆盖原有配置,误关其他已使能的中断。正确操作应该是对目标位进行置位/清除,而非直接覆盖寄存器值:
// 先清除TX中断位,再置位(保留其他中断配置) WRITE_REG(UARTS_ENB_INT[UART_NUM], READ_REG(UARTS_ENB_INT[UART_NUM]) & ~(1 << 1)); WRITE_REG(UARTS_ENB_INT[UART_NUM], READ_REG(UARTS_ENB_INT[UART_NUM]) | (1 << 1));
2. TX FIFO中断触发阈值未配置
ESP32的TX FIFO空中断默认不是"FIFO为空时触发",而是需要手动配置触发阈值。在UART初始化时添加以下代码,将阈值设为0(FIFO为空时触发):
// 配置TX FIFO空中断触发阈值为0 WRITE_REG(UART_CONF0[UART_NUM], (READ_REG(UART_CONF0[UART_NUM]) & ~UART_TX_FIFO_THRHD_M) | (0 << UART_TX_FIFO_THRHD_S));
3. ISR全局变量访问隐患
ISR中直接访问全局变量uart_input_,而非使用传入的uart_input指针,若后续扩展多UART场景会导致逻辑混乱。修改为使用参数指针:
// 替换原有全局变量访问逻辑 char* data = uart_input->data; while (uart_input->length > 0 && (READ_REG(UARTS_ENB_INT[UART_NUM]) & (1 << 1))) { WRITE_REG(UARTS_FIFO[UART_NUM], *data); uart_input->length--; data++; } if (uart_input->length == 0) { WRITE_REG(UARTS_DIS_INT[UART_NUM], (1 << 1)); }
4. 初始触发缺失
首次使能TX中断时,若FIFO为空,需要手动写入一个字节启动发送流程,否则UART不会主动触发空中断。在uart_send函数末尾添加:
// 手动写入第一个字节,启动发送流程 if (uart_input_.length > 0) { WRITE_REG(UARTS_FIFO[UART_NUM], *uart_input_.data); uart_input_.length--; uart_input_.data++; }
内容的提问来源于stack exchange,提问作者Yousef
相关产品推荐
相关产品推荐

