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);