Mâm xôi như một SLAVE I2C


9

Có ai biết cách thiết lập Raspberry Pi 3 như một nô lệ I2C (trong C ++) không?

Trước khi ai đó trả lời rằng điều đó là không thể, điều đó là có thể.

Đây là một liên kết nói rằng nó có thể (tài liệu pigpio)

Đây là mã tôi đang cố sử dụng (từ liên kết ở trên):

#include <pigpio.h>
#include <iostream>
using namespace std;


int main(){
    bsc_xfer_t xfer;
    gpioInitialise();
    xfer.control = (0x0A<<16) | 0x305; // Set I2C slave Address to 0x0A
    int status = bscXfer(&xfer);
    if (status >= 0)
    {
        xfer.rxCnt = 0;
        while(1){
            if (xfer.rxCnt > 0){
                cout << xfer.rxBuf;
            }
       }    
    }

    return 1;
} 

Các kết nối của tôi là SCL đến BCM GPIO 18 và SDA đến BCM GPIO 19. (Đây phải là cách khác: SDA là GPIO 18, SCL là GPIO 19)

Các thiết bị còn lại trên xe buýt đều ổn (đã được thử nghiệm).

Tôi cũng đã kết nối SDA1 và SCL1 của quả mâm xôi với xe buýt I2C này để tôi có thể làm gì i2cdetect -y 1để liệt kê các thiết bị I2C trên xe buýt.

Vấn đề với mã này là mặc dù Địa chỉ chính xác của Raspberry Pi, nhưng i2cdetect -y 1có thể thấy địa chỉ 0x0A, tôi không thể nhận được bất kỳ tin nhắn nào.

Nếu tôi chạy trình thám thính trong quy trình khác, tôi có thể nhận được thông báo chính xác.

Câu trả lời:


6

Điều này gần như giống hệt với câu hỏi trước đây của bạn. Có lẽ bạn nên chỉnh sửa nó thay vì hỏi một câu hỏi mới.

Bạn cần bscXfer ở trong vòng lặp while. Đó là cách cấu trúc xfer được cập nhật với thông tin mới.


Cảm ơn câu trả lời, nó đã làm việc! (Tôi không có đủ danh tiếng để thực hiện +1 cho câu trả lời của bạn)
Sebastião

