本帖最后由 wuxiaoluo 于 2019-5-3 10:19 编辑 用cubeMX生成工程,设置串口为中断接收。 在主程序中开启中断接收,1个字节 主程序while中printf打印变量 MX_USART1_UART_Init(); __HAL_UART_ENABLE_IT(&huart1, UART_IT_ERR); HAL_UART_Receive_IT(&huart1,(uint8_t*) &Uart1_Rx_Byte,1); //接收1个字符 while (1) {printf("\r\n-----RcvCount=%8d , ErrCount=%8d RcErrCount=%8d\r\n",RcvCount,ErrCount,RcErrCount); HAL_Delay(100); } 中断接收什么都不处理 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { RcvCount++; RcvErr = HAL_UART_Receive_IT(&huart1,(uint8_t*) &Uart1_Rx_Byte,1); //再次接收1个字符 } void HAL_UART_ErrorCallback( UART_HandleTypeDef *huart) { ErrCount++; HAL_UART_Receive_IT(&huart1,(uint8_t*) &Uart1_Rx_Byte,1); //恢复接收 } 上位机快速发送字符很快就会没接收了(几十个到几百个字符,随机不等)。无法再次进入接收中断。(RcvCount不会增加 , 返回的 RcvErr=HAL_BUSY (2),就是再次HAL_UART_Receive_IT失败了)有时会while无法发送(pc没收到消息) |
评分
查看全部评分
接收一个字节, 然后通过打印了N个字节, 可有考虑打印一个字节的需时? 应该是跟接收一个字节的需要是一样吧? 对不?
比如你每86us接收一字节, 然后用了N*86us打印, 然后再回来接收另一个字节
你觉得能收到第二个字节么?
评分
查看全部评分
评分
查看全部评分
https://www.stmcu.org.cn/module/forum/thread-619702-1-1.html
评分
查看全部评分
这样没有多大问题。 但是你err中断处理有问题,
err中断你恢复接收,err中断都没清理, 肯定恢复不了啊!进入err中断,计数完要清理掉中断才能继续的。
(recieve中断,你用的那个接收函数帮你做完清理了)
最起码也要最后把错误状态置为 ready先啊。 recieve_it 不会帮你做这个的。
评分
查看全部评分
所以才要用中断接收呀,无论你输出多少,接收来了中断就把字节放到缓存区数组中,这样不会影响输出,也不会影响输入。因为48M以上处理一下接收字节转存到自己写的环形存储buffer需要时间很短,而115200一个字节需要时间大概是 80us,48M主频每条指令是0.02us,中断函数大概不到0.5us,转存接收到的字节数据怎么都不会超过5us,一般在1us以下。所以收发可以同时不受影响的。主线程可以做接收数据分析,接收中断保证数据不丢。中断就是应付这些多任务和等待事件而不影响普通任务用的。
后来发现,根本不会err,只是忙,被发送锁定了,发送执行时遇到接收中断。因为它是HAL库发送时自己标志锁,导致接收函数判断为忙而返回,从而无法开启接收。如果根据忙来做一个while,是可以恢复一部分,但没过多久就收发都不行了。发送也卡死了。HAL的互斥锁,是为了多线程保护usart结构数据的。一般小单片机小任务用不上,反而会出现死锁几率。得不偿失。
解决办法:
最简单把printf的函数的putc改为寄存器发送,这样HAL就只是接收,不会锁死了。
int fputc(int ch,FILE *f)
{
while( __HAL_UART_GET_FLAG( &huart1 , UART_FLAG_TC ) == RESET ); //等待串口1能发送
huart1.Instance->TDR = ch & (uint8_t)0xFFU; //串口1发送数据
return ch;
}
要把所有的HAL_UART_Transmit都换了
如果需要效率,就把中断接收也换了:
1) 在main中初始化结构接收缓充,代替HAL_UART_Receive_IT启动中断接收。:
volatile uint8_t RxByte; //存放接收数据的全局字节变量。volatile保证全局不会被优化掉。
huart1.pRxBuffPtr = &RxByte; //把变量地址赋值给结构,好让在中断中赋值
huart1.RxXferSize = 1; huart1.RxXferCount = 0; //接收长度为1个字节。已接收字节数为0
SET_BIT(huart1.Instance->CR1, USART_CR1_RXNEIE);//接收使能
//SET_BIT(huart1.Instance->CR1, USART_CR1_PEIE);//接收时发生错误产生中断使能。一般不开启
2) 在stm32f0xx_it.c 的中断函数 void USART1_IRQHandler(void) 中处理接收
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
uint32_t isrflags = READ_REG(huart1.Instance->ISR); //各种状态标志寄存器
uint32_t cr1its = READ_REG(huart1.Instance->CR1); //配置寄存器
if(((isrflags & USART_ISR_RXNE) != RESET) &&\
((cr1its & USART_CR1_RXNEIE) != RESET))//接收到字符,且接收中断开启
{
// __HAL_UART_CLEAR_IT(&huart1, USART_ISR_RXNE);//读了就不需要手动清除了
huart1.pRxBuffPtr[0] = READ_REG(huart1.Instance->RDR);//字符放到
HAL_UART_RxCpltCallback(&huart1); //接收回调处理函数
return;//接收处理完就返回
}
//只处理溢出错误,其他错误不处理,不开启其他错误中断
if(((isrflags & USART_ISR_ORE) != RESET) && \
((cr1its & USART_CR1_RXNEIE) != RESET) )
{
__HAL_UART_CLEAR_IT(&huart1, UART_CLEAR_OREF); //手动清除溢出标志
huart1.ErrorCode |= HAL_UART_ERROR_ORE; //记录错误号
}
HAL_UART_ErrorCallback( &huart1 ); //错误回调处理函数
return; //一定要返回,这样不执行原来的处理
/* USER CODE END USART1_IRQn 0 */
//HAL_UART_IRQHandler(&huart1); //上面返回了,这里原来的HAL处理就都不执行了
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
这样就等于接收一个字符的中断调用了。如果需要错误判断,则打开错误中断。
在main中添加串口错误处理函数
SET_BIT(huart->Instance->CR3, USART_CR3_EIE);//溢出错误,帧错误,噪声错误中断使能
SET_BIT(huart1.Instance->CR1, USART_CR1_PEIE);//校验错误中断使能
添加错误处理
/* USER CODE BEGIN 4 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
L2++; //记录错误次数的全局变量值
pRx=0;
uint32_t isrflags = READ_REG(huart->Instance->ISR);//手册上有讲,清错误都要先读SR
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_PE))!=RESET)
{
READ_REG(huart->Instance->RDR);//PE 校验错
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_PE);
huart->ErrorCode = HAL_UART_ERROR_PE;
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_FE))!=RESET)
{
READ_REG(huart->Instance->RDR);//FE 帧错误
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_FE);
huart->ErrorCode = HAL_UART_ERROR_FE;
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_NE))!=RESET)
{
READ_REG(huart->Instance->RDR);//NE 噪音错误
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_NE);
huart->ErrorCode = HAL_UART_ERROR_NE;
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_ORE))!=RESET)
{
READ_REG(huart->Instance->CR1);//ORE 溢出
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE);
huart->ErrorCode = HAL_UART_ERROR_ORE;
}
LastErr = huart->ErrorCode; //记录错误代号
}
发送可以用两个函数代替
添加两个发送函数代替 HAL_Transmit
void Transmit_Data( UART_HandleTypeDef *huart , uint8_t* data , uint32_t len )
{
uint32_t i;
for(i=0;i<len;i++)
{
while( __HAL_UART_GET_FLAG( huart, UART_FLAG_TC ) == RESET ); //ISR, bit 6 (0x40)
huart->Instance->TDR = data[i];
}
}
void Transmit_Str( UART_HandleTypeDef *huart , uint8_t* data )
{
uint8_t *ch=data;
while( (*ch) != 0 )
{
while( __HAL_UART_GET_FLAG( huart, UART_FLAG_TC ) == RESET ); //ISR, bit 6 (0x40)
huart->Instance->TDR = *ch;
ch++;
}
}
你可以CUBEMX简单建一个 工程,开一个串口中断接收 HAL_UART_Receive_IT ,里面啥也不要干,就一个全局变量 RecvCount++。
然后main的while循环里面,每隔300ms打印一个RecvCount++消息到串口。然后电脑100ms连续发送。很快就无法中断接收了。
还是要用寄存器处理才行,HAL库看来只是用来配置设置方便,真正干活部分还得用寄存器或者自己用底层的HAL函数重写了。HAL库特别是低功耗降低主频低速度时,效率下降带来的处理不过来数据问题更明显。
HAL库的自锁问题,换了寄存器改了就没事了。