两个人生猴子不盖被子:一场关于自由与责任的奇幻漂流

核心内容摘要

探索“91视频你懂的”:在内容浪潮中的每一次心跳
wwww,xxxx

免费软件的“干逼”真相:不为人知的成本与价值

前言哈喽今天咱们来聊聊串口UART/USART里的一个“隐藏技能”——静默模式Silent Mode 或 Mute Mode。

这个功能可能很多人没怎么用过简单来说它就像让串口“闭嘴听话”特别适合多设备通信场景能有效避免“抢话”导致的混乱。

今天我就给大家简单介绍一下这个功能别说让你们马上精通至少让第一次接触的朋友有个基本认识哈哈。

那这里先过一下本文介绍的主要内容哈。

串口静默模式是啥像啥串口静默模式是单片机串口的一种特殊状态让串口“只听不讲”-正常模式串口能“说”通过TX引脚发送数据比如打印调试信息到电脑和“听”通过RX引脚接收数据比如收电脑的命令。

-静默模式串口“闭嘴”TX引脚不发送任何数据但RX引脚还能正常接收数据像个安静的“听众”。

举一个简单的生活例子- 我们在某微信群里时正常可以发消息TX也能看消息RX。

但群主说“小明你别发消息只许看” 这就是静默模式——你还能看到群消息接收但不能发发送就跟群禁言一样。

- 串口静默模式就像给TX按了“静音键”单片机只收数据不往外“吱声”。

静默模式的工作原理咋“闭嘴”的静默模式是串口硬件UART模块的一个功能通过寄存器控制实现。

正常串口通信- UART有TX发送和RX接收两条线。

-发送程序把数据写入发送缓冲区UART通过TX引脚输出电信号高低电平。

-接收外部数据通过RX引脚进入接收缓冲区触发中断或由程序读取。

进入静默模式- 通过设置UART的控制寄存器禁用TX功能。

例如APM32 的 USART_CTRL1 寄存器有个RXMUTEEN位或类似标志置1后- TX引脚停止输出信号进入高阻态或固定电平。

- 发送缓冲区的数据不会发送出去。

- RX功能不受影响接收缓冲区照常工作收到数据可触发中断或DMA。

退出静默模式- 清除MUTE位恢复TX功能串口又能“说”了。

- 某些场景下硬件自动退出静默模式比如收到特定数据帧如地址匹配。

- 在APM32中静默模式常用于RS-485或多点通信协议如Modbus RTU、DMX512。

MUTE位控制驱动使能DE信号防止TX输出干扰总线。

- 硬件可检测“空闲帧”一段时间无数据或“地址帧”来进入/退出静默模式。

例如收到匹配的地址字节后UART自动清MUTE位。

静默模式有啥用啥时候需要静默模式主要解决多设备串口通信中的“乱说话”问题还能省电或支持特定协议。

1 避免“抢话”多设备通信-场景多个单片机通过串口连到一条总线像RS-485就像一群人在一根电话线聊天。

-问题大家同时说话发送数据数据会撞车乱成一团。

-静默模式作用- 让从设备从机保持静默只听主机命令。

- 主机发命令比如“设备1报状态”从机收到后检查是不是叫自己叫到才退出静默模式回复。

-例子RS-485网络中主机查询从机温度只有被点名的从机发送数据其他从机“闭嘴”。

2 省电低功耗场景-场景电池供电的设备像物联网传感器用串口通信大部分时间不需要发送。

-问题发送数据激活TX电路费电。

-静默模式作用- 禁用TX降低功耗只接收外部命令。

- 收到特定命令如唤醒再恢复发送。

-例子一个无线传感器节点通过串口接收主机查询平时静默省电。

这种情况可以理解为我们的手机待机只收消息不发省电到飞起。

3 协议支持地址匹配-场景一些协议如Modbus RTU、DMX512要求设备只响应特定地址的命令。

-问题总线上数据很多单片机得先判断“是不是叫我”再决定回不回话。

-静默模式作用- 单片机在静默模式下接收数据检查数据帧的地址字段。

- 如果地址匹配“是我”退出静默模式发送响应否则继续“装死”。

-例子在Modbus RTU网络中从机收到主机命令检查地址字节只有匹配的从机回应。

像我们收快递时快递员喊“张伟拿包裹” 只有叫张伟的人才去签收其他人不动。

4 调试当“隐形人”-场景开发时想让单片机“偷听”串口通信但不干扰现有网络。

-问题单片机随便发送数据可能会打乱其他设备的通信。

-静默模式作用- 让单片机只接收数据记录或分析总线上的通信不发送任何数据。

- 适合调试多设备系统观察协议是否正常。

-例子调试Modbus网络临时加个单片机监听主机和从机的对话分析数据包格式。

就像卧底一样悄悄听别人聊天自己一句话不说。

静默模式的实现硬件和软件咋搞静默模式的实现依赖UART硬件支持软件配置寄存器控制。

1 APM32的静默模式配置APM32的USART模块支持静默模式通常用于RS-485或多点通信。

配置步骤如下

初始化UART- 配置波特率、数据位、停止位等基本参数。

- 使能接收RX可选使能中断或DMA。

进入静默模式- 设置USART_CTRL1寄存器的RXMUTEEN位或硬件特定的地址匹配位。

- TX功能禁用RX继续工作。

处理接收数据- 在中断或轮询中读取接收缓冲区检查数据比如地址字节。

- 如果需要响应清除RXMUTEEN位启用TX发送数据。

退出静默模式- 手动清RXMUTEEN位或硬件自动退出比如地址匹配。

- 恢复正常通信。

技术细节- APM32的RXMUTEEN位在USART_CTRL1寄存器地址匹配功能通过USART_CTRL2的ADDR[3:0]字段设置。

- RS-485模式下需控制驱动使能DE引脚通常接外部RS-485收发器如MAX485。

- 硬件支持“空闲帧检测”IDLE可自动进入静默模式。

2

注意事项-缓冲区管理静默模式下RX仍接收数据确保接收缓冲区不溢出用中断或DMA及时读取。

-总线电平RS-485等半双工总线需注意电平冲突静默模式配合DE信号控制。

-协议兼容确认协议是否需要静默模式比如Modbus RTU需要简单点对点通信不用。

-调试用示波器或逻辑分析仪检查TX引脚确认静默模式是否生效TX无信号。

其实我们配置静默模式像给串口装个“开关”关掉“麦克风”TX软件当“裁判”决定啥时候开。

串口通信例程主机-从机带静默模式我这里设计了一个串口通信例程模拟RS-485总线的主机-从机通信展示静默模式如何避免“抢话”。

1 实验场景-硬件- 两块APM32开发板比如APM32F103或F407一块作主机一块作从机。

- RS-485模块淘宝几十块如MAX485连接两板的USART2PA2-TXPA3-RX。

- RS-485总线A、B线连接两模块GND共地。

- 电脑逻辑分析仪查看实际波形。

-通信协议- 主机发送命令帧[地址][命令][数据][校验]简单示例1字节地址1字节命令。

- 从机在静默模式下接收检查地址匹配则退出静默模式回复[地址][状态]。

- 地址主机0x00从机0x01。

- 命令0x10查询状态。

- 校验简单累加和。

-波特率1152008位数据1位停止无校验。

2 代码实现APM32以下是主机和从机的完整代码基于APM32F407Keil。

复制/* Includes */#include main.h#include Board.h#include stdio.h#include apm32f4xx_gpio.h#include apm32f4xx_adc.h#include apm32f4xx_misc.h#include apm32f4xx_usart.h#include apm32f4xx_tmr.h// 定义角色取消注释其中一个//#define Master#define Slave // 当前为从机模式/* printf using USART1 */#define DEBUG_USART USART1#define APM_COMInit APM_TINY_COMInit/* 初始化USART1用于调试输出连接串口助手 */void USART1_Init(){/* USART1初始化 */USART_Config_T usartConfigStruct;/* 配置USART1参数 */USART_ConfigStructInit(usartConfigStruct); // 初始化默认配置usartConfigStruct.baudRate 115200; // 波特率115200usartConfigStruct.mode USART_MODE_TX_RX; // 收发模式usartConfigStruct.parity USART_PARITY_NONE; // 无奇偶校验usartConfigStruct.stopBits USART_STOP_BIT_1; // 1停止位usartConfigStruct.wordLength USART_WORD_LEN_8B; // 8位数据usartConfigStruct.hardwareFlow USART_HARDWARE_FLOW_NONE; // 无硬件流控制/* 初始化COM1对应USART1 */APM_COMInit(COM1, usartConfigStruct);}#ifdef Master/* 主机模式接收数据缓冲区和索引 */volatile uint8_t rx_data[10], rx_idx 0;/* 初始化USART2作为主机串口 */void USART2_Init(void){// 使能USART2和GPIOA时钟RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_USART

