【好记性不如烂笔头】(四)RT-Thread终端无法输入问题以及输入任意字符导致进入硬件故障的解决办法

一、 背景引入

在使用RT-THread时,出现了一种奇怪的现象,程序运行正常,各种传感器也正常工作,但就是终端无法输入;或是随便点击键盘,立马会出现硬件故障。

二、解决思路

2.1、终端无法输入问题

首先,因为在初建工程时,我的打印是正常的,但是当我打开一个传感器线程时,惊讶的发现,终端就不能输入了,或者输入任意字符就直接硬件故障。
所以第一步,就是关闭问题线程,确保其他线程没有问题。
然后查看优先级,时间片大小是否合适,有没有让出线程动作这些基本操作。
发现。。。没好。我就知道问题不会这么简单。

老规矩,先看论坛有没有前人遇到过:
终端调试台无法输入指令
msh终端无法输入任何信息
额发现没啥用,还是自己找吧。

前面说了,当加入这个线程后,程序出现了终端无法输入的问题,那就仔细检查这个线程,先从初始化开始,

static int Sensor_Rec_Data(void)
{
    rt_err_t ret = RT_EOK;
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;  /* 初始化配置参数 */
    
    /* 查找系统中的串口设备 */
    rt_sensor_device = rt_device_find("uart4");
    if (!rt_sensor_device)
    {
        rt_kprintf("find %s failed!\n", SENSOR_UART);
        return RT_ERROR;
    }
    else
    {
        rt_kprintf("find %s OK!\n", SENSOR_UART);
    }

    config.baud_rate = BAUD_RATE_9600; //修改波特率
    if (rt_device_control(rt_sensor_device, RT_DEVICE_CTRL_CONFIG, &config) != RT_EOK)
    {
        rt_kprintf("rt_device_control failed!\n");
        return RT_ERROR;
    }

    /* 初始化信号量 */
    rt_sem_init(&rx_sem, "sensor", 0, RT_IPC_FLAG_FIFO);

    /* 以中断接收及轮询发送模式打开串口设备 */
    if (rt_device_open(rt_sensor_device, RT_DEVICE_FLAG_INT_RX) != RT_EOK)
    {
        rt_kprintf("rt_device_open failed!\n");
        return RT_ERROR;
    }
    /* 设置接收回调函数 */
    if (rt_device_set_rx_indicate(rt_sensor_device, Uart4_Rec_Cb) != RT_EOK)
    {
        rt_kprintf("rt_device_set_rx_indicate failed!\n");
        return RT_ERROR;
    }

    rx_precess_mutex = rt_mutex_create("precess", RT_IPC_FLAG_PRIO);//该标志已经作废,无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO,内核均按照 RT_IPC_FLAG_PRIO 处理
    if (rx_precess_mutex != RT_NULL )
    {
        rt_kprintf("create rx_precess_mutex ok\n");
    }


    /* 静态初始化线程 1*/
      ret = rt_thread_init(&sensor_thread1,//该线程用于数据解析
                           "snesor1",
                           Rec_Sensor_Thread_Entry,
                           RT_NULL,
                           &sensor_thread1_stack[0],
                           sizeof(sensor_thread1_stack),
                           THREAD1_PRIORITY,
                           THREAD_TIMESLICE);
      /* 创建成功则启动线程 */
        if (ret == RT_EOK)
       {
           LOG_I("rt_thread_startup sensor_thread1");
           rt_thread_startup(&sensor_thread1);
       }
       else
       {
           LOG_I("rt_thread_init sensor_thread Failed...");
           return ret;
       }
      /* 静态初始化线程2 */
     ret = rt_thread_init(&sensor_thread2,//该线程用于数据处理和产生事件
                                "snesor2",
                                Sensor_Data_Processing_Entry,
                                RT_NULL,
                                &sensor_thread2_stack[0],
                                sizeof(sensor_thread2_stack),
                                THREAD2_PRIORITY,
                                THREAD_TIMESLICE);

     /* 创建成功则启动线程 */
       if (ret == RT_EOK)
          {
              LOG_I("rt_thread_startup sensor_thread2");
              rt_thread_startup(&sensor_thread2);
          }
          else
          {
              LOG_I("rt_thread_init sensor_thread Failed...");
              ret = RT_ERROR;
          }
    return ret;
}
INIT_DEVICE_EXPORT(Sensor_Rec_Data);

