使用Raspberry Pi 4B SPI_1接口 控制A5133做Basic T/RX Link(本篇使用SPI1接口,需手動驅動SPI)

SamYang2024-10-23Linux17
  • 前幾篇介紹都是使用Raspberry Pi的SPI_0接口,這方面的現成library較多,如WiringPi ,BCM2835這些Lib 默認的SPI接口都是使用SPI_0

  • 若要使用Raspberry Pi的SPI1接口,就需改成使用linux底層的spidev接口與ioctl函數

  1. 首先先打開Raspberry Pi的SPI1接口

sudo nano /boot/firmware/config.txt
  • 打開SPI1並定義CS pin腳位

dtoverlay=spi1-1cs,cs0_pin=16
  • 存檔後重開RPI

  • 查看SPI1是否成功開啟

ls /dev/spidev1.*
  • 表示已成功開啟SPI_1

image.png

  • 控制A5133的邏輯與行為這邊就不再贅述,基本上就如先前說明的都是些SPI的Write/Read 操作

Demo Code:<Raspberry Pi_SPI_1 FIFO mode_0.5M_1M_2M_4Mbps_Colad9p_V0.7.zip>

P.S.:V0.7為Amiccom第0.7版Ref code抄寫過來的

  • 因為有使用到A5133的GIO2當作RPI的輸入PIN用來判斷RF WTR狀態,因此我們一樣有include bcm2835 lib,只是用來方便判斷I/O使用

  • 全域定義SPI的相關參數,ex:使用的SPI接口,Mode, Speed

#define SPI_DEVICE "/dev/spidev1.0"  // SPI1, CS0
#define SPI_MODE SPI_MODE_0         // SPI 模式 0
#define SPI_BITS_PER_WORD 8         // 每字傳輸 8 位元
#define SPI_SPEED 10000000            // SPI 速度 10 MHz

代碼重點說明:

P.S.請注意spi_fd需定義在main外面的全域變數 int spi_fd;  // SPI 文件描述符

  • 這邊只說明有關SPI如何驅動SPI1使RPI可以正常發出Write/Read等SPI Format

  • 有關A5133的RF流程ex:InitRF,CalRF,TX.RXCMD都與之前的一樣

  • 因為沒有lib,我們就必須手動定義每個SPI會用到的行為,底下說明

Ex:

Write 1 Byte==>使用在strobeCMD,只需要向A5133 發出1Byte的SPI Format

Write 2 Byte==>使用在對REG寫Value時,因為Amiccom的RF IC 寫入REG 值都是採用All in one CS,所以我們就必須要向A5133連續Write 2Byte的SPI Format

Write 9 Byte==>使用在對ID Reg連續"寫入"8Byte ID Code時,一樣All in one CS,1Byte Addr+8Byte Value=9Byte的SPI Format

Write 1 Byte Read 8Byte==>使用在對ID Reg連續"讀出"8Byte ID Code時,1Byte Addr+8Byte Value=9Byte的SPI Format

Write 1 Byte Read 1Byte==>使用在對REG"讀"Value時

Write 3 Byte==>使用在對FIFO Len REG"寫"Value時,因為A5133可以FIFO Ext to 4KByte,因此FIFO Len Reg又分High& Low Byte

Write "len" byte==>len表示任意數,使用在WriteTXData時,因為FIFO Len是可調的,因此就需要有的SPI Format是可調整Write長度內容

Write 1 Byte Read 64Byte==>使用在RxPacket();,對RXBuffer Reg讀出64Byte內容

int init_spi1(int *spi_fd);
int write_spi1_1byte(int spi_fd, uint8_t data);
int write_spi1_2bytes(int spi_fd, uint8_t *data);
int write_spi1_9bytes(int spi_fd, uint8_t *data);
int write1_read8_spi1(int spi_fd, uint8_t write_byte, uint8_t *read_data);
int write1_read1_in_cs(int spi_fd, uint8_t write_byte, uint8_t *read_byte);
int write3_spi1(int spi_fd, uint8_t *data);
int write_len_spi1(int spi_fd, uint8_t *data, size_t len);
int write1_read64_in_cs(int spi_fd, uint8_t write_byte, uint8_t *read_buffer);
  • 基本上完成上述的所有相關SPI Format,就可以開始拼湊A5133的Code

底下說明SPI設定函數

範例1:RF_StrobeCmd函數,這裡會調用write_spi1_1byte函數,這裡指的write_byte=cmd

void RF_StrobeCmd(uint8_t cmd)
{
    uint8_t write_byte=cmd; 
// 執行 1 字節寫入
    if (write_spi1_1byte(spi_fd, write_byte) != 0) {
        printf("SPI 寫入CMD失敗。\n");
    } else {
        printf("已成功向 SPI1 寫入CMD數據:0x%02X\n", write_byte);
    }
}
  • 這裡指的data=StrobeCmd函數write_byte=cmd

// 向 SPI1 寫入 1 字節的函數
int write_spi1_1byte(int spi_fd, uint8_t data) {
    uint8_t tx_buf[1] = {data};  // 傳輸緩衝區

    // 設置 SPI 傳輸結構
    struct spi_ioc_transfer spi_tr = {
        .tx_buf = (unsigned long)tx_buf,
        .rx_buf = 0,
        .len = 1,  // 1 字節寫入長度
        .speed_hz = SPI_SPEED,//定義SPI CLK速度
        .bits_per_word = SPI_BITS_PER_WORD,//定義1Word=8bit
        .delay_usecs = 0,
    };

    // 執行 1次SPI 傳輸
    int ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &spi_tr);
    if (ret < 1) {
        perror("SPI 傳輸失敗");
        return -1;
    }

    return 0;
}

