You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ATmega328PB双SPI总线(SPI0/SPI1)同时运行问题咨询

ATmega328PB双SPI总线同时工作的配置与解决方案

问题背景

在ATmega328PB控制器上尝试同时使用两路SPI总线:SPI0连接ENC28J60以太网芯片,SPI1连接TLE9879;基于Arduino框架,使用EtherCard以太网库和Infineon的TLE9879外部库。目前两路设备可分别在对应SPI总线上独立工作,但无法同时运行。已尝试为SPI1编写自定义传输函数、SPI0保留给以太网设备,仍未解决问题。

核心配置要点

ATmega328PB的SPI1是USART1复用的SPI模式,并非独立硬件SPI,这是关键的易忽略点,必须正确配置USART1为SPI模式才能正常工作:

  1. USART1 SPI模式寄存器配置
    手动设置UCSR1C寄存器启用SPI模式,同时配置对应引脚方向:

    // 配置USART1为SPI主机模式
    UCSR1C = (1 << UMSEL11) | (1 << UMSEL10);
    UBRR1 = 0; // SPI模式下波特率寄存器无效,设为0
    // 使能USART1的发送和接收
    UCSR1B = (1 << TXEN1) | (1 << RXEN1);
    
    // 配置SPI1引脚方向:PB3=SCK1, PB1=MOSI1设为输出;PB2=MISO1设为输入
    DDRB |= (1 << PB3) | (1 << PB1);
    DDRB &= ~(1 << PB2);
    
  2. 严格的片选(CS)引脚管理
    两路SPI设备的CS引脚必须单独控制,同一时间只能激活一个设备的CS:

    • SPI0的CS(通常为D10):访问ENC28J60时拉低,操作完成后立即拉高
    • SPI1的CS(自定义引脚):访问TLE9879时拉低,操作完成后立即拉高
      绝对不能同时拉低两个CS引脚,否则会导致总线数据冲突。
  3. 库兼容性适配

    • EtherCard库默认使用SPI0(硬件SPI),无需修改,但要确保它不会意外修改SPI1的相关寄存器
    • Infineon的TLE9879库如果默认调用SPI0的SPI.transfer(),需要将其替换为自定义的SPI1传输函数,或修改库内的SPI操作逻辑为USART1 SPI模式

可行解决方案示例

1. 封装SPI1读写函数

// SPI1单字节传输(USART1 SPI模式)
uint8_t spi1_transfer(uint8_t data) {
  while (!(UCSR1A & (1 << UDRE1))); // 等待发送缓冲区为空
  UDR1 = data;
  while (!(UCSR1A & (1 << RXC1))); // 等待接收完成
  return UDR1;
}

// SPI1多字节读写
void spi1_transfer_bytes(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len) {
  for (uint16_t i = 0; i < len; i++) {
    while (!(UCSR1A & (1 << UDRE1)));
    UDR1 = tx_buf ? tx_buf[i] : 0xFF;
    while (!(UCSR1A & (1 << RXC1)));
    if (rx_buf) rx_buf[i] = UDR1;
  }
}

2. 修改TLE9879库的SPI操作

找到库中所有调用SPI.transfer()的代码,替换为上述spi1_transfer()函数,同时确保CS引脚的控制逻辑正确。

3. 优化初始化顺序

先初始化SPI0与ENC28J60,再初始化SPI1与TLE9879,避免初始化阶段的寄存器冲突:

#define TLE9879_CS 9 // 自定义SPI1的CS引脚

void setup() {
  // 初始化SPI0与ENC28J60
  SPI.begin();
  ether.begin(sizeof Ethernet::buffer, mymac, 10); // 10为SPI0的CS引脚

  // 初始化SPI1
  UCSR1C = (1 << UMSEL11) | (1 << UMSEL10);
  UBRR1 = 0;
  UCSR1B = (1 << TXEN1) | (1 << RXEN1);
  DDRB |= (1 << PB3) | (1 << PB1);
  DDRB &= ~(1 << PB2);
  pinMode(TLE9879_CS, OUTPUT);
  digitalWrite(TLE9879_CS, HIGH);

  // 初始化TLE9879
  tle9879.init();
}

排查建议

  • 核对SPI1引脚接线是否正确,确认SCK1、MOSI1、MISO1的对应关系
  • 使用逻辑分析仪或示波器监测两路SPI总线的信号,排查是否存在冲突或异常波形
  • 确认两个设备的SPI时钟极性(CPOL)和相位(CPHA)是否匹配各自的规格书,必要时在初始化阶段分别配置

内容的提问来源于stack exchange,提问作者Hemant Bhargava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 10:25:24