2.2、输入任意字符进入硬件故障问题

这个问题,还是在第一个问题基础上遇到的,然后就各种尝试,找论坛看,发现
串口设备运行一段时间程序卡死跟我遇到的情况有点像,评论区有人说是这样的原因
在这里插入图片描述
我一看,我确实打开了两次,一次是drv_usart.h一次是在我这线程中,所以直接关闭这个线程中的打开函数

我查看底层驱动程序,发现并没有打开,所以我这里初始化应该也是第一次打开,所以不是这种原因。
那就按照我写的第一篇进入硬件故障问题解决方案
【好记性不如烂笔头】(三)RT-Thread硬件错误的处理办法1

通过第一个寄存器mepc的值,我定位到了南京沁恒写的中断函数中。根据第二个macuse寄存器值,我定位到了是在这里插入图片描述
中断中操作的时候,产生了错误。在这里插入图片描述
根据这两个结果,再加上我传感器是通过串口中断单字节接收,可能处理速度有点慢,造成了错误。

在RT-Thread中,多个串口中断接收可能导致硬件故障的原因可能有以下几种:

  1. 中断优先级冲突:如果多个串口中断优先级设置不当,可能会导致高优先级中断长时间占用CPU资源,低优先级中断无法及时响应,最终导致硬件故障。确保各个串口中断的优先级配置合理,避免长时间阻塞低优先级中断。

  2. 中断处理时间过长:如果中断服务程序(ISR)中执行的操作过多或时间过长,会导致其他中断无法及时处理。应尽量简化中断服务程序,主要完成快速的硬件操作,将复杂的处理逻辑放到中断后处理(例如中断下半部或任务中)。

  3. 缓冲区溢出:如果串口接收到的数据没有及时处理或缓冲区设置过小,可能会导致缓冲区溢出,最终造成数据丢失或硬件故障。确保串口接收缓冲区足够大,并且接收数据及时处理。

  4. 中断嵌套和冲突:在一些复杂系统中,如果中断嵌套过多或处理不当,可能会导致系统资源耗尽或死锁情况发生。确保中断嵌套深度合理,并合理配置中断屏蔽和使能机制。

所以,为了减小CPU的占用时间,我直接将其改成DMA接收,有的同学可能会问,为什么不早点用DMA,这里我觉得应该@南京沁恒官方,不更新RT-Thread 的DMA串口程序。所以一开始为了方便,节省时间,就造成了这种局面。所以在这里还是希望南京亲沁恒能完善生态,这样用户也就遇不到这种恶心人的问题了,也许会有更多人选择其产品。(题外话哈哈!)

言归正传
说一下大概思路,我的DMA接收比较粗暴。
就是使用串口空闲中断,产生一个空闲信号,此时通过这个接收回调函数,释放接收数据的信号量。在一个线程中,等待接收信号量。然后处理接收到的数据.
至于初始胡部分,直接写在ch32_configure函数中,只是接收,不开DMA中断,开了也行,不过有空闲中断,够了。没必要再加。

1、DMA初始化部分

