ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

单片机通信协议--USART(串口通信)

单片机通信协议--USART(串口通信) 通信协议通信协议并行通信串行通信串口通信USART收发原理通信的起止波特率的设置状态寄存器关键引脚引脚的使用串口的使用简单的开关灯实验通信协议单片机微控制器中的通信协议是实现单片机与外设传感器、显示屏、其他单片机等或外部系统之间数据交换的规则集合涵盖信号时序、数据格式、错误处理等内容。这些协议根据硬件接口、传输方式和应用场景的不同可分为并行通信和串行通信两大类。其中串行通信因布线简单、成本较低在单片机应用中更为广泛。并行通信并行通信通过多根数据线同时传输多位数据如 8 位、16 位并配合时钟线和控制线实现同步。其典型应用包括单片机与并行 LCD1602 显示屏的连接8 位数据总线 RS、RW、E 控制信号以及单片机内部外设如 RAM、Flash与 CPU 之间的通信。由于并行通信占用引脚较多这里不做过多介绍。串行通信串行通信通过单根或两根数据线逐位传输数据布线简单通常 2-4 根线适合中短距离通信是单片机外部通信的主流方式。根据是否需要时钟线同步又可分为异步通信和同步通信。异步模式无需时钟线通过波特率收发双方约定的传输速率同步数据仅需 2 根线TX 发送、RX 接收即可实现全双工通信同步模式需额外时钟线CLK由主机提供时钟信号同步适合更高精度的高速通信较少用)串口通信USARTUSARTUniversal Synchronous/Asynchronous Receiver/Transmitter通用同步/异步收发器是单片机中常用的串行通信协议支持异步通信最常用和同步通信两种模式。其中异步模式通常简称 UART因硬件简单、使用灵活在嵌入式领域应用最为广泛。## 通信方式异步模式UART无需时钟线通过波特率收发双方约定的传输速率同步数据仅需 2 根线TX 发送、RX 接收即可实现全双工通信同步模式USART需额外时钟线CLK由主机提供时钟信号同步适合更高精度的高速通信较少用收发原理VCC 和 GND 用于供电TX 和 RX 用于收发数据。设备 1 的 TX 需与设备 2 的 RX 相接设备 1 的 RX 需与设备 2 的 TX 相接其高低电平均相对于 GND。电平标准是数据 0 和数据 1 的表达方式即传输线缆中规定的电压与数据的对应关系。串口常用的电平标准有以下几种【TTL 电平】3.3V 或 5V 表示 10V 表示 0【RS232 电平】-3~-15V 表示 13~15V 表示 0【RS485 电平】两线压差 2~6V 表示 1-2~-6V 表示 0差分信号## 帧格式每帧数据由以下部分组成按传输顺序起始位1 位低电平逻辑 0标志数据帧开始数据位5-9 位通常 8 位即 1 字节表示有效数据可配置高位在前或低位在前校验位可选1 位用于简单错误检测支持奇校验数据位中 1 的个数为奇数、偶校验1 的个数为偶数或无校验停止位1-2 位高电平逻辑 1标志数据帧结束保证帧间隔通信的起止在没有通信时默认为高电平在进入起始位后电平转换为低电平表示开始发送数据数据呈现出8个字节一位的特性进入最后一位后低电平转化为高电平方便下一次数据的发送。波特率的设置通过配置波特率寄存器中的值来设置分频器的值时钟频率除以分频器中的值再除以16就可以得到波特率。例如当我们需要把波特率设置为115200时STM32的时钟频率为72MHz我们就可以反向推出波特率寄存器中的值72MHz ➗ x ➗ 16 115200x 39.0625状态寄存器在USART中存在一些状态寄存器用来判断收发状态。编号标志位名称英文全称功能说明置 1 条件Bit 5RXNERead Data Register Not Empty接收数据寄存器非空有数据可读接收到 1 帧完整数据存入接收缓冲区Bit 6TCTransmission Complete发送完成整帧数据发送结束发送缓冲区空 移位寄存器发送移位也空即最后 1 位已发送Bit 7TXETransmit Data Register Empty发送数据寄存器空可写新数据发送缓冲区的数据已转移到移位寄存器缓冲区空闲Bit 3FEFraming Error帧错误接收时检测到无效停止位接收数据时停止位不是规定的高电平如配置 1 个停止位但实际未检测到高电平Bit 4OREOverrun Error溢出错误接收缓冲区未读取新数据覆盖旧数据接收缓冲区已满RXNE1但新数据已接收完成旧数据被覆盖Bit 2NENoise Error噪声错误接收数据时检测到干扰接收数据过程中检测到不符合波特率的噪声信号仅异步模式Bit 1PEParity Error奇偶校验错误启用校验时校验位不匹配接收数据的校验位与计算出的校验位不一致需提前配置校验模式关键引脚USARTx_Tx数据的发送引脚将数据发送出去USARTx_Rx数据的接收引脚用来接收数据USARTx_CTS全称 “Clear To Send”清除发送作用是接收方通知发送方 “是否允许继续发送数据”避免因接收方缓冲区满导致数据溢出比软件流控更高效、实时USARTx_RTS全称 “Request To Send”请求发送与USARTx_CTS 是配对使用的 “双向控制信号”核心作用是 发送方主动通知接收方 “我要发数据了请你准备接收”配合 CTS 完成全双工通信的双向流量控制避免数据溢出USARTx_CKUSARTx_CK 是USART通用同步异步收发传输器同步模式Synchronous UART简称 Sync UART 专用的时钟信号引脚全称 “Clock”核心作用是为同步通信提供统一的时钟基准—— 区别于我们常说的异步UART无需时钟靠波特率同步同步 UART需通过 CK 信号实现发送方与接收方的严格时序对齐提升通信速率和可靠性引脚的使用通过查看stm32f103c8t6的引脚分布图可以找到USART1的引脚映射注意此时输出和输入都应该选择复用模式因为CPU不是直接向引脚写数据而是操作USART模块写引脚类型默认映射无重映射重映射Partial Remap配置要点GPIO / 寄存器TX发送PA9PB6配置为「复用推挽输出」GPIO_MODE_AF_PP重映射需使能 AFIO_USART1_REMAP 位RX接收PA10PB7配置为「复用输入」GPIO_MODE_AF_INPUT重映射需使能 AFIO_USART1_REMAP 位CK同步时钟PA8-无重映射引脚仅默认映射有效配置为复用推挽输出主/ 复用输入从异步模式可悬空RTS请求发送PA12PB14需启用 USART_CR3 的 RTSE 位配置为复用推挽输出重映射需使能 AFIO_USART1_REMAP 位CTS清除发送PA11PB13需启用 USART_CR3 的 CTSE 位配置为复用输入重映射需使能 AFIO_USART1_REMAP 位串口的使用串口.h文件#ifndef__USART_H#define__USART_H#includestdio.h#includesys.h#includestdarg.h#includestdlib.h#includestring.h#defineUSART_REC_LEN200// 定义最大接收字节数 200#defineEN_USART11// 使能#defineUSART1_REC_LEN200#defineUSART_nUSART1externu8 USART_RX_BUF[USART_REC_LEN];// 接收缓冲,最大USART_REC_LEN个字节.末字节为换行符externu16 USART_RX_STA;// 接收状态标记externu8 USART1_RX_BUF[USART1_REC_LEN];externu16 USART1_RX_STA;// usart1_printfvoidUSART1_printf(char*fmt,...);// 如果想串口中断接收请不要注释以下宏定义voiduart_init(u32 bound);voidUSART1_Init(u32 bound);#endif串口.c文件的部分内容#ifEN_USART1u8 USART_RX_BUF[USART_REC_LEN];// 接收缓冲区最大USART_REC_LEN个字节// 接收状态// bit15接收完成标志// bit14接收到0x0D回车// bit13~0接收到的有效字节数目u16 USART_RX_STA0;// 接收状态标志u8 USART1_RX_BUF[USART1_REC_LEN];// 接收缓冲区u16 USART1_RX_STA0;// 接收状态标志voidUSART1_Init(u32 bound){GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;// ---- 开启时钟 ----// 使能USART1和GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);// ---- PA9: USART1_TX发送引脚----// 配置为复用推挽输出发送引脚需要输出能力GPIO_InitStructure.GPIO_PinGPIO_Pin_9;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_InitStructure.GPIO_ModeGPIO_Mode_AF_PP;// 复用推挽输出GPIO_Init(GPIOA,GPIO_InitStructure);// ---- PA10: USART1_RX接收引脚----// 配置为浮空输入接收引脚由外部驱动不需要内部上拉/下拉GPIO_InitStructure.GPIO_PinGPIO_Pin_10;GPIO_InitStructure.GPIO_ModeGPIO_Mode_IN_FLOATING;// 浮空输入GPIO_Init(GPIOA,GPIO_InitStructure);// ---- NVIC 中断控制器配置 ----NVIC_InitStructure.NVIC_IRQChannelUSART1_IRQn;// USART1中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority3;// 抢占优先级 3NVIC_InitStructure.NVIC_IRQChannelSubPriority3;// 子优先级 3NVIC_InitStructure.NVIC_IRQChannelCmdENABLE;// 使能该中断通道NVIC_Init(NVIC_InitStructure);// ---- USART1 参数配置 ----USART_InitStructure.USART_BaudRatebound;// 波特率由传入参数决定USART_InitStructure.USART_WordLengthUSART_WordLength_8b;// 8位数据位USART_InitStructure.USART_StopBitsUSART_StopBits_1;// 1位停止位USART_InitStructure.USART_ParityUSART_Parity_No;// 无奇偶校验USART_InitStructure.USART_HardwareFlowControlUSART_HardwareFlowControl_None;// 无硬件流控USART_InitStructure.USART_ModeUSART_Mode_Rx|USART_Mode_Tx;// 收发模式USART_Init(USART1,USART_InitStructure);// 按以上参数初始化USART1// ---- 使能中断 ----USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);// 开启接收非空中断每收到1字节触发一次// ---- 使能USART1 ----USART_Cmd(USART1,ENABLE);// 启动USART1}// 串口1格式化打印函数类似printf通过USART1发送字符串voidUSART1_printf(char*fmt,...){charbuffer[USART1_REC_LEN1];// 发送缓冲区u8 i;va_list arg_ptr;va_start(arg_ptr,fmt);// 获取可变参数起始地址vsnprintf(buffer,USART1_REC_LEN1,fmt,arg_ptr);// 将格式化字符串写入buffer// 逐字节发送buffer中的内容while((iUSART1_REC_LEN)(istrlen(buffer))){USART_SendData(USART1,(u8)buffer[i]);// 发送一个字节while(USART_GetFlagStatus(USART1,USART_FLAG_TC)RESET);// 等待发送完成TC1}va_end(arg_ptr);// 释放可变参数列表}// // 串口中断服务函数函数名固定不可修改//// 工作原理状态机逐字节接收检测到 \r\n(0x0D 0x0A) 时标记接收完成//// USART_RX_STA 的位定义// Bit15 1接收完成收到了 \r\n// Bit14 1已收到 0x0D回车正在等待 0x0A换行// Bit13~0 已接收的有效字节数//// 主函数中通过判断 (USART_RX_STA 0xC000) 来检测是否接收完成// 接收完成后从 USART_RX_BUF[] 读取数据处理完毕后必须将 USART_RX_STA 清零// voidUSART1_IRQHandler(void){u8 Res;// 判断是否为「接收寄存器非空」中断即收到了新数据if(USART_GetITStatus(USART1,USART_IT_RXNE)!RESET){ResUSART_ReceiveData(USART1);// 读取接收到的1字节数据读取后RXNE标志自动清除printf(%c,Res);// 回显把收到的字符原样发回电脑调试用// ---- 状态机判断 ----if((USART1_RX_STA0x8000)0)// Bit150接收尚未完成{if(USART1_RX_STA0x4000)// Bit141之前已收到 0x0D回车{// 此时在等待 0x0A换行if(Res!0x0a)USART1_RX_STA0;// 0x0D后没跟0x0A说明不是正确的结束符清零重来elseUSART1_RX_STA|0x8000;// 收到 \r\n设置Bit15标记接收完成}else// Bit140还没收到过 0x0D{if(Res0x0d){USART1_RX_STA|0x4000;// 收到回车设置Bit14准备接收换行}else{// 普通数据字符存入缓冲区USART1_RX_BUF[USART1_RX_STA0X3FFF]Res;// 低14位是当前数据长度作为数组下标USART1_RX_STA;// 长度1if(USART1_RX_STA(USART1_REC_LEN-1))USART1_RX_STA0;// 数据超出缓冲区长度丢弃本次数据重新开始接收}}}}}#endif简单讲解一下中断处理函数在这里我们使用USART_RX_STA来表示状态第15位表示是否接收完成14位表示接收到了0x0d后面的几位表示接受数据的长度。Bit15 Bit14 Bit13 ~ Bit0┌──────┬──────────┬──────────────┐│ 0x8000 │ 0x4000 │ 0x3FFF ││接收完成 │ 已收0x0D │ 已接收数据长度 │└──────┴──────────┴──────────────┘因此我们先判断是否发生中断然后接收数据并判断接受是否完成如果完成则不对接收到的数据进行处理否则判断是否是回车换行即判断之前是否收到了0x0d再判断当前的是否是0x0amain.c文件intmain(void){// 主程序RCC_Configuration();// 时钟设置USART1_Init(115200);// 串口1初始化波特率设置为115200USART1_RX_STA0xc000;// 接收状态标记初始化USART1_printf(USART1 Test\r\n);// 发送测试信息}简单的开关灯实验intmain(void){// 主程序RCC_Configuration();// 时钟设置USART1_Init(115200);// 串口1初始化波特率设置为115200USART1_RX_STA0xc000;// 接收状态标记初始化while(1){if(USART1_RX_STA0xc000)// 单独的回车换行或者一开始显示数据{//判断是否是单独的回车也就是数据长度为0if((USART1_RX_STA0x3fff)0){USART1_printf(\033[1;47;33m\r\n);// 设置颜色黄字白底USART1_printf( 1y--开LED1灯 1n--关LED1灯 \r\n);USART1_printf( 2y--开LED2灯 2n--关LED2灯 \r\n);USART1_printf( 请输入控制指令按回车键执行 \033[0m\r\n);}elseif((USART1_RX_STA0x3FFF)2USART1_RX_BUF[0]1USART1_RX_BUF[1]y){// 处理LED1开灯指令GPIO_ResetBits(LEDPORT,LED1);// 拉低电平点亮LED1低电平有效USART1_printf(1y -- LED1灯已经点亮\r\n);}elseif((USART1_RX_STA0x3FFF)2USART1_RX_BUF[0]1USART1_RX_BUF[1]n){// 处理LED1关灯指令GPIO_SetBits(LEDPORT,LED1);// 拉高电平熄灭LED1低电平有效USART1_printf(1n -- LED1灯已经熄灭\r\n);}elseif((USART1_RX_STA0x3FFF)2USART1_RX_BUF[0]2USART1_RX_BUF[1]y){// 处理LED2开灯指令GPIO_ResetBits(LEDPORT,LED2);// 拉低电平点亮LED2低电平有效USART1_printf(2y -- LED2灯已经点亮\r\n);}elseif((USART1_RX_STA0x3FFF)2USART1_RX_BUF[0]2USART1_RX_BUF[1]n){// 处理LED2关灯指令GPIO_SetBits(LEDPORT,LED2);// 拉高电平熄灭LED2低电平有效USART1_printf(2n -- LED2灯已经熄灭\r\n);}else{USART1_printf(无效的指令请重新输入\r\n);}}}}感谢舍友https://blog.csdn.net/weixin_74006549/article/details/154572306?spm1001.2014.3001.5502
返回列表