Rockchip-Plattformreferenz für das RK RT-Thread SDK mit RK2108/RK2118 (SPI2APB hauptsächlich RK2118). Unterstützung konkreter ArmSoM-Platinen ist unbestätigt; keine Hardwarevalidierung. Vor Anpassung SoC, SDK/BSP, RT-Thread-Version, Controller, IOMUX, Verdrahtung und Spannungsdomänen prüfen. Befehle und Testergebnisse stammen aus der Quelle, nicht aus ArmSoM-Messungen; keine Hardware- oder Flash-Operationen wurden ausgeführt. Nur das chinesische Original liegt vor; alle vier Sprachen verwenden dessen Abbildungen.
RK-KF-YF-498 · V1.2.0
1. Einführung
1.1 Wichtige Hinweise
SPI2APB legt ein proprietäres RK-Protokoll über Standard-SPI, anders als das allgemeinere RK SPI ohne Zusatzprotokoll. Dies bei der Schaltplanprüfung beachten.
1.2 Controller-Eigenschaften
Unterstützt SPI-Modi 0/2 (0 empfohlen), 8-Bit-Übertragungen, Interrupts und Lesen/Schreiben interner Register. LSB/MSB und Little Endian/Big Endian sind einstellbar. Nur Simplex und Slave werden unterstützt. Der Master muss SPI2APB-konform senden. Grundformat: <CMD-32bits> [ADDR-32bits] [DUMMY-32bits] [READ_BEGING-32bits] [DATA]. Varianten siehe Tabelle.
| Operation | Protocol | CMD-32bits Little Endian |
|---|---|---|
| Daten lesen | read data | <0x00000077> <ADDR> <DUMMY> <READ_BEGING> <DATA> |
| Daten schreiben | write data | <0x00000011> <ADDR> <DATA> |
| REG0 schreiben | write msg reg0 | <0x00010011> <DATA-32bits> |
| REG1 schreiben, Interrupt | write msg reg1 | <0x00020011> <DATA-32bits> |
| Status der letzten Übertragung | query state | <0x000000ff> <DATA-32bits> |
| REG2 lesen | query msg reg2 | <0x000001ff> <DATA-32bits> |
[ADDR-32bits] ist eine unterstützte Speicheradresse, meist SRAM/DRAM. [DUMMY-32bits] ist beliebig und kann 0 sein. [READ_BEGING-32bits] ist 0x000000AA. Rückgaben von query state siehe “State Register Information” im SPI2APB-Kapitel des TRM.
1.3 Schnittstelleneigenschaften
| SoC | Max rate |
|---|---|
| RK2118 | 50MHz |
Die Maximalrate ist theoretisch; Leitungsführung und PCB-Qualität beeinflussen das zu messende Ergebnis.
2. Software
2.1 Quellcodepfade
Framework-Code:
components/drivers/include/drivers/spi.h
components/drivers/spi/spi_core.c
components/drivers/spi/spi_dev.c
Treiberanpassung:
bsp/rockchip/common/drivers/drv_spi2apb.c
bsp/rockchip/common/hal/lib/hal/src/hal_spi2apb.c
Testbefehle; Anwendungen können diesem Treiber folgen:
bsp/rockchip-common/tests/spi2apb_test.c
2.2 Konfiguration
Option mit SPI2APB im Namen aktivieren.
RT-Thread bsp drivers --->
RT-Thread rockchip "project" drivers --->
[*] Enable SPI
[*] Enable SPI0 (SPI2APB)
[ ] Enable SPI1
[ ] Enable SPI2
Platinen-iomux konfigurieren.
2.3 Nutzungskonfiguration
Standard: LSB, Little Endian. Änderungsbeispiel in drv_spi2apb.c:
diff --git a/bsp/rockchip/common/drivers/drv_spi2apb.c b/bsp/rockchip/common/drivers/drv_spi2apb.c
index 15045270fe..ef055d8d03 100644
--- a/bsp/rockchip/common/drivers/drv_spi2apb.c
+++ b/bsp/rockchip/common/drivers/drv_spi2apb.c
@@ -45,7 +45,7 @@ struct rockchip_spi2apb
static struct rockchip_spi2apb rk_spi2apb =
{
- .device.config.mode = (SPI2APB_LITTLE_ENDIAN | SPI2APB_LSB),
+ .device.config.mode = (SPI2APB_LITTLE_ENDIAN | SPI2APB_MSB),
.device.config.clock_polarity = SPI2APB_TXCP_INVERT,
.base = SPI2APB,
.irq = SPISLV0_IRQn,
Nur LSB/MSB ändern; die übrigen Standardwerte beibehalten.
3. Tests
3.1 Konfiguration
RT-Thread bsp test case --->
[*] RT-Thread Common Test case --->
[*] Enable BSP Common TEST
[*] Enable BSP Common SPI2APB TEST
3.2 Testbefehle
spi2apb_test config spi2apb 1 2 # <mode> <polarity>
spi2apb_test cb spi2apb # 注册 SPI2APB->REG1 中断回调
spi2apb_test read spi2apb 0 # 读取 SPI2APB->REG0 信息,建议调试方式,先主 设备端参考 'write msg reg0' 协议写,后从设备端执行该命令读
spi2apb_test read spi2apb 1 # 读取 SPI2APB->REG1 信息,建议调试方式,先主 端参考 'write msg reg1' 协议写,后从端执行该命令读
spi2apb_test query spi2apb # 读取 SPI2APB->SR 信息
spi2apb_test write spi2apb 0x12345678 # 写入 SPI2APB->REG2 信息,建议调试方式,先从 端执行该命令,后主端参考 'query msg reg2' 协议读
Master = SPI master; Slave = RK SPI2APB slave. mode: bit0 0-LSB/1-MSB; bit1 0-LittleEndian/1-BigEndian. polarity nur zum Debuggen ändern; im Betrieb TX invert/RX normal beibehalten, also polarity =
- bit0: 0-RX normal, 1-RX invert; bit1: 0-TX normal, 1-TX invert.
4. Anwendungsintegration
Beide Seiten müssen Datenfluss und Steuerinformationen auf SPI2APB aufbauen. Master-Anwendung und Treiber können vom RK-Referenzcode abgeleitet werden. Quellenort laut Original: SPI-Entwicklungsmaterialien — FAE-Projekt — Rockchip Redmine (rock-chips.com).
4.1 Grundarchitektur

Abbildung des chinesischen Originals (p.7)
API-/Ablauftext der Abbildung (Originalnamen/Nummerierung; kein ausführbarer Code)
spidev_rkspi2apb_app -> spidev_rkspi2apb.c -> SPI driver -> spi master
spi2apb_test.c <-> SPI2APB RTOS driver -> SPI2APB IP -> spi2apb slave
SPI2APB IP -> SRAM
Beispiel: RV1106 SPI master mit RK2118 SPI2APB. Quellcodekomponenten:
| Schicht | Master (Linux 5.10) | Slave (HAL + RTOS) |
|---|---|---|
| Anwendung | spidev_rkspi2apb_app: ioctl/read/write | test_spi2apb.c: REG |
| Kerneltreiber | spidev_rkspi2apb.c: SPI2APB ioctl/read/write | |
| Controllertreiber | spi-rockchip.c | drv_spi2apb.c hal_spi2apb.c |
| Controller | spi master | spi2apb slave |
ioctl-Schnittstelle von spidev_rkspi2apb.c:
#define SPIDEV_RKSPI2APB_GET_DEV_STATE _IOR(SPIDEV_RKSPI2APB_BASE, 0, __u32)
/* 参考 'query msg reg2' 协议 */
#define SPIDEV_RKSPI2APB_SET_DST_ADDR _IOW(SPIDEV_RKSPI2APB_BASE, 1, __u32)
/* 设置传输的目标内存地址,指向从设备端内存地址 */
#define SPIDEV_RKSPI2APB_SEND_MSG _IOW(SPIDEV_RKSPI2APB_BASE, 2, __u32) /* 参考 'write msg reg0' 协议 */
#define SPIDEV_RKSPI2APB_INT_REQ _IOW(SPIDEV_RKSPI2APB_BASE, 3, __u32) /* 参考 'write msg reg1' 协议 */
Lesen/Schreiben in spidev_rkspi2apbc.c (Quellenschreibweise):
write(spidev_fd, buffer_w, size); /* 参考 'write data' 协议,addr 由 ioctl SPIDEV_RKSPI2APB_SET_DST_ADDR 设定 */
read(spidev_fd, buffer_r, size); /* 参考 'read data' 协议,addr 由 ioctl SPIDEV_RKSPI2APB_SET_DST_ADDR 设定 */
Zieladresse vereinbaren: Vorbelegung wird empfohlen. Bekannte SRAM-Adresse im Slave reservieren und am Master per ioctl SPIDEV_RKSPI2APB_SET_DST_ADDR setzen. RT-Thread reserviert meist über spi2apb_buffer_start im Linkerskript. Alternativ SRAM im Slave allozieren und die Adresse durch einen eigenen Austausch an den Master senden, wie im folgenden Ablauf.

Abbildung des chinesischen Originals (p.9)
API-/Ablauftext der Abbildung (Originalnamen/Nummerierung; kein ausführbarer Code)
SPI Master APP -> ioctl SPIDEV_RKSPI2APB_INT_REQ -> SPI master
SPI master -> write reg1 -> SPI2APB slave -> interrupt call back -> SPI2APB APP
SPI2APB APP -> write address info to reg2 -> SPI2APB slave
SPI Master APP -> ioctl SPIDEV_RKSPI2APB_GET_DEV_STATE -> read reg2
4.2 Flash-Object-Dienst
Flash Object überträgt Anfragen über spi/spi2apb, damit der SPI-Master den SPI NOR am SPI2APB-Slave lesen/schreiben kann.
4.2.1 Grundprinzip

Abbildung des chinesischen Originals (p.9)
API-/Ablauftext der Abbildung (Originalnamen/Nummerierung; kein ausführbarer Code)
spi2apb_flash_obj_mst.c -> drv_spi.c -> spi master
spi2apb_test.c -> SRAM_SPI2APB_BASE
spi2apb slave <-> SRAM_SPI2APB_BASE <-> spinor
NOR-Flash-Leseablauf:

Abbildung des chinesischen Originals (p.10)
API-/Ablauftext der Abbildung (Originalnamen/Nummerierung; kein ausführbarer Code)
1. cmd (CMD_READ)
2. write reg1
3. set reg2 (SLAVE_BUSY)
4. rt_mtd_nor_read
5. set reg (SLAVE_SUCCESS)
6. wait status (SLAVE_SUCCESS)
7. read data
8. read data
NOR-Flash-Schreibablauf:

Abbildung des chinesischen Originals (p.10)
API-/Ablauftext der Abbildung (Originalnamen/Nummerierung; kein ausführbarer Code)
1. write data
2. write data
3. cmd (CMD_PROG)
4. write reg1
5. set reg2 (SLAVE_BUSY)
6. rt_mtd_nor_erase/write
6. set reg (SLAVE_SUCCESS)
7. wait status (SLAVE_SUCCESS)
4.2.2 Schnittstellen
flash_obj-Master-Schnittstelle:
rt_err_t spi2apb_flash_obj_mst_init(struct rt_spi_device *spi_device);
/**
* @brief Write sector from remote spinor device.
* @param sec: pointer to spinor 4KB sector offset.
* @param buffer: data buffer.
* @return rt_err_t
*/
rt_err_t spi2apb_flash_obj_mst_write_sector(rt_uint32_t sec, void *buffer);
/**
* @brief Read sector from remote spinor device.
* @param sec: pointer to spinor 4KB sector offset.
* @param buffer: data buffer.
* @return rt_err_t
*/
rt_err_t spi2apb_flash_obj_mst_read_sector(rt_uint32_t sec, void *buffer);
SPI2APB-Wrapper des flash_obj-Masters:
/**
* @brief Write data to remote sram by spi2apb interface.
* @param addr: pointer to remote sram address.
* @param buffer: data buffer.
* @param size: data size.
* @param trigger: if valid write spi2apb reg1 to trigger a interrupt.
* @return rt_err_t
*/
static rt_err_t spi2apb_flash_obj_mst_spi2apb_progsram(uint32_t addr, uint8_t
*buffer, uint32_t size, uint32_t trigger);
/**
* @brief Read data from remote sram by spi2apb interface.
* @param addr: pointer to remote sram address.
* @param buffer: data buffer.
* @param size: data size.
* @param trigger: if valid write spi2apb reg1 to trigger a interrupt.
* @return rt_err_t
*/
static rt_err_t spi2apb_flash_obj_mst_spi2apb_readsram(uint32_t addr, uint8_t
*buffer, uint32_t size, uint32_t trigger);
/**
* @brief Write msg reg in spi2apb.
* @param index: reg index.
* @param val: reg value.
* @param status: if valid check write success.
* @return rt_err_t
*/
static rt_err_t spi2apb_flash_obj_mst_spi2apb_writemsg(uint8_t index, uint32_t
value, bool status);
/**
* @brief Query msg reg2 in spi2apb.
* @return rt_err_t
*/
static uint32_t spi2apb_flash_obj_mst_spi2apb_querymsg(void);
/**
* @brief Query state abort last transmission.
* @return uint32_t status.
*/
static uint32_t spi2apb_flash_obj_mst_spi2apb_querystatus(void);
flash_obj-Slave-Schnittstelle:
rt_err_t spi_test_spi2apb_flash_obj_init(void);
static void spi_test_spi2apb_flash_obj_work(struct rt_work *work, void
*work_data);
rt_err_t spi_test_spi2apb_flash_obj_write(rt_uint8_t index, rt_uint16_t addr,
void *buffer);
rt_err_t spi_test_spi2apb_flash_obj_read(rt_uint8_t index, rt_uint16_t addr, void
*buffer);
rt_err_t spi_test_spi2apb_flash_obj_set_status(rt_uint8_t index, rt_uint8_t
status);
Nach Initialisierung den SPI2APB-Interrupt-Callback registrieren. Die reg1-Bottom-Half-Workqueue analysiert Transaktionen, liest/schreibt spinor und setzt Statusbits der Zustandsmaschine.
4.2.3 Flash-obj-Konfiguration
CONFIG_RT_USING_COMMON_TEST_SPI2APB_FLASH_OBJ = y
4.2.4 Flash-obj-Testbefehle
flash_obj-Slave-Befehle:
spi2apb_test flash_obj spi2apb
flash_obj-Master-Befehle:
1. spi2apb_flash_obj_mst spi2apb_progsram <spi_dev> <size>
2. spi2apb_flash_obj_mst spi2apb_readsram <spi_dev> <size>
3. spi2apb_flash_obj_mst spi2apb_writemsg <spi_dev> <reg> <val>
4. spi2apb_flash_obj_mst spi2apb_querymsg <spi_dev>
5. spi2apb_flash_obj_mst write_sector <spi_dev> <sector> <val> <loops>
6. spi2apb_flash_obj_mst read_sector <spi_dev> <sector> <loops>
7. spi2apb_flash_obj_mst stress <spi_dev> <sector> <n_sec>
like:
spi2apb_flash_obj_mst spi2apb_progsram spi1_0 32
spi2apb_flash_obj_mst spi2apb_readsram spi1_0 32
spi2apb_flash_obj_mst spi2apb_writemsg spi1_0 1 55
spi2apb_flash_obj_mst spi2apb_querymsg spi1_0
spi2apb_flash_obj_mst write_sector spi1_0 512 1 10
spi2apb_flash_obj_mst read_sector spi1_0 512 10
spi2apb_flash_obj_mst stress spi1_0 512 32
spi2apb_progsram verwendet write data; anschließend löst write msg reg1 den SPI2APB-Interrupt aus. spi2apb_readsram verwendet read data und wartet zuvor auf reg2-Status SPI2APB_FLASH_OBJ_CMD_READ_SRAM_READY. spi2apb_writemsg entspricht write msg reg0/1, spi2apb_querymsg read msg reg2. write_sector schreibt NOR flash, read_sector liest, stress schreibt und liest für einen Belastungstest zurück. Alle drei verwenden 4KB-Sektoren.
5. Fehlerbehebung
5.1 SPI2APB-Übertragungsfehler
Nach dem Testabschnitt SPI-Modus 0, lsb, Little Endian konfigurieren und Callback registrieren:
spi2apb_test config spi2apb 1 2
spi2apb_test cb spi2apb
Der Master muss dem Übertragungsprotokoll folgen. Zuerst write reg1 versuchen, um einen SPI2APB-Interrupt auszulösen. Gemeinsame Masse, Master-csn/clock per Oszilloskop, Slave-Verdrahtung und unbestückte 0Ω-Widerstände prüfen. Bei falschen Empfangsdaten Geschwindigkeit reduzieren. LSB/MSB und Byte-Reihenfolge prüfen. Sind nur Teile gültig und hängt das Muster mit der Cache-Zeilengröße zusammen, fehlen wahrscheinlich Slave-Kohärenzoperationen: normalerweise invalidate nach Lesen und flush vor Schreiben. Wurde der Lesepuffer zuvor gelöscht oder beschrieben, auch vor der Übertragung flush ausführen.