rt_err_t uart_dma_rx_configure(struct rt_serial_device *serial,
                                uint8_t *buf,
                                DMA_Channel_TypeDef *DMAy_Channelx,
                                uint32_t RCC_AHBPeriph)
{
    struct ch32_uart *uart;
    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart = (struct ch32_uart *) serial->parent.user_data;

    DMA_InitTypeDef DMA_InitStructure = {0};

    if (RCC_AHBPeriph == RCC_AHBPeriph_DMA1)
    {
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    }
    if (RCC_AHBPeriph == RCC_AHBPeriph_DMA2)
    {
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
    }

    DMA_DeInit(DMAy_Channelx);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&uart->config->Instance->DATAR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buf;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    if (uart->config->Instance == USART2)
    {
        DMA_InitStructure.DMA_BufferSize = UART2_RX_DMA_BUFFER_LEN;
    }
    else if (uart->config->Instance == UART4)
    {
        DMA_InitStructure.DMA_BufferSize = UART4_RX_DMA_BUFFER_LEN;
    }
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMAy_Channelx, &DMA_InitStructure);

    DMA_Cmd(DMAy_Channelx, ENABLE);
    USART_DMACmd(uart->config->Instance, USART_DMAReq_Rx, ENABLE);

    return RT_EOK;
}

2、串口初始化部分

static rt_err_t ch32_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct ch32_uart *uart;
    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart = (struct ch32_uart *) serial->parent.user_data;

    uart->Init.USART_BaudRate             = cfg->baud_rate;
    uart->Init.USART_HardwareFlowControl  = USART_HardwareFlowControl_None;
    uart->Init.USART_Mode                 = USART_Mode_Rx|USART_Mode_Tx;

    switch (cfg->data_bits)
    {
    case DATA_BITS_8:
        uart->Init.USART_WordLength = USART_WordLength_8b;
        break;
    case DATA_BITS_9:
        uart->Init.USART_WordLength = USART_WordLength_9b;
        break;
    default:
        uart->Init.USART_WordLength = USART_WordLength_8b;
        break;
    }

    switch (cfg->stop_bits)
    {
    case STOP_BITS_1:
        uart->Init.USART_StopBits   = USART_StopBits_1;
        break;
    case STOP_BITS_2:
        uart->Init.USART_StopBits   = USART_StopBits_2;
        break;
    default:
        uart->Init.USART_StopBits   = USART_StopBits_1;
        break;
    }
    switch (cfg->parity)
    {
    case PARITY_NONE:
        uart->Init.USART_Parity    = USART_Parity_No;
        break;
    case PARITY_ODD:
        uart->Init.USART_Parity    = USART_Parity_Odd;
        break;
    case PARITY_EVEN:
        uart->Init.USART_Parity    = USART_Parity_Even;
        break;
    default:
        uart->Init.USART_Parity     = USART_Parity_No;
        break;
    }

    if(uart->config->Instance==USART1)
       {
           GPIO_InitTypeDef GPIO_InitStructure={0};
           RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);

           GPIO_InitStructure.GPIO_Pin = UART1_TX_GPIO_PIN;
           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
           GPIO_Init(UART1_TX_GPIO_PORT, &GPIO_InitStructure);

           GPIO_InitStructure.GPIO_Pin = UART1_RX_GPIO_PIN;
           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
           GPIO_Init(UART1_RX_GPIO_PORT, &GPIO_InitStructure);

           USART_Init(uart->config->Instance, &uart->Init);
           USART_Cmd(uart->config->Instance, ENABLE);
       }
       if(uart->config->Instance==USART2)
       {
           GPIO_InitTypeDef GPIO_InitStructure={0};
           RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
           RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

           GPIO_InitStructure.GPIO_Pin = UART2_TX_GPIO_PIN;
           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
           GPIO_Init(UART2_TX_GPIO_PORT, &GPIO_InitStructure);

           GPIO_InitStructure.GPIO_Pin = UART2_RX_GPIO_PIN;
           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
           GPIO_Init(UART2_RX_GPIO_PORT, &GPIO_InitStructure);

           USART_Init(uart->config->Instance, &uart->Init);
           USART_Cmd(uart->config->Instance, ENABLE);

       }
       if(uart->config->Instance==USART3)
       {
           GPIO_InitTypeDef GPIO_InitStructure={0};
           RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
           RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

           GPIO_InitStructure.GPIO_Pin = UART3_TX_GPIO_PIN;
           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
           GPIO_Init(UART3_TX_GPIO_PORT, &GPIO_InitStructure);

           GPIO_InitStructure.GPIO_Pin = UART3_RX_GPIO_PIN;
           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
           GPIO_Init(UART3_RX_GPIO_PORT, &GPIO_InitStructure);

           USART_Init(uart->config->Instance, &uart->Init);
           USART_Cmd(uart->config->Instance, ENABLE);

       }
       if(uart->config->Instance==UART4)
       {
           GPIO_InitTypeDef GPIO_InitStructure={0};
           RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);
           RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

