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);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_43715171/article/details/113396163