[S32K146 RT-Thread] SPI Driver Adaptation[S32K146 RT-Thread] SPI Driver Adaptation
Overview
RT-Thread abstracts the SPI bus driver into an spi bus device driver. Here, we study the SPI bus device driver based on the S32K146 hardware.
RT-Thread official documentation provides detailed instructions on the SPI bus driver:
SPI Bus Driver Adaptation
The main structures involved in the driver are as follows:
● rt_spi_device: the application binds to the bus through this structure and controls the SPI bus via the bus.
● rt_spi_bus: abstraction of the SPI bus device.
● rt_spi_ops: underlying operation functions that the bus device depends on.
The relationship between the structures is as follows:
For the driver connection of the bus device, we need to implement the corresponding ops functions.
Configuring SPI1 with S32 Design Studio
In this experiment, lpspi1 is used. First, use S32 Design Studio to generate the pinmux and clock initialization configuration code.
● Configure SPI1 master parameters
● Configure pimmux
● Configure clk
Adapting SPI1 ops Functions to RT-Thread
Based on the above analysis, implement the ops functions at the chip layer and enable the SPI device driver.
● Configure ops functions
tatic rt_err_t spi_configure(struct rt_spi_device* device,
struct rt_spi_configuration* configuration)
{
struct rt_spi_bus * spi_bus = (struct rt_spi_bus *)device->bus;
struct s32k_spi *spi_device = (struct s32k_spi *)spi_bus->parent.user_data;
status_t ret;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(configuration != RT_NULL);
/* config spi init spi bus */
//LPSPI_DRV_MasterDeinit(spi_device->instance);
switch(configuration->mode & RT_SPI_MODE_3)
{
case RT_SPI_MODE_0:
spi_device->masterconfig->clkPhase = LPSPI_CLOCK_PHASE_2ND_EDGE;
spi_device->masterconfig->clkPolarity = LPSPI_SCK_ACTIVE_LOW;
break;
case RT_SPI_MODE_1:
spi_device->masterconfig->clkPhase = LPSPI_CLOCK_PHASE_1ST_EDGE;
spi_device->masterconfig->clkPolarity = LPSPI_SCK_ACTIVE_LOW;
break;
case RT_SPI_MODE_2:
spi_device->masterconfig->clkPhase = LPSPI_CLOCK_PHASE_2ND_EDGE;
spi_device->masterconfig->clkPolarity = LPSPI_SCK_ACTIVE_HIGH;
break;
case RT_SPI_MODE_3:
spi_device->masterconfig->clkPhase = LPSPI_CLOCK_PHASE_1ST_EDGE;
spi_device->masterconfig->clkPolarity = LPSPI_SCK_ACTIVE_HIGH;
break;
}
/* MSB or LSB */
if(configuration->mode & RT_SPI_MSB)
{
spi_device->masterconfig->lsbFirst = false;
}
else
{
spi_device->masterconfig->lsbFirst = true;
}
if(configuration->max_hz < 10000000)
spi_device->masterconfig->bitsPerSec = configuration->max_hz;
spi_device->masterconfig->bitcount = configuration->data_width;
ret = LPSPI_DRV_MasterInit(spi_device->instance,spi_device->status,spi_device->masterconfig);
RT_ASSERT(ret == STATUS_SUCCESS);
ret = LPSPI_DRV_MasterSetDelay(spi_device->instance, 1, 1, 1);
RT_ASSERT(ret == STATUS_SUCCESS);
return ret == STATUS_SUCCESS ? RT_EOK:RT_ERROR;● xfer ops functions
truct s32k_spi
{
struct rt_spi_bus spi_bus; /* spi bus device */
lpspi_master_config_t * masterconfig; /* lpspi master config */
lpspi_state_t * status; /* lpspi driver status */
uint32_t instance; /* spi instance id */
char * bus_name;
}s32k_spi_t;
static rt_ssize_t spixfer(struct rt_spi_device* device, struct rt_spi_message* message)
{
struct rt_spi_bus * spi_bus = (struct rt_spi_bus *)device->bus;
struct s32k_spi *spi_device = (struct s32k_spi *)spi_bus->parent.user_data;
status_t ret;
RT_ASSERT(device != NULL);
RT_ASSERT(message != NULL);
ret = LPSPI_DRV_MasterTransferBlocking(spi_device->instance,message->send_buf,message->recv_buf,message->length,1000);
RT_ASSERT(ret == STATUS_SUCCESS);
return ret == STATUS_SUCCESS ? message->length:0;● Add the following initialization code to register the spibus device into the system
xtern void LPSPI_DRV_IRQHandler(uint32_t instance);
void S32K14X_LPspi1_Master_Slave_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
LPSPI_DRV_IRQHandler(1);
/* leave interrupt */
rt_interrupt_leave();
}
/* private rt-thread spi ops function */
static struct rt_spi_ops s32k_spi_ops =
{
.configure = spi_configure,
.xfer = spixfer,
};
static struct s32k_spi spi1;
int rt_hw_spi_init(void)
{
int result = 0;
/* config spi strruct */
spi1.instance = INST_LPSPI_1;
spi1.masterconfig = &lpspi_1_MasterConfig0;
spi1.status = &lpspi_1State;
spi1.spi_bus.parent.user_data = (void *)&spi1;
spi1.bus_name = "spi1";
result = rt_spi_bus_register(&spi1.spi_bus, spi1.bus_name, &s32k_spi_ops);
RT_ASSERT(result == RT_EOK);
LOG_D("%s bus init done", spi1.bus_name);
if(result == RT_EOK)
{
INT_SYS_InstallHandler(LPSPI1_IRQn,S32K14X_LPspi1_Master_Slave_IRQHandler,NULL);
}
return result;
}
INIT_APP_EXPORT(rt_hw_spi_init);
Verification
● Add the following test code, input the command spi10 open/config/readid to open and mount it to the spi1 bus, and read the SPI flash ID information on the board.
include <stdio.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include <drivers/spi.h>
#define SPF_R_JEDEC_CMD (0x9Fu)
#define SPF_R_JEDEC_LEN (4u)
struct rt_spi_device spi1_device;
static void spi10(int argc,char *argv[])
{
rt_err_t ret;
struct rt_spi_configuration cfg;
uint8_t sendbuff[4] = {SPF_R_JEDEC_CMD,0x00,0x00,0x00};
uint8_t recvbuff[4] = {0x00,0x00,0x00,0x00};
if(!strcmp(argv[1], "readid"))
{
rt_spi_transfer(&spi1_device,sendbuff,recvbuff,4);
rt_kprintf("read did [%x][%x][%x]\n",recvbuff[1],recvbuff[2],recvbuff[3]);
}
else if(!strcmp(argv[1], "open"))
{
ret = rt_spi_bus_attach_device(&spi1_device,"spi10","spi1",NULL);
if(ret != RT_EOK)
rt_kprintf("attach spi1 faied %d\n",ret);
}
else if(!strcmp(argv[1], "config"))
{
cfg.data_width = 8;
cfg.max_hz = 8000000;
cfg.mode = RT_SPI_MODE_3 | RT_SPI_MSB;
rt_spi_configure(&spi1_device,&cfg);
}
else if(!strcmp(argv[1], "read"))
{
}
}
MSH_CMD_EXPORT(spi10, spi10 flash test)Enter the command to verify that the flash ID information has been read, which indicates that the SPI bus and the SPI flash communication are OK, and that the system has also added the spi1 bus device and the spi10 spi device node.
The ID read in the experiment is consistent with the chip manual.
The test code has been submitted at the following path:
