ArmSoM---SPI開發(fā)指南
本?主要介紹在Rockchip平臺配置spi接口并且使用的方法
開發(fā)板:ArmSoM-W3
Kernel:5.10.160
OS:Debian11
2. SPI接口概述
SPI(Serial Peripheral Interface),即串行外圍設(shè)備接口,是一種同步的,全雙工的,多設(shè)備的,多主機的通信協(xié)議,用于連接外圍設(shè)備,如ADC、DAC、數(shù)據(jù)存儲器、定時器、接受器等。
2.1 spi接口的結(jié)構(gòu)

主機:主機是SPI總線的控制者,它負責控制數(shù)據(jù)傳輸?shù)姆较蚝蛡鬏斔俣取?br>
從機:從機是SPI總線的被控制者,它根據(jù)主機發(fā)出的指令,發(fā)出或接收數(shù)據(jù)。
MOSI(Master Out Slave In):主機輸出從機輸入,用于傳輸從主機到從機的數(shù)據(jù)。
MISO(Master In Slave Out):主機輸入從機輸出,用于傳輸從從機到主機的數(shù)據(jù)。
CK(Serial Clock):時鐘線,用于同步主機和從機之間的數(shù)據(jù)傳輸。
CS(Chip Select):片選線,用于控制主機和從機之間的數(shù)據(jù)傳輸。
此外,SPI接口還有其他可選項,如中斷線(INT)、復位線(RESET)等。
2.2 SPI接口的工作原理
在SPI接口通信過程中,主機發(fā)出一個片選信號,然后在時鐘信號的控制下,主機發(fā)出一個字節(jié)的數(shù)據(jù),從機接收到數(shù)據(jù)之后,也會發(fā)出一個字節(jié)的數(shù)據(jù),主機接收到數(shù)據(jù)之后,發(fā)出一個片選信號,結(jié)束一次通信。
SPI接口有兩種工作模式:主模式和從模式。主模式下,主機控制從機,從機接收主機發(fā)出的指令。從模式下,從機可以接收主機發(fā)出的指令,并向主機發(fā)送數(shù)據(jù)。
3.SPI硬件接口
RK3588旗艦芯片上可使用的spi接口有5組,ArmSoM SOM-3588-LGA核心板采用LGA 506引腳封裝方式將spi資源全部引出,ArmSoM-W3開發(fā)板上40PIN引腳中有兩組spi接口:

4.spi設(shè)備配置
spi驅(qū)動相關(guān)代碼路徑:
drivers/spi/spi.c spi驅(qū)動框架
drivers/spi/spi-rockchip.c rk spi各接口實現(xiàn)
drivers/spi/spidev.c 創(chuàng)建spi設(shè)備節(jié)點,用戶態(tài)使用。
drivers/spi/spi-rockchip-test.c spi測試驅(qū)動,需要自己手動添加到Makefile編譯
Documentation/spi/spidev_test.c 用戶態(tài)spi測試工具
4.1 配置 DTS 節(jié)點
&spi1 {
status = "okay";
//assigned-clock-rates = <200000000>; //默認不用配置,SPI 設(shè)備工作時鐘
max-freq = <48000000>; /* spi internal clk, don't modify */
//dma-names = "tx","rx"; //使能DMA模式
//rx-sample-delay-ns = <10>; //默認不用配置,讀采樣延時
spi_dev@0 {
?compatible = "rockchip,spidev";
?reg = <0>;
?spi-max-frequency = <12000000>;
?spi-lsb-first; //IO 先傳輸 lsb ?
};
};
status:如果要啟用 SPI,則設(shè)為 okay,如不啟用,設(shè)為 disable。
spi_dev@0:本例子使用 CS0,故此處設(shè)為 0,如果使用 CS1,則設(shè)為 1。
compatible:這里的屬性必須與驅(qū)動中的結(jié)構(gòu)體:of_device_id 中的成員 compatible 保持一致。
reg:此處與 spi_dev@0 保持一致,這里設(shè)為0。
spi-max-frequency:此處設(shè)置 spi 使用的最高頻率,ITX-3588J 最高支持 50000000。
這里使用的驅(qū)動是drivers/spi/spidev.c,驅(qū)動設(shè)備加載注冊成功后,會出現(xiàn)類似這個名字的設(shè)備:/dev/spidev1.0。
一些常見的SPI驅(qū)動API接口:
Linux SPI API:
spi_register_driver()
:注冊SPI設(shè)備驅(qū)動程序。spi_unregister_driver()
:注銷SPI設(shè)備驅(qū)動程序。spi_setup()
:設(shè)置SPI總線和設(shè)備的參數(shù)。spi_sync()
:同步方式進行SPI數(shù)據(jù)傳輸。spi_message_init()
:初始化SPI消息結(jié)構(gòu)。spi_message_add_tail()
:向SPI消息添加傳輸操作。spi_sync()
:同步方式進行SPI數(shù)據(jù)傳輸。spi_transfer()
:進行SPI數(shù)據(jù)傳輸。
SPI驅(qū)動中常用的ioctl請求值,這些請求值用于設(shè)置和讀取SPI設(shè)備的各種參數(shù),包括通信模式、字長、數(shù)據(jù)模式和通信速率等。這些請求值通常用于Linux的SPI驅(qū)動編程。以下是一些常用的SPI ioctl請求值:
SPI_IOC_RD_MODE: 讀取SPI設(shè)備的通信模式。
SPI_IOC_WR_MODE: 設(shè)置SPI設(shè)備的通信模式。
SPI_IOC_RD_MODE32: 讀取SPI設(shè)備的32位通信模式。
SPI_IOC_WR_MODE32: 設(shè)置SPI設(shè)備的32位通信模式。
SPI_IOC_RD_LSB_FIRST: 讀取SPI設(shè)備的LSB(Least Significant Bit)優(yōu)先模式。
SPI_IOC_WR_LSB_FIRST: 設(shè)置SPI設(shè)備的LSB優(yōu)先模式。
SPI_IOC_RD_BITS_PER_WORD: 讀取SPI設(shè)備的字長。
SPI_IOC_WR_BITS_PER_WORD: 設(shè)置SPI設(shè)備的字長。
SPI_IOC_RD_MAX_SPEED_HZ: 讀取SPI設(shè)備的最大通信速率。
SPI_IOC_WR_MAX_SPEED_HZ: 設(shè)置SPI設(shè)備的最大通信速率。
SPI_IOC_MESSAGE(N): 一次進行N次雙向或多次讀寫操作。
4.2 SPI測試實驗
將上述設(shè)備樹以及驅(qū)動修改之后更新板卡的內(nèi)核,通過SPI設(shè)備文件來判斷spi驅(qū)動是否加載成功
這里測試使用的是spi1這一組spi相關(guān)接口,檢查spi設(shè)備:
root@linaro-alip:~# ls /dev/spi*
/dev/spidev1.0
root@linaro-alip:~#
測試SPI接口,使用/dev/spidev1.0設(shè)備節(jié)點,實現(xiàn)短接MOSI和MISO線路以自發(fā)自收數(shù)據(jù),同時在未短接時報告錯誤
4.2.1 硬件連接
將SPI1的 MIOS與MOSI引腳(板卡上的7和8)短接即可,如下圖所示