; // 开启USART2时钟RCM_EnableAHB2PeriphClock(RCM_AHB1_PERIPH_GPIOA); // 开启GPIOA时钟// 配置PA2TX和PA3RXGPIO_Config_T gpioConfigStruct;gpioConfigStruct.mode GPIO_MODE_AF; // 复用功能模式gpioConfigStruct.otype GPIO_OTYPE_PP; // 推挽输出gpioConfigStruct.pin GPIO_PIN_2 | GPIO_PIN_3; // PA2和PA3gpioConfigStruct.pupd GPIO_PUPD_NOPULL; // 无上下拉gpioConfigStruct.speed GPIO_SPEED_100MHz; // 高速GPIO_Config(GPIOA, gpioConfigStruct); // 应用GPIO配置// 设置PA2和PA3的复用功能为USART2GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_2, GPIO_AF_USART

; // PA2复用为USART2_TXGPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_3, GPIO_AF_USART

; // PA3复用为USART2_RX// 配置USART2USART_Config_T usartConfigStruct;USART_ConfigStructInit(usartConfigStruct); // 初始化默认配置usartConfigStruct.baudRate 115200; // 波特率115200usartConfigStruct.mode USART_MODE_TX_RX; // 收发模式usartConfigStruct.parity USART_PARITY_NONE; // 无奇偶校验usartConfigStruct.stopBits USART_STOP_BIT_1; // 1停止位usartConfigStruct.wordLength USART_WORD_LEN_8B; // 8位数据usartConfigStruct.hardwareFlow USART_HARDWARE_FLOW_NONE; // 无硬件流控制USART_Config(USART2, usartConfigStruct); // 应用USART2配置USART_Enable(USART

; // 使能USART2// 配置接收中断NVIC_EnableIRQRequest(USART2_IRQn, 1,

; // 使能USART2中断USART_EnableInterrupt(USART2, USART_INT_RXBNE); // 使能接收非空中断// 清除接收标志位确保无残留状态USART_ClearStatusFlag(USART2, USART_FLAG_RXBNE);}/* 发送单个字节通过USART2 */void USART2_SendByte(uint8_t byte){while (USART_ReadStatusFlag(USART2, USART_FLAG_TXBE) RESET); // 等待发送缓冲区空USART_TxData(USART2, byte); // 发送数据}/* 发送命令帧地址命令校验 */void SendCommand(uint8_t addr, uint8_t cmd){uint8_t checksum addr cmd; // 计算校验和USART2_SendByte(addr); // 发送地址USART2_SendByte(cmd); // 发送命令USART2_SendByte(checksum); // 发送校验和}/* 主机主函数 */int main(void){USART1_Init(); // 初始化调试串口USART2_Init(); // 初始化主机串口while (

