STM32F7xx —— 串口通信

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扫描数据;
系统:带系统就在任务中扫描并解析数据。(带系统可以使用信号量去同步数据 -- 这里只提出一种思路)
串口队列可参考:串口队列


    推荐阅读