前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MCU常见通信总线串讲(一)—— UART和USART

MCU常见通信总线串讲(一)—— UART和USART

作者头像
秋名山码神
发布2023-11-08 09:29:15
1.4K0
发布2023-11-08 09:29:15
举报
文章被收录于专栏:码神随笔

🙌秋名山码民的主页 😂oi退役选手,Java、大数据、单片机、IoT均有所涉猎,热爱技术,技术无罪 🎉欢迎关注🔎点赞👍收藏⭐️留言📝

前言

首先明确一个概念,关于MCU中通信总线和通信协议,通信总线是一种用于连接各种外设和模块的物理接口,它可以传输数据和控制信息。通信协议则是指在通信总线上传输数据时所遵循的规则和约定,以确保不同设备之间能够正确地交换信息,我们也可以把他叫做通信总线协议。

系列文章,主要讲解以下几个总线协议,读者可以按需选择:

  1. UART和USART
  2. RS232、RS485总线
  3. IIC总线
  4. SPI总线
  5. CAN总线
  6. USB总线

一、简介

UART:Universal Asynchronous Receiver/Transmitter,通用异步收发器。 USART:Universal Synchronous/Asynchronous Receiver/Transmitter,通用同步/异步串行接收/发送器。

UART是一种通用的异步串行通信协议,它使用起始位、数据位、校验位和停止位来传输数据。在UART通信中,数据的传输是通过固定的波特率进行的,发送和接收端需要事先约定好波特率、数据位、校验位和停止位等参数。UART通常用于连接微控制器、传感器、外围设备等,并且在计算机系统中也广泛应用于串口通信。

USART是一种更为复杂的串行通信协议,它同时支持同步和异步的数据传输方式。与UART不同的是,USART可以选择同步或者异步模式进行通信,并且提供了更多的控制选项,比如硬件流控制、双向通信等。USART通常用于对数据传输速度要求较高、需要双向通信或者需要更灵活控制的场景。

二、帧格式

UART:

  1. 起始位(Start Bit):起始位指示数据帧的开始。它始终是逻辑低电平,并且标志着数据的传输即将开始
  2. 数据位(Data Bits):数据位是实际的数据传输部分。它表示要传输的数据,可以是5位、6位、7位或8位,取决于所选择的数据位长度。
  3. 校验位(Parity Bit):校验位是可选的,用于检测传输过程中的错误。常见的校验方式包括奇校验、偶校验或者不使用校验。校验位的选择取决于通信双方事先约定好的校验规则。
  4. 停止位(Stop Bit):停止位标志着数据帧的结束。它始终是逻辑高电平,用于告知接收端该数据帧已经传输完成

发送过程中,发送方和接收方的波特率需要保持一致,为了减少累计的误差,最多发送1个字节,也就是发送的数据位,最多为8位。

USART支持同步模式,因此USART 需要同步始终信号USART_CK,一般在单片机里面同步信号很少使用,所以USART和UART使用方式是一样的,都使用异步模式。

三、硬件连接

USART和UART在硬件连接上有一些差别,主要是因为USART支持同步通信而UART不支持。下面是它们的硬件连接方式:

  1. UART的硬件连接:
  • UART通常使用三根线进行连接:TX(发送端)、RX(接收端)和地线(GND)。发送端的TX线连接到接收端的RX线,接收端的TX线连接到发送端的RX线。此外,两端的地线需要连接在一起,以确保信号的参考电位相同。
  • 在单片机或者嵌入式系统中,UART通常通过芯片上的引脚来连接,例如MCU的TX引脚连接到外部设备的RX引脚,MCU的RX引脚连接到外部设备的TX引脚。
  1. USART的硬件连接:
  • USART的连接方式与UART类似,但是在同步模式下还需要连接一个时钟线(CLK)。因此,USART在同步模式下通常使用四根线进行连接:TX、RX、CLK和GND。
  • 在使用USART进行同步通信时,发送端和接收端需要共享一个时钟信号,因此需要额外的时钟线来进行连接。

总的来说,UART和USART在硬件连接上的主要区别在于是否需要连接时钟线。

四、工作模式

  • 单工模式(Simplex Communication)的数据传输是单向的。通信双方中,一方固定为发送端,一方则固定为接收端。信息只能沿一个方向传输,使用一根传输线。
  • 半双工模式(Half Duplex)通信使用同一根传输线,既可以发送数据又可以接收数据,但不能同时进行发送和接收。数据传输允许数据在两个方向上传输,但是,在任何时刻只能由其中的一方发送数据,另一方接收数据。因此半双工模式既可以使用一条数据线,也可以使用两条数据线。半双工通信中每端需有一个收发切换电子开关,通过切换来决定数据向哪个方向传输。因为有切换,所以会产生时间延迟,信息传输效率低些。
  • 全双工模式(Full Duplex)通信允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。在全双工模式中,每一端都有发送器和接收器,有两条传输线,信息传输效率高。

显然,在其它参数都一样的情况下,全双工比半双工传输速度要快,效率要高。

五、使用流程

  1. 初始化:首先需要初始化UART模块,包括设置波特率(Baud Rate)、数据位长度、校验位和停止位等参数。这些参数需要与通信的对端设备相匹配,以确保正常的数据传输。
  2. 发送数据:要发送数据,首先将要发送的数据写入到UART发送缓冲区。通过编程方式将数据写入发送缓冲区后,UART模块会自动将数据发送出去。在发送数据之前,需要检查发送缓冲区是否为空,以确保可以安全地写入新的数据。
  3. 接收数据:接收数据时,需要检查接收缓冲区中是否有新的数据可供读取。如果接收缓冲区中有数据可读,可以通过编程方式读取数据并进行处理。
  4. 错误处理:在UART通信过程中,可能会发生一些错误,比如校验错误或者帧错误。在接收数据时,需要及时检查错误标志位,以便进行相应的错误处理和恢复。
  5. 中断处理:为了提高系统的响应速度和效率,通常会使用UART中断来处理接收和发送数据。在使用中断的情况下,需要编写相应的中断服务程序(ISR),以处理接收到的新数据或者发送缓冲区为空的情况。
  6. 关闭和清理:在程序结束或者不再需要使用UART时,需要关闭UART模块,并进行相应的资源清理工作,以释放相关的资源和关闭相应的中断。

具体示例(MCU为STM32F103)

代码语言:javascript
复制
#include "bsp_usart.h"
 /**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	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;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);		

  // 清除发送完成标志
	//USART_ClearFlag(USART1, USART_FLAG_TC);     
}

/*****************  发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	
	/* 取出高八位 */
	temp_h = (ch&0XFF00)>>8;
	/* 取出低八位 */
	temp_l = ch&0XFF;
	
	/* 发送高八位 */
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	
	/* 发送低八位 */
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);
}

最后

如果本文对你有所帮助,还请三连支持一下博主!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-11-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、简介
  • 二、帧格式
  • 三、硬件连接
  • 四、工作模式
  • 五、使用流程
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档