{SendCommand(0x01, 0x

; // 向从机地址0x01发送查询命令0x10for (volatile int i 0; i 1000000; i); // 简单延时}}/* USART2中断处理函数 */void USART2_IRQHandler(void){if (USART_ReadIntFlag(USART2, USART_INT_RXBNE)) // 检查接收非空中断{rx_data[rx_idx] USART_RxData(USART

; // 读取接收数据if (rx_idx

// 收到完整响应地址状态校验{printf(从机 0x%02X: 状态0x%02X\n, rx_data[0], rx_data[1]); // 打印从机响应rx_idx 0; // 重置接收索引}}}#elif defined Slave/* 从机模式接收数据缓冲区、索引和首次接收标志 */volatile uint8_t rx_data[10], rx_idx 0;volatile uint8_t first_data_received 0;/* 初始化USART2作为从机串口 */void USART2_Init(void){// 使能USART2和GPIOA时钟RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_USART

; // 开启USART2时钟RCM_EnableAHB2PeriphClock(RCM_AHB1_PERIPH_GPIOA); // 开启GPIOA时钟// 配置PA2TX和PA3RXGPIO_Config_T gpioConfigStruct;gpioConfigStruct.mode GPIO_MODE_AF; // 复用功能模式gpioConfigStruct.otype GPIO_OTYPE_PP; // 推挽输出gpioConfigStruct.pin GPIO_PIN_2 | GPIO_PIN_3; // PA2和PA3gpioConfigStruct.pupd GPIO_PUPD_NOPULL; // 无上下拉gpioConfigStruct.speed GPIO_SPEED_100MHz; // 高速GPIO_Config(GPIOA, gpioConfigStruct); // 应用GPIO配置// 设置PA2和PA3的复用功能为USART2GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_2, GPIO_AF_USART

; // PA2复用为USART2_TXGPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_3, GPIO_AF_USART

; // PA3复用为USART2_RX// 配置USART2USART_Config_T usartConfigStruct;USART_ConfigStructInit(usartConfigStruct); // 初始化默认配置usartConfigStruct.baudRate 115200; // 波特率115200usartConfigStruct.mode USART_MODE_TX_RX; // 收发模式usartConfigStruct.parity USART_PARITY_NONE; // 无奇偶校验usartConfigStruct.stopBits USART_STOP_BIT_1; // 1停止位usartConfigStruct.wordLength USART_WORD_LEN_8B; // 8位数据usartConfigStruct.hardwareFlow USART_HARDWARE_FLOW_NONE; // 无硬件流控制USART_Config(USART2, usartConfigStruct); // 应用USART2配置USART_Enable(USART

; // 使能USART2// 配置接收中断NVIC_EnableIRQRequest(USART2_IRQn, 1,

; // 使能USART2中断USART_EnableInterrupt(USART2, USART_INT_RXBNE); // 使能接收非空中断// 清除接收标志位确保无残留状态USART_ClearStatusFlag(USART2, USART_FLAG_RXBNE);}/* 发送单个字节通过USART2 */void USART2_SendByte(uint8_t byte){while (USART_ReadStatusFlag(USART2, USART_FLAG_TXBE) RESET); // 等待发送缓冲区空USART_TxData(USART2, byte); // 发送数据}/* 进入静默模式 */void Enter_Silent_Mode(void){if (!USART_ReadStatusFlag(USART2, USART_FLAG_RXBNE)) // 确保无未处理接收数据{USART_EnableMuteMode(USART

; // 启用静默模式printf(从机进入静默模式\n); // 调试输出}}/* 退出静默模式 */void Exit_Silent_Mode(void){USART_DisableMuteMode(USART

; // 禁用静默模式USART2-CTRL1_B.TXEN 1; // 使能发送printf(从机退出静默模式\n); // 调试输出}/* USART2中断处理函数 */void USART2_IRQHandler(void){if (USART_ReadIntFlag(USART2, USART_INT_RXBNE)) // 检查接收非空中断{rx_data[rx_idx] USART_RxData(USART

; // 读取接收数据USART_ClearIntFlag(USART2, USART_INT_RXBNE); // 清除接收中断标志// 首次接收数据后进入静默模式if (!first_data_received){first_data_received 1; // 标记首次接收Enter_Silent_Mode(); // 进入静默模式rx_idx 0; // 重置接收索引return;}if (rx_idx 1 rx_data[0] ! 0x

// 检查地址非本机地址0x01{rx_idx 0; // 重置接收索引Enter_Silent_Mode(); // 重新进入静默模式}else if (rx_idx

// 收到完整命令地址命令校验{if (rx_data[0] 0x01 rx_data[1] 0x

// 验证地址和命令{uint8_t checksum rx_data[0] rx_data[1]; // 计算校验和if (rx_data[2] checksum) // 校验通过{Exit_Silent_Mode(); // 退出静默模式USART2_SendByte(0x

; // 发送地址USART2_SendByte(0x

; // 发送状态USART2_SendByte(0x01 0x

; // 发送校验和Enter_Silent_Mode(); // 重新进入静默模式}}rx_idx 0; // 重置接收索引}}}/* 从机主函数 */int main(void){USART1_Init(); // 初始化调试串口USART2_Init(); // 初始化从机串口while (

{// 空循环中断处理通信}}#endif#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) (__ARMCC_VERSION

)/*!* [urlhome.php?modspaceuid247401]brief[/url] Redirect C Library function printf to serial port.* After Redirection, you can use printf function.** param ch: The characters that need to be send.** param *f: pointer to a FILE that can recording all information* needed to control a stream** retval The characters that need to be send.** note*/int fputc(int ch, FILE* f){/* send a byte of data to the serial port */USART_TxData(DEBUG_USART, (uint8_t)ch);/* wait for the data to be send */while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) RESET);return (ch);}#elif defined (__GNUC__)/*!* [urlhome.php?modspaceuid247401]brief[/url] Redirect C Library function printf to serial port.* After Redirection, you can use printf function.** param ch: The characters that need to be send.** retval The characters that need to be send.** note*/int __io_putchar(int ch){/* send a byte of data to the serial port */USART_TxData(DEBUG_USART, ch);/* wait for the data to be send */while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) RESET);return ch;}/*!* [urlhome.php?modspaceuid247401]brief[/url] Redirect C Library function printf to serial port.* After Redirection, you can use printf function.** param file: Meaningless in this function.** param *ptr: Buffer pointer for data to be sent.** param len: Length of data to be sent.** retval The characters that need to be send.** note*/int _write(int file, char* ptr, int len){int i;for (i 0; i len; i){__io_putchar(*ptr);}return len;}#else#warning Not supported compiler type#endif简单提一下我前面提到了配置设备地址时是需要配置USART_CTRL2的ADDR这一位的我这里并没有配置这个地址是因为手册提到ADDR[3:0]仅在多处理器通信的静默模式下生效。

