
简介面向嵌入式初学者与工业通信开发者的STM32F103 RS485通信学习文档围绕半双工总线原理与STM32的UART外设展开帮助读者理解如何通过GPIO控制DE使能线完成收发模式切换并解决土壤温湿度传感器数据读取与显示的实际问题。文档从RS485协议基础、STM32F103ZET6引脚说明讲起给出RS485_Init、RS485_Send_Data、RS485_Receive_Data等函数的实现思路以及主函数中发送读取指令、延时等待、解析响应并送LCD显示的完整流程同时涉及中断优先级配置与接收缓冲区定义。读者可据此掌握波特率设置、帧格式判断、收发时序控制、数据换算与常见通信异常排查方法并将代码结构迁移到其他带UART的MCU平台形成从硬件连线到软件流程的完整认识。压缩包共1个docx文件约1.22MB以图文说明和代码清单形式组织便于对照阅读与移植。已有2629人学习适合作为RS485入门、课程设计与传感器调试的参考资料。1. 从一份 docx 说起STM32F103 读 RS485 土壤温湿度卡点从来不在协议本身拿到手的这份 STM32F103 RS485 传感器文档真正有用的东西就三块一张 RS485 通信协议原理图、一段跑在 USART2 上的收发代码、一份土壤温湿度传感器的寄存器读法。不少人第一次啃 485 通讯方式原理会把注意力全放在帧格式上等板子真接上电才发现拦路的是另外三件事——方向控制脚到底接的哪个 PG 口、CRC 两个字节谁在前、以及除以 100 还是除以 10。这套代码的主循环朴素得很发 8 字节读指令等 300ms收 9 字节响应把第 4、5 字节拼成湿度、第 6、7 字节拼成温度最后丢给 TFTLCD 显示。适合手上有 F103ZET6 开发板、正在做农业环境采集或工业小节点、想把 485 一次跑通的人。下面按电气层、寄存器层、代码层、现场层拆开讲。2. RS485 电气层与 Modbus 帧先分清哪一层在干活2.1 差分总线为什么能拉长距离以及 A/B 线和终端电阻RS485 用的是两根线 A、B 之间的电压差传信号不是对地电压。接收端只判断 (A-B) 的极性共模干扰同时叠加到两根线上做差之后就被抵消掉这就是它抗干扰的根子。代价是这套逻辑必须有一个相对稳定的共模参考所以两端设备的地要连起来——很多人图省事只接 A、B 两根现场跑几天开始随机丢帧把地线补上就好了。常见做法是双绞线里留一根做 GND屏蔽层单端接地不要两头都接两头都接反而形成地环路。终端电阻是第二个容易漏的点。总线两端各挂一个 120Ω中间节点不挂。短距离、低波特率9600 这种在实验台上不挂也能跑一旦拉到几十米以上信号在末端反射示波器上看是一串振铃MCU 侧表现就是偶发帧错误、CRC 对不上。我在实验室用一米杜邦线验证逻辑从来不加终端电阻到了现场布线第一件事就是量两端电阻断电状态下 A、B 之间应该量到 60Ω 左右两个 120Ω 并联量不到说明要么没装要么中间断了。还有偏置电阻。总线空闲时差分电压接近 0接收器输出可能在高低之间抖动产生一个假的起始位。规范做法是 A 上拉、B 下拉让空闲态确定。不少现成的 485 模块已经把偏置和终端做进去了用之前看一眼模块丝印或者背面电阻别重复加。A、B 反接是最常见的新手事故。反了不会烧现象是发得出去、一个字节都收不回来或者收到全 0xFF。现场没有示波器的时候最快的排查办法就是把 A、B 对调试一次三十秒的事。2.2 拆开{0x01,0x03,0x00,0x06,0x00,0x02,0xC4,0x38}这 8 个字节这帧是标准的 Modbus-RTU 读保持寄存器指令逐字节拆是这样的字节序号值含义00x01从站地址总线上 1 号传感器10x03功能码读保持寄存器20x00起始寄存器地址高字节30x06起始寄存器地址低字节即 0x000640x00寄存器数量高字节50x02寄存器数量低字节即读 2 个60xC4CRC16 低字节70x38CRC16 高字节两个关键约定。一是寄存器地址和数量都是高字节在前这是 Modbus 的规矩跟 CRC 正好相反。二是 CRC 低字节在前发送很多人按「高字节在前」的直觉把 CRC 写反从站直接不响应——它连校验都不过按协议就是静默丢弃你这边看到的现象是发送正常、接收为空很容易误判成硬件坏了。起始地址 0x0006 读两个寄存器这是这款土壤温湿度传感器的寄存器映射湿度占 0x0006、温度占 0x0007也有厂家把温度放前面或者起始地址从 0x0000 开始。换一个型号这 8 个字节里第 2、3 字节和第 6、7 字节都得跟着变。所以我不建议把这帧硬编码在 main 里而是写成一个拼帧函数地址、数量、CRC 现算/* 组一帧 Modbus-RTU 读保持寄存器指令返回总长度 */ u8 Modbus_Build_Read(u8 *out, u8 slave, u16 reg, u16 num) { u16 crc; out[0] slave; out[1] 0x03; out[2] reg 8; /* 寄存器地址高字节在前 */ out[3] reg 0xFF; out[4] num 8; /* 寄存器数量高字节在前 */ out[5] num 0xFF; crc Modbus_CRC16(out, 6); out[6] crc 0xFF; /* CRC 低字节先发 */ out[7] crc 8; return 8; }参数说明slave是从站地址出厂一般为 1多节点时靠它区分reg是起始寄存器num是连续读几个读 2 个就是 4 字节数据。改成读别的传感器只要改reg和numCRC 自动跟上不用再手算。2.3 DE/RE 方向控制半双工的本质是一个时间片问题RS485 是半双工同一时刻总线上只能有一个说话的人。收发器把这件事交给两根控制线DE 高电平使能驱动发送RE 低电平使能接收。市面上大多数 8 脚模块把 DE 和 RE 短接在一起只用一根 IO 控制——这份代码里就是RS485_TX_EN宏定义写的是PGout(7)。方向切换的时序是 485 最容易翻车的地方核心是「什么时候可以拉低 DE」。发送数据时你调USART_SendData只是把字节塞进了数据寄存器它还要经过移位寄存器一位一位往外吐。这时候如果立刻把 DE 拉低最后一个字节的高位就被截断了从站收到一个残缺帧CRC 不过静默丢弃。判断「真正发完」要看 TC 标志Transmission Complete不是 TXETransmit Data Register Empty。TXE 置位只说明数据寄存器空了、可以塞下一个字节此时最后一个字节还在移位寄存器里往外爬。这份代码在RS485_Send_Data末尾是这么写的循环里先等 TC 再 SendData循环结束后再等一次 TC然后才RS485_TX_EN0。最后那次等待是对的等到了才切回接收。严格来说等完 TC 还可以再空转一两个微秒因为 TC 在停止位开始时就置位了保险起见留一点余量。我用 9600 波特率时习惯在拉低 DE 前加一句delay_us(50)几十个微秒不影响吞吐。切回接收之后还有个问题自己刚发出去的数据会被自己听到吗会。半双工收发器在发送时如果 RE 还是使能的A/B 上的差分信号会从 RO 脚回灌到 MCU 的 RX触发 RXNE 中断。所以正确的做法是发送期间关掉 RXNE 中断或者干脆在发送完成后把接收缓冲区清一遍再开。这份代码里RS485_Send_Data结尾有一句RS485_RX_CNT0就是在做这件事但它放在切回接收之前严格讲应该是切回接收、清标志、再清计数顺序别搞反。3. 接线与 USART2 初始化把 PA2、PA3、PG7 落到寄存器上3.1 一张接线表把「能通」的前提先摆平F103ZET6 的 USART2 默认引脚就是 PA2TX、PA3RX不需要重映射。接到 485 收发器模块上DI 接 PA2RO 接 PA3DE/RE 接 PG7或者 PG3取决于你统一到哪个VCC 接 3.3V 或 5V 看模块规格GND 必须共。STM32 引脚收发器引脚说明PA2DIUSART2_TX复用推挽输出PA3ROUSART2_RX浮空或上拉输入PG7DE RE方向控制推挽输出高发送低接收3.3V / 5VVCC按模块电平选3.3V 模块直连GNDGND必须共地别省—A接传感器 A双绞线一对—B接传感器 B与 A 同一对双绞传感器那头一般给四根线电源正、电源负、A、B。供电常见是 5V 或 12V别拿 3.3V 硬喂 12V 的型号上电前用万用表量一下传感器标签。A、B 用双绞线的一对电源另走一对别把电源和信号绞在一起。还有一个不起眼但要命的点uart_init(9600)初始化的是 USART1代码注释写「使 LCD 正常工作」其实 LCD 走的是 FSMC 并口跟串口没关系USART1 在这里就是留给 printf 调试的。USART2 才是 485 通道。两个串口都开 9600 没问题但别在调试时把 printf 的输出和 485 的收发搞混我曾经因为把调试打印接到 USART2 上一边打日志一边读传感器总线直接被自己搅死。3.2RS485_Init逐段拆时钟、GPIO、USART、NVIC 四步初始化函数的骨架是标准的四段式但这几行里有几个地方必须改。void RS485_Init(u32 bound) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* 1. 时钟GPIOA/PG 在 APB2USART2 在 APB1别挂错总线 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG | RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); /* 2. PA2 USART2_TX复用推挽 */ GPIO_InitStructure.GPIO_Pin GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); /* 3. PA3 USART2_RX浮空输入 */ GPIO_InitStructure.GPIO_Pin GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); /* 4. 方向控制脚这里必须和 RS485_TX_EN 的宏指向同一个口 */ GPIO_InitStructure.GPIO_Pin GPIO_Pin_7; /* 原文写的是 Pin_3 */ GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOG, GPIO_InitStructure); /* 5. USART29600 8N1 无流控 收发模式 */ USART_InitStructure.USART_BaudRate bound; 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(USART2, USART_InitStructure); USART_Cmd(USART2, ENABLE); USART_ClearFlag(USART2, USART_FLAG_TC); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); /* 6. NVIC分组 2 下抢占优先级范围 0~3这里给 3 是最低 */ NVIC_InitStructure.NVIC_IRQChannel USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 3; NVIC_InitStructure.NVIC_IRQChannelSubPriority 2; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); RS485_TX_EN 0; /* 上电默认接收 */ }第 4 步是我改动过的地方原文头文件里#define RS485_TX_EN PDout(7)而初始化函数把GPIO_Pin_3配到 GPIOG 上注释还写「CS-485」。宏指向 PG7、初始化配 PG3两边对不上结果就是 PG7 一直是复位后的浮空状态DE 永远不动。现象很有迷惑性USART2 的 TX 引脚上确实有波形用示波器量 PA2 能看到数据但 A/B 上什么都没有因为驱动器根本没使能。这类「宏和初始化各说各话」的问题接手别人工程时第一件事就是全文搜一遍引脚宏逐个对。第 6 步的 NVIC 分组在main里设成NVIC_PriorityGroup_2也就是 2 位抢占 2 位响应抢占优先级取值范围 0~3。给 USART2 配 3 意味着它是最低优先级能被任何更高优先级的中断打断。对 485 来说这不算理想——中断里要判帧、要存缓冲被打断太久可能丢字节。我一般把它提到 1 或者 2把最高优先级留给真正实时的东西。顺带说一下波特率精度。F103 主频 72MHz 时 APB1 是 36MHzUSART2 挂 APB19600 波特率的分频值 36 000 000 / (16 × 9600) 234.375整数部分 2340xEA小数部分 0.375×16 6合起来 BRR 0xEA6。实际波特率 36M/(16×234.375) 误差几乎为零所以 9600 在这个配置下非常稳。如果主频改成 48MHzAPB1 变成 24MHz分频值 156.25误差也在可接受范围。改主频之后如果通信开始丢帧先算一遍 BRR 误差超过 2% 就别指望长距离可靠。3.3 发送、接收与中断三种写法各自的代价发送函数最直白拉高 DE、逐字节发、等 TC、拉低 DEvoid RS485_Send_Data(u8 *buf, u8 len) { u8 t; RS485_TX_EN 1; /* 切发送 */ for (t 0; t len; t) { while (USART_GetFlagStatus(USART2, USART_FLAG_TC) RESET); USART_SendData(USART2, buf[t]); } while (USART_GetFlagStatus(USART2, USART_FLAG_TC) RESET); /* 等最后一个字节移完 */ RS485_RX_CNT 0; /* 丢弃发送期间可能收到的自发数据 */ RS485_TX_EN 0; /* 切回接收 */ }接收有三种常见写法各有取舍。第一种是纯轮询靠「空闲超时」判帧结束也就是这份代码里的RS485_Receive_Data先记下当前计数等 10ms再看计数有没有变没变就认为一帧收完。简单、好懂代价是主循环被阻塞 10ms。第二种是纯中断每来一个字节进一次中断把数据塞进环形缓冲主循环里判长度。实时性好但要自己处理缓冲区溢出和帧边界。第三种是中断 定时器用定时器做 3.5 字符超时最贴近 Modbus 规范代码量也最大。这份代码走的是第一种但中断服务函数里还留着一段环回测试void USART2_IRQHandler(void) { u8 res; if (USART_GetITStatus(USART2, USART_IT_RXNE) ! RESET) { res USART_ReceiveData(USART2); RS485_RX_BUF[RS485_RX_CNT] res; /* 原文缺这一句数据没进缓冲 */ if (RS485_RX_CNT 64) RS485_RX_CNT 0; /* 下面这段是出厂环回自测接真实传感器必须删掉 */ /* RS485_TX_EN 1; delay_ms(1); USART_SendData(USART2, res); ... */ } USART_ClearFlag(USART2, USART_FLAG_TC); }原文的中断里收到一个字节就拉高 DE 把它原样发回去这在家门口做环回测试时没问题——发什么收什么看着挺正常。一旦挂到真实总线上主循环刚发出读指令收发器把差分信号转回 RO中断立刻触发程序把 DE 拉低又拉高在总线上插进一段本不属于这帧的数据传感器那边的 CRC 校验直接失败。这个坑我踩过现象是单独调试时一切正常接上第二个节点就开始随机不响应查了两天才定位到中断里那段 echo。还有一个细节原文的USART2_IRQHandler里读出来的res直接赋给USART2-DR并没有存进RS485_RX_BUF。也就是说接收缓冲永远是空的RS485_Receive_Data里rxlen永远是 0主循环里if(len)永远不成立LCD 上永远是上一次的值。这个漏洞很隐蔽因为它不报错、不死机只是没数据。补上那一句RS485_RX_BUF[RS485_RX_CNT] res;才算真正通了。4. 帧解析与换算9 个字节怎么变成屏幕上的两个数4.1 响应帧结构为什么 len 要卡在 9读 2 个保持寄存器的正常响应是 9 个字节序号内容本例取值说明0从站地址0x011功能码0x032数据字节数0x04两个寄存器共 4 字节3寄存器 1 高字节湿度高位4寄存器 1 低字节湿度低位5寄存器 2 高字节温度高位6寄存器 2 低字节温度低位7CRC 低字节—8CRC 高字节—主循环里那句if(len9) len9;是在做防御如果总线上有干扰多收了几个字节只看前 9 个。这个处理方向是对的但不够——真正该做的是先校验 CRCCRC 不过就整帧丢弃重发而不是拿一堆垃圾数据去算温湿度。我在现场见过因为没做 CRC 校验屏幕上的温度在 -20℃ 和 60℃ 之间乱跳最后发现只是线缆接头氧化导致偶发误码。解析的时候还要检查第 1 字节功能码。如果传感器回了 0x830x03 的最高位置 1说明它收到了指令但拒绝执行后面那个字节是异常码通常是寄存器地址越界。这时候去算温湿度毫无意义应该把异常码打出来看。4.2 CRC16 校验一段 15 行就能写完的代码Modbus 的 CRC16 用的是多项式 0xA0010x8005 的反序初值 0xFFFF输入输出都不反转结果低字节先发。u16 Modbus_CRC16(u8 *buf, u8 len) { u16 crc 0xFFFF; u8 i, j; for (i 0; i len; i) { crc ^ buf[i]; for (j 0; j 8; j) { if (crc 0x0001) { crc 1; crc ^ 0xA001; /* 多项式反序 */ } else { crc 1; } } } return crc; /* 返回值低字节先发 */ }校验响应帧时把前len-2个字节喂进去算出来的 CRC 跟帧尾两个字节比对注意比对时也要按低字节在前拼(buf[len-1] 8) | buf[len-2]。这两个字节的顺序我至少写错过三次每次都是「明明代码逻辑没错就是不通过」血泪经验是先在纸上把期望的 CRC 值算出来再对照代码输出。逐位算法在 72MHz 的 F103 上算 9 个字节大概几十微秒完全够用。如果总线上节点多、轮询频率高可以换成查表法把 256 项表放 Flash 里速度能快一个数量级代价是多占 512 字节。4.3 换算与显示/100、/10和那个装不下的 u8主循环里这两行是整份代码里最需要警惕的地方WD (rs485buf[3] * 256 rs485buf[4]) / 100; /* 湿度 */ SD (rs485buf[5] * 256 rs485buf[6]) / 10; /* 温度 */WD、SD在文件开头被声明成u8取值范围 0~255。湿度 0~100% 没问题温度如果是 -20℃ 到 60℃按「值 温度 × 10」的约定原始寄存器值在 -200 到 600 之间用无符号 16 位读出来再除以 10负数直接变成一个巨大的正数u8再截断一次屏幕上就是个随机的两位数。所以温度这一路必须改成有符号int16_t temp_raw (int16_t)((rs485buf[5] 8) | rs485buf[6]); float temp temp_raw / 10.0f; /* 负数也能正确还原 */湿度那一路除以 100 也要打问号。多数土壤湿度传感器给的原始值是 0~1000 对应 0~100%RH那就该除以 10也有型号直接给 0~100 的百分比那就不用除。这份代码除 100说明它对应的那款传感器输出的是 0~10000 的扩大值。换型号必须先翻手册的寄存器表看清楚「分辨率」和「单位」两栏别照抄除数。验证换算对不对有个不花钱的办法把探头插进一杯清水湿度应该逼近饱和值通常是 100% 附近再插进一盆彻底晾干的土应该接近下限。两个点对上了中间线性基本没问题。温度这一路拿手握住探头读数应该在十几秒内往体温方向爬能爬说明有符号处理是对的。这比反复读代码快得多。显示那两行LCD_ShowxNum(30,190,WD,4,24,0)的参数依次是 x 坐标、y 坐标、数值、显示位数、字体大小、显示模式。数值是整数所以负温度在屏幕上会显示成奇怪的字符要么先用sprintf拼成字符串再显示要么单独处理符号位。这类小问题不影响通信但会让现场调试的人怀疑数据是错的。5. 现场排查五条从「读不到」到「数在跳」的踩坑记录5.1 硬件与电气层的三个坑现象发送时 PA2 有波形A/B 上一片死寂传感器完全没反应。原因RS485_TX_EN的宏指向 PG7RS485_Init里却把 PG3 配成了推挽输出PG7 始终是复位后的浮空态DE 从来没被拉高过。也可能是反过来——宏指向的端口时钟压根没使能。 解决全文搜索引脚宏确认宏、时钟使能、GPIO_Init、实际接线四处指向同一个引脚。改完拿万用表量 DE 脚发送瞬间应该能看到 3.3V 跳变看不到就继续查。现象短线上实验台一切正常拉到三十米外开始随机丢帧CRC 十次错三次。原因总线两端没挂 120Ω 终端电阻信号在末端反射形成振铃或者只接了 A、B 没接 GND两端共模电平漂移。 解决断电量 A、B 之间电阻正常应接近 60Ω。补上终端电阻和地线屏蔽层改成单端接地。还不行就降波特率到 4800 试一次能通说明是线缆质量或长度超了。现象换了个厂家的传感器接线一样、地址一样就是收不到响应。原因A、B 定义反了不同厂家对 A/B 的标注不一致或者传感器供电不足12V 型号只给了 5V。 解决先对调 A、B 试一次三十秒能排除。再用万用表量传感器供电脚的实际电压对着标签核对。5.2 软件与协议层的两个坑现象接上真实传感器后单独调试正常一挂到有两个节点的总线上就互相干扰。原因USART2_IRQHandler里保留了环回自测代码收到一个字节就拉高 DE 原样回发等于在总线上抢话。 解决把中断里所有发送动作删掉中断只负责收字节、存缓冲、清标志。方向切换只在RS485_Send_Data里做一处控制别分散。现象LCD 上温湿度永远是 0但示波器能看到传感器确实回了 9 个字节。原因中断里读出的res没有存进RS485_RX_BUF或者存了但RS485_RX_CNT没自增RS485_Receive_Data里rxlen恒为 0if(len)永远不成立。 解决在中断里补上存缓冲和计数自增加一个溢出保护计数超过 64 归零。调试时可以在if(len)分支里点个灯灯不亮就是这条链路断在缓冲上。6. 多节点轮询与超时重试把一次性 demo 改成能跑现场的东西单节点跑通只是及格线。真实场景里一条总线上挂四五个传感器很常见主循环里那种「发一帧、delay 300ms、收一帧、delay 3000ms」的写法节点一多就乱套——你在等 A 的 3 秒里B 的数据早就该采了。我一般会把节点做成一张表主循环按表轮询每个节点独立记超时和失败次数typedef struct { u8 addr; /* 从站地址 */ u16 reg; /* 起始寄存器 */ u8 fail; /* 连续失败次数 */ u16 hum; /* 最近一次湿度 ×10 */ int16_t temp; /* 最近一次温度 ×10 */ } Node_t; static Node_t nodes[] { {1, 0x0006, 0, 0, 0}, {2, 0x0006, 0, 0, 0} }; void Poll_All(void) { u8 i, len; for (i 0; i sizeof(nodes)/sizeof(nodes[0]); i) { Modbus_Build_Read(txbuf, nodes[i].addr, nodes[i].reg, 2); RS485_Send_Data(txbuf, 8); if (RS485_Wait_Frame(rxbuf, len, 200) len 9 Modbus_CRC16(rxbuf, 7) ((rxbuf[8] 8) | rxbuf[7])) { nodes[i].hum ((rxbuf[3] 8) | rxbuf[4]); nodes[i].temp (int16_t)((rxbuf[5] 8) | rxbuf[6]); nodes[i].fail 0; } else { if (nodes[i].fail 3) nodes[i].fail 3; /* 标记离线UI 上显红 */ } } }关键在RS485_Wait_Frame这个带超时的收帧函数它内部不用delay_ms(10)死等而是用定时器打时间戳超时 200ms 就返回失败。9600 波特率下 9 个字节大约 9.4ms200ms 是二十倍余量够宽松也不会拖死轮询。失败三次就标记离线UI 上把这一路灰掉别让一个坏节点拖慢整条总线。还有一个现场习惯每次改完接线或者换传感器先只挂一个节点跑十分钟确认 CRC 零错误再逐个往上加。我吃过一次亏五个节点一起上电数据乱跳最后发现是其中一个模块的 A/B 焊反了它在总线上持续输出反向电平把另外四个的通信全带偏。从那以后我每次上电前都强制走一遍「单节点 万用表量终端电阻 读三次 CRC 全对」的流程宁可慢十分钟不熬两个通宵。这份文档里的代码骨架和引脚图配上传感器手册的寄存器表一起看才有意义先把RS485_Init和RS485_Send_Data抄进工程跑通再回头读剩下的部分顺序别反。希望帮到你。本文还有配套的精品资源点击获取