串口發送過程配置流程
HAL庫中串口寄存器定義文件:
stm32f429xx.h F429芯片
stm32f767xx.h F767芯片
stm32f103xx.h F103芯片
stm32fnnnx.x.h 其他芯片
可以在其中找到USART_TypeDef:最終會映射到寄存器的地址。
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;
HAL庫中串口函數定義文件:
stm32f7xx_hal_uart.c ,stm32f7xx_hal_usart.c
- 編程USARTx_CR1的M位來定義字長。
- 編程USARTx_CR2的STOP位來定義停止位位數。
- 編程USARTx_BRR寄存器確定波特率。
- 使能USARTx_CR1的UE位使能USARTx。
- 如果進行多緩沖通信,配置USARTx_CR3的DMA使能(DMAT)。具體請參考后面DMA實驗。
- 使能USARTx_CR1的TE位使能發送器。
- 向發送數據寄存器TDR寫入要發送的數據(對於M3,發送和接收共用DR寄存器)。
- 向TRD寄存器寫入最后一個數據后,等待狀態寄存器USARTx_SR(ISR)的TC位置1,傳輸完成。
對於stm32f4:控制寄存器 1 (USART_CR1):
位12 M:字長 (Word length) 該位決定了字長。該位由軟件置 1 或清零。
0:1 起始位,8 數據位,n 停止位
1:1 起始位,9 數據位,n 停止位
注意:在數據傳輸(發送和接收)期間不得更改 M 位
stm32f4控制寄存器 2 (USART_CR2):
位 13:12 STOP:停止位 (STOP bit)
這些位用於編程停止位。
00:1 個停止位
01:0.5 個停止位
10:2 個停止位
11:1.5 個停止位
注意:0.5 個停止位和 1.5 個停止位不適用於 UART4 和 UART5。
串口字節發送流程中的1、2、3設置串口的一些參數。接下來要使能使用到的串口:
同樣在stm32f4控制寄存器 1 (USART_CR1)中可以找到:
位 13 UE:USART 使能 (USART enable)
該位清零后,USART 預分頻器和輸出將停止,並會結束當前字節傳輸以降低功耗。此位由軟件置 1 和清零。
0:禁止 USART 預分頻器和輸出
1:使能 USART
位 3 TE:發送器使能 (Transmitter enable)
該位使能發送器。該位由軟件置 1 和清零。
0:禁止發送器
1:使能發送器
注意:1:除了在智能卡模式下以外,傳送期間 TE 位上的“0”脈沖(“0”后緊跟的是“1”)會在當前字的后面發送一個報頭(空閑線路)。
2:當 TE 置 1 時,在發送開始前存在 1 位的時間延遲。
串口字節發送流程中的4、5、6步驟使能完成之后,接下來進行數據發送也就是7、8步驟。
配置步驟①~⑥:配置字長,停止位,奇偶校驗位,波特率等:
可以在stm32f7xx_hal_uart.c中找到:HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)函數:該函數內部會引用標識符__HAL_USART_ENABLE使能相應串口。
/** * @brief Initializes the UART mode according to the specified * parameters in the UART_InitTypeDef and creates the associated handle . * @param huart: uart handle * @retval HAL status */ HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart) { /* Check the UART handle allocation */ if(huart == NULL) { return HAL_ERROR; } if(huart->Init.HwFlowCtl != UART_HWCONTROL_NONE) { /* Check the parameters */ assert_param(IS_UART_HWFLOW_INSTANCE(huart->Instance)); } else { /* Check the parameters */ assert_param(IS_UART_INSTANCE(huart->Instance)); } if(huart->gState == HAL_UART_STATE_RESET) { /* Allocate lock resource and initialize it */ huart->Lock = HAL_UNLOCKED; /* Init the low level hardware : GPIO, CLOCK */ HAL_UART_MspInit(huart); } huart->gState = HAL_UART_STATE_BUSY; /* Disable the Peripheral */ __HAL_UART_DISABLE(huart); /* Set the UART Communication parameters */ if (UART_SetConfig(huart) == HAL_ERROR) { return HAL_ERROR; } if (huart->AdvancedInit.AdvFeatureInit != UART_ADVFEATURE_NO_INIT) { UART_AdvFeatureConfig(huart); } /* In asynchronous mode, the following bits must be kept cleared: - LINEN and CLKEN bits in the USART_CR2 register, - SCEN, HDSEL and IREN bits in the USART_CR3 register.*/ CLEAR_BIT(huart->Instance->CR2, (USART_CR2_LINEN | USART_CR2_CLKEN)); CLEAR_BIT(huart->Instance->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN)); /* Enable the Peripheral */ __HAL_UART_ENABLE(huart); /* TEACK and/or REACK to check before moving huart->gState and huart->RxState to Ready */ return (UART_CheckIdleState(huart)); }
步驟⑦~⑧發送數據和等待發送完成:可以在stm32f7xx_hal_uart.c中找到:HAL_UART_Transmit函數:
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
函數前面加__weak修飾符,我們稱之為弱函數。對於弱函數,用戶可以在用戶文件中重新定義一個同名函數,最終編譯器編譯的時候會選擇用戶定義的函數。如果用戶沒有定義,那么函數內容就是弱函數定義的內容。
函數聲明:
可以在stm32f7xx_hal_uart.h中找到:void HAL_UART_MspInit(UART_HandleTypeDef *huart);
函數定義(弱函數):里面不做事
__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
}
弱函數被其他函數調用:
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
{
if(huart->gState == HAL_UART_STATE_RESET)
{
/* Allocate lock resource and initialize it */
huart->Lock = HAL_UNLOCKED;
/* Init the low level hardware : GPIO, CLOCK */
HAL_UART_MspInit(huart);
}
}
為什么要定義一個弱函數?
因為在hal庫里面有其他的函數需要調用這樣一個函數,但是里面的內容還不確定如何初始化,所以先定義一個weak函數。然后用戶在可以再去編寫函數的真正內容。這樣的話不會報函數重定義的錯誤。運行流程一樣,但是初始化可能不一樣,使用weak函數的話,好處是我們不會對既有程序流程做任何修改,只需要修改流程中的某部分與用戶相關的代碼即可。
弱函數重新被定義:
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
…//內容
}
__weak關鍵字的好處:
- 對於事先已經定義好的一個流程,我們只希望修改流程中的某部分與用戶相關的代碼,這個時候我們可以采用弱函數定義一個空函數,然后讓用戶自行定義該函數。這樣做的好處是我們不會對既有程序流程做任何修改。
- HAL庫中大量使用__weak關鍵字修飾外設回調函數。
- 外設回調函數供用戶編寫MCU相關程序,大大提高程序的通用性移植性。
- 初始化串口相關參數,使能串口:HAL_UART_Init();
- 串口相關IO口配置,復用配置:在HAL_UART_MspInit中調用HAL_GPIO_Init函數。
- 發送數據,並等待數據發送完成:HAL_UART_Transmit()函數;
然后根據上面的流程,開始編寫代碼:
先編一個初始化函數:
void uart1_init(void)
{
}
然后在HALLIB-stm32f7xx_hal_uart.c中找到:HAL_UART_Init函數,粘貼到初始化函數里,調用它:
然后發現他有一個入口參數UART_HandleTypeDef *huart是結構體指針部分。然后找到UART_HandleTypeDef的定義,可以找到:這個是串口句柄
typedef struct { USART_TypeDef *Instance; /*!< UART registers base address */ UART_InitTypeDef Init; /*!< UART communication parameters */ UART_AdvFeatureInitTypeDef AdvancedInit; /*!< UART Advanced Features initialization parameters */ uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */ uint16_t TxXferSize; /*!< UART Tx Transfer size */ uint16_t TxXferCount; /*!< UART Tx Transfer Counter */ uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */ uint16_t RxXferSize; /*!< UART Rx Transfer size */ uint16_t RxXferCount; /*!< UART Rx Transfer Counter */ uint16_t Mask; /*!< UART Rx RDR register mask */ DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */ DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */ HAL_LockTypeDef Lock; /*!< Locking object */ __IO HAL_UART_StateTypeDef gState; /*!< 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_StateTypeDef RxState; /*!< UART state information related to Rx operations. This parameter can be a value of @ref HAL_UART_StateTypeDef */ __IO uint32_t ErrorCode; /*!< UART Error code */ }UART_HandleTypeDef;
USART_TypeDef是串口的類型。在文件中可以找到:
#define USART2 ((USART_TypeDef *) USART2_BASE) #define USART3 ((USART_TypeDef *) USART3_BASE) #define UART4 ((USART_TypeDef *) UART4_BASE) #define UART5 ((USART_TypeDef *) UART5_BASE)
然后找到UART_InitTypeDef的定義,可以看到,是配置串口外設的一些特性參數。
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;
此時可以在初始化函數中寫:
UART_HandleTypeDef usart1_handler; void uart1_init(void) { usart1_handler.Instance = USART1; usart1_handler.Init.BaudRate = 115200; usart1_handler.Init.WordLength = UART_WORDLENGTH_8B; usart1_handler.Init.StopBits = UART_STOPBITS_1; usart1_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; usart1_handler.Init.Mode = UART_MODE_TX_RX; usart1_handler.Init.Parity = UART_PARITY_NONE; HAL_UART_Init(&usart1_handler); }
《這里面參數都有哪些怎么找?》可以首先在HALLIB-stm32f7xx_hal_uart.c找到assert_param(IS_UART_INSTANCE(huart->Instance));然后雙擊IS_UART_INSTANCE找到它的定義,可以發現如下代碼:然后就知道都可以填啥參數了,可以選擇USART1作為參數。其他的參數設置也是類似的方式。
#define IS_UART_INSTANCE(__INSTANCE__) (((__INSTANCE__) == USART1) || \ ((__INSTANCE__) == USART2) || \ ((__INSTANCE__) == USART3) || \ ((__INSTANCE__) == UART4) || \ ((__INSTANCE__) == UART5) || \ ((__INSTANCE__) == USART6) || \ ((__INSTANCE__) == UART7) || \ ((__INSTANCE__) == UART8))
在HALLIB-stm32f7xx_hal_uart.h中可以找到HAL_UART_MspInit的聲明:void HAL_UART_MspInit(UART_HandleTypeDef *huart);
現在編寫這個函數:最終這個函數會被HAL_UART_Init調用。由於STM32有好幾個UART串口,所以先進行判斷
void HAL_UART_MspInit(UART_HandleTypeDef *huart) { if(huart->Instance==UART1) { } }
端口復用配置過程:
1.GPIO端口時鍾使能。
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIO時鍾
2.復用外設時鍾使能。
比如你要將端口PA9,PA10復用為串口,所以要使能串口時鍾。
__HAL_RCC_USART1_CLK_ENABLE(); //使能串口1時鍾
3.端口模式配置為復用功能。 HAL_GPIO_Init函數。
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //復用推挽輸出
4.配置GPIOx_AFRL或者GPIOx_AFRH寄存器,將IO連接到所需的AFx。HAL_GPIO_Init函數。
GPIO_Initure.Alternate=GPIO_AF7_USART1;//復用為USART1
對於端口,需要設置GPIO_InitTypeDef *GPIO_Init參數。如下
typedef struct { uint32_t Pin; /*!< Specifies the GPIO pins to be configured. This parameter can be any value of @ref GPIO_pins_define */ uint32_t Mode; /*!< Specifies the operating mode for the selected pins. This parameter can be a value of @ref GPIO_mode_define */ uint32_t Pull; /*!< Specifies the Pull-up or Pull-Down activation for the selected pins. This parameter can be a value of @ref GPIO_pull_define */ uint32_t Speed; /*!< Specifies the speed for the selected pins. This parameter can be a value of @ref GPIO_speed_define */ uint32_t Alternate; /*!< Peripheral to be connected to the selected pins. This parameter can be a value of @ref GPIO_Alternate_function_selection */ }GPIO_InitTypeDef;
都設置完之后就是下面的串口相關IO口配置代碼:
void HAL_UART_MspInit(UART_HandleTypeDef *huart) { GPIO_InitTypeDef GPIO_Initure; if(huart->Instance==UART1) { __HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA時鍾 __HAL_RCC_USART1_CLK_ENABLE(); //使能USART1時鍾 GPIO_Initure.Pin=GPIO_PIN_9; //PA9 GPIO_Initure.Mode=GPIO_MODE_AF_PP; //復用推挽輸出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 GPIO_Initure.Alternate=GPIO_AF7_USART1; //復用為USART1 HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9 GPIO_Initure.Pin=GPIO_PIN_10; //PA10 HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10 } }
首先在stm32f7xx_hal_uart.c中找到HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
然后可以發現這里面要調用的一些參數。
最終的代碼:
#include "sys.h" #include "delay.h" #include "usart.h" UART_HandleTypeDef usart1_handler; void uart1_init(void) { usart1_handler.Instance = USART1; usart1_handler.Init.BaudRate = 115200; usart1_handler.Init.WordLength = UART_WORDLENGTH_8B; usart1_handler.Init.StopBits = UART_STOPBITS_1; usart1_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; usart1_handler.Init.Mode = UART_MODE_TX_RX; usart1_handler.Init.Parity = UART_PARITY_NONE; HAL_UART_Init(&usart1_handler); } void HAL_UART_MspInit(UART_HandleTypeDef *huart) { GPIO_InitTypeDef GPIO_Initure; if(huart->Instance==USART1) { __HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA時鍾 __HAL_RCC_USART1_CLK_ENABLE(); //使能USART1時鍾 GPIO_Initure.Pin=GPIO_PIN_9; //PA9 GPIO_Initure.Mode=GPIO_MODE_AF_PP; //復用推挽輸出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 GPIO_Initure.Alternate=GPIO_AF7_USART1; //復用為USART1 HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9 GPIO_Initure.Pin=GPIO_PIN_10; //PA10 HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10 } } int main(void) { u8 buff[]="test"; Cache_Enable(); //打開L1-Cache HAL_Init(); //初始化HAL庫 Stm32_Clock_Init(432,25,2,9); //設置時鍾,216Mhz delay_init(216); uart1_init(); while(1) { HAL_UART_Transmit(&usart1_handler,buff,sizeof(buff),1000); delay_ms(300); } }