Overview
这部分可以分成 3 大块:
Serial Communications 串行通信基础
重点概念:
| 对比 | Serial 串行 | Parallel 并行 |
|---|---|---|
| 传输方式 | bit by bit,一个一个 bit 传 | 多个 bit 同时传 |
| 线路数量 | 少,通常 1 条或少数几条 | 多,需要多条数据线 |
| 适合场景 | MCU 引脚有限、长距离传输 | 短距离、高吞吐 |
| 问题 | 速度相对低 | 长线容易干扰、占引脚 |
考试/理解重点:Serial communication 更适合嵌入式系统,因为 MCU 引脚有限,而且很多外设通信不需要很宽的数据总线。
Synchronous vs Asynchronous 同步/异步通信
| 对比 | Synchronous 同步 | Asynchronous 异步 |
|---|---|---|
| 时钟 | 发送方和接收方共享/同步时钟 | 不共享时钟 |
| 数据形式 | 连续数据流/block | 一个 character 一个 character 传 |
| 额外 bit | 通常不需要 start/stop bit | 需要 start bit 和 stop bit |
| 速度 | 通常更快 | 通常较慢 |
| 典型协议 | I2C, SPI | UART |
记忆方式:
I2C/SPI = synchronous,因为它们有 clock line。
UART = asynchronous,因为没有 clock line,而是靠 baud rate + start/stop bits。
三种常见串行通信协议
UART
UART 是 Universal Asynchronous Receiver-Transmitter。严格来说,它不是像 I2C/SPI 那样的“协议”,而是 MCU 里的一个硬件模块,用来做异步串行通信。
特点:
- 没有 clock line
- 使用 TX 和 RX 两根主要数据线
- 需要双方设置相同 baud rate
- 每个数据包有 start bit 和 stop bit
- 常用于 MCU 和 PC terminal 通信
本讲例子中,UART 初始化常见代码是:
UART1_Init(9600);
UART1_Write_Text("Start");
I2C
I2C 是 Inter-Integrated Circuit,是同步、multi-master、multi-slave 的串行通信协议。
特点:
-
两根线:
SCL:Serial Clock
SDA:Serial Data
-
支持多个 slave
- 每个 slave 有地址
- 常用于 EEPROM、ADC、DAC、传感器等
Lecture 里的 EEPROM 例子中,7-bit 地址是 0x50,写操作时变成:
0x50 << 1 = 0xA0
如果是读操作,则最低位 R/W bit 为 1,所以是:
0xA1
SPI
SPI 是 Serial Peripheral Interface,也是同步通信协议。
四根主要信号线:
| 信号 | 含义 |
|---|---|
| SCLK | Serial Clock |
| MOSI | Master Output Slave Input |
| MISO | Master Input Slave Output |
| SS | Slave Select |
SPI 通常比 I2C 快,但需要更多线。多个 slave 时,每个 slave 通常需要单独的 SS 线。
PIC18 上的通信模块
Lecture 3 主要讲了两个模块:
EUSART
用于 UART 通信。
相关寄存器:
| 功能 | 常见寄存器 |
|---|---|
| 发送 Tx | TXREGx, TXSTAx, BAUDCONx, SPBRGx |
| 接收 Rx | RCREGx, RCSTAx, INTCON, PIEx, PIRx |
Tx 关键配置:
TXSTA1.TXEN = 1; // enable transmission
TXSTA1.SYNC = 0; // asynchronous mode
TXSTA1.BRGH = 1; // high baud rate
BAUDCON1.BRG16 = 0; // 8-bit baud generator
SPBRG1 = 51; // baud rate = 9600
Rx 关键配置:
RCSTA1.SPEN = 1; // enable serial port
RCSTA1.CREN = 1; // enable continuous receive
INTCON.GIE = 1; // enable global interrupt
INTCON.PEIE = 1; // enable peripheral interrupt
PIE1.RC1IE = 1; // enable EUSART1 receive interrupt
MSSP
用于同步串行通信:
- I2C mode
- SPI mode
相关寄存器:
| 寄存器 | 作用 |
|---|---|
| SSPxSTAT | status |
| SSPxCON1 / SSPxCON3 | control |
| SSPxBUF | data buffer |
| SSPxADD | address |
| SSPxSR | shift register |
Serial vs Parallel
Serial Communication 串行通信
串行通信的特点是:
数据按 bit 一个一个传输。
比如一个 byte:
10110010
串行通信会这样传:
1 → 0 → 1 → 1 → 0 → 0 → 1 → 0
也就是说,同一时间只传一个 bit。
优点:
- 用线少
- 省 MCU 引脚
- 适合长距离通信
- 嵌入式系统里很常见
缺点:
- 同一时刻只传一个 bit,所以理论上不如并行快
Parallel Communication 并行通信
并行通信的特点是:
多个 bit 同时传输。
比如同样传一个 byte:
10110010
并行通信可以用 8 根线同时传:
b7 b6 b5 b4 b3 b2 b1 b0
也就是说,同一时间传多个 bit。
优点:
- 短距离时速度快
- 一次能传很多 bit
缺点:
- 需要很多线
- 占用很多 MCU 引脚
- 长距离时容易出现 interference,也就是线路之间互相干扰
考试记忆点
最重要的一句话:
Serial uses fewer wires and is better for MCUs with limited pins. Parallel can be faster over short distance but uses more wires and may suffer interference over long cables.
中文理解:
串行通信省引脚、适合 MCU 和较长距离;并行通信短距离速度快,但占线多,长距离容易干扰。
Synchronous vs Asynchronous
串行通信又可以分成:
- Synchronous communication 同步通信
- Asynchronous communication 异步通信
Synchronous Communication 同步通信
同步通信的核心是:
Sender 和 receiver 的 clock 是同步的。
也就是说,发送方和接收方有统一的时钟节奏。
常见例子:
- I2C
- SPI
为什么它们是 synchronous?
因为它们都有 clock line。
I2C 里有:
SCL = clock line
SDA = data line
SPI 里有:
SCLK = clock line
MOSI / MISO = data lines
所以 I2C 和 SPI 都属于同步通信。
Asynchronous Communication 异步通信
异步通信的核心是:
Sender 和 receiver 没有共享 clock。
那没有 clock 怎么知道什么时候读数据?
靠三个东西:
- Baud rate
- Start bit
- Stop bit
UART 就是异步通信。
UART 发送一个字符时,大概结构是:
Start bit | Data bits | Stop bit
例如发送 8-bit data:
0 | 8 data bits | 1
其中:
- start bit 通常是 0
- stop bit 通常是 1
- receiver 看到 start bit 后,就按照约定好的 baud rate 读取后面的 data bits
Synchronous vs Asynchronous 对比
| 对比点 | Synchronous | Asynchronous |
|---|---|---|
| 是否共享 clock | 是 | 否 |
| 是否需要 start/stop bit | 通常不需要 | 需要 |
| 速度 | 通常更快 | 通常较慢 |
| 数据流 | 连续传输 | 一个 character 一个 character 传 |
| 典型协议 | I2C, SPI | UART |
三个协议先建立印象
现在你先记住这个总表:
| 协议 | 类型 | 关键词 |
|---|---|---|
| UART | Asynchronous | TX, RX, baud rate, start/stop bits |
| I2C | Synchronous | SDA, SCL, address, multi-slave |
| SPI | Synchronous | SCLK, MOSI, MISO, SS, fast |
一句话总结:
UART 靠 baud rate,I2C 靠 address,SPI 靠 slave select。
UART
UART = Universal Asynchronous Receiver-Transmitter
它用于 asynchronous serial communication。
注意一个细节:
UART 严格来说不是 I2C/SPI 那种 protocol,而是 MCU 里的一个硬件模块/电路。
它负责把 MCU 里的 parallel data 转换成 serial data 发出去,也负责把接收到的 serial data 转回 MCU 可处理的数据。
UART 用几根线?
最核心两根:
| 线 | 作用 |
|---|---|
| TX | transmit,发送数据 |
| RX | receive,接收数据 |
连接时要交叉:
MCU TX → PC/Terminal RX
MCU RX ← PC/Terminal TX
所以不是 TX 接 TX,而是 TX 接 RX。
UART 为什么不需要 clock line?
因为它是 asynchronous。
发送方和接收方提前约定好:
Baud rate = 9600 bps
然后 receiver 看到 start bit 后,就按照这个速度去采样后面的 data bits。
例如一个常见 UART frame:
Start bit | 8 data bits | Stop bit
可以理解为:
0 | data | 1
- start bit 通常是 low,也就是 0
- stop bit 通常是 high,也就是 1
- idle 状态通常也是 high
Baud rate 是什么?
Baud rate 是 UART 的传输速率。
例如:
UART1_Init(9600);
意思是初始化 UART,速率为 9600 bps。
这里 bps 通常可以理解为:
每秒传输多少 bit。
如果两边 baud rate 不一致,就会读错数据。
比如:
MCU: 9600 bps
PC terminal: 115200 bps
这样通信大概率会乱码。
UART Transmit 发送配置
Tx 示例:
TXSTA1.TXEN = 1; // enable transmission
TXSTA1.SYNC = 0; // enable asynchronous mode
TXSTA1.BRGH = 1; // select high baud rate
BAUDCON1.BRG16 = 0; // select 8-bit baud rate
SPBRG1 = 51; // set baud rate to 9600
逐句解释:
| 代码 | 含义 |
|---|---|
TXEN = 1 |
enable transmission,允许发送 |
SYNC = 0 |
asynchronous mode,异步模式 |
BRGH = 1 |
high baud rate |
BRG16 = 0 |
使用 8-bit baud rate generator |
SPBRG1 = 51 |
设置 baud rate 为 9600 |
UART Receive 接收配置
Rx 示例:
RCSTA1.SPEN = 1; // enable serial port
RCSTA1.CREN = 1; // enable continuous receive
INTCON.GIE = 1; // enable global interrupt
INTCON.PEIE = 1; // enable peripheral interrupt
PIE1.RC1IE = 1; // enable EUSART1 receive interrupt
逐句解释:
| 代码 | 含义 |
|---|---|
SPEN = 1 |
enable serial port |
CREN = 1 |
continuous receive,连续接收 |
GIE = 1 |
global interrupt enable |
PEIE = 1 |
peripheral interrupt enable |
RC1IE = 1 |
enable EUSART1 receive interrupt |
重点是:
如果用 interrupt 接收 UART 数据,除了开 UART receive interrupt,还要开 global interrupt 和 peripheral interrupt。
8. 最简单的 UART echo 程序
echo 逻辑:
char uart_rd;
void main() {
ANSELC = 0; // Configure PORTC pins as digital
UART1_Init(9600); // Initialize UART module at 9600 bps
Delay_ms(100); // Wait for UART module to stabilize
UART1_Write_Text("Start");
UART1_Write(13);
UART1_Write(10);
while (1) {
if (UART1_Data_Ready()) {
uart_rd = UART1_Read();
UART1_Write(uart_rd);
}
}
}
它做的事情是:
- 初始化 UART
- 发送
"Start" - 一直等待输入
- 如果 PC terminal 发送了一个字符给 MCU
- MCU 读取这个字符
- MCU 再把它发回 PC terminal
这就是 UART echo。
UART is asynchronous, so it does not use a clock line. It uses baud rate, start bit, and stop bit to synchronize data transfer.
TX and RX should be cross-connected.
For UART receiving with interrupt, SPEN, CREN, GIE, PEIE, and RC1IE should be enabled.
TXREG is used for transmit data, and RCREG is used for received data.
I2C
I2C = Inter-Integrated Circuit
它是一种:
synchronous, multi-master, multi-slave, serial communication protocol
拆开看:
| 关键词 | 含义 |
|---|---|
| synchronous | 有 clock line |
| multi-master | 可以有多个 master |
| multi-slave | 可以有多个 slave |
| serial | 数据 bit by bit 传输 |
| protocol | 它是一套通信规则 |
在本课里,常见结构是:
MCU = Master
EEPROM = Slave
2. I2C 用几根线?
I2C 只用两根主要线:
| 线 | 全称 | 作用 |
|---|---|---|
| SDA | Serial Data | 传输数据 |
| SCL | Serial Clock | 传输时钟 |
所以 I2C 是 synchronous,因为它有 SCL clock line。
记忆:
SDA = data
SCL = clock
I2C 为什么适合连接多个外设?
因为 I2C 使用 address 区分不同 slave。
比如一条 I2C bus 上可以接:
MCU
├── EEPROM, address 1
├── sensor, address 2
└── ADC, address 3
所有设备共享 SDA 和 SCL,但每个 slave 有自己的 address。Master 先发送 address,被选中的 slave 才响应。
所以 I2C 的关键词是:
address-based communication
I2C 和 UART 的最大区别
| 对比 | UART | I2C |
|---|---|---|
| 类型 | asynchronous | synchronous |
| clock line | 没有 | 有 SCL |
| 主要线 | TX, RX | SDA, SCL |
| 是否有地址 | 通常没有 | 有 slave address |
| 常见用途 | MCU 和 PC terminal | MCU 和 EEPROM / sensor |
一句话:
UART 靠 baud rate,I2C 靠 clock + address。
EEPROM 地址:7-bit 和 8-bit
EEPROM 的 7-bit address 是:
0x50
但是 I2C 发送时,通常会把 7-bit address 左移 1 位,再加上 R/W bit。
公式:
8-bit address = 7-bit address << 1 + R/W bit
所以:
Write 写操作
R/W bit = 0
0x50 << 1 = 0xA0
所以写地址是:
0xA0
Read 读操作
R/W bit = 1
0xA0 + 1 = 0xA1
所以读地址是:
0xA1
注意 Lecture 代码里有时会出现 0xA2 和 0xA3,这是因为 EEPROM 的 chip enable address 位不同,但核心逻辑一样:
write address = even number
read address = write address + 1
I2C 写 EEPROM 的基本流程
典型写入流程:
I2C1_Start();
I2C1_Wr(0xA2); // device address + Write
I2C1_Wr(2); // EEPROM memory address
I2C1_Wr(0xAA); // data to write
I2C1_Stop();
逐句解释:
| 代码 | 含义 |
|---|---|
I2C1_Start() |
发起 I2C 通信 |
I2C1_Wr(0xA2) |
选择 EEPROM,并指定 write |
I2C1_Wr(2) |
要写入 EEPROM 的第 2 个地址 |
I2C1_Wr(0xAA) |
写入数据 0xAA |
I2C1_Stop() |
结束通信 |
逻辑是:
Start → Slave address + W → Memory address → Data → Stop
I2C 读 EEPROM 的基本流程
典型读取流程:
I2C1_Start();
I2C1_Wr(0xA2); // device address + Write
I2C1_Wr(2); // EEPROM memory address
I2C1_Repeated_Start();
I2C1_Wr(0xA3); // device address + Read
PORTB = I2C1_Rd(0u); // read data, no acknowledge
I2C1_Stop();
为什么读之前还要先 write?
因为要先告诉 EEPROM:
我要读 EEPROM 内部的哪个 memory address。
所以读取 EEPROM 的流程是:
Start
→ Slave address + W
→ Memory address
→ Repeated Start
→ Slave address + R
→ Read data
→ Stop
这里的 Repeated Start 很重要。它表示没有完全释放 bus,而是直接从“指定地址”切换到“读取模式”。
8. I2C1_Rd(0u) 里的 0u 是什么?
在 mikroC 里:
I2C1_Rd(0u)
表示:
Read the data with NO acknowledge.
如果读多个 byte,前面的 byte 通常发送 ACK,最后一个 byte 发送 NACK,表示:
我读完了,不需要继续发送了。
所以单 byte 读取时经常用:
I2C1_Rd(0u);
I2C is synchronous because it uses SCL as the clock line.
I2C uses two wires: SDA for data and SCL for clock.
I2C uses slave addresses, so multiple devices can share the same bus.
For EEPROM reading, the MCU first sends the memory address using write mode, then uses repeated start and read mode to read data.
7-bit address
0x50becomes0xA0for write and0xA1for read.
SPI 是什么?
SPI = Serial Peripheral Interface
它是一种:
synchronous, master-slave, serial communication protocol
关键词:
| 关键词 | 含义 |
|---|---|
| synchronous | 有 clock line |
| master-slave | 一个 master 控制一个或多个 slave |
| serial | bit by bit 传输 |
| protocol | 一套通信规则 |
和 I2C 一样,SPI 也是同步通信,因为它有 clock。
SPI 的 4 根主要线
SPI 最重要的是这 4 根线:
| 信号 | 全称 | 作用 |
|---|---|---|
| SCLK | Serial Clock | master 提供时钟 |
| MOSI | Master Output Slave Input | master 发给 slave |
| MISO | Master Input Slave Output | slave 发给 master |
| SS | Slave Select | 选择某个 slave |
记忆方法:
MOSI: Master → Slave
MISO: Slave → Master
也就是:
MOSI = master sends data out
MISO = master receives data in
SPI 为什么是 synchronous?
因为 SPI 有:
SCLK
SCLK 是 master 产生的 clock signal。
Slave 根据这个 clock 来发送/接收数据。
所以 SPI 不需要像 UART 那样依靠 start bit 和 stop bit 来判断字符边界。
SPI 如何选择 slave?
SPI 用 SS / Slave Select 线选择 slave。
比如一个 master 接 3 个 slave:
Master
├── SS1 → Slave 1
├── SS2 → Slave 2
└── SS3 → Slave 3
当 master 要和 Slave 2 通信时,就使能 Slave 2 的 SS 线。
重点:
I2C 用 address 选择 slave;SPI 用 SS line 选择 slave。
SPI 和 I2C 对比
| 对比 | I2C | SPI |
|---|---|---|
| 通信类型 | synchronous | synchronous |
| 线数 | 2 根:SDA, SCL | 通常 4 根:SCLK, MOSI, MISO, SS |
| slave 选择方式 | address | SS line |
| 速度 | 较慢 | 通常更快 |
| 多 slave 扩展 | 容易,因为共用 SDA/SCL | slave 越多,SS 线越多 |
| 常见用途 | EEPROM、sensor | 高速外设、display、ADC、flash |
一句话:
I2C saves wires, SPI is usually faster.
SPI 和 UART 对比
| 对比 | UART | SPI |
|---|---|---|
| 类型 | asynchronous | synchronous |
| clock line | 没有 | 有 SCLK |
| 主要线 | TX, RX | SCLK, MOSI, MISO, SS |
| 是否需要 baud rate | 需要 | 不像 UART 那样依赖 baud rate |
| 是否需要 start/stop bit | 需要 | 通常不需要 |
| 典型连接 | MCU ↔ PC terminal | MCU ↔ peripheral |
三个协议总表
现在把 UART、I2C、SPI 放在一起记:
| 协议 | 类型 | 主要线 | 选择对象方式 | 关键词 |
|---|---|---|---|---|
| UART | asynchronous | TX, RX | 点对点 | baud rate, start bit, stop bit |
| I2C | synchronous | SDA, SCL | address | multi-slave, EEPROM |
| SPI | synchronous | SCLK, MOSI, MISO, SS | slave select | fast, more wires |
UART uses baud rate. I2C uses address. SPI uses slave select.