MicroPython 標準函式庫裡提供的 I2C 模組,只能讓 Raspberry pi pico (RP2040) 做 I2C Master,如果今天要讓 pico 做 Slave 只能改用 C++ 函式庫。真的是這樣嗎?
本文是 AI 神經網路自走車 第三篇,分享如何用 MicroPython 來實現 I2C Slave,並且與 Raspberry pi 做溝通,就讓我們一起學習吧!
目錄
- 1 準備硬體元件
- 2 Step 1:使用開源的 I2CResponder
- 3 Step 2:決定 pico 腳位
- 4 Step 3:決定 Raspberry pi 腳位
- 5 Step 4:連結兩者
- 6 Step 5:Raspberry pi 啟用 I2C 硬體
- 7 Step 4:Raspberry pi 安裝 python-smbus
- 8 Step 5 – 1:撰寫 MicroPython 測試接收
- 9 Step 5 – 2:從 Raspberry pi (I2C Master) 測試發送資料
- 10 Step 6 – 1:撰寫 MicroPython 測試發送
- 11 Step 6 – 2:從 Raspberry pi (I2C Master) 測試接收資料
- 12 Step 7:把接收和發送合併再一起
準備硬體元件
這次製作需要準備的材料為:
Step 1:使用開源的 I2CResponder
I2C Slave 的實現已經有神人分享 source code 在 github 上面
https://github.com/epmoyer/I2CResponder
大家可以直接把檔案 i2c_responder.py 複製到自己專案裡面,然後我們一步步開始實作。
Step 2:決定 pico 腳位
從 pico 的 pinout 來看,只要是藍色的都可以當作 I2C,而且內建兩組 I2C 供你使用。這邊以 I2C 0 的 GPIO 4, 5 來實作 (對應到 pin 6, 7)。
Step 3:決定 Raspberry pi 腳位
我們以 Raspberry pi 扮演 I2C Master 的角色,如果你手邊有其他的 SBC 也可以替換使用。我們選擇 I2C 1,也就是 GPIO 2, 3 來實作(對應 pin 3, 5)。
Step 4:連結兩者
用跳線將兩者 I2C 的 SCL、SDA 對接,記得也要共接地,不然訊號會過不去哦!
pico | pi |
I2C 0 SDA (pin 6) | I2C 1 SDA (pin 3) |
I2C 0 SCL (pin 7) | I2C 1 SCL (pin 5) |
GND | GND |
Step 5:Raspberry pi 啟用 I2C 硬體
需特別注意的是,Raspberry pi 預設不會啟動 I2C 1,所以我們需要到 raspi-config 做設定。在 console 裡下
sudo raspi-config
選擇 Interfacing Options
選擇 I2C
確認要 enable I2C
確認後即可啟用 I2C
Step 4:Raspberry pi 安裝 python-smbus
要在 Raspberry pi 的 python 環境下使用 I2C,需要先安裝 library。在 console 輸入
pip3 install python-smbus
Step 5 – 1:撰寫 MicroPython 測試接收
首先進入 pico 的 python console,依照我們使用的 I2C channel 以及 GPIO pin 號下去初始化 I2CResponder。I2C Slave 硬體位置我們以 0x50 為示例。
from i2c_responder import I2CResponder
channel = 0
sda_pin = 4
scl_pin = 5
addr = 0x50
i2c_slave = I2CResponder(channel, sda_pin, scl_pin, addr)
初始化完後,我們使用無窮迴圈來監聽 I2C 是否有收到資料,若有就分批把資料抓出來,每次最多抓五十個,最後再印出來
while True:
data = []
while i2c_slave.write_data_is_available():
data += i2c_slave.get_write_data(50)
print('receive data', data)
run 下去後,pico 就會在那邊等待,現在我們切換到 pi 那邊發出資料看看。
Step 5 – 2:從 Raspberry pi (I2C Master) 測試發送資料
一樣我們到 python console 下,用以下程式碼測試。其中
- pico_addr 是我們在宣告 I2CResponder 指定的 slave address
- reg_addr 可以自由給定,這邊以 0x01 為例
- 我們以 write_block_data 一次寫入多筆數據測試看看
import smbus
channel = 1
pico_addr = 0x50
reg_addr = 0x01
i2c_master = smbus.SMBus(channel)
i2c_master.write_block_data(pico_addr, reg_addr, [0x30, 0x40, 0x50])
按下 enter 送出後,回到 pico console 如果看到印出以下訊息,表示成功接收!
研究收到的資料,其中:
- 陣列第一個值表示 Master write 指令指定的 address,由於我們指定 0x01,所以這邊會顯示為 1
- 陣列第二個值表示後面有多少個元素是這次 write 指令所帶的資料,因為我們發送 0x30, 0x40, 0x50,所以為 3
- 後面為這次發送的資料
# [<reg_addr>, <data_count>, <data>, ...]
[1, 3, 30, 40, 50]
依照上述原則,如果今天收到以下資料,會是怎麼樣呢?
[1, 2, 32, 12, 5, 4, 32, 21, 23, 67]
解答:
總共收到兩筆 write 指令,分別為
[1, 2, 32, 12]
[5, 4, 32, 21, 23, 67]
- 第一筆指令指定位置為 0x01,共帶入兩個資料
- 第二筆指令指定位置為 0x05,共帶入四個資料
相對用 C++ 的 arduino 框架寫起來較為麻煩些,需要自行判斷資料斷點。
Step 6 – 1:撰寫 MicroPython 測試發送
剛剛我們測試了 pico I2C Slave 接收資料,但有時候也需要回傳資料到 Master。
流程上必須透過 Master 發出 Read 指令 Slave 才能回送資料,因此 C++ 的實現會透過監聽函數來處理。但 I2CResponder 只能透過迴圈不斷 polling,算是一個小缺點。
我們試著修改剛剛寫的無窮迴圈來實現
while True:
if i2c_slave.read_is_pending():
i2c_slave.put_read_data(0xFF)
run 下去後,pico 就會在那邊等待,現在我們切換到 pi 那邊接收資料看看。
Step 6 – 2:從 Raspberry pi (I2C Master) 測試接收資料
一樣我們到 python console ,用以下程式碼測試。
data = i2c_master.read_word_data(pico_addr, reg_addr)
print('receive', data)
按下 enter 後,如果看到印出以下表示成功
receive 255
Step 7:把接收和發送合併再一起
我們修改前面的 code,讓他在同一個迴圈內可以同時處理兩件事
def check_if_need_response():
if i2c_slave.read_is_pending():
i2c_slave.put_read_data(0xFF)
def check_receive():
data = []
while i2c_slave.write_data_is_available():
data += i2c_slave.get_write_data(50)
print('receive data', data)
while True:
check_receive()
check_if_need_response()
完成!這樣就能夠在 MicroPython 環境下讓 Raspberry pi pico 做 I2C Slave 了!如果覺得我文章內容對你有幫助的話,請在文章後面幫我按 5 個讚!讓我知道大家都喜歡什麼內容哦!
範例原始碼在此下載:github
AI 神經網路自走車
上一篇:用 Raspberry pi pico 與 HC-SR04 做超音波距離測
下一篇:十分鐘快速認識 ROS (Robot Operating System)