使用spi-gpio-custom模組配置SPI匯流排

lsgxeva發表於2024-10-26

使用spi-gpio-custom模組配置SPI匯流排

來源 https://www.xuzhe.tj.cn/index.php/2023/10/26/spi-gpio-customspi/

參考專欄 https://www.zhihu.com/column/c_1698084667767709696

1. 引言

SPI(Serial Peripheral Interface)是一種常見的序列通訊協議,廣泛應用於微控制器與外部裝置的連線。

Linux核心中的spi-gpio與spi-bitbang模組可使用GPIO引腳進行SPI的位操作,spidev模組可將SPI暴露給使用者空間。但是,這些模組並不能“直接”使用:它們被其他核心驅動程式使用。沒有辦法動態地說“我想在這些引腳上使用一個SPI”。相反,我們需要重新配置、編譯核心。

spi-gpio-custom模組允許動態配置SPI匯流排及其節點,無需重新編譯核心。對於測試、概念驗證非常方便。此外,spi-gpio-custom模組的速度也相當不錯,測試顯示它可以達到1 MHz以上。

2. 安裝spi-gpio-custom

以openwrt系統為例,安裝模組包是最方便的。如果還要修改其它的openwrt構建設定,可在make menuconfig中選上此模組,然後重新編譯openwrt。

2.1 離線安裝模組包

線上安裝最便捷,會自動安裝依賴包,但需要網路條件。離線安裝需要先下載好所需要的包,之後手動安裝。

可使用LuCI網頁介面安裝,操作簡單。簡單直觀,本文不再介紹。

下面介紹命令列安裝方法,可在沒有配置LuCI功能,或LuCI頁面出錯打不開時使用。

下載包:kernel、kmod-spi-bitbang、kmod-spi-dev、kmod-spi-gpio、kmod-spi-gpio-custom。其中只kmod-spi-gpio-custom是需要的spi-gpio-custom包,其它均為依賴包。

包要與作業系統及硬體主控對上。比如包名:

kmod-spi-gpio-custom_3.18.29-1-8876e460a901ba0991338a5b1846e893_ramips_24kec.ipk 

其中,各部分的含義為:

  • kmod-: 核心模組(Kernel Module)。
  • spi-gpio-custom: 核心模組的具體名稱。
  • 3.18.29: 這是該核心模組所依賴的 Linux 核心版本。
  • -1: 這是該軟體包版本的修訂號,通常用於追蹤小的更新或修補。
  • 8876e460a901ba0991338a5b1846e893: 這是一個校驗和或唯一識別符號,用於確保軟體包的完整性和唯一性。
  • ramips: 這表示該模組是為基於 Ralink/MediaTek MIPS 架構(ramips)的裝置編譯的。
  • 24kec: 這是該 MIPS 架構下的一個特定型號或子架構。
  • .ipk: 這是軟體包的副檔名,表明它是一個用於 OpenWRT 的安裝包。

模組包的安裝命令opkg install。如安裝kmod-spi-gpio-custom包:

opkg install kmod-spi-gpio-custom*.ipk

在命令中,可用*號用替代後續字元。

按照這個順序依次安裝:kernel、kmod-spi-bitbang、kmod-spi-dev、kmod-spi-gpio、kmod-spi-gpio-custom。

檢驗是否安裝成功:

opkg list-installed | grep spi-gpio

看到kmod-spi-gpio-custom,說明已安裝好。

安裝時,如還遇到缺少依賴包問題,需根據提示,依次安裝。

2.2 編譯安裝

在系統原始碼根目錄,輸入配置指令:

make menuconfig

在kernel model -> spi support,選中”kmod-spi-gpio-custom”,系統會自動選中三個依賴包:“kmod-spi-bitbang”、“kmod-spi-dev”、“kmod-spi-gpio”。

儲存退出後,重新編譯openwrt:

make j=2 //雙執行緒編譯

3 使用spi-gpio-custom模組配置SPI匯流排

spi-gpio-custom 的便利就在於可動態配置SPI匯流排,不需“修改dts檔案、編譯系統”的繁瑣操作。也就是說,透過spi-gpio-custom使用spi時,dts檔案中可以沒有任何spi功能的定義。

常規的一個螢幕介面引腳定義