//           GPIO_InitStructure.GPIO_Pin = UART4_TX_GPIO_PIN;
//           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
//           GPIO_Init(UART4_TX_GPIO_PORT, &GPIO_InitStructure);

           GPIO_InitStructure.GPIO_Pin = UART4_RX_GPIO_PIN;
           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
           GPIO_Init(UART4_RX_GPIO_PORT, &GPIO_InitStructure);

           uart4_dma_rx_configure();

           USART_Init(uart->config->Instance, &uart->Init);
           USART_Cmd(uart->config->Instance, ENABLE);

       }
       if(uart->config->Instance==UART5)
       {
           GPIO_InitTypeDef GPIO_InitStructure={0};
           RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);
           RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD, ENABLE);

           GPIO_InitStructure.GPIO_Pin = UART5_TX_GPIO_PIN;
           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
           GPIO_Init(UART5_TX_GPIO_PORT, &GPIO_InitStructure);

           GPIO_InitStructure.GPIO_Pin = UART5_RX_GPIO_PIN;
           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
           GPIO_Init(UART5_RX_GPIO_PORT, &GPIO_InitStructure);

           USART_Init(uart->config->Instance, &uart->Init);
           USART_Cmd(uart->config->Instance, ENABLE);

       }
    return RT_EOK;
}

3、串口中断初始化函数

static rt_err_t ch32_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct ch32_uart *uart;
    RT_ASSERT(serial != RT_NULL);
    uart = (struct ch32_uart *)serial->parent.user_data;
    switch (cmd)
    {
    /* disable interrupt */
    case RT_DEVICE_CTRL_CLR_INT:
        /* disable rx irq */
        NVIC_DisableIRQ(uart->config->irq_type);
        /* disable interrupt */

        if ((uart->config->name == "uart2")||(uart->config->name == "uart4"))//开启串口空闲中断
        {
            USART_ITConfig(uart->config->Instance, USART_IT_IDLE, DISABLE);
        }
        else {
            USART_ITConfig(uart->config->Instance, USART_IT_RXNE, DISABLE);
        }
        break;
    /* enable interrupt */
    case RT_DEVICE_CTRL_SET_INT:
        /* enable rx irq */
        NVIC_EnableIRQ(uart->config->irq_type);
        /* enable interrupt */

        if ((uart->config->name == "uart2")||(uart->config->name == "uart4"))//开启串口空闲中断
        {
            USART_ITConfig(uart->config->Instance, USART_IT_IDLE, ENABLE);
        }
        else
        {
            USART_ITConfig(uart->config->Instance, USART_IT_RXNE, ENABLE);
        }

        break;
    }
    return RT_EOK;
}

4、串口中断部分函数

static void uart_isr(struct rt_serial_device *serial)
{
    rt_uint8_t data;
    struct ch32_uart *uart = (struct ch32_uart *) serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);
    if (USART_GetITStatus(uart->config->Instance, USART_IT_RXNE) != RESET)
    {
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
        USART_ClearITPendingBit(uart->config->Instance, USART_IT_RXNE);
    }
    else if (USART_GetITStatus(uart->config->Instance, USART_IT_IDLE) != RESET)
    {
        data = uart->config->Instance->STATR;//清除标志位
        data = uart->config->Instance->DATAR;
        data &= 0;
        serial->parent.rx_indicate(&serial->parent, 1);//这个1没有任何意义,仅表示产生空闲中断,产生空闲回调
    }
}

通过这4个,上层应用的话,就跟平时串口操作一样,将接收到的数据放入缓冲区就行了。

完事,散会!!!!

  • 12
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值