1. 项目概述:STM32通过I2C驱动SC16IS752扩展串口
在嵌入式开发中,STM32系列单片机因其出色的性能和丰富的外设资源被广泛应用。但实际项目中,我们常常遇到原生UART接口数量不足的情况。SC16IS752这款双通道UART扩展芯片,恰好能通过I2C或SPI接口为STM32扩展出两个全功能串口。我最近在一个工业控制器项目中就采用了这种方案,实测波特率可达921600bps且稳定性良好。
SC16IS752是NXP推出的桥接芯片,最大特点是支持标准I2C总线(最高3.4MHz)或SPI接口(最高15MHz)作为控制接口。本文将重点讲解I2C接口的驱动实现,因为I2C布线更简单且多数STM32型号都内置硬件I2C外设。该方案特别适合以下场景:
- 需要连接多个Modbus设备但MCU串口不足
- 调试口与通信口需要物理隔离
- 远距离通信(通过I2C扩展后可延长UART传输距离)
2. 硬件设计与连接要点
2.1 硬件选型与电路设计
推荐使用SC16IS752IPW型号(TSSOP-28封装),其工作电压2.3V-3.6V与STM32完美兼容。典型应用电路需注意:
- 电源滤波:在VDD引脚就近放置0.1μF陶瓷电容
- I2C上拉:SCL/SDA线需接2.2kΩ上拉电阻(3.3V系统)
- 地址配置:通过A0/A1引脚设置I2C地址(默认0x48)
- 中断处理:连接IRQ引脚到STM32外部中断输入
重要提示:若通信距离超过30cm,建议使用PCA9615等I2C缓冲器增强驱动能力
2.2 STM32引脚配置示例(以STM32F103C8T6为例)
c复制// I2C1配置
#define I2C_SCL_PIN GPIO_Pin_6 // PB6
#define I2C_SDA_PIN GPIO_Pin_7 // PB7
// SC16IS752中断引脚
#define IRQ_PIN GPIO_Pin_0 // PA0
3. 软件驱动实现详解
3.1 初始化流程
完整的设备初始化应包含以下步骤:
- 复位芯片(写0x08到CHANNEL_STATE_REG)
- 设置时钟源(通常选择外部1.8432MHz晶振)
- 配置FIFO(建议启用128字节深度)
- 设置波特率(需计算分频值)
- 启用中断(IER寄存器)
波特率计算公式:
math复制DL = (XTAL_input / (波特率 × 16)) - 1
例如1.8432MHz晶振下,115200bps对应的DL值为0:
c复制void SC16IS752_Init(I2C_TypeDef* I2Cx) {
uint8_t config[] = {
0x08, // 复位命令
0x80, // 使能分频器
0x00, // DL高字节
0x00 // DL低字节
};
I2C_WriteMulti(I2Cx, SC16IS752_ADDR, CHANNEL_CTRL_REG, config, 4);
}
3.2 关键函数实现
3.2.1 数据发送函数
c复制void UART_SendData(I2C_TypeDef* I2Cx, uint8_t channel, uint8_t* data, uint16_t len) {
// 检查THR是否就绪
while(!(I2C_ReadByte(I2Cx, SC16IS752_ADDR, LINE_STATUS_REG) & 0x20));
// 写入数据
I2C_WriteMulti(I2Cx, SC16IS752_ADDR,
channel ? CHANNEL_B_THR : CHANNEL_A_THR,
data, len);
}
3.2.2 中断服务例程
c复制void EXTI0_IRQHandler(void) {
if(EXTI_GetITStatus(EXTI_Line0) != RESET) {
uint8_t iir = I2C_ReadByte(I2C1, SC16IS752_ADDR, INT_IDENT_REG);
switch(iir & 0x3F) {
case 0x04: // 接收数据可用
UART_ReceiveHandler();
break;
case 0x0C: // 字符超时
FIFO_Flush();
break;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
4. 调试技巧与常见问题
4.1 I2C通信故障排查
若无法正常通信,建议按以下步骤检查:
- 用逻辑分析仪捕获I2C波形,确认:
- 起始/停止条件是否完整
- 设备地址是否正确(默认0x48)
- ACK/NACK响应状态
- 检查电源电压(3.3V±10%)
- 测量晶振是否起振(应有1.8432MHz正弦波)
4.2 典型问题解决方案
问题1:接收数据出现乱码
可能原因:
- 波特率不匹配(重新计算DL值)
- 线路干扰(增加120Ω终端电阻)
- FIFO溢出(减小接收中断响应时间)
问题2:中断无法触发
检查:
- IRQ引脚配置(应设置为浮空输入)
- 中断使能位(IER寄存器bit0)
- GPIO外部中断配置(需开启AFIO时钟)
实测发现,在STM32F4系列上,I2C时钟超过400kHz时需降低GPIO速度至2MHz
5. 性能优化建议
5.1 DMA传输配置
对于高速通信场景(>500kbps),建议启用DMA:
c复制// 配置I2C1的DMA通道
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&I2C1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)txBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = bufLen;
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
I2C_DMACmd(I2C1, ENABLE);
5.2 低功耗优化
- 空闲时关闭接收器(EFCR寄存器bit0)
- 使用睡眠模式(写0x01到CHANNEL_STATE_REG)
- 动态调整I2C速度(高速传输后降频)
我在实际项目中总结出一个技巧:定期读取LSR寄存器并统计错误计数,当校验错误率超过1%时自动降低波特率,这个机制使系统在强干扰环境下仍能保持可靠通信。