介面功能
MISO SPI輸出
MOSI SPI輸入
CLK SPI時鐘輸入
DC 資料/命令 切換控制,低電平表示輸入命令,高電平表示輸入資料
RES 復位螢幕
BLK 背光控制
VCC 電源
GND 接地

配置舉例

配置一個ID為1的匯流排,使用GPIO3作為CLK,GPIO4作為MOSI,GPIO5作為MISO,在SPI模式0下工作、最大頻率為20KHz、GPIO2作為CS的裝置。

命令引數 功能描述
<id> ID to used as device_id for the corresponding bus (required)
<sck> GPIO pin ID to be used for bus SCK (required)
<mosi> GPIO pin ID to be used for bus MOSI (required*)
<miso> GPIO pin ID to be used for bus MISO (required*)
<modeX> Mode configuration for slave X in the bus (required) * (see /include/linux/spi/spi.h)
<maxfreqX> Maximum clock frequency in Hz for slave X in the bus (required)
<csX> GPIO pin ID to be used for slave X CS (required**)

執行命令:

insmod spi-gpio-custom bus0=1,3,4,5,0,20000,2

請注意GPIOx中的x,不是引腳編號(pin),而是引腳名稱。

如需修改spi匯流排的配置,需先解除安裝後再載入:

rmmod spi-gpio-custom 
insmod spi-gpio-custom <new parameters>

更復雜的配置,參考 原始文件: SPI over GPIO in OpenWrt

#include "dev-spi.h"
// ......
/*
#define AP143_GPIO_LED_WLAN 13
#define AP143_GPIO_LED_IOT 16
#define AP143_GPIO_LED_PWR 11 //14
*/
// ......
#define AP143_GPIO_BTN_WPS 4
#define AP143_KEYS_POLL_INTERVAL 20 /* msecs */
#define AP143_KEYS_DEBOUNCE_INTERVAL (3 * AP143_KEYS_POLL_INTERVAL)
#define AP143_MAC0_OFFSET 5 //0
#define AP143_MAC1_OFFSET 6
#define AP143_WMAC_CALDATA_OFFSET 0x1000
// ......
static struct spi_board_info spi_test = {  
	
    .modalias = "spi_test",  
    .mode = SPI_MODE_0,  
    .irq = 0,  
    .max_speed_hz = 12 * 1000 * 1000,  
    .bus_num = 10,  
    .chip_select = 1, //模擬gpio用不到該成員  
    .controller_data = (void*)(0),
		
};  
// ......
// static void __init ap143_setup(void)
spi_register_board_info(&spi_test, 1);

=========

MT7628dan 增加SPI介面

來源 https://blog.csdn.net/likang517/article/details/80864918

SPI是可以全雙工通訊的一種序列匯流排,兩個裝置之間雙向通訊的話一般使用3根線:SCLK,MISO,MOSI,多個裝置之間雙向通訊的話,每個裝置還需要再加上一根地址線CSn。相比之下I2C只能半雙工,而且一般需要上拉電阻,但無論幾個裝置,都只需要2根線。更多基礎知識請谷歌百度。

MT7628DAN晶片只有一個主控制器,但是有兩個片選訊號,可以接2個裝置,其中一個已經被SPI FLASH佔用,所以需要啟用另外一個裝置。

1、修改核心配置檔案

make menuconfig

配置完成後退出儲存。

1、修改檔案mt7628an.dtsi

spi0: spi@b00 {
compatible = "ralink,mt7621-spi";
reg = <0xb00 0x100>;
resets = <&rstctrl 18>;
reset-names = "spi";
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&spi_pins>,<&spi_cs1_pins>;
status = "disabled";

};

spi@b00代表一個spi控制器,是一個platform device,compatible = "ralink,mt7621-spi";和 platform driver 中的of_match_table 對應(如果要支援片選1,還得修改num_cs=2,前提是mt7628的spi控制器本來就支援兩個片選).相同就會進入到probe函式中,再呼叫spi_register_master()註冊一個spi主控制器.<&spi_cs1_pins>是新增片選引腳,檔案mt7628an.dtsi中有定義:

spi_pins: spi {
spi {
ralink,group = "spi";
ralink,function = "spi";
};
};


spi_cs1_pins: spi_cs1 {
spi_cs1 {
ralink,group = "spi cs1";
ralink,function = "spi cs1";
};
};

3、修改MT7628.dts 檔案

/dts-v1/;

#include "mt7628an.dtsi"


