[原创] 【ST NUCLEO-U575ZI-Q 测评 】基于环形缓冲区实现串口接收驱动

qinyunti   2022-12-15 21:53 楼主

前言

上一篇我们进行了串口的收发测试,在串口接收中断时原样返回数据,测试了收发回环。这显然不能直接给应用层使用,实际项目中需要给应用层提供好用的串口收发接口。这里通过环形缓冲区的方式实现串口的接收,应用层只需要调用读接口从缓冲区不断查询接收数据即可。

 

过程

临界段处理

由于缓冲区的基本数据流是串口接收中断中写缓冲区,读接口函数读缓冲区,存在多方使用缓冲区资源,所以对缓冲区资源访问时必须做临界段处理。这里简单的使用开关中断处理。

 

__disable_irq();

__enable_irq();

 

由于写缓冲区只在串口接收中断中进行,所以uart_rx_handler写缓冲区就不需要再做临界段处理了,只有uart_read读缓冲区时需要进行临界段处理。

当然更好的方式是,只关串口中断对应的优先级中断,而不是关所有中断。

环形缓冲区结构

通过以下结构体实现

 

分别设计了读写指针,当前有效数据个数,缓冲区大小。

 

写缓冲区时in_u32写指针递增,到末尾后绕到最开始,如果缓冲区满则丢失。

 

读缓冲区时out_u32读指针递增,到末尾后绕到最开始。

 

typedef struct

 

{

 

uint32_t datalen_u32;

 

uint32_t maxlen_u32;

 

uint32_t in_u32;

 

uint32_t out_u32;

 

uint8_t* buffer_pu8;

 

}ring_buffer_t;

 

 

代码

添加文件uart.c,内容如下

#include

#include "stm32u575xx.h"

#include "stm32u5xx_ll_gpio.h"

#include "stm32u5xx_ll_bus.h"

#include "stm32u5xx_ll_lpuart.h"

 

 

uint8_t uart_ring_buffer[128];

 

typedef struct

{

    uint32_t datalen_u32;

    uint32_t maxlen_u32;

    uint32_t in_u32;

    uint32_t out_u32;

    uint8_t* buffer_pu8;

}ring_buffer_t;

 

ring_buffer_t s_ring_buffer_t=

{

.datalen_u32 = 0,

.maxlen_u32 = sizeof(uart_ring_buffer),

.in_u32 = 0,

.out_u32 = 0,

.buffer_pu8 = uart_ring_buffer,

};

 

void uart_rx_handler(const uint8_t *buffer, uint32_t length)

{

    for(uint32_t i=0;i

    {

        if(s_ring_buffer_t.datalen_u32 < s_ring_buffer_t.maxlen_u32)

        {

            s_ring_buffer_t.buffer_pu8[s_ring_buffer_t.in_u32] = buffer;

            s_ring_buffer_t.datalen_u32++;

            s_ring_buffer_t.in_u32++;

            s_ring_buffer_t.in_u32 %= s_ring_buffer_t.maxlen_u32;

        }

        else

        {

            /* full */

            break;

        }

    }

}

 

int uart_read(uint8_t *buff, uint32_t len)

{

    uint32_t readlen = 0;

    uint32_t mask;

    if(s_ring_buffer_t.datalen_u32 == 0)

    {

        return 0;

    }

    __disable_irq();

    for(uint32_t i=0;i

    {

  if(s_ring_buffer_t.datalen_u32 > 0)

{

buff = s_ring_buffer_t.buffer_pu8[s_ring_buffer_t.out_u32];

s_ring_buffer_t.datalen_u32--;

s_ring_buffer_t.out_u32++;

s_ring_buffer_t.out_u32 %= s_ring_buffer_t.maxlen_u32;

readlen++;

}

else

{

break;

}

    }

    __enable_irq();

    return readlen;

}

 

int uart_write(uint8_t *buff, uint32_t len)

{

    for(uint32_t i=0; i

{

  LL_LPUART_TransmitData8(LPUART1,buff);

  while(LL_LPUART_IsActiveFlag_TXE_TXFNF(LPUART1)!=1);

}

    return 0;

}

 

 

void LPUART1_IRQHandler(void)

{

uint8_t ch;

if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1) == SET)

{

ch = LL_LPUART_ReceiveData8(LPUART1);

uart_rx_handler(&ch, 1);

}

if(LL_LPUART_IsActiveFlag_TXE_TXFNF(LPUART1))

{

 

}

}

 

void uart_init(void)

{

 

LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOG);

  /* PG7 TX */

LL_GPIO_InitTypeDef GPIO_InitStruct;

//LL_GPIO_StructInit(&GPIO_InitStruct);

GPIO_InitStruct.Pin = LL_GPIO_PIN_7;

GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;

GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;

GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;

GPIO_InitStruct.Alternate = LL_GPIO_AF_8;

LL_GPIO_Init(GPIOG, &GPIO_InitStruct);

  /* PG8 RX */

GPIO_InitStruct.Pin = LL_GPIO_PIN_8;

GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;

GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;

GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;

GPIO_InitStruct.Alternate = LL_GPIO_AF_8;

LL_GPIO_Init(GPIOG, &GPIO_InitStruct);

 

LL_APB3_GRP1_EnableClock(LL_APB3_GRP1_PERIPH_LPUART1);

LL_LPUART_InitTypeDef LPUART_InitStruct;

LPUART_InitStruct.BaudRate = 115200;

LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;

LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;

LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;

LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;

LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;

LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;

LL_LPUART_Init(LPUART1, &LPUART_InitStruct);

 

LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);