4.2.2 測試程序
用戶應用層使用spidev驅(qū)動的步驟如下:
打開SPI設(shè)備文件:用戶可以通過打開/dev/spidevX.Y文件來訪問SPI設(shè)備,其中X是SPI控制器的編號,Y是SPI設(shè)備的編號。
配置SPI參數(shù):用戶可以使用ioctl命令SPI_IOC_WR_MODE、SPI_IOC_WR_BITS_PER_WORD和SPI_IOC_WR_MAX_SPEED_HZ來設(shè)置SPI模式、數(shù)據(jù)位數(shù)和時鐘速度等參數(shù)。
發(fā)送和接收數(shù)據(jù):用戶可以使用read和write系統(tǒng)調(diào)用來發(fā)送和接收SPI數(shù)據(jù)。寫入的數(shù)據(jù)將被傳輸?shù)絊PI設(shè)備,而從設(shè)備讀取的數(shù)據(jù)將被存儲在用戶提供的緩沖區(qū)中。
關(guān)閉SPI設(shè)備文件:當不再需要與SPI設(shè)備通信時,用戶應該關(guān)閉SPI設(shè)備文件。
總結(jié)起來,spidev驅(qū)動提供了一種簡單而靈活的方式來與SPI設(shè)備進行通信,使得用戶可以輕松地在Linux系統(tǒng)上開發(fā)和控制SPI設(shè)備。
armsom團隊 的測試程序spi_test.c如下:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#define SPI_DEV_PATH "/dev/spidev1.0"
int fd;
static unsigned mode = SPI_MODE_0;
static uint8_t bits = 8;
static uint32_t speed = 1000000; // 設(shè)置SPI速度為1MHz
static uint16_t delay;
void transfer(int fd, uint8_t const *tx, uint8_t *rx, size_t len)
{
? ?int ret;
? ?struct spi_ioc_transfer tr = {
? ? ? ?.tx_buf = (unsigned long)tx,
? ? ? ?.rx_buf = (unsigned long)rx,
? ? ? ?.len = len,
? ? ? ?.delay_usecs = delay,
? ? ? ?.speed_hz = speed,
? ? ? ?.bits_per_word = bits,
? ? ? ?.cs_change = 0, // 設(shè)置為1以在每次傳輸前切換片選,這里不切換片選
? ?};
? ?ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
? ?if (ret < 1) {
? ? ? ?perror("SPI transfer failed");
? ?}
}
void spi_init(void)
{
? ?int ret;
? ?// 打開 SPI 設(shè)備
? ?fd = open(SPI_DEV_PATH, O_RDWR);
? ?if (fd < 0) {
? ? ? ?perror("Can't open SPI device");
? ? ? ?exit(1);
? ?}
? ?// 設(shè)置 SPI 工作模式
? ?ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
? ?if (ret == -1) {
? ? ? ?perror("Can't set SPI mode");
? ? ? ?exit(1);
? ?}
? ?// 設(shè)置位數(shù)
? ?ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
? ?if (ret == -1) {
? ? ? ?perror("Can't set bits per word");
? ? ? ?exit(1);
? ?}
? ?// 設(shè)置SPI速度
? ?ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
? ?if (ret == -1) {
? ? ? ?perror("Can't set max speed");
? ? ? ?exit(1);
? ?}
? ?// 打印設(shè)置
? ?printf("SPI mode: 0x%x\n", mode);
? ?printf("Bits per word: %d\n", bits);
? ?printf("Max speed: %d Hz\n", speed);
}
int main(int argc, char *argv[])
{
? ?if (argc != 2) {
? ? ? ?printf("Usage: %s <string_to_send>\n", argv[0]);
? ? ? ?return 1;
? ?}
? ?char *tx_buffer = argv[1]; // 獲取要發(fā)送的字符串作為命令行參數(shù)
? ?// 初始化SPI接口
? ?spi_init();
? ?// 設(shè)置要接收數(shù)據(jù)的緩沖區(qū)
? ?unsigned char rx_buffer[strlen(tx_buffer) + 1];
? ?// 執(zhí)行SPI數(shù)據(jù)傳輸
? ?transfer(fd, tx_buffer, rx_buffer, strlen(tx_buffer));
? ?// 打印發(fā)送和接收的數(shù)據(jù)
? ?printf("Sent: %s\n", tx_buffer);
? ?printf("Received: %s\n", rx_buffer);
? ?// 關(guān)閉SPI設(shè)備
? ?close(fd);
? ?return 0;
}
4.2.3 編譯&運行
開發(fā)板上下載gcc編譯器:
apt upgrade
apt update
apt install gcc
編譯:
gcc spi_test.c -o spi
短接與不短接的運行情況如下:
