STM32L4 CubeMX之USART
一、USART介绍
1.USART相关配置
UART的初始化主要是确定数据帧格式和双方通信的波特率,关于HAL库UART初始化结构体的定义:
// Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart.h
/**
* @brief UART Init Structure definition
*/
typedef struct
{
uint32_t BaudRate; /*!< This member configures the UART communication baud rate.
The baud rate register is computed using the following formula:
LPUART:
=======
Baud Rate Register = ((256 * lpuart_ker_ckpres) / ((huart->Init.BaudRate)))
where lpuart_ker_ck_pres is the UART input clock (divided by a prescaler if applicable)
UART:
=====
- If oversampling is 16 or in LIN mode,
Baud Rate Register = ((uart_ker_ckpres) / ((huart->Init.BaudRate)))
- If oversampling is 8,
Baud Rate Register[15:4] = ((2 * uart_ker_ckpres) / ((huart->Init.BaudRate)))[15:4]
Baud Rate Register[3] = 0
Baud Rate Register[2:0] = (((2 * uart_ker_ckpres) / ((huart->Init.BaudRate)))[3:0]) >> 1
where uart_ker_ck_pres is the UART input clock (divided by a prescaler if applicable) */
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 f_PCLK/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. */
#if defined(USART_PRESC_PRESCALER)
uint32_t ClockPrescaler; /*!< Specifies the prescaler value used to divide the UART clock source.
This parameter can be a value of @ref UART_ClockPrescaler. */
#endif /* USART_PRESC_PRESCALER */
} UART_InitTypeDef;
/** @defgroup UART_Stop_Bits UART Number of Stop Bits
*/
#define UART_STOPBITS_0_5 USART_CR2_STOP_0 /*!< UART frame with 0.5 stop bit */
#define UART_STOPBITS_1 0x00000000U /*!< UART frame with 1 stop bit */
#define UART_STOPBITS_1_5 (USART_CR2_STOP_0 | USART_CR2_STOP_1) /*!< UART frame with 1.5 stop bits */
#define UART_STOPBITS_2 USART_CR2_STOP_1 /*!< UART frame with 2 stop bits */
/** @defgroup UART_Parity UART Parity
*/
#define UART_PARITY_NONE 0x00000000U /*!< No parity */
#define UART_PARITY_EVEN USART_CR1_PCE /*!< Even parity */
#define UART_PARITY_ODD (USART_CR1_PCE | USART_CR1_PS) /*!< Odd parity
/** @defgroup UART_Mode UART Transfer Mode
*/
#define UART_MODE_RX USART_CR1_RE /*!< RX mode */
#define UART_MODE_TX USART_CR1_TE /*!< TX mode */
#define UART_MODE_TX_RX (USART_CR1_TE |USART_CR1_RE) /*!< RX and TX mode */
/** @defgroup UART_Hardware_Flow_Control UART Hardware Flow Control
*/
#define UART_HWCONTROL_NONE 0x00000000U /*!< No hardware control */
#define UART_HWCONTROL_RTS USART_CR3_RTSE /*!< Request To Send */
#define UART_HWCONTROL_CTS USART_CR3_CTSE /*!< Clear To Send */
#define UART_HWCONTROL_RTS_CTS (USART_CR3_RTSE | USART_CR3_CTSE) /*!< Request and Clear To Send */
/** @defgroup UART_Over_Sampling UART Over Sampling
*/
#define UART_OVERSAMPLING_16 0x00000000U /*!< Oversampling by 16 */
#define UART_OVERSAMPLING_8 USART_CR1_OVER8 /*!< Oversampling by 8 */
/** @defgroup UART_OneBit_Sampling UART One Bit Sampling Method
*/
#define UART_ONE_BIT_SAMPLE_DISABLE 0x00000000U /*!< One-bit sampling disable */
#define UART_ONE_BIT_SAMPLE_ENABLE USART_CR3_ONEBIT /*!< One-bit sampling enable */
以UART为例介绍其句柄结构体定义如下:
// Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart.h
/**
* @brief UART handle Structure definition
*/
typedef struct __UART_HandleTypeDef
{
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 */
__IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
uint16_t RxXferSize; /*!< UART Rx Transfer size */
__IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
uint16_t Mask; /*!< UART Rx RDR register mask */
#if defined(USART_CR1_FIFOEN)
uint32_t FifoMode; /*!< Specifies if the FIFO mode is being used.
This parameter can be a value of @ref UARTEx_FIFO_mode. */
uint16_t NbRxDataToProcess; /*!< Number of data to process during RX ISR execution */
uint16_t NbTxDataToProcess; /*!< Number of data to process during TX ISR execution */
#endif /*USART_CR1_FIFOEN */
void (*RxISR)(struct __UART_HandleTypeDef *huart); /*!< Function pointer on Rx IRQ handler */
void (*TxISR)(struct __UART_HandleTypeDef *huart); /*!< Function pointer on Tx IRQ handler */
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 */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Half Complete Callback */
void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Complete Callback */
void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Half Complete Callback */
void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Complete Callback */
void (* ErrorCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Error Callback */
void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Complete Callback */
void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Transmit Complete Callback */
void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Receive Complete Callback */
void (* WakeupCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Wakeup Callback */
#if defined(USART_CR1_FIFOEN)
void (* RxFifoFullCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Fifo Full Callback */
void (* TxFifoEmptyCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Fifo Empty Callback */
#endif /* USART_CR1_FIFOEN */
void (* MspInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp Init callback */
void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp DeInit callback */
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
} UART_HandleTypeDef;
/** @addtogroup UART_Exported_Functions_Group1 Initialization and de-initialization functions
*/
/* Initialization and de-initialization functions ****************************/
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_HalfDuplex_Init(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_LIN_Init(UART_HandleTypeDef *huart, uint32_t BreakDetectLength);
HAL_StatusTypeDef HAL_MultiProcessor_Init(UART_HandleTypeDef *huart, uint8_t Address, uint32_t WakeUpMethod);
HAL_StatusTypeDef HAL_UART_DeInit(UART_HandleTypeDef *huart);
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
void HAL_UART_MspDeInit(UART_HandleTypeDef *huart);
/* Callbacks Register/UnRegister functions ***********************************/
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
HAL_StatusTypeDef HAL_UART_RegisterCallback(UART_HandleTypeDef *huart, HAL_UART_CallbackIDTypeDef CallbackID,
pUART_CallbackTypeDef pCallback);
HAL_StatusTypeDef HAL_UART_UnRegisterCallback(UART_HandleTypeDef *huart, HAL_UART_CallbackIDTypeDef CallbackID);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
/** @addtogroup UART_Exported_Functions_Group2 IO operation functions
*/
/* IO operation functions *****************************************************/
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_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart);
/* Transfer Abort functions */
HAL_StatusTypeDef HAL_UART_Abort(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortTransmit(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortReceive(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_Abort_IT(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortTransmit_IT(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortReceive_IT(UART_HandleTypeDef *huart);
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);
void HAL_UART_AbortCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart);
/** @addtogroup UART_Exported_Functions_Group3 Peripheral Control functions
*/
/* Peripheral Control functions ************************************************/
HAL_StatusTypeDef HAL_LIN_SendBreak(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_MultiProcessor_EnableMuteMode(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_MultiProcessor_DisableMuteMode(UART_HandleTypeDef *huart);
void HAL_MultiProcessor_EnterMuteMode(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_HalfDuplex_EnableTransmitter(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_HalfDuplex_EnableReceiver(UART_HandleTypeDef *huart);
/** @addtogroup UART_Exported_Functions_Group4 Peripheral State and Error functions
*/
/* Peripheral State and Errors functions **************************************************/
HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart);
uint32_t HAL_UART_GetError(UART_HandleTypeDef *huart);
/* Private functions -----------------------------------------------------------*/
/** @addtogroup UART_Private_Functions UART Private Functions
*/
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
void UART_InitCallbacksToDefault(UART_HandleTypeDef *huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
HAL_StatusTypeDef UART_SetConfig(UART_HandleTypeDef *huart);
HAL_StatusTypeDef UART_CheckIdleState(UART_HandleTypeDef *huart);
HAL_StatusTypeDef UART_WaitOnFlagUntilTimeout(UART_HandleTypeDef *huart, uint32_t Flag, FlagStatus Status,
uint32_t Tickstart, uint32_t Timeout);
void UART_AdvFeatureConfig(UART_HandleTypeDef *huart);
其中UART的API函数主要可分为如下几类
初始化/反初始化函数 | API函数 |
---|---|
初始化/反初始化函数 | HAL_UART_Init() / HAL_UART_DeInit(); HAL_UART_MspInit() / HAL_UART_MspDeInit(); |
I / O 操作函数 | HAL_UART_Transmit() / HAL_UART_Receive(); HAL_UART_Transmit_IT() / HAL_UART_Receive_IT(); HAL_UART_Transmit_DMA() / HAL_UART_Receive_DMA(); |
回调函数 | HAL_UART_TxCpltCallback() / HAL_UART_RxCpltCallback(); HAL_UART_TxHalfCpltCallback() / HAL_UART_RxHalfCpltCallback(); HAL_UART_ErrorCallback() / HAL_UART_AbortCpltCallback(); HAL_UART_RegisterCallback() / HAL_UART_UnRegisterCallback(); |
控制函数 | UART_SetConfig() / UART_AdvFeatureConfig(); |
状态与错误 | HAL_UART_GetState() / HAL_UART_GetError(); |
HAL库UART函数库介绍
1、串口发送/接收函数
- HAL_UART_Transmit();串口发送数据,使用超时管理机制
- HAL_UART_Receive();串口接收数据,使用超时管理机制
- HAL_UART_Transmit_IT();串口中断模式发送
- HAL_UART_Receive_IT();串口中断模式接收
- HAL_UART_Transmit_DMA();串口DMA模式发送
- HAL_UART_Transmit_DMA();串口DMA模式接收
串口发送数据:
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
功能:串口发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)
参数:
- UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
- *pData 需要发送的数据
- Size 发送的字节数
- Timeout 最大发送时间,发送数据超过该时间退出发送
举例: HAL_UART_Transmit(&huart1, (uint8_t *)HHH, 3, 0xffff); //串口发送三个字节数据,最大传输时间0xffff
中断模式接收数据:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口中断接收,以中断方式接收指定长度数据。
大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。接收到数据时,会触发串口中断。
再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)
参数:
- UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
- *pData 接收到的数据存放地址
- Size 接收的字节数
举例: HAL_UART_Receive_IT(&huart1,(uint8_t *)&value,1); //中断接收一个字符,存储到value中
2、串口中断函数
- HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数
- HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口发送中断回调函数
- HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口发送一半中断回调函数(用的较少)
- HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中断回调函数
- HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少)
- HAL_UART_ErrorCallback();串口接收错误函数
串口接收中断回调函数:
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
功能:HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在其中设置代码,
串口中断接收完成之后,会进入该函数,该函数为空函数,用户需自行修改,
参数:
- UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDefhuart1; 别名就是huart1
举例: HAL_UART_RxCpltCallback(&huart1){ //用户代码 }
串口中断处理函数
HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
功能:对接收到的数据进行判断和处理 判断是发送中断还是接收中断,然后进行数据的发送和接收,在中断服务函数中使用
如果接收数据,则会进行接收中断处理函数
/* UART in mode Receiver ---------------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Receive_IT(huart);
}
如果发送数据,则会进行发送中断处理函数
/* UART in mode Transmitter ------------------------------------------------*/
if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
3、串口查询函数
HAL_UART_GetState(); 判断UART的接收是否结束,或者发送数据是否忙碌
举例:
while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX) //检测UART发送结束
二、UART使用之中断
UART通常有三种I / O方式,分别为轮询、中断和DMA,下面先以中断为例展示UART程序开发步骤。
2.1. CubeMX配置UART1
这里仅是配置UART,如配置GPIO请参考STM32之CubeL4 (GPIO+EXIT)
USART1的GPIO引脚配置(GPIO参数按默认配置即可)如下:
USART1初始化参数配置如下(默认即可):
USART1的NVIC中断配置(包括中断优先级分组、抢占优先级、子优先级、使能中断等)如下:
由于我们的USART1只使用中断,并没有使用DMA,所以DMA不需要配置。到这里USART1的配置完成,生成代码后到KEIL MDK V5工程中继续新增用户控制逻辑和回调函数。
2.2 完成用户控制逻辑
前面已经介绍过USART HAL库的API函数,其中初始化与反初始化函数已经由CubeMX生成了,我们在用户控制逻辑中主要实现的是I / O函数与处理完成回调函数,由于这里是采用中断方式接收/发送,所以我们主要用到的HAL API如下:
// Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart.h
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size););//串口中断模式发送
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);//串口中断模式接收
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);//串口发送中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串口接收中断回调函数
由上面的函数声明可知,UART接收/发送函数需要指定数据长度,当达到指定的数据长度后触发中断,但我们往往不知道将要接收到的数据长度是多少?所以我们不能依赖HAL_UART_Receive_IT直接接收一段数据,常需要在接受完成回调函数中设置接收完成标志位(常把收到回车换行符作为该段数据结束的标志),在用户控制逻辑中只有通过该标志位确定该段数据接收完成后才对其进行处理。
C标准库 I / O 重定向
#include <stdio.h>
namespace std {
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
FILE __stdout;
FILE __stdin;
FILE __stderr;
int fgetc(FILE *f)
{
/* Your implementation of fgetc(). */
return 0;
}
int fputc(int c, FILE *stream)
{
/* Your implementation of fputc(). */
}
int ferror(FILE *stream)
{
/* Your implementation of ferror(). */
}
long int ftell(FILE *stream)
{
/* Your implementation of ftell(). */
}
int fclose(FILE *f)
{
/* Your implementation of fclose(). */
return 0;
}
int fseek(FILE *f, long nPos, int nMode)
{
/* Your implementation of fseek(). */
return 0;
}
int fflush(FILE *f)
{
/* Your implementation of fflush(). */
return 0;
}
}
下面我们以实现printf打印数据到USART1为例(即重定义fputc函数)说明C标准库指定输入/输出控制终端的实现过程。重定义的fputc函数代码如下:
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */
// Core\Src\usart.c
/* USER CODE BEGIN 0 */
//文件句柄重定向到标准库输入输出
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
FILE __stdout;
FILE __stdin;
FILE __stderr;
//文件输出流重定向到串口USART1
int fputc(int ch, FILE *stream)
{
/* Your implementation of fputc(). */
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
while(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE) == RESET); //等待发送结束
return ch;
}
要发送/接收数据,一般需要相应的缓冲区暂存数据,同时需要知道数据长度,接收/发送完成标志位等元素,我们可以把这些变量组织成一个结构体变量统一管理,我们定义的数据结构如下:
// Core\Inc\main.h
/* USER CODE BEGIN Private defines */
#define USART1_TX_LEN 256
#define USART1_RX_LEN 256
typedef struct{
uint8_t TxBuff[USART1_TX_LEN];
uint16_t TxSize;
uint8_t Tx_end_flag;
uint8_t RxBuff[USART1_RX_LEN];
uint8_t aRxBuff;
uint16_t RxSize;
uint8_t Rx_end_flag;
}USART_BuffTypeDef;
extern USART_BuffTypeDef usart1_buf;
/* USER CODE END Private defines */
// main.c
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
USART_BuffTypeDef usart1_buf;
/* USER CODE END PV */
控制逻辑代码实现:
USART_BuffTypeDef结构定义在main.h中,初始化结构体变量usart1_buf在main.c中,usart1_buf作为全局变量,在系统启动时默认初始化为0,所以这里可以省略赋初值。
接下来完成接收/发送回调函数的编写,在接收回调函数中把回车换行符/r或/n作为一段数据的结束标志符,检测到结束标识符则结束标志置位,否则将该字符存放到接收数据缓冲区内;在发送回调函数中返回已发送数据段的字符总数,最后清空发送缓冲区和标志位,两个回调函数的实现代码如下:
// Core\Src\usart.c
/* USER CODE BEGIN 1 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
if(usart1_buf.aRxBuff == '\n' || usart1_buf.aRxBuff == '\r')
{
usart1_buf.Rx_end_flag = 1;
usart1_buf.RxBuff[usart1_buf.RxSize] = '\0';
}
else
{
usart1_buf.RxBuff[usart1_buf.RxSize] = usart1_buf.aRxBuff;
usart1_buf.RxSize++;
}
HAL_UART_Receive_IT(huart, &usart1_buf.aRxBuff, 1);
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
if(usart1_buf.Tx_end_flag == 0)
{
printf("\r\n\r\n发送完成,已发送字符个数为:%d\r\n",usart1_buf.TxSize);
usart1_buf.TxSize = 0;
usart1_buf.Tx_end_flag = 1;
memset(usart1_buf.TxBuff,0,usart1_buf.TxSize);//清除发送BUFF里的数据
}
}
}
// main.c
int main(void)
{
/***** 这里省去 ****/
/* USER CODE BEGIN 2 */
if(HAL_UART_Receive_IT(&huart1, &usart1_buf.aRxBuff, 1) != HAL_OK) //必须添加
{
Error_Handler();
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(usart1_buf.Rx_end_flag == 1)
{
printf("\r\n\r\n接收到的字符个数为:%d\r\n接收到的数据为:\r\n",usart1_buf.RxSize);
usart1_buf.TxSize = usart1_buf.RxSize;
usart1_buf.Tx_end_flag = 0;
if(HAL_UART_Transmit_IT(&huart1, usart1_buf.RxBuff, usart1_buf.RxSize) != HAL_OK)
{
Error_Handler();
}
usart1_buf.RxSize = 0;
usart1_buf.Rx_end_flag = 0;
memset(usart1_buf.RxBuff,0,usart1_buf.RxSize);//清除接受BUFF里的数据
}
HAL_Delay(10);
}
/* USER CODE END 3 */
}
上面出现了汉字字符,程序对英文字符的支持是最好的,汉字字符需要注意编码问题,KEIL MDK选择ARM Compiler V6编译器若要使汉字能正常显示,需要选择GB2312编码,但对于该编码AC6编译器会给出字符编码的警告信息,忽略即可。
在程序中使用了C语言的函数sprintf / memset / strlen,因此需要在头文件中包含相应的头文件,同时把main.c中包含的头文件转移到main.h中了。
到这里USART1 Interrupt工程就完成了,编译无错误(有几个跟汉字字符编码相关的警告可忽略),这里我使用的是STM32L431RCT6开发板烧写程序之后,运行结果跟预期一致(注意需要选择波特率115200,勾选发送新行以便添加回车换行符):
总结一下UART接收中断
因为中断接收函数只能触发一次接收中断,所以我们需要在中断回调函数中再调用一次中断接收函数
具体流程:
1、初始化串口
2、在main中第一次调用接收中断函数
3、进入接收中断,接收完数据 进入中断回调函数
4、修改HAL_UART_RxCpltCallback中断回调函数,处理接收的数据,
5 回调函数中要调用一次HAL_UART_Receive_IT函数,使得程序可以重新触发接收中断
HAL_UART_Receive_IT(中断接收函数) -> USART2_IRQHandler(void)(中断服务函数) -> HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中断处理函数) -> UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数) -> HAL_UART_RxCpltCallback(huart)(中断回调函数)
HAL_UART_RxCpltCallback函数就是用户要重写在main.c里的回调函数。
更新printf打印中文出现警告的解决办法
1.最近开发的项目工程一般都是用AC6编译器,好处是编译速度快,优化等级高,但是AC6对于中文支持不好,项目文件目录一定不要有中文路径,否则不能使用gotodef跳转功能。
2.如果项目内有大量使用printf打印中文字符串,则AC6编译过程会弹出警告信息,虽然不影响使用,但是十分烦人。
3.解决办法 可以填入 -Wno-invalid-source-encoding
将该警告信息屏蔽。
以上