//LL_LPUART_EnableIT_TXE_TXFNF(LPUART1);

__NVIC_EnableIRQ(LPUART1_IRQn);

__NVIC_SetPriority(LPUART1_IRQn, 3);

 

LL_LPUART_Enable(LPUART1);

}

 

 

uart.h中提供三个接口

#ifndef UART_H

#define UART_H

 

#include

void uart_init(void);

int uart_read(uint8_t *buff, uint32_t len);

int uart_write(uint8_t *buff, uint32_t len);

 

#endif

 

 

 

测试

Main.c

#include "stm32u575xx.h"

#include "stm32u5xx_ll_gpio.h"

#include "stm32u5xx_ll_bus.h"

#include "uart.h"

 

void SysTick_Handler(void)

{

static volatile uint32_t num = 0;

if(num++ >= 1000)

{

LL_GPIO_TogglePin(GPIOB, 1u<<7);

LL_GPIO_TogglePin(GPIOG, 1u<<2);

LL_GPIO_TogglePin(GPIOC, 1u<<7);

num=0;

}

HAL_IncTick();

}

 

 

void delay(uint32_t t)

{

volatile uint32_t timeout = t;

while(t--);

}

 

 

 

int main(void)

{

HAL_Init();

LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_PWR);

HAL_PWREx_EnableVddIO2();

HAL_PWREx_ConfigSupply(PWR_SMPS_SUPPLY);

#if 1

LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_PWR);

HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

 

RCC_OscInitTypeDef pRCC_OscInitStruct;

pRCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;

pRCC_OscInitStruct.HSIState = RCC_HSI_ON;

pRCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;

pRCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;

pRCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;

pRCC_OscInitStruct.PLL.PLLM = 1;

pRCC_OscInitStruct.PLL.PLLMBOOST = RCC_PLLMBOOST_DIV1;

pRCC_OscInitStruct.PLL.PLLN = 20;

pRCC_OscInitStruct.PLL.PLLP = 1;

pRCC_OscInitStruct.PLL.PLLQ = 1;

pRCC_OscInitStruct.PLL.PLLR = 2;

pRCC_OscInitStruct.PLL.PLLRGE = RCC_PLLVCIRANGE_0;

pRCC_OscInitStruct.PLL.PLLFRACN = 0;  /* */

HAL_RCC_OscConfig(&pRCC_OscInitStruct);

 

RCC_ClkInitTypeDef pRCC_ClkInitStruct;

pRCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;

pRCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;

pRCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

pRCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;

pRCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

pRCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV2;

HAL_RCC_ClockConfig(&pRCC_ClkInitStruct, FLASH_LATENCY_4);

#endif

 

LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);

LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOG);

LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);

 

LL_GPIO_InitTypeDef GPIO_InitStruct;

//LL_GPIO_StructInit(&GPIO_InitStruct);

GPIO_InitStruct.Pin = LL_GPIO_PIN_7;

GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;

GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;

GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;

GPIO_InitStruct.Alternate = LL_GPIO_AF_0;

LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

 

GPIO_InitStruct.Pin = LL_GPIO_PIN_2;

GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;

GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;

GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;

GPIO_InitStruct.Alternate = LL_GPIO_AF_0;

LL_GPIO_Init(GPIOG, &GPIO_InitStruct);

 

GPIO_InitStruct.Pin = LL_GPIO_PIN_7;

GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;

GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;

GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;

GPIO_InitStruct.Alternate = LL_GPIO_AF_0;

LL_GPIO_Init(GPIOC, &GPIO_InitStruct);

 

uart_init();

 

uint8_t buffer[128];

while(1)

{

int len=0;

if((len = uart_read(buffer, sizeof(buffer))) >0)

{

uart_write(buffer, len);

}

}

}

 

 

上位机持续发送,测试发送的内容是否和接收到的内容完全一样,测试稳定性。

 

同时也可以测试发送效率。

image-20221215215111-1.png  可以看到收发数据完全一致,测试OK。

 

总结

以上实现了串口驱动,可以方便的供应用层使用。

这里发送函数直接使用的查询方式,当然也可以类似的使用环形缓冲区中断发送。

read缓冲区时还可以实现超时控制等等。

回复评论 (1)

测评汇总:免费申请|ST NUCLEO-U575ZI-Q https://bbs.eeworld.com.cn/thread-1228653-1-1.html

玩板看这里: https://bbs.eeworld.com.cn/elecplay.html EEWorld测评频道众多好板等你来玩,还可以来频道许愿树许愿说说你想要玩的板子,我们都在努力为大家实现!
点赞  2023-1-12 09:43
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复