CM4_PSPI使用参考
Revision History¶
Revision No. | Description |
Date |
---|---|---|
1.0 | Initial release | 06/04/2024 |
1. 概述¶
1.1. PSPI¶
PSPI支持半双工spi通讯协议,包含CS、CK、MOSI、MISO引脚。spi通讯,片选信号CS、时钟CK、MOSI都由master控制,MISO由slave device控制。
1.2. 特性支持¶
BUS | PM-PSPI |
---|---|
BANK ADDR | 0x19 |
FIFO | Y |
DMA | Y |
Full-Duplex | N |
Half-Duplex | Y |
Interrupt | Y |
Max Length | 4096(byte) |
CS Change | Y |
CPOL | Y |
CPHA | Y |
bits_per_word | Y(dma only 8bits) |
CS NUM | 2 |
CS Polarity | Y |
Speed | 23437Hz - 50MHz |
3-wire | Y |
4-wire | Y |
1.3. speed的计算方法:¶
speed = (source clock rate) / (2 * (divisor + 1))
1.4. source clock list¶
source clock rate | divisor range |
---|---|
12MHz | 0 - 0xff |
24MHz | 0 - 0xff |
50MHz | 0 - 0xff |
100MHz | 0 - 0xff |
1.5. 引脚说明¶
PSPI包含CS、CK、DI、DO四根引脚,当作为4线使用时,DI表示data input,是接收数据的引脚,DO表示data output,是发送数据的引脚。当作为3线使用时,使用CS、CK、DI三根引脚,数据的收发都在DI完成。
2. 使用介绍¶
2.1. RTOS config配置¶
RTOS下使用PSPI,需要开启CONFIG_PSPI_SUPPORT驱动编译选项
# Feature_Name = [DRV] PSPI driver support # Description = PSPI driver support # Option_Selection = TRUE, FALSE CONFIG_PSPI_SUPPORT = TRUE
2.2. 驱动路径¶
sc/driver/sysdriver/pspi/drv/src/drv_pspi.c sc/driver/sysdriver/pspi/drv/pub/drv_pspi.h sc/driver/sysdriver/pspi/hal/iford/hal_pspi.c sc/driver/sysdriver/pspi/hal/iford/hal_pspi_cfg.h sc/driver/sysdriver/pspi/hal/iford/hal_pspi.h
2.3 SYSDESC定义¶
<pspi0> [reg_u32] 0x40003200; [interrupts_u32] INT_IRQ_PSPI02HOST; [dma_u8] 1; [status_u8] 1;
属性 | 说明 | 备注 |
---|---|---|
<pspi0> |
用于和驱动匹配 | 禁止修改 |
reg_u32 | IO_address Address_size | 禁止修改 |
interrupts_u32 | 中断号 | 禁止修改 |
dma_u8 | urdma mode使能 | 1 :使能 0 :禁用 |
status_u8 | 节点使能,用于是否使能该pspi0 | 1: 使能 0: 不使能 |
2.4 引脚复用¶
即PADMUX,用于将PSPI信号连接到某组具体的引脚上。设定修改在proj/sc/driver/sysdriver/padmux/hal/iford/src/*-padmux.c当中,以便统一管理。第一列为引脚号定义,第二列为引脚复用的padmod编号,第三列puse为功能定义。需要注意的是,第一列的引脚号不能重复;对应的第二列的padmod编号与引脚号要匹配的上。
proj/sc/driver/sysdriver/padmux/hal/iford/src/iford-ssc029a-s01a-padmux.c pad_info_t schematic[] = { // for example // {PAD_I2C1_SCL, PINMUX_FOR_I2C1_MODE_1, MDRV_PUSE_I2C1_SCL}, // {PAD_I2C1_SDA, PINMUX_FOR_I2C1_MODE_1, MDRV_PUSE_I2C1_SDA}, // for pspi0 {PAD_PM_PSPI0_DI, PINMUX_FOR_PSPI0_MODE_1, MDRV_PUSE_PSPI0_DI}, {PAD_PM_PSPI0_DO, PINMUX_FOR_PSPI0_MODE_1, MDRV_PUSE_PSPI0_DO}, {PAD_PM_PSPI0_CK, PINMUX_FOR_PSPI0_MODE_1, MDRV_PUSE_PSPI0_CK}, {PAD_PM_PSPI0_CZ, PINMUX_FOR_PSPI0_MODE_1, MDRV_PUSE_PSPI0_CZ}, }; int size_of_schematic = sizeof(schematic);
3. API 说明¶
3.1. 设备获取与关闭¶
3.1.1. 设备获取¶
-
函数声明
pspi_handle *drv_pspi_open(u8 bus);
-
功能说明
指定pspi设备默认初始化并获得handle pointer。
-
参数
参数 说明 bus pspi bus number -
返回值
返回值 说明 pspi_handle * handle 指针
3.1.2. 设备关闭¶
-
函数声明
void drv_pspi_close(pspi_handle *handle);
-
功能说明
释放被open获取的pspi。
-
参数
参数 说明 handle 被open获取的pspi handle指针 -
返回值
void
3.2. 设备设定¶
3.2.1. 设备设定¶
-
函数声明
s32 drv_pspi_ioctrl(pspi_handle *handle, u32 cmd, void *arg);
-
功能说明
对设备通讯mode、cs select、speed、bits_per_word等做设定;或者注册async回调函数。
-
参数
参数 说明 handle pspi handle指针 cmd 选择操作类别:
PSPI_CMD_SETUP:通讯设定
PSPI_CMD_ASYNC_SET:注册异步回调arg 通讯设定结构体指针,或者回调函数指针
通讯设定结构体声明:
struct pspi_setup { u32 speed; u32 mode; #define PSPI_CPHA 0x01 /* clock phase */ #define PSPI_CPOL 0x02 /* clock polarity */ #define PSPI_CS_HIGH 0x04 /* chipselect active high? */ #define PSPI_LSB_FIRST 0x08 /* per-word bits-on-wire */ #define PSPI_3WIRE 0x10 /* SI/SO signals shared */ #define PSPI_SLAVE 0x20 u8 bus_select; u8 chip_select; u8 bits_per_word; /* The number of bits in an SPI transmission*/ u8 data_lane; u16 delay_cycle; /* cs is inactive*/ u16 wait_cycle; /* cs is active */ };
回调函数声明:
void (*pspi_async_callback)(void *);
-
返回值
返回值 说明 s32 设定结果:
-1:handle指针为空
0:设定成功
3.3. 通讯¶
3.3.1. 通讯调用¶
-
函数声明
s32 drv_pspi_transfer(pspi_handle *handle, struct pspi_transfer *transfer, u8 msg_num, s32 timeout_ms);
-
功能说明
发起通讯。
由于pspi只支持半双工通讯,所以在每个struct pspi_transfer结构体当中,tx_buf和rx_buf只能有一个为有效值,另一个必须为0,否则将错误返回;cs_change表示当前结构体发起的读或写通讯后,cs是否回到idle电平,或者继续保持active电平。
-
参数
参数 说明 handle pspi handle 指针 transfer 通讯内容结构体指针,当一次进行多次传输时,为结构体数组首地址 msg_num 通讯结构体个数,表示进行多少次读写操作 timeout_ms 超时等待,暂未支持
通讯内容结构体声明:
struct pspi_transfer { u8 *tx_buf; // 发送数据buffer地址 u8 *rx_buf; // 接收数据buffer地址 u32 length; // 通讯字节数 u8 cs_change; // 0: 完成后cs保持active电平; 1:完成后cs回复idle电平 };
-
返回值
返回值 说明 s32 通讯结果:
0:通讯成功
非0:通讯失败
3.4. 参考用例¶
static int pspi_flash_demo(CLI_t * cli, char * p) { pspi_handle *handle; struct pspi_setup setup = {0}; struct pspi_transfer tfr[2] = {0}; int ret = 0; int i; unsigned char pspi_wr_buf[MAX_BUF_SIZE] = {0}; unsigned char pspi_rd_buf[MAX_BUF_SIZE] = {0}; // pspi_controller setup setup.chip_select = 0; setup.bits_per_word = 8; setup.delay_cycle = 0; setup.wait_cycle = 0; setup.speed = 6000000; setup.mode = 0; // setup.mode |= PSPI_3WIRE; handle = drv_pspi_open(0); if (!handle) { CamOsPrintf("drv_pspi_open fail\n"); return eCLI_PARSE_ERROR; } handle->slave = 0; drv_pspi_ioctrl(handle, PSPI_CMD_SETUP, (void *)&setup); // write enable pspi_wr_buf[0] = 0x06; tfr[0].cs_change = 1; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x1; ret = drv_pspi_transfer(handle, &tfr[0], 1, 0); // unlock pspi_wr_buf[0] = 0x1F; pspi_wr_buf[1] = 0xA0; pspi_wr_buf[2] = 0x00; tfr[0].cs_change = 1; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x3; ret = drv_pspi_transfer(handle, &tfr[0], 1, 0); pspi_wr_buf[0] = 0x0F; pspi_wr_buf[1] = 0xA0; tfr[0].cs_change = 0; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x2; tfr[1].cs_change = 1; tfr[1].rx_buf = pspi_rd_buf; tfr[1].tx_buf = NULL; tfr[1].length = 0x1; //ret = drv_pspi_transfer(handle, &tfr[0], 2, 0); // write enable pspi_wr_buf[0] = 0x06; tfr[0].cs_change = 1; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x1; ret = drv_pspi_transfer(handle, &tfr[0], 1, 0); // erase block 0 pspi_wr_buf[0] = 0xd8; pspi_wr_buf[1] = 0x00; pspi_wr_buf[2] = 0x00; pspi_wr_buf[3] = 0x00; tfr[0].cs_change = 1; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x4; ret = drv_pspi_transfer(handle, &tfr[0], 1, 0); // wait oip CamOsUsDelay(20000); // read page to cache pspi_wr_buf[0] = 0x13; pspi_wr_buf[1] = 0x00; pspi_wr_buf[2] = 0x00; pspi_wr_buf[3] = 0x00; tfr[0].cs_change = 1; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x4; ret = drv_pspi_transfer(handle, &tfr[0], 1, 0); // read data from cache pspi_wr_buf[0] = 0x03; pspi_wr_buf[1] = 0x00; pspi_wr_buf[2] = 0x00; pspi_wr_buf[3] = 0x00; tfr[0].cs_change = 0; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x4; tfr[1].cs_change = 1; tfr[1].rx_buf = pspi_rd_buf; tfr[1].tx_buf = NULL; tfr[1].length = MAX_BUF_SIZE; ret = drv_pspi_transfer(handle, &tfr[0], 2, 0); if (ret) { CamOsPrintf("pspi_flash fail, %d .\n", ret); } for (i = 0; i < MAX_BUF_SIZE; i++) { if (pspi_rd_buf[i] != 0xFF) { CamOsPrintf("erase fail\n"); CamOsHexdump((char *)pspi_rd_buf, MAX_BUF_SIZE); break; } } // write enable pspi_wr_buf[0] = 0x06; tfr[0].cs_change = 1; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x1; ret = drv_pspi_transfer(handle, &tfr[0], 1, 0); // write data to cache pspi_wr_buf[0] = 0x02; pspi_wr_buf[1] = 0x00; pspi_wr_buf[2] = 0x00; tfr[0].cs_change = 0; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x3; ret = drv_pspi_transfer(handle, &tfr[0], 1, 0); for (i = 0; i < MAX_BUF_SIZE; i++) { pspi_wr_buf[i] = i; } tfr[0].cs_change = 1; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = MAX_BUF_SIZE; ret = drv_pspi_transfer(handle, &tfr[0], 1, 0); // program execute pspi_wr_buf[0] = 0x10; pspi_wr_buf[1] = 0x00; pspi_wr_buf[2] = 0x00; pspi_wr_buf[3] = 0x00; tfr[0].cs_change = 1; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x4; ret = drv_pspi_transfer(handle, &tfr[0], 1, 0); // wait oip CamOsUsDelay(10000); // read page to cache pspi_wr_buf[0] = 0x13; pspi_wr_buf[1] = 0x00; pspi_wr_buf[2] = 0x00; pspi_wr_buf[3] = 0x00; tfr[0].cs_change = 1; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x4; ret = drv_pspi_transfer(handle, &tfr[0], 1, 0); // wait oip CamOsUsDelay(1000); // read data from cache pspi_wr_buf[0] = 0x03; pspi_wr_buf[1] = 0x00; pspi_wr_buf[2] = 0x00; pspi_wr_buf[3] = 0x00; tfr[0].cs_change = 0; tfr[0].rx_buf = NULL; tfr[0].tx_buf = pspi_wr_buf; tfr[0].length = 0x4; tfr[1].cs_change = 1; tfr[1].rx_buf = pspi_rd_buf; tfr[1].tx_buf = NULL; tfr[1].length = MAX_BUF_SIZE; ret = drv_pspi_transfer(handle, &tfr[0], 2, 0); if (ret) { CamOsPrintf("pspi_flash fail, %d .\n", ret); } for (i = 0; i < MAX_BUF_SIZE; i++) { if (pspi_rd_buf[i] != i) { CamOsPrintf("data check fail\n"); CamOsHexdump((char *)pspi_rd_buf, MAX_BUF_SIZE); break; } } return eCLI_PARSE_OK; } SS_RTOS_CLI_CMD(pspi_flash, "pspi flash demo", " pspi_flash\n", pspi_flash_demo);