STM32-UART序列槽通信
一、UART資料傳輸過程
1.字元發送
首先在初始化完USART的時候,但我們要發送一個位元組的資料,那麼先把這個資料寫進USART_DR,這個時候TXE (Transmit data register empty)位由硬體清零,表示發送資料寄存器不為空。如果移位寄存器為空,表示此時USART正在發送資料,則要等待
前傳輸結束時(也就是移位寄存器為空)把TDR中的資料複制進移位寄存器,如果此時USART沒有在發送資料,處于空閑狀态,對USART_DR寄存器的寫操作直接把資料放
進移位寄存器,資料傳輸開始, TXE位立即被置起。當移位寄存器裡面的資料也被完全發送時,移位寄存器為空,則TC位被置起,表示資料傳輸完成。
2.字元接收
當接收的資料到來的時候,先存放在移位寄存器中,然後再将移位寄存器中的資料轉移到RDR中,RXNE位被置位。它表明移位寄存器的内容被轉移到RDR。換句話說,資料已經被接收并且可以被讀出(包括與之有關的錯誤标志)。如果RXNEIE位被設定,産生中斷。
3.幾個重要的标志位
TXE: (Transmit data register empty)表示發送資料寄存器為空,也就是如果目前TDR中的資料沒有被放進移位寄存器,就不為空,TXE被置起,如果TDR中的資料被放進了移位寄存器,那麼TXE被清零。如果TXEIE位被設定,此标志将産生一個中斷,也就是TXE被置起的時候,就會産生中斷。
TC: (Transmission complete)表示目前資料發送完成,也就是移位寄存器裡面的資料為空,則表示資料發送完成,TC為就被置起,如果USART_CR1中的TCIE為’1’,則産生中斷。
RXNE: 它表明移位寄存器的内容被轉移到RDR。換句話說,資料已經被接收并且可以被讀出(包括與之有關的錯誤标志)。如果RXNEIE位被設定,産生中斷。
二、UART初始化配置
本工程可以參考官方固件庫中的例程修改\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\USART\Interrupt和Printf。
本示例使用的是USART2,可以根據情況修改為自己需要的。
1.初始化配置
- 使能時鐘
- 配置中斷優先級
- 初始化GPIO
- 初始化USART
- 使能USART的發送中斷
- 打開USART
/**
* @brief Configures the different system clocks.
* @param None
* @retval None
*/
void UART_RCC_Configuration(void)
{
/* Enable GPIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
}
/**
* @brief Configures the different GPIO ports.
* @param None
* @retval None
*/
void UART_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Configure USART2 Rx as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure USART2 Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/**
* @brief Configures the nested vectored interrupt controller.
* @param None
* @retval None
*/
void UART_NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the USARTy Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void UART_INIT(void)
{
USART_InitTypeDef USART_InitStructure;
/* System Clocks Configuration */
UART_RCC_Configuration();
/* NVIC configuration */
UART_NVIC_Configuration();
/* Configure the GPIO ports */
UART_GPIO_Configuration();
/* USART2 configuration ------------------------------------------------------*/
/* USARTy and USARTz configured as follow:
- BaudRate = 9600 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
*/
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/* nRTS:請求以發送(Request To Send), n 表示低電平有效。如果使能 RTS 流控制,當
USART 接收器準備好接收新資料時就會将 nRTS 變成低電平;當接收寄存器已滿時,
nRTS 将被設定為高電平。該引腳隻适用于硬體流控制。
nCTS:清除以發送(Clear To Send), n 表示低電平有效。如果使能 CTS 流控制,發送
器在發送下一幀資料之前會檢測 nCTS 引腳,如果為低電平,表示可以發送資料,如果為
高電平則在發送完目前資料幀之後停止發送。該引腳隻适用于硬體流控制。*/
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/* Configure USART2 */
USART_Init(USART2, &USART_InitStructure);
/* Enable USART2 Receive and Transmit interrupts */
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
// USART_ITConfig(USART2, USART_IT_TXE, ENABLE);
/* Enable the USART2 */
USART_Cmd(USART2, ENABLE);
}
2.如何使用printf函數向序列槽列印資料
如果要使用printf列印序列槽資料,需要包含頭檔案stdio.h,在官方的參考例程Printf中可以找到這樣一段代碼複制下來:
該代碼就是對printf進行重定向,具體不必深究。
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART */
USART_SendData(USART2, (uint8_t) ch);
/* Loop until the end of transmission */
while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)
{}
return ch;
}
例程:序列槽接收不定長消息
要求:通過電腦,就像聊天一樣,每次可以發送不定長的消息,每次點選一次發送,就把目前的消息也就是字元串列印到LCD上。
要做到以上的要求,我們可以對收到的消息加一個時間來限制。首先每次我通過電腦點選一次發送,那麼在開發闆接收到該資料的第一個位元組的時候,就設定一個标志位,表示正在接收消息,然後打開定時器,設定50ms的逾時時長,也就是從接收資料的第一個位元組開始,計時50ms,50ms内所接收到的資料就認為是目前接收到的消息。當然逾時的時間,可以具體調節,比如說我們要傳輸的資料很大的時候,可以把逾時時間設定得長一些,如果對我20位元組以内得資料,50ms簡直是綽綽有餘。
下面是主要部分的代碼:
#include "uart.h"
#include <stdio.h>
#include <string.h>
uint8_t RxBuffer[20];
uint8_t RxCounter = 0x00;
_Bool Rx_flag = 0;
uint16_t uart_cnt = 0;
/**
* @brief Configures the different system clocks.
* @param None
* @retval None
*/
void UART_RCC_Configuration(void)
{
/* Enable GPIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
}
/**
* @brief Configures the different GPIO ports.
* @param None
* @retval None
*/
void UART_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Configure USART2 Rx as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure USART2 Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/**
* @brief Configures the nested vectored interrupt controller.
* @param None
* @retval None
*/
void UART_NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the USARTy Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void UART_INIT(void)
{
USART_InitTypeDef USART_InitStructure;
/* System Clocks Configuration */
UART_RCC_Configuration();
/* NVIC configuration */
UART_NVIC_Configuration();
/* Configure the GPIO ports */
UART_GPIO_Configuration();
/* USART2 configuration ------------------------------------------------------*/
/* USARTy and USARTz configured as follow:
- BaudRate = 9600 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
*/
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/* nRTS:請求以發送(Request To Send), n 表示低電平有效。如果使能 RTS 流控制,當
USART 接收器準備好接收新資料時就會将 nRTS 變成低電平;當接收寄存器已滿時,
nRTS 将被設定為高電平。該引腳隻适用于硬體流控制。
nCTS:清除以發送(Clear To Send), n 表示低電平有效。如果使能 CTS 流控制,發送
器在發送下一幀資料之前會檢測 nCTS 引腳,如果為低電平,表示可以發送資料,如果為
高電平則在發送完目前資料幀之後停止發送。該引腳隻适用于硬體流控制。*/
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/* Configure USART2 */
USART_Init(USART2, &USART_InitStructure);
/* Enable USART2 Receive and Transmit interrupts */
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
// USART_ITConfig(USART2, USART_IT_TXE, ENABLE);
/* Enable the USART2 */
USART_Cmd(USART2, ENABLE);
}
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART */
USART_SendData(USART2, (uint8_t) ch);
/* Loop until the end of transmission */
while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)
{}
return ch;
}
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
Rx_flag = 1;
uart_cnt = 0;
/* Read one byte from the receive data register */
RxBuffer[RxCounter++] = USART_ReceiveData(USART2);
if(RxCounter > 20)
{
RxCounter = 0;
memset(RxBuffer,0,sizeof(RxBuffer));
}
}
}
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
if(Rx_flag && ++uart_cnt >= 50)
{
uart_display_flag = 1;
uart_cnt = 0;
RxCounter = 0;
Rx_flag = 0;
strcpy((char*)lcd_buf,(char*)RxBuffer);
memset(RxBuffer,0,sizeof(RxBuffer));
}
}
}
main函數裡面包含的相關代碼:
void main(void)
{
UART_INIT();
if(uart_display_flag)
{
uart_display_flag = 0;
memset(lcd_buf2,0,sizeof(lcd_buf2));
sprintf((char*)lcd_buf2,"%-20.20s",lcd_buf);
LCD_DisplayStringLine(Line4,lcd_buf2);
printf("%s\r\n",lcd_buf2);
}
}