範例2:Write 1 Byte Read 8Byte

這邊舉例RF_ReadID函數,這邊會調用到write1_read8_spi函數,來完成對0x06Reg做read動作,並從MISO Pin Read 8Byte的ID Code

  • write_byte=0x46=Read 0x06(IDCODE_REG)

  • read_byte預留8byte數組

void RF_ReadID(Uint8* ptr)
{ 
	uint8_t write_byte=IDCODE_REG | 0x40;
        uint8_t read_data[8];
	// 在同一个片选信号周期内发送 1 字节并接收 8 字节
        if (write1_read8_spi1(spi_fd, write_byte, read_data) != 0) {
        printf("SPI Read ID 失敗。\n");
        }
	else
	{
         	printf("已成功Read ID Code=");
	 	for (int i=0; i<8; i++)
		 {
          		*ptr++ =  read_data[i];
	  		 printf("0x%02X ",  read_data[i]);
	 	 }
	   printf("\n");
	}
}
  • write1_read8_spi1函數

  • tx_buf=write_byte

  • rx_buf=read_byte

  • 最後記得將rx_buf第1Byte~第8Byte複製到read_data裡,因為SPI是全雙工,所以第0Byte的timing被write_byte占用,所以才會需要複製第1Byte~第8Byte

// 向 SPI1 寫入 1 字節並讀取 8 字節的函數
int write1_read8_spi1(int spi_fd, uint8_t write_byte, uint8_t *read_data) {
    uint8_t tx_buf[9] = {0};  // 傳輸緩衝區
    uint8_t rx_buf[9] = {0};  // 接收緩衝區

    // 設置要發送的 1 字節
    tx_buf[0] = write_byte;

    // 設置 SPI 傳輸結構
    struct spi_ioc_transfer spi_tr = {
        .tx_buf = (unsigned long)tx_buf,
        .rx_buf = (unsigned long)rx_buf,
        .len = 9,  // 1 字節寫入 + 8 字節讀取
        .speed_hz = SPI_SPEED,
        .bits_per_word = SPI_BITS_PER_WORD,
        .delay_usecs = 0,
    };

    // 執行 SPI 傳輸
    int ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &spi_tr);
    if (ret < 1) {
        perror("SPI 傳輸失敗");
        return -1;
    }

    // 將接收到的數據拷貝到 read_data 中
    memcpy(read_data, &rx_buf[1], 8);
    return 0;
}

範例3:RF_ReadReg函數,這裡會調用write1_read1_in_cs函數,也就是發1byte然後Read 1 Byte Value,這用在讀取Reg數值時使用


Uint8 RF_ReadReg(Uint8 addr)
{
    uint8_t write_byte = addr | 0x40;  // 要寫入的 1 字節
    uint8_t read_byte = 0;      // 用於存儲讀取的 1 字節

    // 執行在一次 CS 片選內的 1 字節寫入和讀取
    if (write1_read1_in_cs(spi_fd, write_byte, &read_byte) != 0) {
        printf("SPI 1 字節寫入和 1 字節讀取失敗。\n");
    } else {
        printf("已成功向 SPI1 寫入 1 字節:0x%02X\n", write_byte);
        printf("已成功從 SPI1 讀取 1 字節:0x%02X\n", read_byte);
    }
    return read_byte;
}
  • write1_read1_in_cs

  • 因為總SPI還是需要2Byte,因此跟之前Write1byte_Read8byte,寫法差不多,tx_buf & rx_buf都要定義2byte數組

  • 其中tx_buf因為1byte=write_byte,另一byte沒用到就填入0x00

  • 最後記得Return時要return rx_buf的第2字節=rx_buf[1],因為第1字節=rx_buf[0]的timing已經被tx_buf給佔用,所以要回傳時記得是回傳第2字節才對

// 在一次 CS 片選內寫入 1 字節並讀取 1 字節的函數
int write1_read1_in_cs(int spi_fd, uint8_t write_byte, uint8_t *read_byte) {
    uint8_t tx_buf[2] = {write_byte, 0x00};  // 傳輸緩衝區:第一個字節為寫入,第二個字節為讀取
    uint8_t rx_buf[2] = {0};                // 接收緩衝區

    // 設置 SPI 傳輸結構
    struct spi_ioc_transfer spi_tr = {
        .tx_buf = (unsigned long)tx_buf,
        .rx_buf = (unsigned long)rx_buf,
        .len = 2,  // 傳輸 2 字節
        .speed_hz = SPI_SPEED,
        .bits_per_word = SPI_BITS_PER_WORD,
        .delay_usecs = 0,
    };

    // 執行 SPI 傳輸
    int ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &spi_tr);
    if (ret < 1) {
        perror("SPI 傳輸失敗");
        return -1;
    }

    // 將接收到的數據存入 read_byte
    *read_byte = rx_buf[1];
    return 0;
}

基本上掌握住這些,其他的write_spi1_2bytes,write_spi1_9bytes,write_len_spi1,write3_spi1,write1_read64_in_cs函數就都是差不多的形式




















发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。