/ {
compatible = "mediatek,mt7628an-eval-board", "mediatek,mt7628an-soc";
model = "Mediatek MT7628AN evaluation board";


memory@0 {
device_type = "memory";
reg = <0x0 0x2000000>;
};
};


&pinctrl {
state_default: pinctrl0 {
gpio {
ralink,group = "i2c";
ralink,function = "gpio";
};
};
};


&wmac {
status = "okay";
};


&spi0 {
status = "okay";


m25p80@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
m25p,chunked-io = <32>;


partition@0 {
label = "u-boot";
reg = <0x0 0x30000>;
read-only;
};


partition@30000 {
label = "u-boot-env";
reg = <0x30000 0x10000>;
read-only;
};


factory: partition@40000 {
label = "factory";
reg = <0x40000 0x10000>;
read-only;
};


partition@50000 {
label = "firmware";
reg = <0x50000 0x7b0000>;
};
};
spidev@1 {
compatible = "rohm,dh2228fv";
reg = <1 0>;
spi-max-frequency = <1000000>;
};
};


&wmac {
status = "okay";

};

spi_register_master註冊spi主控制器時就會掃描這些裝置,並註冊這些裝置。 status = “okay”表示選中,否則不能編譯進核心. m25p80@0表示在spi片選0下掛了一個m25p80的裝置,reg=<0,0>表示片選0,compatible = "jedec,spi-nor"; 與驅動檔案匹配.如果要在spi控制器的片選1上掛一個裝置,就要修改dts檔案,修改如下:

spidev@1 {
compatible = "rohm,dh2228fv";
reg = <1 0>;
spi-max-frequency = <1000000>;

};

compatible = "rohm,dh2228fv";與 spidev.c檔案中compatible 一致。

4、儲存修改檔案,執行make 生產升級韌體

lede-snapshot-r7346-7b74b40-ramips-mt76x8-mt7628-squashfs-sysupgrade.bin

5、燒寫韌體檢視裝置檔案:

6、好不好用還沒有測試。

7、編譯spi-test進行spi測試

8、執行

root@OpenWrt:/# spidev_test -D /dev/spidev0.1

出現如下一連串錯誤


spi mode: 0x0[ 196.130000] ------------[ cut here ]------------

bits per word:[ 196.130000] WARNING: CPU: 0 PID: 161 at drivers/spi/spi-mt7621.c:137 mt7621_spi_transfer_one_message+0x158/0x360()

8

max speed: 5[ 196.140000] Modules linked in:00000 Hz (500 KH qcserialz)

pppoe ppp_async option iptable_nat usb_wwan sierra pppox ppp_generic nf_nat_ipv4 nf_conntrack_ipv6 nf_conntrack_ipv4 ipt_REJECT ipt_MASQUERADE xt_time xt_tcpudp xt_state xt_nat xt_multiport xt_mark xt_mac xt_limit xt_id xt_conntrack xt_comment xt_TCPMSS xt_REDIRECT xt_LOG xt_CT usbserial spidev slhc nf_reject_ipv4 nf_nat_masquerade_ipv4 nf_nat_ftp nf_nat nf_log_ipv4 nf_defrag_ipv6 nf_defrag_ipv4 nf_conntrack_rtcache nf_conntrack_ftp nf_conntrack iptable_raw iptable_mangle iptable_filter ip_tables crc_ccitt i2c_gpio i2c_algo_bit i2c_dev i2c_core mt76x8 ralink_eeprom_api ledtrig_usbdev ip6t_REJECT nf_reject_ipv6 nf_log_ipv6 nf_log_common ip6table_raw ip6table_mangle ip6table_filter ip6_tables x_tables ipv6 mmc_block mmc_core leds_gpio ohci_platform ohci_hcd ehci_platform ehci_hcd gpio_button_hotplug usbcore nls_base usb_common

[ 196.220000] CPU: 0 PID: 161 Comm: spi32766 Tainted: G W 3.18.20 #8
[ 196.230000] Stack : 00000000 00000000 00000000 00000000 803441f2 00000042 00000000 801ae398

00000001 8f8c7b88 802a53d0 802fc9c3 000000a1 8034341c 8f900bf8 8f8c7b88
00010000 80309578 00000000 80047400 00000003 80024170 00000089 8f8c7b88
802a88d4 8f979d74 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
...

[ 196.270000] Call Trace:
[ 196.270000] [<800140b4>] show_stack+0x48/0x70
[ 196.270000] [<800242ec>] warn_slowpath_common+0x84/0xb4
[ 196.280000] [<800243a4>] warn_slowpath_null+0x18/0x24
[ 196.290000] [<801ae398>] mt7621_spi_transfer_one_message+0x158/0x360
[ 196.290000] [<801ad4e8>] spi_pump_messages+0x3cc/0x438
[ 196.300000] [<80039b9c>] kthread_worker_fn+0xa8/0xf4
[ 196.300000] [<80039cc0>] kthread+0xd8/0xe4
[ 196.310000] [<80004878>] ret_from_kernel_thread+0x14/0x1c
[ 196.310000]
[ 196.310000] ---[ end trace 828b306131dd7246 ]---
can't send spi message: Input/output error
Aborted

查了好幾天問題,沒明白找到了一個帖子

http://dev.archive.openwrt.org/ticket/20521?action=new&attachfilebutton=Attach+file#no1

原因是

The spidev_test is transmitting a 38 bytes array, that is too large. The spi-mt7621.c will reject when Tx length > 16 (full duplex).

然後修改mt7621.c檔案,把全雙工註釋掉可正常執行spi-test。

============

QCA9531修改暫存器值控制GPIO

來源 https://blog.csdn.net/cocos_yang/article/details/109249418

由高通9531晶片規格書可知,晶片對應的GPIO有18個GPIO0-17,下圖是規格書定義。下面以SKYLAB的SKW99模組為例進行說明。

SKYLAB的SKW99模組使用原始碼為QSDK,GPIO0-3預設為JTAG功能,GPIO9和10為uart串列埠,剩下的GPIO11-16為預設為燈的功能,GPIO17為WPS按鍵功能。

GPIO11-17對應功能原始碼地址為:

qsdk/target/linux/ar71xx/files/arch/mips/ath79/mach-ap147.c

#define AP147_GPIO_LED_WAN	4
#define AP147_GPIO_LED_LAN1	16
#define AP147_GPIO_LED_LAN2	15
#define AP147_GPIO_LED_LAN3	14
#define AP147_GPIO_LED_LAN4	11
#define AP147_GPIO_LED_STATUS	13
#define AP147_GPIO_LED_WLAN_2G	12

#define AP147_GPIO_BTN_WPS	17

#define AP147_KEYS_POLL_INTERVAL	20	/* msecs */
#define AP147_KEYS_DEBOUNCE_INTERVAL	(3 * AP147_KEYS_POLL_INTERVAL)

#define AP147_MAC0_OFFSET	0x1000

下面以在韌體中控制GPIO0進行講述說明,韌體中需要有io工具:

1、將GPIO1轉為GPIO功能:

下面是晶片規格書中關於GPIO0-3功能的定義:對應暫存器的地址為0x1804006C

讀取暫存器0x1804006C暫存器的值:

root@OpenWrt:~# io -4 0x1804006C
1804006c: 00000020
root@OpenWrt:~#
高通規格書,沒有詳細介紹bit1值對應的功能,根據實踐:

預設0,對應jtag(預設),1對應的是GPIO功能。

將bit1設定為1,則對應暫存器的值應設定為0x00000022

root@OpenWrt:/# io -4 0x1804006c 0x00000022 //寫暫存器值
root@OpenWrt:/#
root@OpenWrt:/# io -4 0x1804006c //讀暫存器值
1804006c: 00000022
root@OpenWrt:/#
2、對GPIO1進行操作

步驟如下:

(1)先註冊GPIO1

root@OpenWrt:/# echo 1 > /sys/class/gpio/export
root@OpenWrt:/#
root@OpenWrt:/# echo out > /sys/class/gpio/gpio1/direction
root@OpenWrt:/#
(2)檢視系統GPIO的狀態

root@OpenWrt:/# cat /sys/kernel/debug/gpio
GPIOs 0-17, ath79:
gpio-0 (sysfs ) out lo
gpio-1 (sysfs ) out lo
gpio-13 (sysfs ) out hi
gpio-17 (WPS button ) in hi
root@OpenWrt:/#
(3)對GPIO1進行拉高操作

(4)對GPIO1進行拉低操作

同理Link1燈GPIO16的操作控制如下:暫存器為0x1804003C,bit0-bit7對應GPIO16.

============== End

相關文章