一、 背景引入
在使用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中,多个串口中断接收可能导致硬件故障的原因可能有以下几种:
-
中断优先级冲突:如果多个串口中断优先级设置不当,可能会导致高优先级中断长时间占用CPU资源,低优先级中断无法及时响应,最终导致硬件故障。确保各个串口中断的优先级配置合理,避免长时间阻塞低优先级中断。
-
中断处理时间过长:如果中断服务程序(ISR)中执行的操作过多或时间过长,会导致其他中断无法及时处理。应尽量简化中断服务程序,主要完成快速的硬件操作,将复杂的处理逻辑放到中断后处理(例如中断下半部或任务中)。
-
缓冲区溢出:如果串口接收到的数据没有及时处理或缓冲区设置过小,可能会导致缓冲区溢出,最终造成数据丢失或硬件故障。确保串口接收缓冲区足够大,并且接收数据及时处理。
-
中断嵌套和冲突:在一些复杂系统中,如果中断嵌套过多或处理不当,可能会导致系统资源耗尽或死锁情况发生。确保中断嵌套深度合理,并合理配置中断屏蔽和使能机制。
所以,为了减小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个,上层应用的话,就跟平时串口操作一样,将接收到的数据放入缓冲区就行了。
完事,散会!!!!