原本 ESP-Hosted-NG 是給 Respberry Pi 的一個 SPI/SDI 通訊的網卡/Bluethooth 方案, 其實 NG 系列的 library 也可以通用到其他的 linux 系統 (但是有機率會失敗),這裡測試移植這個方案到 RV1106 的 buildroot 系統。
系統環境設定
需要先準備這些設定,才開始進行:
- 需要先有 rv1106 linux 的編譯專案,要自行下載然後配置,測試能不能 ./build.sh 直接編譯整個 linux image 出來
- 需要下載 esp32_hosted_ng,進去後到 /esp/esp_driver/ 做 ./setup -f 他會自動下載 esp-idf,完成之後,要做 source ./esp-idf/export.sh 套用到編譯環境。 (注意 esp32_hosted_ng 一定要用 tag 上的版本,不要直接用 master,本文是使用 (git checkout -f release/ng-1.0.4.0.0) 注意要先 checkout 過去
注意事項
- ESP32 的功耗比較大,建議測試時要外接電源,然後先做 SPI 通訊線路
- 這個 esp32_hosted_ng 方案比 SPI 多出三條 esp32 output -> rv1106 input 的線 (data_ready, reset, handshake)
接線方式
接線是對 RV1106 到 ESP32 之間的接線,首先要觀察 ESP32 文件上面的寫法,這些腳位對應到開發板上面的這些腳位:
| ESP32HostedNG | ESP32 | RV1106 |
|---|---|---|
| IO15 | GPIO15/HSPI_CS0 | SPI0_CS1_M0/GPIO1_D2_d |
| IO14 | GPIO14/HSPI_CLK | SPI0_CLK_M0/GPIO1_C1_d |
| IO12 | GPIO12/HSPI_Q (MISO) | SPI0_MISO_M0/GPIO1_C3_d |
| IO13 | GPIO13/HSPI_ID (MOSI) | SPI0_MOSI_M0/GPIO1_C2_d |
| IO2 | GPIO2/HSPI_WP0 (DATAREADY) | GPIO1_C7_d |
| IO4 | GPIO4/HSPI_HD (HANDSHAKE) | GPIO1_C6_d |
| EN | EN/RESET | GPIO1_C5_d |
注意上面的 handshake, dataready 腳位,這是自己另外定義的腳位,這是由 host 的程式控制的,所以翻到專案上面可以看到 host/spi/esp_spi.h 有寫著 HANDSHAKE 腳位數字,但是不可以直接對應手冊上的數字,這是不對的。
DATAREADY -> GPIO1_C7_d 就是 1 * 32 + (2 * 8 + 7) = 55
HANDSHAKE-> GPIO1_C6_d 就是 1 * 32 + (2 * 8 + 6) = 54
HANDSHAKE-> GPIO1_C6_d 就是 1 * 32 + (2 * 8 + 6) = 54
RESET -> GPIO1_C5_d 就是 1 * 32 + (2 * 8 + 5) = 53
所以稍後會在 esp_spi.h 上面做一些調整。
Handshake/dataready/reset 線的原理
這三條線是一定要接入的,因為主機端載入整個 esp32 的時候,就會先對這個 esp32 重啟 (reset),然後等待 esp32 傳送 handshake, dataready 訊號後開始同步。
改 RV1106 的 DTS 設備樹設定
1. 移除衝突 SPI
rv1106-luckfox-pico-pro-max-ipc.dtsi
// SPI
&spi0 {
pinctrl-0 = <&spi0m0_clk &spi0m0_miso &spi0m0_mosi &spi0m0_cs0>;
#address-cells = <1>;
#size-cells = <0>;
spidev@0 {
status = "disabled"; // 暫時先關閉
compatible = "rockchip,spidev";
spi-max-frequency = <50000000>;
reg = <0>;
};
fbtft@0 {
status = "disabled"; // 暫時先關閉
compatible = "sitronix,st7789v";
reg = <0>;
spi-max-frequency = <20000000>;
fps = <30>;
buswidth = <8>;
debug = <0x7>;
led-gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_HIGH>;//BL
dc-gpios = <&gpio2 RK_PB1 GPIO_ACTIVE_HIGH>;//DC
reset-gpios = <&gpio1 RK_PC3 GPIO_ACTIVE_LOW>;//RES
};
};
2. 加入 SPI 腳位定義
rv1106-pinctrl.dtsi
&pinctrl {
...
esp32_pins {
/* SPI */
esp32_spi_pins: esp32-spi-pins {
rockchip,pins =
<1 RK_PC1 4 &pcfg_pull_none>, /* SPI0_CLK - GPIO1_C1 */
<1 RK_PC3 6 &pcfg_pull_none>, /* SPI0_MISO - GPIO1_C3 */
<1 RK_PC2 6 &pcfg_pull_none>, /* SPI0_MOSI - GPIO1_C2 */
<1 RK_PC0 4 &pcfg_pull_none>;
/* 因為衝突而換 cs0 */
/* <1 RK_PD2 4 &pcfg_pull_none>; /* SPI0_CS1 GPIO1_D2 */
/* 因為衝突而換 cs1 */
/* <1 RK_PC0 4 &pcfg_pull_none>; /* SPI0_CS0 GPIO1_C0 */
};
/* 控制腳:全部設 GPIO function */
esp32_ctrl_pins: esp32-ctrl-pins {
rockchip,pins =
<1 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>, /* RESET */
<1 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>, /* HANDSHAKE */
<1 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>; /* DATAREADY */
};
};
}
3. 加入 SPI 功能
rv1106g-luckfox-pico-pro-max.dts
/********** SPI **********/
&spi0 {
// ESP32
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&esp32_spi_pins &esp32_ctrl_pins>;
// num-cs = <2>; // 將支援數量改為 2 (如果要用 CS1 而不是 CS0,這個要打開)
};
設定 esp32 firmware 編譯環境
- 首先在 esp_driver 下做 source esp-idf/export.sh
- 在 network_adapter 下輸入指令 idf.py menuconfig,進去 Example Configuration 把 Transport layer 改 SPI
- 輸入指令 idf.py build
- 輸入指令 idf.py set-target esp32
- 輸入指令 idf.py build
- build 出來的東西會在 network_adapter 的 build 目錄下。
設定 SPI 額外的接腳 - 在 host
首先是在 ./host/spi/esp_spi.h 這文件裡面,直接修改參數:
#define HANDSHAKE_PIN 54
#define SPI_IRQ gpio_to_irq(HANDSHAKE_PIN)
#define SPI_DATA_READY_PIN 55
#define SPI_DATA_READY_IRQ gpio_to_irq(SPI_DATA_READY_PIN)
#define SPI_BUF_SIZE 1600
把剛才的 pin 腳定位重改。
設定 SPI Mode - 在 network_adapter (esp32 firmware) 和 host 端
基於 SPI Mode 2 的不穩定性比較高,所以換成 SPI Mode 3,同時要修改 ./host/spi/esp_spi.c 上的
uint8_t g_spi_mode = SPI_MODE_3;
這個模式也需要同步改到 esp32,在 ./network_adapter 裡面的 spi_slave_api.c 中,也要改:
uint8_t g_spi_mode = SPI_MODE_3;
設定 host .ko 的 Makefile 交叉編譯環境
- 先做 RV1106 buildroot 本身的設定
- ./build.sh kernelconfig,然後打開 cfg80211, BT_HCIBLUECARD, wifi, insmod, rmmod 這些功能
- ./build.sh buildrootconfig ,打開 iw, BR2_PACKAGE_BLUEZ5_UTILS, BR2_PACKAGE_WPA_SUPPLICANT
- 到 sysdrv/cfg/package.mk 把 CONFIG_SYSDRV_ENABLE_WIFI=y 打開
- ./build.sh (編譯全部, 或是 rootfs, kernel 這兩個)
- 複製 kernel 目錄過去 host 編譯的路徑 (以防裡面內容被修改)
cp -r ./luckfox-pico/sysdrv/source/objs_kernel [path_to]/esp_hosted_ng/kernel-build
- 設定 esp32_hosted_ng/host 的 Makefile
# Toolchain and kernel
CROSS_COMPILE := arm-rockchip830-linux-uclibcgnueabihf-
KERNEL := /home/hpcslag/esp-idf/esp-hosted/esp_hosted_ng/kernel-build
ARCH := arm
target=spi
- 是否要打開 AP 模式支援 (成為 wifi hotspot,要打開 iw list 的支援才會出現 AP 模式)
# Toolchain and kernel
CROSS_COMPILE := arm-rockchip830-linux-uclibcgnueabihf-
KERNEL := /home/hpcslag/esp-idf/esp-hosted/esp_hosted_ng/kernel-build
ARCH := arm
target=spi
CONFIG_AP_SUPPORT=y
改 main.c 加入忽略用文件當驅動載入的 flag:
在 #includes 之後直接加入這行:
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
全部編譯
- 在 host 下面直接 make (如果出現 hci 編譯錯誤相關訊息,可以考慮是否要把 bluethooth 功能關掉,只需要建立 esp_bt_stub.c,然後在 makefile 下面的 module_objects += 前面把 esp_bt.o 改成 esp_bt_stub.o
- 在 network_adapter 下面直接 idf.py build
- 在 rv1106 linux 目錄下 ./build kernel
esp_bt_stub.o
#include <linux/kernel.h>
int esp_init_bt(void)
{
pr_info("esp_hosted: BT disabled (stub)\n");
return 0;
}
void esp_deinit_bt(void)
{
pr_info("esp_hosted: BT disabled (stub)\n");
}
void esp_hci_update_rx_counter(int len)
{
(void)len;
// no-op
}
燒錄 rv1106
只需要燒錄 kernel 就好了,但是如果前面有把 iw, bluethooth, wifi tools 燒進去,要整個重新燒錄。
燒錄 esp32 firmware
燒錄 esp32 可以使用 esp32 flash download tool ,燒錄時需要對準不同的 partition 記憶體位置燒錄,具體的記憶體位置可以看 ./build/flasher_args.json ,裡面寫的 offset 就是對準記憶體位置
如果要 debug 訊息,可以另外接上 UART debugger USB,把 TX 對到 ESP32 的 GPIO1 (RXD 0), GPIO2 (TXD 0) ,但是打開 serial port 的時候會占用,燒錄時要關閉其他的占用。
建議打開 debug 訊息,因為要確認 esp32 上面燒的是不是 SPI mode:
: *********************************************************************
I (570) stats: ESP-Hosted Firmware version :: NG-1.0.5.0.7
I (578) stats: Transport used :: SDIO only
I (585) stats: *********************************************************************
向這個訊息就是錯誤的,要是 SPI Only
移動 host file 到機器上準備啟動
在 host 編譯完成後會得到 esp32_spi.ko ,直接複製到 rv1106 系統,然後使用指令執行:
insmod esp32_spi.ko resetpin=53 clockspeed=10
這裡是指定了 resetpin 位置,然後 clockspeed 是 MHz,這個數字剛好不會產生問題,其他數字要自己測試。
如果要熱移除模組,可以直接輸入
rmmod -f esp32_spi.ko
除錯時可以用這個指令打印結果:
dmesg
除錯
接入過程中,發現各種問題導致失敗,這裡會描述不同的錯誤發生的原因。
版本問題
一起動時發現會一直有 offset drop 的問題
到 esp_spi.c 的 esp_spi_work 裡面加入
esp_info("esp_spi_work... (txready: %d, rxpending: %d)\n",trans_ready,rx_pending);
然後在這行 ret 後面打印內容:
ret = spi_sync_transfer(spi_context.esp_spi_dev, &trans, 1);
esp_info("spi rx ret=%d len=%d first16=%*ph\n", ret, SPI_BUF_SIZE, 16, rx_buf);
結果發現在 03 00 00 00 1f 這裡給的事件 id 不是 1 (1 是主機發出的 BOOT 的訊號)
於是就先把整個版本切回一樣的,重新編譯 host, esp32 ,燒錄之後就成功了。
其他類似的問題是 clock 速度,現在是手動調整的,目前我使用 10MHz 是可以用的,但是再快就不行,esp32 firmware 上面寫的上限是 20MHz。 (這個速度也可能受到線的影響,尤其是杜邦線,甚至長度也不可以超過 10M)
Reference:
[1] https://wiki.luckfox.com/Luckfox-Pico-Pro-Max/GPIO/
[2] https://github.com/espressif/esp-hosted/issues/387
[3] https://github.com/espressif/esp-hosted/issues/489
[4] https://www.samkwort.com/luckfox_esp_hosted
[5] https://github.com/espressif/esp-hosted/blob/master/esp_hosted_ng/docs/spi_protocol.md
[6] https://github.com/espressif/esp-hosted/blob/master/esp_hosted_ng/docs/setup.md#11-host-software-setup
沒有留言:
張貼留言