首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

ARM9 做主機、STM32 做從機實現 I2C 通訊的完整指南

     編輯:程式設計綜合問答

架構概述

ARM9 處理器作為 I2C 主機,STM32 單晶片作為 I2C 從機。這屬於非典型應用(CPU 間通常用 UART/CAN/RS485),但完全可行。關鍵在於從機視為一個具備固定位址的 I2C 裝置,主機完全控制讀寫時序。

從機(STM32)實現要點

1. 硬體配置

  • 開啟 I2C 時鐘(RCC_APB1Periph_I2C1/2)
  • 配置 GPIO 為開漏輸出替代功能(SCL/SDA 上拉電阻 4.7kΩ)
  • 設定 I2C 模式為 I2C_Mode_I2C,自有位址(7-bit,如 0x50)
  • 啟用事件中斷(ITEVTEN)與錯誤中斷(ITERREN)

2. 從機狀態機核心邏輯

STM32 I2C 從機透過事件中斷處理以下狀態:

void I2C1_EV_IRQHandler(void)
{
    uint32_t event = I2C_GetLastEvent(I2C1);

    switch (event)
    {
        case I2C_EVENT_SLAVE_RECEIVER_MATCHED:
            // 主機要寫資料給從機:準備接收
            rxIndex = 0;
            break;

        case I2C_EVENT_SLAVE_BYTE_RECEIVED:
            // 接收一個位元組(可能是命令碼或參數)
            rxBuffer[rxIndex++] = I2C_ReceiveData(I2C1);
            break;

        case I2C_EVENT_SLAVE_TRANSMITTER_MATCHED:
            // 主機要讀資料:準備發送
            txIndex = 0;
            // 根據先前收到的命令碼決定回應內容
            PrepareTxBuffer(rxBuffer[0]);
            I2C_SendData(I2C1, txBuffer[txIndex++]);
            break;

        case I2C_EVENT_SLAVE_BYTE_TRANSMITTED:
            // 主機要求下一個位元組
            if (txIndex < txLength)
                I2C_SendData(I2C1, txBuffer[txIndex++]);
            break;

        case I2C_EVENT_SLAVE_STOP_DETECTED:
            // 主機發送 STOP,結束一次傳輸
            I2C1->CR1 |= 0x0001; // 讀 SR1 再讀 SR2 清除 STOPF
            ProcessCommand();    // 處理完整命令
            break;
    }
}

3. 通訊協定設計(建議)

參考 RS485/Modbus 風格,定義簡單帧格式:

位元組說明
0從機位址 (含 R/W bit,由硬體自動處理)
1命令碼 (CMD)
2..N參數/資料
最後CRC/Checksum (可選)

範例命令:

  • 0x01:讀取 ADC 數值 → 從機回應 2 位元組
  • 0x02:設定 PWM 占空比 → 參數 2 位元組,無回應
  • 0x10:讀取裝置版本 → 回應字串

主機(ARM9/Linux)端實現

在 Linux 使用 i2c-dev 介面:

int file = open("/dev/i2c-1", O_RDWR);
ioctl(file, I2C_SLAVE, 0x50); // 從機 7-bit 位址

// 寫命令:設定 PWM = 50%
uint8_t buf[3] = {0x02, 0x7F, 0x00}; // CMD, 高位元組, 低位元組
write(file, buf, 3);

// 讀取 ADC
uint8_t cmd = 0x01;
write(file, &cmd, 1);
uint8_t resp[2];
read(file, resp, 2);
uint16_t adc = (resp[0] << 8) | resp[1];

常見坑與對策

現象原因解決
主機讀取全 0xFF從機未在 TXE 中斷填入資料確保 I2C_EVENT_SLAVE_TRANSMITTER_MATCHED 時預載第一個位元組
從機收不到資料GPIO 未設開漏或上拉電阻缺失檢查原理圖,SCL/SDA 必須外接 4.7kΩ 上拉至 VDD
位址衝突7-bit 位址與總線上其他裝置重複修改從機位址,掃描總線確認唯一
通訊隨機失敗時鐘延展未處理或中斷優先級過低提高 I2C 中斷優先級;必要時在主機端加入重試機制

為何不建議 CPU 間用 I2C?

  • 速率低(標準 100kHz,快速 400kHz),不適合大量資料
  • 半雙工、無硬體流控,錯誤恢復複雜
  • 線長受限(通常 < 1 米),抗干擾能力弱

替代方案:UART (RS232/RS485)、CAN、SPI (短距離)、Ethernet。若必須用 I2C,參考 RS485 協定設計應用層帧格式與重傳機制。

參考資源

  • ST 官方範例:STM32Cube_FW_F1/I2C/I2C_TwoBoards_ComSlave
  • Linux I2C Tools:i2cdetect -y 1 掃描從機位址
  • 《STM32 開發指南》I2C 從機章節

總結

STM32 做 I2C 從機核心在於事件中斷狀態機與自訂應用層協定。將從機視為一個智慧型 EEPROM,主機發「命令+參數」,從機回「資料/ACK」。建議優先評估 UART/CAN 等更適合 CPU 間通訊的介面。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved