RISC-V MCU中文社区

【求助】 求助:串口中断触发卡死

发表于 开源蜂鸟E203 2025-10-20 11:15:43
0
19
1

当用下列代码运行时,串口中断可以正常触发,回环数据接受正常。


#define UART_BAUDRATE 115200


static void uart_pinmux_init(void) {
#if defined(SOC_HBIRDV2)

    GPIOA->IOFCFG |= IOF_UART_MASK;          // 使能 UART RX/TX 的 IOF 复用
#elif defined(SOC_HBIRD)
    GPIO->IOFCFG  |= IOF_UART_MASK;
#endif
}

/* 低层使能 UART 接收中断、FIFO */
static void uart_rx_irq_enable(UART_TypeDef *U) {
    /* FCR: bit0 FIFO_EN, bit1 RX_FIFO_RST, bit2 TX_FIFO_RST
       0x07 = 使能 FIFO 并清 RX/TX FIFO */
    U->FCR = 0x07;

    /* IER: bit0 ERBFI(接收数据可用), bit2 ELSI(行状态) */
    U->IER |= (1u << 0) | (1u << 2);
}

/* 读取并处理 IIR,按类型清空接收 FIFO;返回是否还有挂起中断 */
static int uart_service_interrupt(UART_TypeDef *U) {
    /* IIR bit0 == 0 表示有挂起中断;[3:1] 是中断类型 */
    while ( (U->IIR & 0x01u) == 0 ) {
        uint32_t iid = (U->IIR >> 1) & 0x07u;
        switch (iid) {
            case 0x02: /* THRE - 发送保持寄存器空,若需要可填充发送缓冲 */
                (void)U->LSR; /* 读一下 LSR,很多实现里这是无害的 */
                break;
            case 0x03: /* RLS - 接收线路状态,比如帧/奇偶/溢出错误 */
                (void)U->LSR; /* 读取 LSR 清除异常标志 */
                break;
            case 0x06: /* RX 超时(FIFO 中有数据但超时)fallthrough */
            case 0x04: /* RDA - 接收数据可用 */
            {
                /* 把 FIFO 里的字节读完 */
                while (U->LSR & 0x01u) {  /* LSR bit0: Data Ready */
                    uint8_t c = (uint8_t)U->RBR;
                    /* 轻量回显(ISR 内尽量短),生产上建议放环形缓冲 */
                    uart_write(U, c);
                }
                break;
            }
            default:
                /* 其它:MODEM 等,按需处理 */
                (void)U->IIR;
                break;
        }
    }
    /* 到这儿 IIR bit0 为 1(无挂起中断) */
    return 0;
}

/* UART0 PLIC 中断服务函数 */
void plic_uart0_handler(void) {
    uart_service_interrupt(UART0);
}

/* 板级初始化:Pinmux + UART 初始化 + 开 RX 中断 */
static void board_init(void) {
    uart_pinmux_init();

    /* 初始化 UART:波特率/8N1 等(假定 uart_init 做好这些) */
    uart_init(UART0, UART_BAUDRATE);

    /* 开 FIFO、开接收中断(IER) */
    uart_rx_irq_enable(UART0);

    uart_puts(UART0, "UART Interrupt Test (115200 8N1)\r\n");
    uart_puts(UART0, "Type something to see echo...\r\n");
}

void run_nomal_mode(SPI_TypeDef *spi, UART_TypeDef *uart)
{
    /* 初始化板级(仅串口) */
    board_init();

    /* 注册 UART0 的 PLIC 中断,优先级 1(可按需调整) */
    int32_t rc = PLIC_Register_IRQ(PLIC_UART0_IRQn, 1, plic_uart0_handler);
    if (rc != 0) {
        //printf("UART0 interrupt register failed!\r\n");
        uart_puts(UART0, "UART0 interrupt register failed!\r\n");
        while (1) { }
    }

    /* 开全局中断 */
    __enable_irq();

    /* 主循环空转(中断处理收发) */
    while (1) {
        /* 可在此做其它任务;尽量不要在 ISR 里做重活 */
        // delay_1ms(10);
    }
}

而当用下列代码时,会卡死在uart_write(UART0, c),

#define UART_BAUDRATE 115200


static volatile uint8_t uart_rx_flag = 0;   // ISR置位,主循环读取后清零

/* 可选:把 UART 引脚复用到 IOF(按你板卡选择 GPIOA 或 GPIO) */
static void uart_pinmux_init(void) {
#if defined(SOC_HBIRDV2)
    /* HBirdv2: UART 通常走 GPIOA 的 IOF */
    GPIOA->IOFCFG |= IOF_UART_MASK;          // 使能 UART RX/TX 的 IOF 复用
#elif defined(SOC_HBIRD)
    /* 老 HBird: 如果有 GPIO->IOFCFG 则用之;没有就忽略 */
    GPIO->IOFCFG  |= IOF_UART_MASK;
#endif
}

