1. 串行通信基础:为什么我们需要SCI和SPI?
在嵌入式系统开发中,无论是让单片机读取一个温湿度传感器的数据,还是驱动一块TFT屏幕显示图像,设备之间的“对话”都离不开串行通信。想象一下,如果每个设备之间都用8根、16根甚至32根线并行连接来传输数据,你的电路板会瞬间变成一团乱麻,成本、功耗和物理空间都会成为大问题。串行通信的核心思想就是“化繁为简”,把数据排成一队,通过一根或少数几根数据线,一位一位地按顺序发送出去。这就像把一队士兵从宽阔的大马路(并行总线)转移到一条独木桥(串行总线)上过河,虽然单个士兵通过的速度慢了,但架桥的成本和复杂度大大降低,在大多数场景下,整体的效率和可靠性反而更高。
串行通信主要分为两大阵营:异步通信和同步通信。它们的核心区别在于“如何同步”。异步通信,比如我们常说的UART(通用异步收发器)或本文要讲的SCI(串行通信接口),它没有统一的时钟线。发送方和接收方事先约定好一个速度(波特率),然后数据包自带“起跑信号”(起始位)和“终点线”(停止位)。接收方就靠着这些标志和约定的速度,努力对齐并解读数据。这种方式硬件简单,只需要两根线(TX和RX)就能实现全双工,非常适合单片机与电脑串口调试、GPS模块、蓝牙模块等设备通信。但它的缺点也很明显:对时钟精度要求高,如果双方时钟偏差累积,就容易读错数据。
而同步通信,以SPI(串行外设接口)为代表,则提供了一条“节拍器”线——时钟线(SCK)。主设备控制着时钟节拍,从设备严格跟着这个节拍来发送或接收每一位数据。这就好比乐队指挥(主设备)打着拍子,乐手(从设备)们跟着拍子演奏,节奏完全同步,几乎不会出错。SPI通常需要四根线(SCK, MOSI, MISO, SS),能实现高速的全双工通信,广泛用于连接Flash存储器、SD卡、触摸屏、各类传感器等。它的缺点是线多,且一个主设备通常需要为每个从设备单独提供一根片选线(SS),当从设备很多时,会占用大量IO口。
所以,选择SCI还是SPI,不是一个谁好谁坏的问题,而是一个“合适”的问题。如果你需要远距离、抗干扰、设备简单的通信,SCI(UART)是经典选择。如果你追求板内设备间的高速、可靠数据交换,SPI则是更优解。接下来,我们就深入这两种接口的“五脏六腑”,看看它们是如何工作的,以及在配置和使用时有哪些必须注意的“坑”。
2. SCI接口深度解析:从帧结构到容错机制
SCI,在Freescale(现NXP)的微控制器中,通常指的就是其UART模块的实现。理解SCI,核心在于理解它的异步帧格式和内在的同步与容错机制。
2.1 帧结构:数据是如何被打包的?
一个标准的SCI数据帧,绝不是简单地把数据位扔到线上。为了保证接收方能正确识别,它被精心包装成一个“数据包”:
- 空闲状态:数据线(TXD/RXD)通常保持在高电平(逻辑1)。
- 起始位:一个比特时间的低电平(逻辑0)。这是接收方的“起床铃”,告诉它:“注意,一帧数据要开始了!”接收器检测到这个下降沿,就会启动内部计时,准备采样后续数据。
- 数据位:紧接着起始位之后,就是实际要传输的数据,可以是5、6、7、8或9位(具体由配置决定)。通常我们传输一个字节(8位),LSB(最低有效位)先行。
- 奇偶校验位(可选):用于简单的错误检测。发送方根据数据位中“1”的个数,计算并附加一个校验位,使整个帧(包括校验位)中“1”的个数为奇数(奇校验)或偶数(偶校验)。接收方重新计算并比对,不一致则报告校验错误。
- 停止位:1位、1.5位或2位的高电平。它标志着一帧的结束,并确保线路恢复到空闲状态(高电平),为检测下一个起始位的下降沿做好准备。
注意:起始位和停止位带来了额外的开销。比如传输一个8位数据,无校验,1位停止位,那么一帧总共是10位。如果你以9600波特率(每秒9600比特)通信,实际有效数据速率只有 9600 * (8/10) = 7680 bps。这是计算实际吞吐量时必须考虑的。
2.2 波特率容错:时钟不匹配时,通信如何不“跑偏”?
这是SCI设计中最精妙的部分之一。理想情况下,发送器和接收器使用完全相同的波特率时钟。但现实中,晶振有误差,温度会漂移,双方的时钟总有微小差异。如果接收方采样点随着时间累积,慢慢滑到了数据位的边缘甚至外部,就会采样到错误的值,导致通信失败。
SCI硬件通过两个机制来对抗这种“漂移”:
帧内重同步:接收器并非只在每个比特开始时采样一次。以常见的16倍过采样为例,它将一个比特时间划分为16个RT(接收器定时器)时钟周期。它会在每个比特时间的中间段(如第8、9、10个RT周期)进行三次采样,以“少数服从多数”的原则确定该比特的值(例如,两次高一次低,则判为高)。更重要的是,接收器会在帧内的每个有效下降沿(如起始位、数据位从1到0的跳变)进行重同步,重新对齐自己的RT时钟。这就像跑步时,你不仅看着终点,还会在途中不断瞄着领跑者调整自己的步伐。
波特率容错计算:即使有重同步,如果收发双方时钟差异太大,累积误差仍可能导致在停止位采样时“踩空”。文档中给出的“慢速数据容忍度”和“快速数据容忍度”计算,定量地告诉了我们这个安全范围有多大。
以传输8位数据(无校验,1位停止位)为例,接收器需要9个比特时间 * 16 RT周期 + 7个RT周期 = 151个RT周期来开始对停止位进行采样(从起始位下降沿算起,到RT8时刻)。而发送器发送完这8位数据和一个停止位,需要9个比特时间 * 16 RT周期 = 144个RT周期。
- 慢速容忍度:如果接收器时钟比发送器慢,那么当发送器发完帧时(144 RTt),接收器才数到151 RTr。允许的最大误差是
(151 - 144) / 151 ≈ 4.63%。这意味着接收器时钟即使比发送器慢4.63%,仍能正确采样到停止位。 - 快速容忍度:如果接收器时钟比发送器快,计算方式类似,容忍度约为
4.375%。
实操心得:这个4.6%左右的容限是理论极限,在实际工程中必须留足余量。通常,我们会确保收发双方的波特率误差控制在2%以内,尤其是通信距离较长或环境干扰较大时。选择晶振时,要关注其精度(如±50ppm,即±0.005%)。很多通信失败,排查到最后就是晶振精度不够或波特率计算寄存器配置有误。
2.3 错误处理与唤醒机制
SCI硬件能自动检测几种常见错误:
- 噪声错误:对停止位的三次采样值(RT8, RT9, RT10)不一致。
- 帧错误:在停止位应出现的位置,采样到的主要是逻辑0(即没有检测到有效的停止位高电平)。这通常意味着波特率严重不匹配或线路受到严重干扰。
- 溢出错误:前一帧数据还未被CPU读取,新一帧数据已经接收完毕并要覆盖数据寄存器,导致旧数据丢失。
在多接收器系统中(如一主多从),SCI支持唤醒功能。通过设置RWU位,可以让接收器进入“待机”状态,忽略总线上的普通数据。唤醒方式有两种:
- 空闲线唤醒:当总线出现连续的高电平(空闲状态)时,唤醒所有接收器。适用于消息间用空闲字符分隔的协议。
- 地址标记唤醒:当检测到一个数据帧的最高位(MSB)为1时,将该帧识别为地址帧并唤醒接收器。接收器解析地址,如果是发给自己的,则继续接收后续数据帧;否则,再次进入待机。这种方式允许消息中包含空闲字符,效率更高。
3. SPI接口深度解析:主从时钟同步的艺术
如果说SCI是两位约定好步调的跑者,那SPI就是一位指挥和一群乐手。SPI的同步特性使其速度远超UART(轻松达到几十Mbps),但其配置也更为复杂,核心就在于理解时钟极性(CPOL)和时钟相位(CPHA)。
3.1 SPI总线信号与主从架构
一个典型的SPI总线包含四根线:
- SCK:串行时钟,由主设备产生。
- MOSI:主设备输出,从设备输入。
- MISO:主设备输入,从设备输出。
- SS/CS:从设备选择,低电平有效。主设备通过拉低对应从设备的SS线来选中它。
SPI是全双工的。数据在SCK的边沿驱动,并在相对的边沿被采样。主设备和被选中的从设备同时通过MOSI和MIO线交换数据。这意味着,主设备在发送一个字节的同时,也会收到从设备返回的一个字节。即使你只想发送命令,也会读回一个字节(可能是从设备的状态或无用数据),反之亦然。
3.2 时钟模式(CPOL与CPHA):SPI配置的“万恶之源”
这是SPI最让人困惑也最关键的地方。CPOL和CPHA两个位的组合,定义了四种SPI模式(Mode 0-3)。它们决定了:
- CPOL(时钟极性):SCK在空闲时的电平。
- CPOL=0:空闲时SCK为低电平。
- CPOL=1:空闲时SCK为高电平。
- CPHA(时钟相位):数据在SCK的哪个边沿被采样。
- CPHA=0:数据在第一个SCK边沿(即SCK从空闲状态跳变到活动状态的边沿)被采样。
- CPHA=1:数据在第二个SCK边沿被采样。
为了直观理解,我们以CPOL=0, CPHA=0(即Mode 0)为例:
- 空闲时,SCK为低(CPOL=0),SS线被拉低选中从设备。
- 主设备在SCK的第一个边沿(即上升沿,因为从低变高)之前,就将数据位放到MOSI线上。
- 在SCK的上升沿(第一个边沿),从设备采样MOSI线上的数据(CPHA=0)。同时,主设备也在这个上升沿采样MISO线上的数据。
- 主设备在SCK的下降沿(第二个边沿)之前,准备好下一位数据。
- 在SCK的下降沿,主从设备为下一位数据的传输做准备(对于CPHA=0,下降沿是数据变化的时刻)。
关键在于,主设备和从设备的CPOL、CPHA模式必须完全一致!否则数据采样会完全错位。大多数SPI器件的数据手册都会明确说明其支持的SPI模式。
避坑指南:在初始化SPI时,务必先查阅所有连接设备的 datasheet,确认其支持的SPI模式。一个常见的错误是,单片机默认配置为Mode 0,而传感器要求Mode 3,导致通信失败。调试时,用逻辑分析仪抓取SCK、MOSI、MISO波形,对照模式图分析,是排查此类问题最快的方法。
3.3 传输宽度与双缓冲机制
SPI支持8位或16位传输宽度(通过XFRW位配置)。这不仅仅是传输数据长度的区别,更影响了数据寄存器的访问方式。
- 8位模式:只使用SPIDRL寄存器。读写操作都在这个寄存器上进行。
- 16位模式:SPIDRH和SPIDRL组成一个16位寄存器。访问顺序有严格要求,错误的访问顺序会导致标志位无法正确清除或数据丢失。
SPI采用了双缓冲结构。这意味着它有一个发送数据寄存器和一个发送移位寄存器,以及一个接收数据寄存器和一个接收移位寄存器。当CPU向发送数据寄存器写入数据后,该数据会在下一个传输开始时,自动加载到发送移位寄存器中并逐位发出。同时,CPU可以立即写入下一个要发送的数据到发送数据寄存器,实现“背靠背”连续发送,提高效率。接收端同理,正在接收的数据暂存于接收移位寄存器,接收完成后自动转移到接收数据寄存器供CPU读取,移位寄存器则可以立刻开始接收下一帧。
3.4 波特率计算与模式故障
SPI的波特率由主设备的系统总线时钟分频得到。计算公式为:波特率分频系数 = (SPPR + 1) * 2^(SPR + 1),最终波特率 = 总线时钟 / 波特率分频系数。SPPR和SPR是SPIBR寄存器中的两个3位字段,提供了丰富的分频选择。例如,在25MHz总线时钟下,通过查表或计算,可以获得从12.5Mbps到12.21kbps等多种速率。
模式故障(MODF)是SPI主模式下的一个保护机制。当SPI配置为主模式且MODFEN位使能时,如果其SS引脚被外部拉低(意味着有另一个设备试图成为主机),MODF标志位会被置位,SPI会自动切换为从模式并停止传输,以防止总线冲突。这在多主SPI系统中是必要的。
4. 实战配置:以S12ZVHY微控制器为例
理论说得再多,不如一行代码。我们以Freescale S12ZVHY的SCI和SPI模块为例,看看如何配置它们。这里假设使用C语言和常见的嵌入式开发环境。
4.1 SCI模块初始化与数据收发
假设我们需要配置SCI0为9600波特率,8位数据,无校验,1位停止位,使能接收中断。
// 假设总线时钟为25MHz #define BUS_CLOCK 25000000UL #define BAUD_RATE 9600UL void SCI0_Init(void) { // 1. 计算波特率寄存器值 SBR // SCI波特率 = Bus Clock / (16 * SBR) // 因此 SBR = Bus Clock / (16 * Desired Baud Rate) uint16_t sbr = (uint16_t)((BUS_CLOCK) / (16 * BAUD_RATE)); // 2. 禁用SCI收发器,以便配置 SCI0CR2 &= ~(SCI0CR2_TE_MASK | SCI0CR2_RE_MASK); // 3. 配置波特率寄存器 (SBR12-SBR0) SCI0BDH = (uint8_t)((sbr >> 8) & 0x1F); // 高5位 SCI0BDL = (uint8_t)(sbr & 0xFF); // 低8位 // 4. 配置控制寄存器1: 8位数据,无奇偶校验 SCI0CR1 = 0x00; // M=0 (8位), PE=0 (无校验) // 5. 配置控制寄存器2: 使能接收器、发送器,使能接收中断 SCI0CR2 = SCI0CR2_RE_MASK | SCI0CR2_TE_MASK | SCI0CR2_RIE_MASK; // 6. 使能SCI中断(在中断控制器中) // EnableInterrupts; 或具体的中断使能指令 } // SCI0接收中断服务例程 interrupt void SCI0_Rx_ISR(void) { uint8_t status = SCI0SR1; uint8_t data; // 检查接收数据寄存器满标志 if (status & SCI0SR1_RDRF_MASK) { data = SCI0DRL; // 读取数据,会自动清除RDRF标志 // 处理接收到的数据,例如放入环形缓冲区 // ... } // 检查其他错误标志(帧错误、噪声错误、溢出错误) if (status & (SCI0SR1_FE_MASK | SCI0SR1_NF_MASK | SCI0SR1_OR_MASK)) { // 错误处理,例如记录错误日志,复位接收状态 // 读取数据寄存器可以清除一些错误标志,但通常需要更复杂的恢复 volatile uint8_t clear = SCI0DRL; // 读数据寄存器以清除标志 // 可能需要重新初始化SCI或采取其他措施 } } // 发送一个字节(轮询方式) void SCI0_SendByte(uint8_t data) { while(!(SCI0SR1 & SCI0SR1_TDRE_MASK)) { // 等待发送数据寄存器空 } SCI0DRL = data; // 写入数据启动发送 }注意事项:波特率计算时,
SBR必须是整数,否则会产生误差。上述计算中,sbr可能不是整数,需要四舍五入或取整。实际误差应控制在容限范围内。另外,在配置波特率等关键参数前,务必先禁用收发器(TE=0, RE=0),否则可能导致不可预知的传输。
4.2 SPI模块初始化(主模式)
假设配置SPI0为主模式,模式0(CPOL=0, CPHA=0),波特率约1MHz,8位数据。
#define SPI_BUS_CLOCK 25000000UL // 25MHz #define SPI_BAUD_RATE 1000000UL // 1MHz void SPI0_Master_Init(void) { // 1. 禁用SPI (SPE=0),以便安全配置 SPI0CR1 &= ~SPI0CR1_SPE_MASK; // 2. 配置SPI控制寄存器2 // 本例使用正常模式,非双向,等待模式下SPI停止,8位传输 SPI0CR2 = 0x00; // 默认值即可:XFRW=0 (8位), MODFEN=0 (禁用模式故障,SS用作GPIO), SPISWAI=0 // 3. 计算并配置波特率寄存器 // 波特率 = Bus Clock / ((SPPR+1) * 2^(SPR+1)) // 目标分频系数 = 25MHz / 1MHz = 25 // 查表或计算:SPPR=0b001 (SPPR=1), SPR=0b011 (SPR=3) // 分频系数 = (1+1) * 2^(3+1) = 2 * 16 = 32 -> 波特率=25MHz/32=781.25kHz (最接近1MHz的配置) // 或者选择 SPPR=0b000, SPR=0b100 (分频系数=32) 结果相同 // 我们选择 SPPR=0, SPR=4 (即SPPR[2:0]=000, SPR[2:0]=100) SPI0BR = (0 << 6) | (0 << 3) | 0x04; // SPPR=0, SPR=4 // 4. 配置SPI控制寄存器1 // SPE=1(使能), SPIE=0(先禁用中断), MSTR=1(主模式), CPOL=0, CPHA=0, SSOE=0, LSBFE=0(MSB先发) SPI0CR1 = SPI0CR1_SPE_MASK | SPI0CR1_MSTR_MASK; // CPOL和CPHA在复位后默认就是0,所以可以不写 // 5. 配置SS引脚为通用输出高电平(软件控制片选) // 假设SS引脚对应PORTB0 DDRB |= 0x01; // 设置为输出 PORTB |= 0x01; // 输出高电平(不选中) } // SPI全双工交换一个字节 uint8_t SPI0_ExchangeByte(uint8_t txData) { // 等待发送缓冲区空 while(!(SPI0SR & SPI0SR_SPTEF_MASK)); // 写入数据,启动传输 SPI0DRL = txData; // 等待接收完成 while(!(SPI0SR & SPI0SR_SPIF_MASK)); // 读取接收到的数据 return SPI0DRL; } // 使用片选发送数据块 void SPI0_WriteBlock(uint8_t slaveIndex, uint8_t* pData, uint16_t size) { // 根据slaveIndex拉低对应的SS引脚(例如PORTB0) PORTB &= ~0x01; for(uint16_t i=0; i<size; i++) { SPI0_ExchangeByte(pData[i]); // 发送并忽略接收 } // 传输完成,释放片选 PORTB |= 0x01; }关键点解析:
- 片选(SS)控制:在SPI主模式下,如果
MODFEN=0,则SS引脚不会被SPI模块自动控制,需要软件将其配置为GPIO输出,并手动控制其电平来选通从设备。拉低选中,拉高释放。- 标志位清除顺序:SPI的状态标志清除有严格顺序。对于SPIF(接收完成),必须先读SPISR(状态寄存器),再读SPIDRL(数据寄存器)。对于SPTEF(发送空),必须先读SPISR,再写SPIDRL。代码中的
while循环和读写操作隐含地遵循了这个顺序。- 波特率选择:计算出的分频系数可能无法精确得到目标波特率,需要选择最接近的配置。上例中目标1MHz,实际得到781.25kHz,需确认从设备是否能接受此速率。
5. 常见问题排查与调试技巧
在实际项目中,通信接口调不通是家常便饭。下面是一些常见问题的排查思路和调试技巧。
5.1 SCI通信失败排查清单
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 完全无数据 | 1. 硬件连接错误(TX/RX接反) 2. 波特率配置错误 3. 收发器未使能(TE/RE位) 4. 引脚复用功能未开启 | 1. 用万用表或示波器检查TX/RX线连接。 2. 双机互发测试,用逻辑分析仪抓取波形,测量比特时间,反推实际波特率。 3. 检查SCI控制寄存器2(SCICR2)的TE和RE位是否置1。 4. 检查芯片的引脚功能复用控制寄存器,确保引脚被配置为SCI功能而非GPIO。 |
| 收到乱码 | 1. 波特率不匹配(最常见) 2. 数据格式不一致(数据位、停止位、校验位) 3. 电气电平不匹配(如3.3V与5V器件直连) 4. 地线未共地 | 1.首要检查:用逻辑分析仪对比发送和接收波形,看比特宽度是否一致。计算双方波特率生成寄存器的值。 2. 确认双方的数据位长度(8/9)、停止位(1/2)、奇偶校验设置是否完全相同。 3. 检查双方器件供电电压,若不同需使用电平转换芯片。 4. 确保发送端和接收端有可靠的地线连接。 |
| 偶尔丢数据 | 1. 接收缓冲区溢出(CPU处理太慢) 2. 中断优先级冲突,导致接收中断被延迟 3. 线路干扰 | 1. 检查接收中断服务程序(ISR)是否执行时间过长。使用环形缓冲区,ISR中只做最简单的数据搬运。 2. 提高接收中断的优先级,避免被其他长时间中断阻塞。 3. 检查硬件,长距离通信建议使用RS-232/RS-485等差分标准,并做好屏蔽。 |
| 只能发不能收(或反之) | 1. 单向测试时,未将自发自收(环回)断开 2. 对方设备故障或未上电 3. 自身接收/发送中断或DMA未正确配置 | 1. 如果做了硬件环回测试,记得恢复正常连接。 2. 确认对方设备正常工作。 3. 检查中断使能位(RIE, TIE)和中断向量表配置。 |
调试利器——逻辑分析仪:对于串行通信调试,一个支持协议分析的逻辑分析仪(如Saleae)是必备的。它能直观显示波形、自动解析UART/SPI协议、测量时序、发现毛刺,效率远超示波器手动测量。
5.2 SPI通信失败排查清单
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 从设备无响应 | 1. 片选(SS)信号问题(未拉低、时序不对) 2. 时钟模式(CPOL/CPHA)不匹配 3. 从设备供电或复位问题 4. 波特率过高 | 1.首要检查:用逻辑分析仪同时抓取SCK、MOSI、MISO、SS四根线。确认SS信号在数据传输前被正确拉低,并在结束后拉高。 2.第二检查项:对照从设备数据手册,确认主设备SPI的CPOL和CPHA设置与从设备要求完全一致。抓取波形看数据采样边沿是否正确。 3. 测量从设备的电源和复位引脚。 4. 尝试降低SPI波特率,特别是布线较长时。 |
| 主设备收不到数据(MISO一直为高阻或固定电平) | 1. 从设备输出使能问题 2. MISO引脚配置错误(应为主输入) 3. 从设备未被正确激活或处于错误状态 | 1. 确认从设备在SS有效且收到时钟时,是否被正确驱动MISO线。有些设备需要先发送特定命令字才会输出数据。 2. 检查主设备MISO引脚是否配置为输入(或复用功能)。 3. 阅读从设备手册,确认其初始化序列和读写时序。 |
| 数据错位(如0x55收成0xAA) | 1. 数据传输位序(LSB/MSB First)不匹配 2. 时钟极性或相位有1位偏差 | 1. 检查主从设备的LSBFE(或类似)配置,确保位序一致。通常MSB先行。 2. 仔细核对CPOL和CPHA,有时错一个模式会导致数据看起来是反码或移位。 |
| 高速传输时数据错误 | 1. 信号完整性问题(过冲、振铃) 2. 时序裕量不足 3. 主从设备时钟抖动 | 1. 观察SCK和MOSI/MISO波形,看是否有严重的振铃或边沿过缓。可能需要串联小电阻(如22-100欧姆)进行阻抗匹配。 2. 降低时钟频率,看问题是否消失。计算建立时间和保持时间是否满足从设备要求。 3. 在极端温度或电压下测试。 |
SPI模式记忆口诀:记不住四种模式?可以这样记:Mode = (CPOL << 1) | CPHA。即CPOL为高位,CPHA为低位。Mode 0就是00,Mode 3就是11。大多数SPI Flash器件支持Mode 0和Mode 3。
6. 进阶应用与设计考量
掌握了基本配置和调试后,我们来看看一些更深入的应用场景和设计选择。
6.1 SCI的软件流控与硬件流控
当发送端速度高于接收端处理能力时,会发生数据溢出。除了提高接收端处理能力或使用更大的缓冲区,还可以使用流控。
- 软件流控(XON/XOFF):接收端在缓冲区快满时,向发送端发送一个特殊字符(XOFF,如0x13)让其暂停;当缓冲区有空闲时,再发送另一个字符(XON,如0x11)让其继续。实现简单,但会占用数据带宽,且不能用于二进制数据(可能和XON/XOFF字符冲突)。
- 硬件流控(RTS/CTS):使用额外的两根线。接收端的RTS(请求发送)信号告诉发送端“我是否可以接收”;发送端在发送前检查对方的CTS(清除发送)信号是否为有效电平。硬件流控实时、可靠,不占用数据带宽,但需要额外的引脚和连接。
6.2 SPI的多从机连接与菊花链
- 独立片选(标准方式):每个从设备独占一根SS线。主设备通过拉低不同的SS线来选择通信对象。优点是逻辑简单,从设备间互不影响;缺点是占用IO口多。
- 菊花链(Daisy-Chain):所有从设备的MISO和MOSI首尾相连,形成一个环。主设备只连接第一个从设备的MOSI和最后一个从设备的MISO。数据像接力棒一样从一个设备传到下一个。主设备发送一个很长的数据帧,经过所有从设备后,再读回一个同样长的帧,其中包含了链上所有设备的响应。这种方式节省IO,但协议复杂,所有设备必须支持菊花链模式,且通信效率低(数据要穿过所有设备)。
6.3 低功耗模式下的行为
在嵌入式系统中,低功耗至关重要。S12ZVHY的SCI和SPI模块在等待模式和停止模式下的行为需要关注:
- SCI:通过
SCISWAI位控制。若该位置1,进入等待模式后,SCI时钟停止以省电,但任何正在进行的传输会暂停,并在退出等待模式后恢复。在停止模式下,SCI完全关闭,但接收引脚上的有效边沿可以唤醒CPU。 - SPI:通过
SPISWAI位控制,行为类似。在从模式下,即使主设备设置了SPISWAI,如果外部SPI主设备仍在提供时钟,从设备为了保持同步,可能无法完全停止时钟,功耗节省有限。设计低功耗系统时,需要综合考虑通信模块的状态。
6.4 使用DMA解放CPU
无论是SCI还是SPI,在大量数据传输时,频繁的中断会消耗大量CPU资源。此时,直接内存访问(DMA)控制器是绝佳帮手。你可以配置DMA通道,在SCI接收数据寄存器满(RDRF)或SPI接收完成(SPIF)时,自动将数据搬运到指定的内存缓冲区;或者在发送寄存器空(TDRE/SPTEF)时,自动从内存缓冲区加载新数据。CPU只需在缓冲区满或空时进行批量处理,极大提高了系统效率。在配置DMA时,需要特别注意数据宽度、地址递增模式以及和中断的配合。
最后,无论是SCI还是SPI,稳定的通信都建立在扎实的硬件基础上。良好的PCB布局(缩短走线、避免平行长线)、正确的电源去耦(每个芯片电源引脚附近放置0.1uF电容)、可靠的地平面,都是保证高速数据可靠传输的隐形基石。在软件上,则要牢记添加超时机制、校验和(如CRC)以及错误恢复流程,让你的嵌入式系统在面对复杂环境时依然坚如磐石。