多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

RL78串口配置实战:从SAU到寄存器的完整调试指南

RL78串口配置实战:从SAU到寄存器的完整调试指南 做瑞萨RL78项目有一年多了从最初对着数据手册翻寄存器翻到怀疑人生到后来能一口气把串口调通中间踩过的坑确实不少。串口这东西在嵌入式开发里属于“不起眼但离不开”的角色RL78芯片的串口配置和其它MCU有点不太一样尤其是它那个串行阵列单元SAU的抽象方式第一次接触的人很容易绕晕。这篇文章我打算把基于RL78芯片的串口使用配置整个捋一遍从时钟到寄存器再到实际代码和调试尽量写得像带人走一遍完整流程而不是干巴巴贴数据手册。刚上手RL78的、或者想从其它平台转过来的朋友应该都能少走点弯路。1. 先搞明白RL78串口到底是什么位置的角色很多做STM32过来的朋友对串口的印象是先找UART外设然后配GPIO复用、配波特率、开中断基本一套熟路。但RL78不一样它把串口、SPI、I2C这类外设统一收进了一个叫SAUSerial Array Unit的模块里所以你在数据手册里找“UART”很可能找半天找不到得先找到SAU这个总入口。1.1 弄清SAU和“串口外设”的关系SAU本质上是一个通用的串行通信单元里面分了若干个通道Channel。在RL78/G13、G14这些常见系列上SAU通常有4个通道每个通道都可以独立配置成UART、CSI也就是SPI、简易I2C等不同的串行协议。这就意味着你要用串口实际要做的事情是“把SAU通道0配成UART模式”而不是像有些MCU那样直接打开一个名叫UART1的外设。这个设计有个好处就是同一套寄存器框架可以复用到多类通信协议上但你学习和调试的曲线也变陡了。最直观的感觉是写配置代码时SMR、SCR、SDR这一组寄存器配UART和配SPI都要用只是其中某些位的含义完全不同。所以第一步不能急先把通道和模式绑定这件事记牢后面才不容易错。1.2 选通道前必须先看引脚和功能分配通道选哪个不是拍脑袋决定的得看芯片的引脚功能复用表。RL78的引脚复用控制比STM32要“土”一些STM32有AFIO或AFR寄存器可以通过软件把某个引脚映射到特定外设RL78则更像传统的单片机引脚功能很多时候是硬件定死的能做的只是通过寄存器把某个引脚从普通的数字IO切换成外设功能引脚而且切换方向是固定的。举个例子在很多RL78/G13系列里UART0的发送引脚通常固定在P10接收引脚固定在P11你如果想用P3.0去做UART0的发送多半是做不到的。所以选通道之前先查数据手册里那个“Pin Function List”表格确认你需要的引脚具备UART功能然后再开始配寄存器。我在项目里踩过一次这个坑板子上UART0的P10、P11被一个外部传感器占用了想着能不能换到别的引脚结果翻了半天手册发现RL78/G13上能用的UART通道就那几个固定引脚组合最后只能改板子教训很深刻。提示在选定串口通道前先确认引脚功能复用表上有没有这个通道的输入和输出引脚且确认这两个引脚没有被板级其它外设占用这个顺序不能乱。2. 串口初始化前先做好这三件基础准备很多刚上手的人一上来就填寄存器结果怎么调波形都不对。其实串口能正常工作除了寄存器本身要配好之外还有三个非常容易被忽略的准备工作时钟树、引脚复用和中断入口。这三件事不做好后面全是白搭。2.1 时钟树串口的工作时钟从哪来RL78的串口波特率发生器使用的时钟源和CPU主频不一定相同。这一点很关键。在RL78系列里SAU的计数时钟来自fCLK而fCLK又会被分频为fMCK和fCLK后再给各个通道使用。中文数据手册里常见的写法是说UART波特率可以基于fCLK来算但不同芯片、不同配置下fCLK的值会有区别比如内建高速振荡器频率是8MHz经过倍频后CPU可能跑32MHz但SAU时钟能否拿到32MHz又取决于寄存器里的分频配置。我自己的习惯是先在项目代码顶部把时钟配置固定下来然后单独打印或者用一个全局变量把系统时钟值存下来再用这个值去算波特率而不是脑子里想当然觉得一定是多少。实际项目中因为时钟切换导致的串口乱码现象非常常见尤其是瑞萨的Code Generator工具生成初始化代码时系统时钟那块配置最好保持不动否则稍改一个分频系数串口的波特率就会跟着跑偏而且很难查。2.2 引脚复用寄存器没开功能串口永远没波形RL78的引脚默认很多是数字IO甚至还有一部分是高阻输入你需要把对应的输出锁存寄存器P和方向寄存器PM设好同时把外设输入输出控制寄存器比如Serial Input Switch Control Register相关或PMxx、Pxx配合的功能切换位配置成“引脚连接到SAU通道”。这一步最容易漏。具体来说一般需要把TXD引脚的方向位设为输出也就是PMx.x置0然后把Px.x写1还是写0要看引脚空闲电平需求。RXD引脚方向位设为输入即PMx.x置1。但这只是把引脚设置成了普通IO的输入输出方向真正把它们从IO切换到UART功能还得看一个叫“引脚功能选择”的位。以UART0为例通常在PMC寄存器或者某些系列的PIOR寄存器里可以控制是否把P10/P11分配给串口功能。不同的芯片型号命名不太一样一定基于你自己用的那个型号的数据手册来查。如果发现示波器看不到TXD的波形优先回来看这个复用开关开了没有比反复调波特率寄存器要高效得多。2.3 中断向量表打开发送与接收的中断入口RL78的串口中断在中断向量表里有单独入口比如INTST0是UART0发送结束中断INTSR0是UART0接收完成中断还有INTSRE0是接收错误中断。注意这三个中断是独立的和某些MCU把收发错误合在一个中断里不太一样。如果写代码时只调用了接收中断但忘了开发送中断入口发送虽然也能出数据但你可能拿不到“发送完成”的信号影响后续逻辑。另外RL78的中断使能有两层一层在外设寄存器里比如开启接收完成标志中断将数据读出来另一层在CPU的中断使能寄存器里也就是IE或MK寄存器。在RL78里对应的是Interrupt Mask Register和Interrupt Request Flag Register你需要把目标中断的掩码清0允许CPU响应同时要在主程序中用__EI()指令打开全局中断。我见过有人在接收中断里加了while(1)等待数据然后使用的全局中断根本没有打开结果程序卡得死死的排查半天才知道是全局中断没开。3. 核心寄存器配置实操一步一步来准备工作做完以后再去看寄存器就清晰多了。RL78的SAU在UART模式下核心寄存器其实就四个家族SMR、SCR、SDR、SSR再辅以SIR状态标志清除寄存器、SIO和SO等控制寄存器。3.1 UART模式下的寄存器组合SMR、SCR、SDR、SSR这四个寄存器各管一件事SMRSerial Mode Register负责配置通道的工作模式、数据位宽、是否带奇偶校验、停止位长度等基本协议参数。SCRSerial Control Register负责操作模式的开关、发送接收使能、时钟相位极性等运行时控制。SDRSerial Data Register比较特殊它是一个“多功能寄存器”低16位用于存放发送或接收的数据高8位放的是波特率分频因子。读写这个寄存器时要特别小心别把分频系数覆盖掉了。SSRSerial Status Register是状态寄存器里面有发送完成标志TSF、接收完成标志TSI或缓冲区空标志等查询和中断都要看它。在实际配置时顺序也有讲究。通常先把SMR设定为UART模式对应的值然后设置SDR里的波特率因子再配置SCR使能发送和接收最后清一次错误标志。如果先使能了收发再去改SMR很多位是写不进去或者需要重新触发通道初始化才能生效的。3.2 波特率的计算方法和选值技巧波特率是串口配置里最核心的参数。RL78的UART波特率计算公式大概是波特率 fCLK / (2 × (SDR低位的波特率因子 1) × 分频系数)具体分频系数受SMR的低两位控制比如选择fCLK输入还是fMCK输入、是否再除以2等所以要先把时钟源选清楚。举个例子假设fCLK是32MHz我想得到一个115200bps的波特率直接用公式反推因子值(fCLK / 波特率) 32,000,000 / 115,200 ≈ 277.78由于是整数分频的关系实际可设置的分频因子会让实际波特率略有偏差。在115200这样比较高的速率下RL78一般可以做得比较接近误差大约在百分之零点几完全够用。但如果波特率选得很贪心比如一次配到1Mbps以上误差就可能被放大了需要注意。我自己的经验是算完波特率分频因子后去数据手册的“波特率设置示例表”里核对一下。瑞萨手册经常会附一张常用波特率对应的SDR推荐值和误差表确认误差在±2%以内再继续。因为RL78很多型号没有小数分频和自动波特率修正全凭整数因子凑凑不好会出现偶发乱码。3.3 完整初始化C代码示例以下以RL78/G13系统时钟32MHzUART0波特率1152008位数据、无校验、1位停止位为例写一段接近实际项目的初始化代码。注意不同系列寄存器名称可能略不同但思路通用。void R_UART0_Create(void) { // 1. 引脚复用把 P10 配置为 TXD0P11 配置为 RXD0 // 具体位名称以数据手册为准下面是逻辑示意 PM1 0xFC; // P10、P11 方向P10输出P11输入 P1 | 0x04; // P10 默认空闲电平为高 // 设置引脚功能切换到串口 PIOR 0xFE; // 假设此位把P10/P11从IO切换到UART0 // 2. 停止SAU通道0操作 ST0 | 0x01; // 等待通道停止完成 while ((ST0 0x01) ! 0x01) {} // 3. 配置SMRUART模式8位数据无校验1个停止位 SMR0 0x0022; // 典型值UART模式 时钟分频选择 // 4. 配置SDR波特率因子 数据缓冲 // 波特率因子计算后写入高字节低字节这里放0 SDR0 0x0D00; // 此值为示例需按实际波特率计算 // 5. 配置SCR使能发送和接收 SCR0 0x4017; // 使能TXD、RXD设置空闲电平高LSB first // 6. 清错误标志 SIR0 0x0007; // 7. 打开接收中断和错误中断发送中断按需打开 MK0 0xFE; // 取消INTSR0屏蔽 MK0 0xFD; // 取消INTST0屏蔽 MK0 0xFB; // 取消INTSRE0屏蔽 // 8. 最后启动通道 SS0 | 0x01; }这里要注意SDR0的波特率因子部分是放在高位字节的很多朋友初次看寄存器字段定义时容易把0x0D00当成数据值结果发了个0D 00出来波形怎么对都对不上其实就是分频因子和数据没分开理解。提示SDR寄存器并不是“只写数据”的寄存器它高8位是波特率分频因子低8/16位才是发送/接收数据写之前一定分清。4. 发送/接收逻辑和中断处理里容易踩的坑寄存器配好了代码也能编译烧录了不代表串口就稳定了。真正搞嵌入式的人都知道发送和接收的中断流程才是最容易出问题的部分。4.1 发送流程从“丢最后一个字节”说起RL78的UART发送有一个特点发送数据缓冲区TXB和移位寄存器TXS是分开的。你写数据到SDR寄存器数据会先进TXB然后由硬件自动送进移位寄存器一位一位发出去。当TXB空了硬件会置一个“发送缓冲空”标志此时可以继续往SDR里写下一个字节当最后的停止位也就是整帧发完发送移位寄存器也空了才会有“发送完成”标志。很多人的发送中断是挂在“发送缓冲空”上面的因为这样可以在上一字节还在移位时就把下一字节准备好效率高。但如果发送完最后一个字节后不再往SDR里写数据这个缓冲空标志会一直保持你如果判断错了状态位可能在所有数据发完后还往SDR里写垃圾或者在启动发送时因为标志本来就置位了产生一个假中断。我建议在简单项目里发送不要用中断直接轮询“发送缓冲空”位一次写一字节全部写完再退出。只有在需要高吞吐的协议场景下再用发送中断同时用“发送完成”中断来收尾避免漏掉最后的停止位之前就调用关闭串口之类的操作。4.2 接收流程RXD触发沿和噪声过滤RL78的UART接收是异步接收内部会对RXD引脚采样。数据手册里会提到一个数字噪声滤波器可以通过寄存器位决定滤波时间。这个功能在工业现场特别有用但如果设置得不好会导致短脉冲的错误数据进不来。接收中断的触发条件是“接收完成”标志置位表示SDR寄存器里已经有一个完整的字节等待你读取。这里有个细节数据手册会提示你必须在错误标志被清除之前读取接收数据否则某些情况下数据寄存器的内容可能被下一次接收覆盖。在实际代码里我的做法是进接收中断以后先读SSR状态看有没有PE、FE、OVE这些错误标志。如果有错误先把SIR里的错误标志清掉再决定要不要丢弃数据。如果没有错误直接读SDR0拿数据。清一次接收完成标志相关的操作。这么做能保证在异常环境下程序不至于因为未处理错误标志而挂死。4.3 出错标志的处理顺序有些朋友会问串口偶发乱码或一直接到0xFF到底是硬件问题还是软件问题很多时候还真不是波特率不准而是接收错误后没有正确复位。RL78的OVERFLOW、PARITY ERROR、FRAME ERROR标志一旦置位如果程序没有去清除后续接收会被阻塞或者立即产生错误中断。我在项目里就碰到过一次通信一段时间后整个串口像“死”了一样读SSR发现是一个超限错误置位了但我的中断服务程序里只处理了接收完成没去碰错误标志导致后续中断一直被错误占住。后来在接收中断的最前面先检查并处掉错误标志问题立刻解决。注意接收错误后必须先清SIR中的错误标志再读数据这个顺序不要反过来。否则数据可能已经被覆盖读了也没意义。5. 常见问题与排查技巧实录这部分我把实际项目里遇到过的、群友问得最多的几类问题和排查手法整理成一个小台账方便大家遇到相同现象时快速定位。5.1 现象一完全没有波形排查步骤按照概率排序引脚复用开关没开TXD引脚还处于普通IO状态甚至方向都没配好——用万用表量引脚电平如果空闲电平都不是逻辑高多半是引脚功能没切。通道根本没有启动SS0对应通道位没置1或者配置完成后没有使能发送RXE/TXE。波特率因子写错了导致实际波特率极端化比如算成几MHz脉冲宽度非常窄示波器上看像没有波形。程序卡在时钟切换或等待某个标志的循环里导致串口初始化代码没被执行到——加断点看PC指针位置。5.2 现象二波特率对不上这种情况表现为能收到数据但全是乱码或者偶尔正确。用逻辑分析仪抓TXD引脚的一帧波形看起始位到停止位的位宽。量到位宽后去反推实际波特率如果和预期不一致回过来检查fCLK的值是否真的如你所想查看时钟初始化部分有没有被改动。SDR写入的波特率分频因子是否覆盖正确尤其要注意你写入的是高位还是低位。分频系数是否按SMR的低两位配置正确比如多除了一个2。也可以用一个简单方法自查把串口调试助手的波特率从9600开始依次往上试看哪个能解出正常的数据再根据这个倒推实际的时钟频率往往能找到原因。5.3 现象三接收中断进了一次就再也不进常见原因是接收中断服务程序里少了清标志动作或者错误中断把接收给顶掉了。RL78的中断标志不会自动清零需要软件清。如果你用了Code Generator注意看中断服务程序模板里是否有清标志的步骤没有的话补上。另外一种情况是RXD引脚悬空或者没有接上拉上电后引脚电平浮空抖动触发了一次噪声错误错误标志置位后接收被挂住。这种问题在上电初期的串口调试中很常见必要时RXD外接10k上拉电阻。5.4 排查用的“土办法”逻辑分析仪与串口空转最后分享我自己的一个调试手法。手头没有高端示波器不要紧一个几十块钱的逻辑分析仪就够用了。只要把TXD、GND两根线接上用逻辑分析仪抓串口帧一眼就能看出电平是否正常、波特率是否偏差、数据位是否对。另外我习惯在初始化串口后先让它自己发一串固定的ASCII字符比如“RL78 UART Ready\r\n”然后再接电脑看。这样做有两个好处一是能快速验证发送链路是否通二是如果发送乱码不用等到接收端才暴露问题直接看逻辑分析仪的波形就能定位是发送端配置的问题而不是接收端误判。如果你用瑞萨官方的Code Generator生成代码也可以把它的初始化配置当作参考但不要完全依赖理解每个寄存器背后的含义更重要。因为Code Generator生成的代码只是给你一个“能够跑起来”的配置不一定是最适合你项目时序场景的。6. 和STM32的串口配置对比转过来的人怎么适应因为标题和热词里都提到了cubemx配置串口这类关键词说明很多朋友是从STM32转到瑞萨平台的。这里我也聊聊两者在串口配置上的差异帮助大家加速适应。STM32CubeMX确实是好用图形化点一点串口初始化代码基本自动生成引脚冲突也会自动检查。瑞萨也有类似的工具叫Code Generator现在IDE里还集成了更智能的配置工具但用起来的体验和CubeMX还是有差别尤其是Code Generator生成代码的分层结构比较特殊新手容易找不到修改点。从寄存器层面来说STM32的USART寄存器是围绕着波特率寄存器BRR、控制寄存器CR1/CR2/CR3、状态寄存器SR和数据寄存器DR来展开的RL78则是SMR、SCR、SDR、SSR这一套。两者最大的不同在于SDR这个寄存器的“分频因子数据缓冲”合体设计以及SAU通道可以复用成多种模式。理解了这一点之后再看瑞萨的例程就不会觉得陌生。从调试习惯来说STM32的HAL库提供了一个很方便的HAL_UART_Receive_IT机制串口空闲中断也很好用RL78没有现成的空闲检测中断需要自己靠定时器或者接收超时逻辑去判断一帧数据结束。如果你的项目里通信数据是不定长的建议直接开一个定时器做超时判断不要在串口接收中断里做太多延时处理。心得从STM32过渡到RL78串口这块真正花时间的不在语法和API而在“外设模型”的转换。只要把SAU通道、SDR寄存器合体、错误标志清楚流程这三个关键点记住其它细项查手册就行。最后再分享一个实用习惯串口配置这块的文章网上不算多很多细节我也是踩了坑才记住的。个人建议还在学习阶段的朋友拿到一块RL78开发板后别急着写业务逻辑先把串口单独调通并且写一个小函数库封装好发送字符串、接收单字节、接收一帧超时判断这几个基础功能。后面所有调试信息打印、传感器数据读取、上位机通信都能直接用会节省大量时间。另外一个习惯是用LOG宏封装所有串口打印并且加一个开关宏。调试阶段打开LOG输出发布阶段统一关掉。在RL78这种以低功耗、资源精简见长的芯片上这种习惯能让你在调试效率和最终资源占用之间找到平衡。串口配置本身不复杂把基础打好后面整个项目都会顺很多。
返回列表