文章

串口数据接收

空闲中断+DMA

针对不定长数据接收,在STM32芯片中最常见的方式就是使用串口空闲中断+DMA的方式来接收数据,这种在有着硬件支持的情况下是最方便的,数据通过DMA的方式搬运到指定的数组内,在检测到接收空闲的时候触发中断,这时候一包数据就已经传输完成了,只需要读取指定数据内的数据即可。

生成的伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/********** uart.h **********/

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

#define MAX_RECEIVE_LEN 64

/* UART receive data define */
typedef struct
{
    uint8_t data[MAX_RECEIVE_LEN];
    uint8_t length;
    uint8_t trigger;
}UartReceiveHandle;

/********** main.c **********/
/* start uart1 idle interrupt */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
/* start the uart1 dma receive */
HAL_UART_Receive_DMA(&huart1, (uint8_t *)uart_recv.data, MAX_RECEIVE_LEN);


/********** stm32f1xx_it.c **********/
void USART1_IRQHandler(void)
{
    /* USER CODE BEGIN USART1_IRQn 0 */

    /* USER CODE END USART1_IRQn 0 */
    //   HAL_UART_IRQHandler(&huart1);
    /* USER CODE BEGIN USART1_IRQn 1 */
    /* disable the default IRQHandler */
    USER_UART_IRQHandler(&huart1);
    /* USER CODE END USART1_IRQn 1 */
}

void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{
    if (USART1 == huart->Instance)
    {   /* Check whether the interrupt is idle */
        if (RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
        {
            /* Clear idle interrupt flag (otherwise it will keep entering interrupt) */
            __HAL_UART_CLEAR_IDLEFLAG(&huart1);

            /* Stop this DMA transfer */
            HAL_UART_DMAStop(&huart1);
            /* Calculate the length of the received data */
            uart_recv.length = MAX_RECEIVE_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
            uart_recv.trigger = TRUE;
        }
    }
}

/********** receive data process ***********/
if (uart_recv.trigger==TRUE)
{
    /* user process code */
    HAL_UART_Transmit(&huart1,uart_recv.data,uart_recv.length,0xff);
    /* user process end*/

    /* clear receive data */
    uart_recv.trigger=FALSE;
    uart_recv.length=0;
    memset(uart_recv.data,0,MAX_RECEIVE_LEN);
    /* restart the uart1 dma receive */
    HAL_UART_Receive_DMA(&huart1, (uint8_t *)uart_recv.data, MAX_RECEIVE_LEN);
}

空闲中断

在有空闲中断的时候,仅需要接收每个字节然后手动搬运到数组内就可以了,在检测到空闲中断的时候就表示这一包数据接收完成了。

生成的伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/* start uart1 idle&rxne interrupt */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);


void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{
    uint8_t receive_byte=0;
    if (USART1 == huart->Instance)
    {   
        /* Check whether the interrupt is Receive Data register not empty */ 
        if (RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
        {
            HAL_UART_Receive(&huart1, &receive_byte, 1, 0xff);
            uart_recv.data[uart_recv.length++]=receive_byte;
            if(uart_recv.length==MAX_RECEIVE_LEN)
            {   /* prevent receive data oversize the buffer */
                uart_recv.length=MAX_RECEIVE_LEN-1;
            }

            __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
        }
        /* Check whether the interrupt is idle */
        if (RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
        {
            /* Clear idle interrupt flag (otherwise it will keep entering interrupt) */
            __HAL_UART_CLEAR_IDLEFLAG(&huart1);
            uart_recv.trigger = TRUE;
        }
    }
}

/* receive data process */
if (uart_recv.trigger==TRUE)
{
    /* user process code */
    HAL_UART_Transmit(&huart1,uart_recv.data,uart_recv.length,0xff);
    /* user process end*/

    /* clear receive data */
    uart_recv.trigger=FALSE;
    uart_recv.length=0;
    memset(uart_recv.data,0,MAX_RECEIVE_LEN);
}

单数据接收中断

在只有中断数据接收中断的时候,通过手动将接收到的数据传入目标数组,同时手动判断是否接收完成了,进入伪空闲中断模式。

这里的判断是否接收完成需要一个时间基准,在每次接收到数据的时候更新这个值,通过1ms的系统定时器中断来判断是否接收完成了。这里需要注意,不同波特率的情况下判断数据是否接收完成的条件不同,如:

9600bps=960bytes per second,这时接收一个字节差不多就需要1ms,就需要加大中断的判断时间。

115200bps=11520bytes per second,这时候接收一个字节仅需要0.1ms左右,在1ms内都没有接收到新的数据就可以判断该包数据已经传输完成了。

伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/* UART receive data define */
typedef struct
{
    uint8_t data[MAX_RECEIVE_LEN];
    uint8_t length;
    uint8_t length_mark;
    uint8_t trigger;
    uint32_t ms;
    uint32_t tick;
}UartReceiveHandle;

/* start uart1 rxne interrupt */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);

void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{
    uint8_t receive_byte = 0;
    if (USART1 == huart->Instance)
    {
        /* Check whether the interrupt is Receive Data register not empty */
        if (RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
        {
            HAL_UART_Receive(&huart1, &receive_byte, 1, 0xff);
            uart_recv.data[uart_recv.length++] = receive_byte;
            if (uart_recv.length == MAX_RECEIVE_LEN)
            { /* prevent receive data oversize the buffer */
                uart_recv.length = MAX_RECEIVE_LEN - 1;
            }

            __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);

            uart_recv.ms = HAL_GetTick();
            uart_recv.tick = SysTick->VAL;
        }
    }
}

void SysTick_Handler(void)
{
    /* USER CODE BEGIN SysTick_IRQn 0 */

    /* USER CODE END SysTick_IRQn 0 */
    HAL_IncTick();
    /* USER CODE BEGIN SysTick_IRQn 1 */
    if ((uart_recv.length == uart_recv.length_mark) &&
        (uart_recv.ms != HAL_GetTick()) && 
        (uart_recv.length!=0))
    {
        uart_recv.trigger = 1;
    }
    else
    {
        uart_recv.length_mark=uart_recv.length;
    }
    /* USER CODE END SysTick_IRQn 1 */
}

当然,以上的中断接收仅以ST的芯片作为样例,其他芯片只要采用同样的实现方式一样可行。

本文由作者按照 CC BY 4.0 进行授权