上一篇我们进行了串口的收发测试,在串口接收中断时原样返回数据,测试了收发回环。这显然不能直接给应用层使用,实际项目中需要给应用层提供好用的串口收发接口。这里通过环形缓冲区的方式实现串口的接收,应用层只需要调用读接口从缓冲区不断查询接收数据即可。
由于缓冲区的基本数据流是串口接收中断中写缓冲区,读接口函数读缓冲区,存在多方使用缓冲区资源,所以对缓冲区资源访问时必须做临界段处理。这里简单的使用开关中断处理。
__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);
}
}
}
上位机持续发送,测试发送的内容是否和接收到的内容完全一样,测试稳定性。
同时也可以测试发送效率。
以上实现了串口驱动,可以方便的供应用层使用。
这里发送函数直接使用的查询方式,当然也可以类似的使用环形缓冲区中断发送。
read缓冲区时还可以实现超时控制等等。