通过USART接收数据我遇到了一些麻烦。我实际想要实现的是,我可以通过USART接收没有特定长度的命令(只有最大可能的长度)。所以我使用中断例程检查收到的每个字符,但我仍然无法实现我想要的。每次收到一个新角色时都会调用该例程,但不知何故HAL_UART_Receive_IT(& huart1,rx_data,buff_size_rx)不会实时升级,然后当我检查rx_data [指针]时我看不到收到的字符,但是几次之后,它位于rx_data缓冲区中。
到目前为止我所拥有的:
int pointer =0;
...
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if ( USART1->ISR & UART_IT_TXE) {
}
if ( USART1->ISR & UART_IT_RXNE) {
HAL_UART_Receive_IT(&huart1,rx_data,buff_size_rx);
if(rx_data[pointer]=='\0') {
pointer=0;
readCommand(rx_data);
clearBuffer(rx_data,buff_size_rx);
} else {
pointer++;
if(pointer>=buff_size_rx) {
pointer=0;
}
}
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
答案 0 :(得分:4)
HAL_UART_Receive_IT()
并不意味着从中断处理程序调用,而是通过中断启动接收固定字节数。
可能的解决方法是在 HAL_UART_IRQHandler()
完成后检查输入缓冲区,即在/* USER CODE BEGIN USART1_IRQn 1 */
部分。处理命令时,您可以将句柄结构中的pRxBuffPtr
和RxXferCount
重置为其原始值,以便再次从缓冲区的起点开始。
另一个可怕的可能的解决方法是调用缓冲区大小为1的HAL_UART_Receive_IT()
,并设置一个HAL_UART_RxCpltCallback()
处理程序,每次检查收到的字节,并调用必要时再次HAL_UART_Receive_IT()
。
当然,你可以在没有HAL的情况下做到这一点,正如PeterJ和其他人(总是)建议的那样。
UART->BRR
值,或从hal复制相关代码。UART->CR1=USART_CR1_RE|USART_CR1_TE|USART_CR1_UE|USART_CR1_RXNEIE;
现在,您正在接收中断。UART->SR
读入临时变量,然后检查它。UART->DR
,否则(稍后)进行错误处理。在嵌入式应用程序中,中断响应和处理时间通常是 critical ,而HAL只是浪费了很多。
答案 1 :(得分:2)
普通的HAL库对于连续接收或不同长度的命令没有用。
如果您安装了完整的HAL包,则可以查看 L ow L evel界面的示例。
Projects\STM32F411RE-Nucleo\Examples_LL\USART\USART_Communication_Rx_IT_Continuous
最重要的是让你接受连续接收:
void Configure_USART(void) {
/* (1) Enable GPIO clock and configures the USART pins *********************/
/* Enable the peripheral clock of GPIO Port */
USARTx_GPIO_CLK_ENABLE();
/* Configure Tx Pin as : Alternate function, High Speed, Push pull, Pull up */
LL_GPIO_SetPinMode(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_MODE_ALTERNATE);
USARTx_SET_TX_GPIO_AF();
LL_GPIO_SetPinSpeed(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinOutputType(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_PULL_UP);
/* Configure Rx Pin as : Alternate function, High Speed, Push pull, Pull up */
LL_GPIO_SetPinMode(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_MODE_ALTERNATE);
USARTx_SET_RX_GPIO_AF();
LL_GPIO_SetPinSpeed(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinOutputType(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_PULL_UP);
/* (2) NVIC Configuration for USART interrupts */
/* - Set priority for USARTx_IRQn */
/* - Enable USARTx_IRQn */
NVIC_SetPriority(USARTx_IRQn, 0);
NVIC_EnableIRQ(USARTx_IRQn);
/* (3) Enable USART peripheral clock and clock source ***********************/
USARTx_CLK_ENABLE();
/* (4) Configure USART functional parameters ********************************/
/* TX/RX direction */
LL_USART_SetTransferDirection(USARTx_INSTANCE, LL_USART_DIRECTION_TX_RX);
/* 8 data bit, 1 start bit, 1 stop bit, no parity */
LL_USART_ConfigCharacter(USARTx_INSTANCE, LL_USART_DATAWIDTH_8B, LL_USART_PARITY_NONE, LL_USART_STOPBITS_1);
/* No Hardware Flow control */
/* Reset value is LL_USART_HWCONTROL_NONE */
// LL_USART_SetHWFlowCtrl(USARTx_INSTANCE, LL_USART_HWCONTROL_NONE);
/* Oversampling by 16 */
/* Reset value is LL_USART_OVERSAMPLING_16 */
// LL_USART_SetOverSampling(USARTx_INSTANCE, LL_USART_OVERSAMPLING_16);
/* Set Baudrate to 115200 using APB frequency set to 100000000/APB_Div Hz */
/* Frequency available for USART peripheral can also be calculated through LL RCC macro */
/* Ex :
Periphclk = LL_RCC_GetUSARTClockFreq(Instance); or
LL_RCC_GetUARTClockFreq(Instance); depending on USART/UART instance
In this example, Peripheral Clock is expected to be equal to
100000000/APB_Div Hz => equal to SystemCoreClock/APB_Div
*/
LL_USART_SetBaudRate(USARTx_INSTANCE, SystemCoreClock/APB_Div, LL_USART_OVERSAMPLING_16, 115200);
/* (5) Enable USART *********************************************************/
LL_USART_Enable(USARTx_INSTANCE);
}
USART IT处理程序看起来应该是
void USARTx_IRQHandler(void)
{
/* Check RXNE flag value in SR register */
if(LL_USART_IsActiveFlag_RXNE(USARTx_INSTANCE) && LL_USART_IsEnabledIT_RXNE(USARTx_INSTANCE))
{
/* RXNE flag will be cleared by reading of DR register (done in call) */
/* Call function in charge of handling Character reception */
USART_CharReception_Callback();
}
else
{
/* Call Error function */
Error_Callback();
}
}
最后要设置的是Callback
void USART_CharReception_Callback(void);
您可以将字节放入缓冲区并在主循环中或您想要的位置处理它。
答案 2 :(得分:1)
由于我今天偶然发现了这个问题,却找不到一个好的解决方案,因此我想提出一个非常简单的解决方案,它使用了大多数HAL,但避免了所描述的问题...
我的方法的简短依据是:
在void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
的最后一个用户代码部分(对于相应的USART实例,如果使用多个),请使用以下命令启用IRQ:
__HAL_UART_ENABLE_IT(&huartx, UART_IT_RXNE);
然后将所需的代码放入中断函数:
void USART3_IRQHandler(void) {
/* USER CODE BEGIN USART3_IRQn 0 */
CallMyCodeHere();
return; // To avoid calling the default HAL handler at all
// (in case you want to save the time)
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3); // This is the CubeMX generated HAL handler
/* USER CODE BEGIN USART3_IRQn 1 */
/* USER CODE END USART3_IRQn 1 */
}
不要在任何地方使用HAL_UART_Receive_IT,它将禁用IRQ,并且如果您希望每次接收都被呼叫,则需要重新启用它。
答案 3 :(得分:0)
您可以使用HAL使其正常运行!它可能不像其他实现那么优雅,但是是可行的。
您必须创建一个错误处理程序函数,然后在发生溢出错误时,调用HAL_UART_RCV_IT的函数必须刷新UART RX。
此外,我使用两个缓冲区。当中断正在填充一个缓冲区时,主循环将另一个缓冲区清空。
这里对我来说效果很好:
typedef enum
{
UARTREADY = 0,
UARTBUSY = 1,
UARTDATA = 2,
UARTERROR = 3
} enumUartStatus;
while(1){
if(UARTREADY == isUsart3RxReady()){
Usart3RxBuffer((char *)&SOMRxBytesBuffer[use_buffer_index], RCV_BUFFER_BANK_SIZE); // receive bytes in the raw buffer
if(use_buffer_index == RCV_BUFFER_BANK1_INDEX){
use_buffer_index = RCV_BUFFER_BANK2_INDEX;
rxb1_stats++;
}else{
use_buffer_index = RCV_BUFFER_BANK1_INDEX;
rxb2_stats++;
}
}
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if(huart == NULL){
return;
}
if(huart->Instance == USART3){
if(HAL_UART_GetError(huart) == HAL_UART_ERROR_FE){
Usart3Ready = UARTREADY;
}else{
Usart3Ready = UARTERROR;
}
}
}
void Usart3RxBuffer(char *buffer, unsigned short rxbuffersize){
/* Reset transmission flag */
if(Usart3Ready != UARTREADY)
{
return;
}
if(HAL_UART_GetState(&huart3) == HAL_UART_STATE_READY){
/*##-3- Put UART peripheral in reception process ###########################*/
if (HAL_UART_Receive_IT(&huart3, (uint8_t *)buffer, rxbuffersize) != HAL_OK)
{
// TODO: Error_Handler();
DEBUG_TRACE(DEBUG_MSK_MAIN, "UART3 error starting receiver!\r\n");
}else{
// An interrupt HAL_UART_ErrorCallback hit right here !!!!
// There is an overrun error happening here so we have to retry
// this is because we are using the Receive_IT in a continous communication and there is no handshake or flow control
if(Usart3Ready != UARTERROR){
/* Busy waiting to receive bytes */
Usart3Ready = UARTBUSY;
}
}
}
if(Usart3Ready == UARTERROR){
HAL_UART_AbortReceive_IT(&huart3);
Usart3Ready = UARTREADY;
}
}
答案 4 :(得分:0)
以下是通过中断接收数据和空闲线路检测的完整示例:
main.c
中启用接收中断和空闲线路检测: /* USER CODE BEGIN USART2_Init 2 */
__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE); // enable receive intterupts
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // enable idle line detection
/* USER CODE END USART2_Init 2 */
USARTx_IRQHandler
中的 stm32f4xx_it.c
中整理空闲线路事件:void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart2); // taken from https://electronics.stackexchange.com/questions/471272/setting-up-stm32-timer-for-uart-idle-detection#comment1353999_480556
uart2_idleHandler();
} else {
uart2_handler();
}
return;
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
测试:
char my_uart_buffer[256];
int my_uart_buffer_index = 0;
void uart2_handler(void){
char buff;
HAL_UART_Receive (&huart2, (uint8_t *)&buff, 1, 400);
my_uart_buffer[my_uart_buffer_index++] = buff;
}
void uart2_idleHandler(){
my_uart_buffer_index = 0;
}
uart2_idleHandler()
。p/c *my_uart_buffer@20
检查您的缓冲区。Here 是在 STM32F407 Discovery 板上运行的完整示例。