3.22. SPI trong mã lệnh¶
machine.SPI bọc một bộ điều khiển SPI phần cứng; các đường CS là các đầu ra Pin thông thường được quản lý bởi tập lệnh. Tạo một phiên bản SPI với id bus, tốc độ đồng hồ mong muốn, và (nếu cần) chế độ:
from machine import SPI, Pin
spi = SPI(0, baudrate=1_000_000, polarity=0, phase=0)
cs = Pin("P3", Pin.OUT, value=1) # CS idle high
id chọn khối SPI phần cứng nào sẽ sử dụng; các số có sẵn và các chân SCK/MOSI/MISO tương ứng phụ thuộc vào bo mạch (xem Bo mạch OpenMV). baudrate là tần số SCK tính bằng hertz -- tốc độ thực tế mà phần cứng đạt được có thể thấp hơn một chút do phân chia đồng hồ, và giá trị được in của đối tượng SPI sẽ hiển thị điều này.
Chân CS được khởi tạo với value=1 để nó ở trạng thái không được kích hoạt. Mỗi giao dịch kích hoạt CS (kéo xuống thấp), dịch chuyển các byte, và giải phóng CS (kéo lên cao) trở lại.
3.22.1. Đọc, ghi, trao đổi¶
Ba phương thức bao gồm các trường hợp thông dụng:
cs.value(0)
spi.write(b"\x10\x20\x30") # send 3 bytes, ignore what comes back
cs.value(1)
cs.value(0)
data = spi.read(4) # read 4 bytes; sends 0x00 while reading
cs.value(1)
rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x9F\x00", rx) # send 0x9F, 0x00; receive 2 bytes
cs.value(1)
write() là đường dẫn nhanh chỉ ghi; bộ điều khiển đẩy các byte và loại bỏ bất cứ thứ gì ngoại vi gửi lại trên MISO. read() là hình ảnh đảo ngược -- nó tạo ra N xung SCK trong khi gửi một byte giả cố định (0 theo mặc định) trên MOSI và lưu các byte MISO. write_readinto() là dạng song công hoàn toàn: nó gửi các byte từ một bộ đệm và lưu các byte MISO đồng thời vào một bộ đệm khác. Nhiều ngoại vi sử dụng mẫu này -- "gửi một byte lệnh, rồi đọc phản hồi trong lần truyền tiếp theo" -- do đó hai thao tác này tự nhiên phù hợp với một lệnh gọi write_readinto.
Hầu hết các ngoại vi yêu cầu đường CS phải duy trì được kích hoạt trong toàn bộ giao dịch (từ byte lệnh đến byte phản hồi), vì vậy hãy giữ các dấu ngoặc cs.value(0) / cs.value(1) xung quanh toàn bộ chuỗi, không phải xung quanh mỗi lần gọi phương thức.
3.22.2. Đọc cảm biến điển hình¶
Nhiều cảm biến SPI tổ chức trạng thái của chúng dưới dạng một tập hợp các thanh ghi nội bộ và tuân theo cùng một cấu trúc trao đổi: gửi địa chỉ thanh ghi (với cờ đọc/ghi ở bit trên cùng), sau đó đọc hoặc ghi các byte của thanh ghi. Đọc thanh ghi 0x0F trên một thiết bị như vậy:
rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x8F\x00", rx) # 0x80 = "read" flag, then reg 0x0F
cs.value(1)
register_value = rx[1]
Byte MISO đầu tiên là rác (thiết bị vẫn đang nhận lệnh vào thời điểm đó); byte MISO thứ hai chứa nội dung thanh ghi. Định dạng byte lệnh chính xác -- bit nào là cờ đọc/ghi, địa chỉ có tự động tăng khi đọc nhiều byte hay không -- được mô tả trong tờ dữ liệu của thiết bị.
3.22.3. Bit-banging¶
Phiên bản SPI ở trên sử dụng một khối SPI phần cứng: một ngoại vi chuyên dụng bên trong MCU với thanh ghi dịch chuyển và bộ tạo đồng hồ riêng để tạo ra các dạng sóng SCK / MOSI / MISO bằng phần cứng. Phần mềm chỉ cần đưa cho nó một byte; các bit di chuyển trên dây mà không cần CPU can thiệp thêm, cho phép CPU tự do làm các công việc khác song song.
Phương án thay thế là bit-banging: phần mềm lặp qua từng bit và điều khiển trực tiếp các chân GPIO để tạo ra cùng dạng sóng đó. Không có ngoại vi phần cứng nào tham gia -- CPU điều khiển SCK xuống thấp, đặt MOSI, kéo SCK lên cao, lấy mẫu MISO, và cứ như vậy cho mỗi bit của mỗi byte. Điều này chiếm dụng CPU trong suốt giao dịch và chạy chậm hơn khối phần cứng, nhưng nó hoạt động trên bất kỳ chân nào và không cần một khối phần cứng phải rảnh.
machine.SoftSPI là hiện thực bit-bang của cùng một SPI API:
from machine import SoftSPI, Pin
spi = SoftSPI(baudrate=500_000, polarity=0, phase=0,
sck=Pin("P2"), mosi=Pin("P0"), miso=Pin("P1"))
Sử dụng nó khi thiết bị cần nằm trên các chân không được nối với khối SPI phần cứng, hoặc khi tất cả các khối phần cứng đều đang được sử dụng. 500 kHz là giới hạn thoải mái trên hầu hết các cam; CPU bận trong suốt giao dịch.