STM32F7xx —— 串口通信
目录
STM32F7xx —— 串口通信
一、串口初始化过程
二、几个重要的串口函数
三、几个重要的结构
四、基本接口设计
一、串口初始化过程 1、时钟使能;
2、GPIO初始化;
3、串口波特率设置;
4、串口控制;
5、数据发送与接收
二、几个重要的串口函数
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
// 串口初始化HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
// 串口发送HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
// 串口接收__HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)// 串口中断使能void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
// 设置中断优先级void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
// 使能中断
三、几个重要的结构
// 串口初始化结构体 包含了串口句柄 波特率配置 发送接收缓存 dma等
// 我们只描述前两个基本功能,对效率要求极高可以使用DMA。
typedef struct
{
USART_TypeDef*Instance;
/*!< UART registers base address*/UART_InitTypeDefInit;
/*!< UART communication parameters*/UART_AdvFeatureInitTypeDef AdvancedInit;
/*!< UART Advanced Features initialization parameters */uint8_t*pTxBuffPtr;
/*!< Pointer to UART Tx transfer Buffer */uint16_tTxXferSize;
/*!< UART Tx Transfer size*/uint16_tTxXferCount;
/*!< UART Tx Transfer Counter*/uint8_t*pRxBuffPtr;
/*!< Pointer to UART Rx transfer Buffer */uint16_tRxXferSize;
/*!< UART Rx Transfer size*/uint16_tRxXferCount;
/*!< UART Rx Transfer Counter*/uint16_tMask;
/*!< UART Rx RDR register mask*/DMA_HandleTypeDef*hdmatx;
/*!< UART Tx DMA Handle parameters*/DMA_HandleTypeDef*hdmarx;
/*!< UART Rx DMA Handle parameters*/HAL_LockTypeDefLock;
/*!< Locking object*/__IO HAL_UART_StateTypeDefgState;
/*!< UART state information related to global Handle management
and also related to Tx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */__IO HAL_UART_StateTypeDefRxState;
/*!< UART state information related to Rx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */__IO uint32_tErrorCode;
/*!< UART Error code*/}UART_HandleTypeDef;
// 串口的操作句柄 如 USART1 USART2 USART3等
typedef struct
{
__IO uint32_t CR1;
/*!< USART Control register 1,Address offset: 0x00 */
__IO uint32_t CR2;
/*!< USART Control register 2,Address offset: 0x04 */
__IO uint32_t CR3;
/*!< USART Control register 3,Address offset: 0x08 */
__IO uint32_t BRR;
/*!< USART Baud rate register,Address offset: 0x0C */
__IO uint32_t GTPR;
/*!< USART Guard time and prescaler register,Address offset: 0x10 */
__IO uint32_t RTOR;
/*!< USART Receiver Time Out register,Address offset: 0x14 */
__IO uint32_t RQR;
/*!< USART Request register,Address offset: 0x18 */
__IO uint32_t ISR;
/*!< USART Interrupt and status register,Address offset: 0x1C */
__IO uint32_t ICR;
/*!< USART Interrupt flag Clear register,Address offset: 0x20 */
__IO uint32_t RDR;
/*!< USART Receive Data register,Address offset: 0x24 */
__IO uint32_t TDR;
/*!< USART Transmit Data register,Address offset: 0x28 */
} USART_TypeDef;
// 设置串口的各个参数 波特率 字长 停止位 奇偶校验 收发模式 硬件流 过采样
// 字长:8位/9位
// 停止位:1位/2位
typedef struct
{
uint32_t BaudRate;
/*!< This member configures the UART communication baud rate.
The baud rate register is computed using the following formula:
- If oversampling is 16 or in LIN mode,
Baud Rate Register = ((PCLKx) / ((huart->Init.BaudRate)))
- If oversampling is 8,
Baud Rate Register[15:4] = ((2 * PCLKx) / ((huart->Init.BaudRate)))[15:4]
Baud Rate Register[3] =0
Baud Rate Register[2:0] =(((2 * PCLKx) / ((huart->Init.BaudRate)))[3:0]) >> 1*/uint32_t WordLength;
/*!< Specifies the number of data bits transmitted or received in a frame.
This parameter can be a value of @ref UARTEx_Word_Length */uint32_t StopBits;
/*!< Specifies the number of stop bits transmitted.
This parameter can be a value of @ref UART_Stop_Bits */uint32_t Parity;
/*!< Specifies the parity mode.
This parameter can be a value of @ref UART_Parity
@note When parity is enabled, the computed parity is inserted
at the MSB position of the transmitted data (9th bit when
the word length is set to 9 data bits;
8th bit when the
word length is set to 8 data bits). */uint32_t Mode;
/*!< Specifies whether the Receive or Transmit mode is enabled or disabled.
This parameter can be a value of @ref UART_Mode */uint32_t HwFlowCtl;
/*!< Specifies whether the hardware flow control mode is enabled
or disabled.
This parameter can be a value of @ref UART_Hardware_Flow_Control */uint32_t OverSampling;
/*!< Specifies whether the Over sampling 8 is enabled or disabled, to achieve higher speed (up to fPCLK/8).
This parameter can be a value of @ref UART_Over_Sampling */uint32_t OneBitSampling;
/*!< Specifies whether a single sample or three samples' majority vote is selected.
Selecting the single sample method increases the receiver tolerance to clock
deviations. This parameter can be a value of @ref UART_OneBit_Sampling */
}UART_InitTypeDef;
四、基本接口设计 【STM32F7xx —— 串口通信】我们使用中断接收,普通发送。中断接收到的数据放入队列中,在外部可配置每路串口的功能。
// 抽象出一个串口设备结构体
typedef struct
{
UART_HandleTypeDef handle;
// 串口句柄
uart_queue_t recv;
// 接收队列
uart_queue_t send;
// 发送队列
uint8_t ret;
// 接收的值
} uart_dev_t;
static uart_dev_t uart1_dev;
static uart_dev_t uart2_dev;
// 对外只有通道 不再包含USART1...
typedef enum
{
UART_CHANNEL_NONE,
UART_CHANNEL_1,
UART_CHANNEL_2,
UART_CHANNEL_NUM
} uart_channel_t;
// 宏定义串口的基本信息 之所以这样写,方便移植修改
#define UART1_CHANNELUSART1
#define UART1_PREEMPT_PRIOUART1_PRIORITY
#define UART1_IRQUSART1_IRQn
#define UART1_IRQ_FUNCUSART1_IRQHandler
#define UART1_CLK_ENABLE()__HAL_RCC_USART1_CLK_ENABLE()
#define UART1_TX_PORTGPIOA
#define UART1_TX_PINGPIO_PIN_10
#define UART1_TX_AFGPIO_AF7_USART1
#define UART1_TX_CONFIG()GPIOConfigExt(UART1_TX_PORT, UART1_TX_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, UART1_TX_AF)
#define UART1_RX_PORTGPIOA
#define UART1_RX_PINGPIO_PIN_9
#define UART1_RX_AFGPIO_AF7_USART1
#define UART1_RX_CONFIG()GPIOConfigExt(UART1_RX_PORT, UART1_RX_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, UART1_RX_AF)#define UART2_CHANNELUSART2
#define UART2_PREEMPT_PRIOUART2_PRIORITY
#define UART2_IRQUSART2_IRQn
#define UART2_IRQ_FUNCUSART2_IRQHandler
#define UART2_CLK_ENABLE()__HAL_RCC_USART2_CLK_ENABLE()
#define UART2_TX_PORTGPIOA
#define UART2_TX_PINGPIO_PIN_2
#define UART2_TX_AFGPIO_AF7_USART2
#define UART2_TX_CONFIG()GPIOConfigExt(UART2_TX_PORT, UART2_TX_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, UART2_TX_AF)
#define UART2_RX_PORTGPIOA
#define UART2_RX_PINGPIO_PIN_3
#define UART2_RX_AFGPIO_AF7_USART2
#define UART2_RX_CONFIG()GPIOConfigExt(UART2_RX_PORT, UART2_RX_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, UART2_RX_AF)
// 串口的基本操作
// 串口1
static void uart1_var_init(void)
{
uart1_dev.recv = uart1_queue_recv;
uart1_dev.send = uart1_queue_send;
UartQueueInit(&uart1_dev.recv);
UartQueueInit(&uart1_dev.send);
}static void uart1_gpio_init(void)
{
UART1_RX_CONFIG();
UART1_TX_CONFIG();
}static void uart1_mode_init(uint32_t bound)
{
UART1_CLK_ENABLE();
uart1_dev.handle.Instance = UART1_CHANNEL;
uart1_dev.handle.Init.BaudRate = bound;
// 波特率
uart1_dev.handle.Init.WordLength = UART_WORDLENGTH_8B;
// 字长为8位数据格式
uart1_dev.handle.Init.StopBits = UART_STOPBITS_1;
// 一个停止位
uart1_dev.handle.Init.Parity = UART_PARITY_NONE;
// 无奇偶校验位
uart1_dev.handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
// 无硬件流控
uart1_dev.handle.Init.Mode = UART_MODE_TX_RX;
// 收发模式
HAL_UART_Init(&uart1_dev.handle);
// HAL_UART_Init()会使能UART1
}static void uart1_nvic_init(void)
{
HAL_NVIC_SetPriority(UART1_IRQ, UART1_PREEMPT_PRIO, 3);
HAL_NVIC_EnableIRQ(UART1_IRQ);
__HAL_UART_ENABLE_IT(&uart1_dev.handle, UART_IT_RXNE);
}// 串口2
static void uart2_var_init(void)
{
uart2_dev.recv = uart2_queue_recv;
uart2_dev.send = uart2_queue_send;
UartQueueInit(&uart2_dev.recv);
UartQueueInit(&uart2_dev.send);
}static void uart2_gpio_init(void)
{
UART2_RX_CONFIG();
UART2_TX_CONFIG();
}static void uart2_mode_init(uint32_t bound)
{
UART2_CLK_ENABLE();
uart2_dev.handle.Instance = UART2_CHANNEL;
uart2_dev.handle.Init.BaudRate = bound;
uart2_dev.handle.Init.WordLength = UART_WORDLENGTH_8B;
uart2_dev.handle.Init.StopBits = UART_STOPBITS_1;
uart2_dev.handle.Init.Parity = UART_PARITY_NONE;
uart2_dev.handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
uart2_dev.handle.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&uart2_dev.handle);
}static void uart2_nvic_init(void)
{
HAL_NVIC_EnableIRQ(UART2_IRQ);
HAL_NVIC_SetPriority(UART2_IRQ, UART2_PREEMPT_PRIO, 1);
__HAL_UART_ENABLE_IT(&uart2_dev.handle, UART_IT_RXNE);
}
// 抽象出一个初始化的结构体 每个串口都有的操作
// 定义一个串口的列表
// 下面的函数就是扫描列表 到这里为止,这些接口都是给内部使用的,外部文件用不到。
typedef struct
{
uint8_t channel;
uart_dev_t *dev;
void (* var_init_cb)(void);
void (* gpio_init_cb)(void);
void (* mode_init_cb)(uint32_t bound);
void (* nvic_init_cb)(void);
} uart_config_t;
static const uart_config_t uart_configs[] =
{
{UART_CHANNEL_1, &uart1_dev, uart1_var_init, uart1_gpio_init, uart1_mode_init, uart1_nvic_init},
{UART_CHANNEL_2, &uart2_dev, uart2_var_init, uart2_gpio_init, uart2_mode_init, uart2_nvic_init},
};
static uart_dev_t *uart_dev_get(uint8_t channel)
{
uint8_t i;
for(i = 0;
i < ARRAY_SIZE(uart_configs);
++i)
{
if(uart_configs[i].channel == channel)
{
return uart_configs[i].dev;
}
}return 0;
}static void uart_var_init(uint8_t channel)
{
uint8_t i;
for(i = 0;
i < ARRAY_SIZE(uart_configs);
++i)
{
if(uart_configs[i].channel == channel)
{
uart_configs[i].var_init_cb();
break;
}
}
}static void uart_gpio_init(uint8_t channel)
{
uint8_t i;
for(i = 0;
i < ARRAY_SIZE(uart_configs);
++i)
{
if(uart_configs[i].channel == channel)
{
uart_configs[i].gpio_init_cb();
break;
}
}
}static void uart_mode_init(uint8_t channel, uint32_t bound)
{
uint8_t i;
for(i = 0;
i < ARRAY_SIZE(uart_configs);
++i)
{
if(uart_configs[i].channel == channel)
{
uart_configs[i].mode_init_cb(bound);
break;
}
}
}static void uart_nvic_init(uint8_t channel)
{
uint8_t i;
for(i = 0;
i < ARRAY_SIZE(uart_configs);
++i)
{
if(uart_configs[i].channel == channel)
{
uart_configs[i].nvic_init_cb();
break;
}
}
}
// 这里的函数就是非常重要的了,都是给外部使用的。
// 初始化函数,同步发送 异步发送 接收处理
void UartInit(uint8_t channel, uint32_t bound)
{
uart_var_init(channel);
uart_gpio_init(channel);
uart_mode_init(channel, bound);
uart_nvic_init(channel);
}void UartSendSync(uint8_t channel, uint8_t *buffer, uint16_t length)
{
uart_dev_t *dev = uart_dev_get(channel);
HAL_UART_Transmit(&dev->handle, buffer, length, 10);
}void UartSendWriteAsyn(uint8_t channel, uint8_t *buffer, uint16_t length)
{
uint16_t i;
uart_dev_t *dev = uart_dev_get(channel);
if(0 == dev)
{
return;
}for(i = 0;
i < length;
++i)
{
UartQueuePush(&dev->send, buffer[i]);
}
}uint8_t UartSendReadAsyn(uint8_t channel, uint8_t *c)
{
uart_dev_t *dev = uart_dev_get(channel);
if(0 == dev)
{
return 0;
}return UartQueuePop(&dev->send, c);
}uint8_t UartRecv(uint8_t channel, uint8_t *c)
{
uart_dev_t *dev = uart_dev_get(channel);
return UartQueuePop(&dev->recv, c);
}
// 这里我没有使用串口的回调函数。
// 中断服务函数 接收到数据就加入到队列中。在UartRecv读队列数据并处理。
void UART1_IRQ_FUNC(void)
{
if(__HAL_UART_GET_IT(&uart1_dev.handle, UART_IT_RXNE) != RESET)
{
HAL_UART_Receive(&uart1_dev.handle, (uint8_t *)&uart1_dev.ret, 1, 1000);
UartQueuePush(&uart1_dev.recv, uart1_dev.ret);
}HAL_UART_IRQHandler(&uart1_dev.handle);
}void UART2_IRQ_FUNC(void)
{
if(__HAL_UART_GET_IT(&uart2_dev.handle, UART_IT_RXNE) != RESET)
{
HAL_UART_Receive(&uart2_dev.handle, (uint8_t *)&uart2_dev.ret, 1, 1000);
UartQueuePush(&uart2_dev.recv, uart2_dev.ret);
}HAL_UART_IRQHandler(&uart2_dev.handle);
}
到这里,串口的初始化,发送,接收的接口就封装好了。
裸机:裸机就在while(1)中调用UartRecv扫描数据;
系统:带系统就在任务中扫描并解析数据。(带系统可以使用信号量去同步数据 -- 这里只提出一种思路)
串口队列可参考:串口队列