天天看點

STM32-UART序列槽通信STM32-UART序列槽通信

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.初始化配置

  1. 使能時鐘
  2. 配置中斷優先級
  3. 初始化GPIO
  4. 初始化USART
  5. 使能USART的發送中斷
  6. 打開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);
	}
}