而我们的场景是单主机单从机软件地址检查已足够硬件匹配非必须。

3 运行效果

烧录代码- 主机代码烧到一块APM32连接RS-485模块和USB转串口接电脑,逻辑分析仪。

- 从机代码烧到另一块APM32连接RS-485模块。

连接硬件- RS-485模块的A、B线连接两板GND共地。

- 电脑打开串口助手波特率115200查看主机输出。

运行- 主机每秒发送命令[0x01][0x10][0x11]地址0x01命令0x10校验0x11。

- 从机在静默模式下接收检查地址0x01匹配后退出静默模式回复[0x01][0x11][0x12]地址0x01状态0x11校验0x12。

- 从机发完响应后重新进入静默模式。

- 串口助手显示From Slave 0x01: Status0x11From Slave 0x01: Status0x11- 逻辑分析仪也可抓取对应波形如下。

6.

常见问题Q1为啥要用静默模式不用行不行不用静默模式也可以但多设备通信容易乱数据冲突。

静默模式像“纪律委员”让从机轮流说话避免抢话。

点对点通信单片机连电脑一般不用。

Q2静默模式会不会漏收数据不会静默模式只禁用TXRX正常。

只要缓冲区够大或用中断及时读数据不会丢。

Q3怎么确认静默模式生效用示波器看TX引脚静默时无信号或固定高/低电平。

或加调试LED进入静默时点亮。

Q4从机为啥不一直静默从机需要响应主机命令所以匹配地址后退出静默发送数据。

发完再静默保持总线安静。

Q5RS-485模块有啥

注意事项确保A、B线接对主机A连从机AGND共地。

RS-485是半双工静默模式配合DE信号驱动使能防止冲突。

Q6静默和休眠的区别特性静默模式休眠模式目标禁用串口TX保持RX防冲突或支持协议全局省电CPU和外设停工串口状态TX禁用RX正常串口通常全关可配置唤醒CPU状态正常运行停止运行等待唤醒功耗仅TX略省电整体不变极低uA级典型场景多设备通信RS-

Modbus低功耗待机传感器

7.

总结-静默模式是串口的“闭嘴”功能禁用TX只接收RX适合多设备通信。

-原理通过寄存器如APM32的MUTE位控制TX禁用RX正常配合地址匹配。

-用途避免数据冲突、省电、支持协议如Modbus、调试监听。

-例程主机-从机通信展示了静默模式的实际效果从机只在被点名时“开口”。

总结而言静默模式是串口通信中一项关键的智能控制手段它帮助设备在复杂的通信环境中实现精准、高效且安全的数据交换。

本文例程USART_MUTE_EXAMPLE.zip(

7

17 KB, 下载次数:

---------------------作者DKENNY链接https://bbs.21ic.com/icview-

-

html来源21ic.com此文章已获得原创/原创奖标签著作权归21ic所有任何人未经允许禁止转载。

吃瓜大赛,爆料聚集地-吃瓜大赛,爆料聚集地应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123