STM32串口通信
一、初识串口
1. 串口协议和RS-232标准
串口协议
串口协议是一种在串行通信中使用的数据传输协议。它规定了数据如何在发送端进行打包发送,以及在接收端如何进行解包和解释。在串口通信中,数据是一位一位地在一条通信线路上传输的,这与并行通信中同时传输多位数据形成对比。
串口协议主要包括起始位、数据位、奇偶校验位和停止位。起始位用于表示数据传输的开始,通常是一个逻辑 0 电平。数据位则是实际要传输的数据内容,其长度可以是 5、6、7 或 8 位等。奇偶校验位是可选的,用于对数据进行简单的错误检测,比如奇校验就是保证包括校验位在内的数据位中 “1” 的个数为奇数。停止位用于表示数据传输的结束,通常是一个或多个逻辑 1 电平。
RS-232标准
电气特性
RS - 232 是一种串行通信标准,它规定了接口的电气特性、机械特性和功能特性等多个方面。在电气特性方面,RS - 232 使用正负电压来表示逻辑电平。例如,在标准的 RS - 232 电平中,逻辑 0 用 + 3V 到 +15V 之间的电压表示,逻辑 1 用 - 3V 到 - 15V 之间的电压表示。这种使用正负电压的方式可以增强信号的抗干扰能力,使得数据在较长距离的传输过程中能够保持较好的稳定性。
不过,由于 RS - 232 电平与大多数数字电路中的 TTL(晶体管 - 晶体管逻辑)电平(0 - 0.8V 表示逻辑 0,2V - 5V 表示逻辑 1)不兼容,所以在实际应用中,如果要将 RS - 232 接口与 TTL 电路连接,通常需要使用电平转换芯片,如 MAX232。这个芯片可以将 RS - 232 电平转换为 TTL 电平,反之亦然,方便了不同电平设备之间的通信。
机械特性
RS - 232 标准定义了接口的物理形状和引脚排列。最常见的是 DB - 9 接口,它有 9 个引脚,每个引脚都有特定的功能。例如,引脚 2 用于发送数据(TXD),引脚 3 用于接收数据(RXD),引脚 5 用于接地(GND)。这些引脚的定义使得不同设备之间可以通过标准的接口进行连接,只要它们都遵循 RS - 232 标准的机械和电气特性。
2. RS232电平与TTL电平的区别
电平定义不同
RS232 电平:在 RS232 标准中,逻辑 0 被定义为 + 3V 到 +15V 之间的电压,逻辑 1 被定义为 - 3V 到 - 15V 之间的电压。这种采用正负电压表示逻辑电平的方式,主要是为了增强信号的抗干扰能力,使得数据能够在较长距离的传输过程中保持稳定。例如,在一些工业环境或者通信距离较远的场景下,RS232 电平能够有效地减少外界电磁干扰对信号的影响。
TTL 电平:TTL(晶体管 - 晶体管逻辑)电平的定义则相对简单。通常,0 - 0.8V 表示逻辑 0,2V - 5V 表示逻辑 1。TTL 电平主要应用于数字集成电路内部,它是基于晶体管的开关特性来实现逻辑电平的判断。因为在数字电路中,晶体管的导通和截止状态可以很方便地用来表示 0 和 1 这两种逻辑状态。
抗干扰能力差异
RS232 电平:由于 RS232 使用较高的正负电压来表示逻辑电平,所以它具有较强的抗干扰能力。当信号在传输过程中受到外界电磁干扰时,只要干扰信号没有使电平超出规定的范围(例如,使正电压低于 +3V 或者使负电压高于 - 3V),接收端仍然能够正确地识别信号的逻辑状态。这种抗干扰能力使得 RS232 适用于一些较为恶劣的工业环境或者通信距离较长的场合。
TTL 电平:TTL 电平的抗干扰能力相对较弱。因为其逻辑电平的范围较窄,例如,逻辑 1 的下限是 2V,当受到一定的干扰使电压下降到接近 2V 时,就可能导致接收端误判逻辑状态。所以 TTL 电平一般适用于短距离、干扰较小的通信环境,比如在一块印刷电路板(PCB)内部的芯片之间的通信。
应用场景不同
RS232 电平:RS232 主要用于设备之间的远距离通信。例如,计算机与外部调制解调器之间的通信,早期的计算机通过 RS232 接口与调制解调器相连,利用 RS232 电平的抗干扰特性和长距离传输能力,实现计算机与电话网络之间的数据传输。在工业控制领域,一些距离较远的设备(如 PLC 与上位机)之间的通信也常采用 RS232 标准。
TTL 电平:TTL 电平更多地应用于数字集成电路内部以及短距离通信。在一块电路板上,芯片之间的通信通常采用 TTL 电平,如微控制器与外围芯片(如存储器芯片、传感器接口芯片等)之间的通信。因为在这种短距离环境下,外界干扰相对较小,TTL 电平能够满足通信要求,并且其电平定义与数字电路的工作原理相匹配,方便电路设计和实现。
与其他电路的兼容性及转换需求
RS232 电平:由于 RS232 电平与常见的数字电路(如 TTL 电路)的电平不兼容,所以在将 RS232 接口与 TTL 电路连接时,通常需要使用电平转换芯片,如 MAX232。该芯片能够将 RS232 电平转换为 TTL 电平,或者将 TTL 电平转换为 RS232 电平,从而实现两种不同电平设备之间的通信。
TTL 电平:TTL 电平相对容易与其他数字电路兼容,因为它是数字集成电路中常用的电平标准。但是,当需要将 TTL 电平的信号传输到采用 RS232 电平的设备时,同样需要进行电平转换。
3. "USB/TTL转232"模块(以CH340芯片模块为例)的工作原理
硬件连接与初始化
当 CH340 芯片模块插入计算机的 USB 接口后,计算机的 USB 控制器会检测到设备插入,并对其进行复位操作,随后开始 USB 枚举过程2 。
在 USB 枚举过程中,计算机会获取 CH340 设备的描述符,包括厂商 ID、设备 ID 和 Class 类别等信息,然后根据这些信息为设备匹配相应的 USB 设备驱动程序2.
CH340 芯片内部集成了 USB 总线接口电路和异步串口电路等模块。其引脚包括 USB 信号引脚 UD+、UD-,用于连接到计算机的 USB 总线数据线;电源引脚 VCC 和 GND,可支持 3.3V 或 5V 电源电压输入;以及串口数据传输引脚 TXD、RXD 等,用于与外部的 TTL 串口设备连接.
数据发送过程
当计算机中的应用程序需要通过串口发送数据时,数据首先由应用程序通过 USB 驱动程序发送到计算机的 USB 控制器。
USB 控制器将数据按照 USB 协议进行打包,并通过 USB 总线发送给 CH340 芯片。
CH340 芯片接收到 USB 数据后,会将其转换为 TTL 电平的串口数据,并从 TXD 引脚输出,发送给与之连接的外部 TTL 串口设备。
数据接收过程
当外部 TTL 串口设备有数据发送时,数据从 CH340 芯片的 RXD 引脚输入。
CH340 芯片将接收到的 TTL 电平串口数据转换为 USB 数据格式,并通过 USB 总线发送给计算机的 USB 控制器。
计算机的 USB 控制器接收到数据后,将其传递给 USB 驱动程序,最后由 USB 驱动程序将数据提供给相应的应用程序进行处理2.
电平转换与信号处理
CH340 芯片在进行 USB 与 TTL 串口之间的数据转换时,还需要进行电平转换。由于 USB 接口使用的是 USB 电平标准,而 TTL 串口使用的是 TTL 电平标准,两者电平不兼容,因此 CH340 芯片需要将 USB 电平转换为 TTL 电平,以便与外部的 TTL 串口设备进行通信,反之亦然2.
此外,CH340 芯片还支持一些 MODEM 联络信号,如 RTS(请求发送)、CTS(清除发送)、DTR(数据终端就绪)、DSR(数据设备就绪)、DCD(数据载波检测)和 RI(振铃指示)等。这些信号可以用于实现串口通信中的流控制和状态指示等功能,增强了串口通信的可靠性和灵活性.
二、串口传输文件的实验
1.传输实验
将两个线用杜邦线正确连接,这里我将TXD连接RXD,RXD连接TXD,5V连接5V,GND连接GND。
连接到两台电脑之后,将两台电脑串口调试助手打开,波特率,校验位等参数要一致,随后选择一张图片进行传输
发送端:
接收端:
随后在串口调试助手文件所在路径会出现所传输的DAT类型的文件,鼠标右键点击属性,将文件类型改成jpg类型文件即可
2.故障分析
如果只接 TX–RX,RX–TX 这样两根线,不接电源线或 GND 地线,文件传输通常是不能正常工作的,原因如下:
缺少电源无法驱动电路:不接电源线,设备就无法获得正常工作所需的电能,相关的芯片、电路等都无法启动和运行,自然也就无法进行文件传输。因为文件传输需要各个电子元件处于正常的工作状态,在无电源驱动的情况下,如 CH340 芯片等无法进行信号的转换与处理等工作,进而导致传输无法进行。
无 GND 导致信号参考点缺失:
电平判断错误:GND 在电路中作为零电位参考点,为信号的电平提供了基准。在数据传输过程中,接收端需要通过对比输入信号与 GND 的电位差来判断信号是逻辑 0 还是逻辑 1 。如果不接 GND,接收端就无法准确地确定信号电平的高低,从而导致数据接收错误或无法接收。比如 TTL 电平中,0-0.8V 表示逻辑 0,2V-5V 表示逻辑 1,而判断这个电压范围是相对于 GND 而言的,没有 GND 则无法进行准确判断.
共模干扰问题:类似于 RS485 接口通信时的情况,即使是采用差分信号传输数据的方式,如不接 GND,也可能会出现共模电压超出正常范围的情况。当共模电压超出收发器允许的范围时,就会影响通信的稳定可靠,甚至损坏接口。虽然串口通信一般采用非差分信号,但在实际环境中同样可能受到各种电磁干扰等,导致信号电平出现波动,没有 GND 作为参考和稳定电位,信号的稳定性和准确性难以保证.
信号回流问题:在电路中,电流需要形成完整的回路才能正常工作。GND 作为电流的返回路径,对于信号的传输至关重要。如果不接 GND,信号的回流路径就会被切断,从而导致信号无法正常传输。特别是对于一些高速信号或长距离传输的信号,信号回流的完整性对信号质量的影响更为明显.
三、STM32串口实验
1. 简单发送“Hello Windows”
打开cubeMX,新建工程
选择自己的芯片
配置SYS
配置RCC
设置MODE为 异步通信(Asynchronous)
参数设置 波特率为115200 Bits/s,传输数据长度为8 Bit,奇偶检验无,停止位1
时钟配置:
随后进行项目配置
)
打开工程后,在main.c的while循环里面输入以下代码
HAL_UART_Transmit(&huart1, (uint8_t *)"Hello Windows!\r\n", 16 , 0xffff);
HAL_Delay(1000);
编译后打开串口助手,可以看到:
若去掉延时:
出现了数据丢失(由于传输速度很快,有时候会看不到)
2. 扩展功能
当上位机给stm32发送一个字符“#”后,stm32暂停发送“hello windows!”;发送一个字符“*”后,stm32继续发送;
main.c代码:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
uint8_t rcData = '*';
uint8_t flag = 1; // 用于控制是否继续发送 "hello windows"
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
// 检查是否需要发送 "hello windows"
if (flag)
{
uint8_t hello[20] = "Hello Windows\r\n ";
HAL_UART_Transmit_IT(&huart1, hello, 20);
HAL_Delay(500); // 延迟500ms
}
// 接收中断使能
HAL_UART_Receive_IT(&huart1, &rcData, 1);
// 检查接收到的数据
if (rcData == '#')
{
// 如果接收#
flag = 0; // 停止发送 "hello windows"
}
else if (rcData == '*')
{
// 如果接收*
flag = 1; // 继续发送 "hello windows"
uint8_t hello[20] = "Hello Windows\r\n ";
HAL_UART_Transmit_IT(&huart1, hello, 20); // 立即发送 "hello windows"
HAL_Delay(500); // 延迟500ms
}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
效果:
视频1.0
四、STM32中断与DMA原理与编程
1.中断方式实现三中扩展功能
配置方面,和上面的差不多,只需要打开中断就可以
main.c代码
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include <string.h>
void SystemClock_Config(void);
char c;//指令 0:停止 1:开始
char message[]="Hello Windows\n";//输出信息
char tips1[]="Start.....\n";//提示2
char tips2[]="Stop......\n";//提示3
int flag=0;//标志 #:停止发送 *.开始发送
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
//设置接受中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c,1);
//当flag为1时,每秒发送一次信息
//当flag为0时,停止
while (1)
{
if(flag==1){
//发送信息
HAL_UART_Transmit(&huart1, (uint8_t *)&message, strlen(message),0xFFFF);
//延时
HAL_Delay(1000);
}
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//当输入的指令为0时,发送提示并改变flag
if(c=='#'){
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF);
}
//当输入的指令为1时,发送提示并改变flag
else if(c=='*'){
flag=1;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips1, strlen(tips1),0xFFFF);
}
//重新设置中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c,1);
}
/* USER CODE END 4 */
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
实验效果:
视频2.0
2. STM32采用串口DMA方式
创建STM32CubeMX项目
选芯片的方面、SYS、RCC配置我省略了,上面有讲到
USART使能中断
DMA设置:
点击DMA Settings的Add添加通道,传输速率设置为中速Medium,模式设置为Normal,右侧选择Memory(RX、TX都要这么配置)
后面的是时钟配置和文件生成,和上面的差不多,在这里就不多说了
代码编写
代码:
uint8_t Senbuff[] = "Hello world!"; //定义数据发送数组
代码:
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
HAL_Delay(1000);
效果:
五、逻辑分析
点击上面的魔术棒
六、心得体会
1、理论知识的深化理解
通过学习串口协议、RS - 232 标准以及不同电平标准(如 RS232 电平与 TTL 电平)的区别,我对串口通信的底层原理有了清晰的认识。了解到串口协议如何规定数据的格式、传输速率、校验方式等关键要素,这为后续的编程实践奠定了坚实的基础。RS - 232 标准中关于信号电气特性和机械特性的规定,让我明白了在实际硬件连接时需要注意的要点,例如其电平范围与 TTL 电平的巨大差异,使得在进行不同设备间的串口连接时,必须要考虑电平转换的问题,像使用 “USB/TTL 转 232” 模块(以 CH340 芯片模块为例)来实现基于 STM32 的串口与传统 RS - 232 设备的通信对接。理解这些理论知识,就像是在黑暗中点亮了一盏明灯,使我在面对复杂的串口通信问题时,能够从原理层面去分析和解决。
2、实践操作的经验积
串口传输文件实验
在进行两台笔记本电脑借助 “USB/TTL 转 RS232” 模块和杜邦线建立串口连接并传输文件的实验中,我亲身体验到了波特率对传输时间的显著影响。根据预估的文件大小和波特率计算传输时间的过程,让我更加深入地理解了它们之间的数学关系。当实际进行传输时,发现实际传输时间会受到多种因素的干扰,如系统资源占用、串口助手软件的效率等。这使我认识到在实际工程应用中,不能仅仅依靠理论计算,还需要充分考虑实际环境和设备的各种情况。同时,在实验中发现只接 TX - RX、RX - TX 两根线而不接电源线或 GND 地线时,文件传输无法正常工作。这是因为没有电源线,设备无法获得工作所需的电能,而缺少 GND 地线会导致信号参考点缺失,从而使接收端无法准确判断信号电平,也破坏了信号的回流路径,进一步证明了硬件连接完整性对于串口通信的重要性。
STM32 与上位机串口通信编程
在使用 STM32CubeMX 配合 Keil,采用 HAL 库进行 STM32 与上位机的串口通信编程时,我学会了如何正确设置 USART1 的参数,如波特率为 115200、1 位停止位、无校验位等。在实现 STM32 系统给上位机连续发送 “hello windows!” 的过程中,当不加延时语句时,上位机端出现了接收数据丢失的现象。这让我意识到在串口通信中,数据发送和接收的速率匹配是至关重要的。如果发送端过快地发送数据,而接收端处理速度跟不上,就会导致数据丢失。通过添加适当的延时语句或者采用更合理的数据发送控制策略,如中断或 DMA 方式,可以有效地解决这个问题。
在扩展功能的实现中,即根据上位机发送的字符 “#” 暂停发送,“*” 继续发送的任务里,我深入学习了串口中断的编程方法。在 HAL 库中,通过重写HAL_UART_RxCpltCallback函数来处理串口接收中断,根据接收到的字符来控制数据发送标志位,从而实现对数据发送的灵活控制。这使我对 STM32 的中断机制有了更深入的理解,学会了如何在中断服务程序中正确地处理数据和控制程序流程,同时也体会到了中断在实时性要求较高的串口通信中的重要性。它能够及时响应上位机的指令,实现对 STM32 数据发送的动态控制,提高了系统的交互性和灵活性。
在使用串口 DMA 方式进行 STM32 向上位机连续发送数据的实验中,我感受到了 DMA 传输的高效性。DMA 能够直接在内存和外设之间进行数据传输,无需 CPU 的过多干预,大大提高了数据传输的速率,减轻了 CPU 的负担。在编程过程中,需要正确地配置 DMA 通道、数据传输方向、数据量等参数,这让我对 STM32 的 DMA 控制器有了更深入的了解。通过与串口中断方式的对比,我更加清晰地认识到不同数据传输方式的特点和适用场景,能够根据实际项目的需求选择最合适的方法。
3、问题解决与调试能力的提升
在整个学习和实践过程中,不可避免地遇到了各种各样的问题,如串口连接不稳定、数据传输错误、程序运行异常等。通过不断地排查硬件连接、检查代码逻辑、使用调试工具(如示波器、逻辑分析仪、Keil 的调试功能等),我逐渐积累了丰富的问题解决和调试经验。例如,当遇到串口数据接收错误时,我学会了首先检查硬件连接是否正确,包括串口线是否松动、电平转换模块是否正常工作等;然后检查串口参数设置是否一致,如波特率、数据位、停止位、校验位等;最后通过在代码中添加调试输出语句或者使用调试工具查看寄存器的值来深入分析程序逻辑是否存在问题。这种从硬件到软件、从宏观到微观的问题排查方法,使我在面对复杂的工程问题时能够更加有条不紊地进行分析和解决,提高了我的工程实践能力和问题解决能力。
4、对 STM32 开发的整体认识
通过这次串口通信的学习,我对 STM32 的开发有了更全面的认识。串口通信作为 STM32 与外部设备进行数据交互的重要手段之一,与其他外设(如 GPIO、定时器、中断控制器、DMA 控制器等)相互配合,共同构建起了一个完整的 STM32 应用系统。在实际项目开发中,需要综合考虑各个外设的功能和特点,合理地进行资源分配和任务调度,以实现系统的最优性能。例如,在串口通信中,可以利用定时器来实现数据发送的定时控制,或者利用中断来处理串口事件的异步响应,通过 DMA 来提高数据传输的效率等。这让我认识到 STM32 开发是一个系统性的工程,需要全面掌握各个方面的知识和技能,并且能够灵活地运用它们来解决实际问题。
总之,本次 STM32 串口通信的学习经历让我在理论知识和实践能力方面都取得了巨大的进步。我不仅深入理解了串口通信的原理和技术细节,还通过实际编程和实验操作,掌握了 STM32 串口通信的开发方法和技巧,提高了自己的问题解决和调试能力,对 STM32 开发有了更全面的认识。在未来的学习和工作中,我将继续深入学习 STM32 的其他知识和技术,不断提升自己的嵌入式系统开发能力,为今后的项目开发打下更加坚实的基础。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/a6667778884554/article/details/144310693
|