@ Sebastião Thật tốt khi biết nó hoạt động. Tôi thường chỉ tìm ra khi một cái gì đó không hoạt động :(
joan

Cũng đáng đề cập rằng chúng ta nên xóa bộ đệm sau khi đọc nó bởi vì nếu không nó có thùng rác từ các tin nhắn cũ. memset( xfer.rxBuf, '\0', sizeof(char)*BSC_FIFO_SIZE );sẽ làm điều đó
Sebastião

Bạn có biết cách đặt tốc độ bus I2C và phù thủy là tốc độ mặc định và tối đa không?
Sebastião

Tốc độ mặc định là 100kbps. Tôi nghĩ tốc độ tối đa là 3,8Mps. Xem (từ bộ nhớ) / boot / lớp phủ / README để biết chi tiết.
joan

10

Vì chủ đề này được trình bày rất kém và đoạn trích của Sebastião và đã giúp tôi giải quyết vấn đề này, tôi muốn thêm một giải pháp hoàn chỉnh về cách thiết lập RaspberryPi ngay tại đây (đã thử nghiệm trên RPi 3 và Zero W)!

Thiết lập một nô lệ làm việc:

Chuẩn bị

Hãy chắc chắn đã nhận xét dòng này trong /boot/config.txt của bạn :

dtparam=i2c_arm=on

Phụ thuộc

Tiếp theo, cài đặt g ++pigpio bằng lệnh này:

sudo apt install g++ pigpio

Chốt

Như đã nêu trong tài liệu, các chân là GPIO 18 (SDA) và 19 (SCL) . Trang web này giúp định vị chúng trên RaspberryPi của bạn (bố cục phải giống với RaspberryPi 2, 3, Zero và Zero W)

Sơ đồ này từ trang web sẽ giúp: Sơ đồ GPIO RaspberryPi

Phần mềm

Giải trình

Như đã nói, giải pháp này dựa trên đoạn mã của Sebastião . Tôi đã sửa đổi nó với sự giúp đỡ của giải pháp của joan .

Tôi cũng đã cố gắng hiểu ý nghĩa của mã bằng cách sử dụng tài liệu cho hàm bscXfer.

Trong mã nguồn, dữ liệu trong bsc_xfer_tstruct được sử dụng thêm hoặc nhận tin nhắn nhưng chúng chỉ được áp dụng khi bscXferđược thực thi với địa chỉ cho struct (như joan đã chỉ ra trong giải pháp của mình).

Số nguyên bsc_xfer_t.control có một vai trò khá đặc biệt, trong đó nêu rõ nhiều thứ như địa chỉ I²C nô lệ và nhiều trạng thái khác được ghi lại trong tài liệu .

Để hiểu rõ hơn về điều này, tôi đã sao chép các phần quan trọng của tài liệu vào mã nguồn và thay đổi một số thứ hoặc thuê ngoài chúng thành các chức năng riêng biệt.

Mã nguồn

Địa chỉ có thể được thay đổi thành bất cứ điều gì bạn muốn (miễn là không vượt quá 127 (còn gọi là 7F (16) hoặc 1111111 (2) ).

Vì tôi không giỏi về C ++, bạn phải bình luận, những gì bạn muốn bạn sẽ không làm. Bạn nên chạy closeSlavechức năng sau khi hoàn thành thử nghiệm của mình.

Ở đây tệp SlaveTest.cpp :

#include <pigpio.h>
#include <iostream>

using namespace std;

void runSlave();
void closeSlave();
int getControlBits(int, bool);

const int slaveAddress = 0x03; // <-- Your address of choice
bsc_xfer_t xfer; // Struct to control data flow

int main(){
    // Chose one of those two lines (comment the other out):
    runSlave();
    //closeSlave();

    return 0;
}

void runSlave() {
    gpioInitialise();
    cout << "Initialized GPIOs\n";
    // Close old device (if any)
    xfer.control = getControlBits(slaveAddress, false); // To avoid conflicts when restarting
    bscXfer(&xfer);
    // Set I2C slave Address to 0x0A
    xfer.control = getControlBits(slaveAddress, true);
    int status = bscXfer(&xfer); // Should now be visible in I2C-Scanners

    if (status >= 0)
    {
        cout << "Opened slave\n";
        xfer.rxCnt = 0;
        while(1){
            bscXfer(&xfer);
            if(xfer.rxCnt > 0) {
                cout << "Received " << xfer.rxCnt << " bytes: ";
                for(int i = 0; i < xfer.rxCnt; i++)
                    cout << xfer.rxBuf[i];
                cout << "\n";
            }
            //if (xfer.rxCnt > 0){
            //    cout << xfer.rxBuf;
            //}
    }
    }else
        cout << "Failed to open slave!!!\n";
}

void closeSlave() {
    gpioInitialise();
    cout << "Initialized GPIOs\n";

    xfer.control = getControlBits(slaveAddress, false);
    bscXfer(&xfer);
    cout << "Closed slave.\n";

    gpioTerminate();
    cout << "Terminated GPIOs.\n";
}


int getControlBits(int address /* max 127 */, bool open) {
    /*
    Excerpt from http://abyz.me.uk/rpi/pigpio/cif.html#bscXfer regarding the control bits:

    22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
    a  a  a  a  a  a  a  -  -  IT HC TF IR RE TE BK EC ES PL PH I2 SP EN

    Bits 0-13 are copied unchanged to the BSC CR register. See pages 163-165 of the Broadcom 
    peripherals document for full details. 

    aaaaaaa defines the I2C slave address (only relevant in I2C mode)
    IT  invert transmit status flags
    HC  enable host control
    TF  enable test FIFO
    IR  invert receive status flags
    RE  enable receive
    TE  enable transmit
    BK  abort operation and clear FIFOs
    EC  send control register as first I2C byte
    ES  send status register as first I2C byte
    PL  set SPI polarity high
    PH  set SPI phase high
    I2  enable I2C mode
    SP  enable SPI mode
    EN  enable BSC peripheral
    */

    // Flags like this: 0b/*IT:*/0/*HC:*/0/*TF:*/0/*IR:*/0/*RE:*/0/*TE:*/0/*BK:*/0/*EC:*/0/*ES:*/0/*PL:*/0/*PH:*/0/*I2:*/0/*SP:*/0/*EN:*/0;

    int flags;
    if(open)
        flags = /*RE:*/ (1 << 9) | /*TE:*/ (1 << 8) | /*I2:*/ (1 << 2) | /*EN:*/ (1 << 0);
    else // Close/Abort
        flags = /*BK:*/ (1 << 7) | /*I2:*/ (0 << 2) | /*EN:*/ (0 << 0);

    return (address << 16 /*= to the start of significant bits*/) | flags;
}

Lưu ý rằng trong một số trường hợp, bạn muốn byte đầu tiên là byte lệnh chứ không phải là một phần của dữ liệu chung của bạn.

EDIT: Cũng lưu ý rằng, mặc dù điều này hoạt động tốt cho mục đích thử nghiệm, @crasic đã chỉ ra (nhận xét đầu tiên), rằng có một cách tốt hơn (nhưng cũng được ghi chép kém) với các sự kiện thay vì sử dụng một vòng lặp vô tận. Điều đó sẽ tốt hơn khi được sử dụng với nhiều ứng dụng.

Biên dịch & thực thi

Bạn có thể biên dịch cái này với

g++ slaveTest.cpp -lpthread -lpigpio -o slaveTest

và thực hiện với

sudo ./slaveTest

Kiểm tra với một bậc thầy

Để nhanh chóng kiểm tra nó một bản gốc, một tùy chọn phổ biến là sử dụng smbus, dễ dàng hơn nhiều và có thể được tìm thấy bằng cách tìm kiếm đơn giản với một công cụ tìm kiếm bạn chọn.

Lựa chọn của tôi trong ngắn hạn:

  • Tra cứu các chân SDA, SCL (chúng khác nhau như một bậc thầy!)
  • Chạy sudo apt install python3 python3-smbus
  • Sao chép đoạn mã dưới đây để ví dụ như masterI2C.py vào RPi của bạn
  • Mở một vỏ python với đoạn trích này bằng cách sử dụng python3 -i masterI2C.py
  • Chạy sendData(0x03, 'Hello World of I2C!')để gửi dữ liệu

Bậc thầy trăn:

import smbus

bus = smbus.SMBus(1)

def sendData(slaveAddress, data):
    intsOfData = list(map(ord, data))
    bus.write_i2c_block_data(slaveAddress, intsOfData[0], intsOfData[1:])

Hình ảnh thử nghiệm này:

Tự kiểm tra


Tôi hy vọng rằng tôi có thể làm rõ chủ đề này cho những người khác.

(Khi gặp sự cố bất ngờ, khởi động lại pi Raspberry nô lệ của tôi thường giúp tôi.)


2
Câu trả lời chính xác. Hai gợi ý cho sự hoàn thiện trong học tập. Sử dụng vòng lặp while là ổn đối với tải tính toán thấp và truyền thông chậm, và sao chép những gì người ta sẽ làm trên nhiều bộ vi điều khiển đơn giản, nhưng cách tốt hơn là sử dụng sự kiện bằng cách sử dụng ngắt trên TxStatus. Đây là tài liệu kém trên BCM, nhưng có sẵn. # 2 Cũng giống như tài liệu kém, là sử dụng DMA. Nếu bạn quan tâm đến việc phát triển một giải pháp I2C Slave hoàn chỉnh, chơi tốt với các ứng dụng khác trên hệ thống linux, đó sẽ là nơi tiếp theo để bắt đầu đào.
crasic

1
@crasic Thật tuyệt khi nghe điều đó. Vì tôi không vào C ++ (ngay bây giờ), tôi thực sự không biết làm thế nào để làm điều đó. Nhưng tôi sẽ để lại một ghi chú trong giải pháp cho những người khác biết làm thế nào cho đúng.
LinusCDE98
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.