本帖最后由 liuxiang5119 于 2019-1-25 11:23 编辑
第二步:串口中断接收(自学摸索 有问题欢迎指教)从cube导出的hal库,设置时候给开了中断,在stm32f4xx_hal_msp.c中,所有的底层硬件初始化都这这里,所以cube更新硬件设置后,这个文件以及它的.h文件都得替换下;初始化的函数是在main里边,因为使用ucos,所以把初始化代码放在了bsp_ser里边
1、串口中断在ucos里边得使用ucos自己的中断向量表以及中断函数配置,不能使用hal库自带的中断初始化,否则进不去ucos中断,所以在cube配置的时候,我把串口中断给关了,然后初始化中断用以下函数替代:
BSP_IntVectSet(BSP_INT_ID_USART2, BSP_Ser2_ISR_Handler);//BSP_Ser2_ISR_Handler为中断处理函数
BSP_IntPrioSet(BSP_INT_ID_USART2,4);
BSP_IntEn(BSP_INT_ID_USART2);
BSP_IntVectSet(BSP_INT_ID_USART3, BSP_Ser3_ISR_Handler);//BSP_Ser3_ISR_Handler为中断处理函数
BSP_IntPrioSet(BSP_INT_ID_USART3,4);
BSP_IntEn(BSP_INT_ID_USART3);
//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
HAL_UART_Receive_IT(&Uart3Handle, (uint8_t *)aRxBuffer3, USART3_REC_SIZE);
//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
HAL_UART_Receive_IT(&Uart2Handle, (uint8_t *)aRxBuffer2, USART2_REC_SIZE);
2、中断数据的接收,网上有人说在回调处理效率比较低,所以直接在中断里边去处理,完了再调用HAL_UART_IRQHandler(&Uart3Handle);个人认为实际上中断处理时间是一样的,区别是先读数据还是先读状态寄存器处理错误标志,当然主频低的话可能会造成数据读取错误。主频率168M实际测不出来到底有多大的影响,后期实际应用再测试吧。void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance==USART2)//串口2数据处理
{
while (HAL_UART_Receive_IT(&Uart2Handle, (uint8_t *)aRxBuffer2, USART2_REC_SIZE) != HAL_OK);
}
else if (huart->Instance==USART3)//串口3数据处理
{
BSP_LED_Toggle(2);
if (USART3_RX_STA < (USART3_REC_LEN - 1)) //还可以接收数据
{
__HAL_TIM_SET_COUNTER(&htim3,0);//计数器清空
if (USART3_RX_STA == 0)
{
BSP_TIM_ENABLE(&htim3); //使能定时器3的中断
}
USART3_RX_BUF[USART3_RX_STA++] = aRxBuffer3[0]; //记录接到的值USART3_RX_BUF[USART3_RX_STA++] = aRxBuffer3[0]; //记录接到的值
}else
{
//USART3_RX_BUF[(USART3_REC_LEN-1)] = aRxBuffer3[0]; //记录最后一个数
BSP_UART_RXC(&Uart3Handle);
}
if (HAL_UART_Receive_IT(&Uart3Handle, (uint8_t *)aRxBuffer3, USART3_REC_SIZE) != HAL_OK)
{
__HAL_UNLOCK(&Uart3Handle);
BSP_printf(("BSP USART3 RECEIVE IT ERR in bsp_ser.c line 219\r"));
}
}
}
void BSP_Ser3_ISR_Handler(void)
{
HAL_UART_IRQHandler(&Uart3Handle); //调用HAL库中断处理公用函数
}
这里只做了串口3的接收处理,实际测试200字节,间隔50ms循环发送没出现问题
另外记录下发送这块,被自己给坑了,如果在中断模式,发送数据前需要判断设备状态,否则连续两条发送数据会丢失第二条(开始真心不知道)HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&USART3_RX_BUF, USART3_RX_STA&0x7FFF);
HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&USART3_RX_BUF, USART3_RX_STA&0x7FFF);
如上所示,执行的时候第一条会返回HAL_OK,然后进入串口发送中断程序,第二条直接返回HAL_BUSY,所以第二条数据是不会发送的!
改成下边这条:
while(HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&USART3_RX_BUF, USART3_RX_STA&0x7FFF) != HAL_OK);
while(HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&USART3_RX_BUF, USART3_RX_STA&0x7FFF) != HAL_OK);
第二条程序会等待设备空闲,然后再执行,这里如果后期要用,需要做个等待时间处理,防止一直等待造成程序假死!
3、串口发送重定向
串口重定向,网上搜的程序一般都是
CPU_INT32S fputc(CPU_INT32S ch, FILE *f)
{
HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&ch, 1);这个或者下面那条命令
HAL_UART_Transmit(&Uart3Handle, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
这里给改成下面那条命令,HAL_UART_Transmit这个是非阻塞发送,和接收中断容易引起互锁,改成HAL_UART_Transmit_IT,这条是中断发送,但是有个问题,就是上边说的需要查询设备状态,否则会丢数据! 所以加个while判断状态,或者不怕效率低直接在后边加个小延时程序也可以!
CPU_INT32S fputc(CPU_INT32S ch, FILE *f)
{
while(HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&ch, 1) != HAL_OK);
return ch;
}
这两天继续折腾了下串口,只因为想用串口调试做个回显,比如说CRT上调试 可以直接敲字符直接显示,类似于linux系统的调试本来想法很简单,谁知道实现过程有点坑
1、使用串口中断接收以及中断发送,那么实时回显就很难做到,串口接收中断里边不可能直接开个发送中断的;
2、既然这样,那就再中断里直接做个阻塞发送,反正一个字符占用很少时间;
3、做完之后测试,键盘一个个敲,基本没有问题,想着我直接复制200多个字符过去,然后问题就出现了,串口中断假死了,应该是互锁了,发送的无法获取串口空闲状态,HAL库好麻烦,开始一直去处理假定返回USART_BUSY,然后怎么去处理,是直接解锁串口,还是去初始化;初始化倒是能好使,但是容易出错直接进入硬件错误
4、后来想了下,既然中断里边没法做大数据回显 那就不再中断里做了 ucos直接另外开了一个任务函数 只做数据回显用 那么就涉及到能不能回显的及时,以及大数据回显时候会不会丢数据
5、通过调试基本实现,想法是这样,在任务里做了个5ms延时,5ms内如果没有新数据接收到,那么任务该组数据接收完成,不管是一个字符还是一大串数据都没问题,接收完再回显,5ms的延时 这个时间足够,而且人眼反应速度也没这么快
代码如下:static void AppTaskSerEcho (void *p_arg)
{
OS_ERR err;
(void)p_arg;
CPU_SR_ALLOC();
while (DEF_TRUE) {
#ifdef USART3_ECHO
if ((USART3_TX_STA != 0)&&(USART3_TX_STA == USART3_TX_REC))//接收到数据 并且5ms内没接收到数据 则回显
{
CPU_CRITICAL_ENTER();
HAL_UART_Transmit(&Uart3Handle, (uint8_t *)&USART3_TX_BUF, USART3_TX_STA, 0xFFFF);
USART3_TX_STA = 0;
USART3_TX_REC = 0;
CPU_CRITICAL_EXIT();
}
USART3_TX_REC = USART3_TX_STA;//记录上一次数据
#endif
OSTimeDlyHMSM(0, 0, 0, 5,
OS_OPT_TIME_HMSM_STRICT,
&err);
}
}
|