/* 低层使能 UART 接收中断、FIFO */
static void uart_rx_irq_enable(UART_TypeDef *U) {
    /* FCR: bit0 FIFO_EN, bit1 RX_FIFO_RST, bit2 TX_FIFO_RST
       0x07 = 使能 FIFO 并清 RX/TX FIFO */
    U->FCR = 0x07;

    /* IER: bit0 ERBFI(接收数据可用), bit2 ELSI(行状态) */
    U->IER |= (1u << 0) | (1u << 2);
}

/* 读取并处理 IIR,按类型清空接收 FIFO;返回是否还有挂起中断 */
static int uart_service_interrupt(UART_TypeDef *U) {
    /* IIR bit0 == 0 表示有挂起中断;[3:1] 是中断类型 */
    while ( (U->IIR & 0x01u) == 0 ) {
        uint32_t iid = (U->IIR >> 1) & 0x07u;
        switch (iid) {
            case 0x02: /* THRE - 发送保持寄存器空,若需要可填充发送缓冲 */
                (void)U->LSR; /* 读一下 LSR,很多实现里这是无害的 */
                break;
            case 0x03: /* RLS - 接收线路状态,比如帧/奇偶/溢出错误 */
                (void)U->LSR; /* 读取 LSR 清除异常标志 */
                break;
            case 0x06: /* RX 超时(FIFO 中有数据但超时)fallthrough */
            case 0x04: /* RDA - 接收数据可用 */
            {    uart_rx_flag = 1;          // 通知主循环
                 U->IER &= ~(1u << 0);      // ★ 关闭 ERBFI(接收可用中断),防止中断风暴
                 return 0;                  // ★ 立即退出 ISR,把处理权交给主循环
               /*
                while (U->LSR & 0x01u) {
                    uint8_t c = (uint8_t)U->RBR;
                    uart_rx_flag =1;
                    uart_write(U, c);

                    printf("123uart_rx_flag=%d",uart_rx_flag);
                }
                break;*/
            }
            default:
                /* 其它:MODEM 等,按需处理 */
                (void)U->IIR;
                break;
        }
    }
    /* 到这儿 IIR bit0 为 1(无挂起中断) */
    return 0;
}

/* UART0 PLIC 中断服务函数 */
void plic_uart0_handler(void) {
    uart_service_interrupt(UART0);
}

/* 板级初始化:Pinmux + UART 初始化 + 开 RX 中断 */
static void board_init(void) {
    uart_pinmux_init();

    /* 初始化 UART:波特率/8N1 等(假定 uart_init 做好这些) */
    uart_init(UART0, UART_BAUDRATE);

    /* 开 FIFO、开接收中断(IER) */
    uart_rx_irq_enable(UART0);

    uart_puts(UART0, "UART Interrupt Test (115200 8N1)\r\n");
    uart_puts(UART0, "Type something to see echo...\r\n");
}

int main(void)
{
    /* 初始化板级(仅串口) */
    board_init();
    uart_rx_flag =0;
    uint8_t test = 0;
    uint8_t c;
    uint8_t test2;
    uint8_t test3;
    /* 注册 UART0 的 PLIC 中断,优先级 1(可按需调整) */
    int32_t rc = PLIC_Register_IRQ(PLIC_UART0_IRQn, 1, plic_uart0_handler);
    if (rc != 0) {
        //printf("UART0 interrupt register failed!\r\n");
        uart_puts(UART0, "UART0 interrupt register failed!\r\n");
        while (1) { }
    }

    /* 开全局中断 */
    __enable_irq();

    /* 主循环空转(中断处理收发) */
    while (1) {
        test = uart_rx_flag;
        if (uart_rx_flag==1) {
            uart_rx_flag = 0;

            /* 把这波 FIFO 数据全部读掉(直到 LSR.DR=0) */
            while (UART0->LSR & 0x01u) {
                test2 = UART0->LSR & 0x01u;
                c = (uint8_t)UART0->RBR;

                //uart_puts(UART0, "IRQ RX\r\n");      // 用 uart_puts,别用 printf
            }

            uart_write(UART0, c);            // 回显或存入你的缓冲
            //printf("frame_result_tccccc=%d",c);

            //uart_puts(UART0, "IRQ RX\r\n");      // 用 uart_puts,别用 printf

            /* ★ 关键:读完再开 ERBFI,允许下一次中断 */
            UART0->IER |= (1u << 0);
        }

        //uart_puts(UART0, "IRQ RX\r\n");      // 用 uart_puts,别用 printf
        delay_1ms(1000);


    }
}

两个代码区别在于,一个发送数据在中断服务函数中完成,一个是在while中完成,在while中完成的就存在卡死的现象。跪求大佬指导!

喜欢1
用户评论
Andew

Andew 实名认证

懒的都不写签名

积分
问答
粉丝
关注
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
RV-STAR 开发板