2022-08-14 10:29:05 +08:00
|
|
|
/*
|
2022-08-17 00:43:24 +08:00
|
|
|
* Copyright (c) 2006-2022, Synwit Technology Co.,Ltd.
|
2022-08-14 10:29:05 +08:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*
|
|
|
|
* Change Logs:
|
|
|
|
* Date Author Notes
|
|
|
|
* 2021-07-01 lik first version
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "drv_spi.h"
|
|
|
|
|
|
|
|
#ifdef RT_USING_SPI
|
|
|
|
#ifdef BSP_USING_SPI
|
|
|
|
|
|
|
|
//#define DRV_DEBUG
|
|
|
|
#define LOG_TAG "drv.spi"
|
|
|
|
#include <drv_log.h>
|
|
|
|
|
|
|
|
#if !defined(BSP_USING_SPI0) && !defined(BSP_USING_SPI1)
|
|
|
|
#error "Please define at least one BSP_USING_SPIx"
|
|
|
|
/* this driver can be disabled at menuconfig ? RT-Thread Components ? Device Drivers */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct swm_spi_cs
|
|
|
|
{
|
|
|
|
GPIO_TypeDef *GPIOx;
|
|
|
|
uint32_t gpio_pin;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct swm_spi_cfg
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
SPI_TypeDef *SPIx;
|
|
|
|
SPI_InitStructure spi_initstruct;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* swm spi dirver class */
|
|
|
|
struct swm_spi_device
|
|
|
|
{
|
|
|
|
struct swm_spi_cfg *spi_cfg;
|
|
|
|
struct rt_spi_configuration *configure;
|
|
|
|
struct rt_spi_bus spi_bus;
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef BSP_USING_SPI0
|
|
|
|
#ifndef SPI0_BUS_CONFIG
|
|
|
|
#define SPI0_BUS_CONFIG \
|
|
|
|
{ \
|
|
|
|
.name = "spi0", \
|
|
|
|
.SPIx = SPI0, \
|
|
|
|
.spi_initstruct.clkDiv = SPI_CLKDIV_16, \
|
|
|
|
.spi_initstruct.FrameFormat = SPI_FORMAT_SPI, \
|
|
|
|
.spi_initstruct.SampleEdge = SPI_SECOND_EDGE, \
|
|
|
|
.spi_initstruct.IdleLevel = SPI_HIGH_LEVEL, \
|
|
|
|
.spi_initstruct.WordSize = 8, \
|
|
|
|
.spi_initstruct.Master = 1, \
|
|
|
|
.spi_initstruct.RXThreshold = 0, \
|
|
|
|
.spi_initstruct.RXThresholdIEn = 0, \
|
|
|
|
.spi_initstruct.TXThreshold = 0, \
|
|
|
|
.spi_initstruct.TXThresholdIEn = 0, \
|
|
|
|
.spi_initstruct.TXCompleteIEn = 0, \
|
|
|
|
}
|
|
|
|
#endif /* SPI0_BUS_CONFIG */
|
|
|
|
#endif /* BSP_USING_SPI0 */
|
|
|
|
|
|
|
|
#ifdef BSP_USING_SPI1
|
|
|
|
#ifndef SPI1_BUS_CONFIG
|
|
|
|
#define SPI1_BUS_CONFIG \
|
|
|
|
{ \
|
|
|
|
.name = "spi1", \
|
|
|
|
.SPIx = SPI1, \
|
|
|
|
.spi_initstruct.clkDiv = SPI_CLKDIV_16, \
|
|
|
|
.spi_initstruct.FrameFormat = SPI_FORMAT_SPI, \
|
|
|
|
.spi_initstruct.SampleEdge = SPI_SECOND_EDGE, \
|
|
|
|
.spi_initstruct.IdleLevel = SPI_HIGH_LEVEL, \
|
|
|
|
.spi_initstruct.WordSize = 8, \
|
|
|
|
.spi_initstruct.Master = 1, \
|
|
|
|
.spi_initstruct.RXThreshold = 0, \
|
|
|
|
.spi_initstruct.RXThresholdIEn = 0, \
|
|
|
|
.spi_initstruct.TXThreshold = 0, \
|
|
|
|
.spi_initstruct.TXThresholdIEn = 0, \
|
|
|
|
.spi_initstruct.TXCompleteIEn = 0, \
|
|
|
|
}
|
|
|
|
#endif /* SPI1_BUS_CONFIG */
|
|
|
|
#endif /* BSP_USING_SPI1 */
|
|
|
|
|
|
|
|
static struct swm_spi_cfg swm_spi_cfg[] =
|
|
|
|
{
|
|
|
|
#ifdef BSP_USING_SPI0
|
|
|
|
SPI0_BUS_CONFIG,
|
|
|
|
#endif
|
|
|
|
#ifdef BSP_USING_SPI1
|
|
|
|
SPI1_BUS_CONFIG,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct swm_spi_device spi_bus_obj[sizeof(swm_spi_cfg) / sizeof(swm_spi_cfg[0])] = {0};
|
|
|
|
|
|
|
|
static rt_err_t swm_spi_configure(struct rt_spi_device *device,
|
|
|
|
struct rt_spi_configuration *configure)
|
|
|
|
{
|
|
|
|
RT_ASSERT(device != RT_NULL);
|
|
|
|
RT_ASSERT(configure != RT_NULL);
|
|
|
|
|
|
|
|
struct swm_spi_device *spi_drv = rt_container_of(device->bus, struct swm_spi_device, spi_bus);
|
|
|
|
spi_drv->configure = configure;
|
|
|
|
struct swm_spi_cfg *spi_cfg = spi_drv->spi_cfg;
|
|
|
|
|
|
|
|
if (configure->mode & RT_SPI_SLAVE)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.Master = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.Master = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (configure->mode & RT_SPI_3WIRE)
|
|
|
|
{
|
|
|
|
return RT_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (configure->data_width == 8)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.WordSize = 8;
|
|
|
|
}
|
|
|
|
else if (configure->data_width == 16)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.WordSize = 16;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return RT_EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (configure->mode & RT_SPI_CPHA)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.SampleEdge = SPI_SECOND_EDGE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.SampleEdge = SPI_FIRST_EDGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (configure->mode & RT_SPI_CPOL)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.IdleLevel = SPI_HIGH_LEVEL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.IdleLevel = SPI_LOW_LEVEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (configure->max_hz >= SystemCoreClock / 2)
|
|
|
|
{
|
2022-08-17 00:43:24 +08:00
|
|
|
spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_2;
|
2022-08-14 10:29:05 +08:00
|
|
|
}
|
|
|
|
else if (configure->max_hz >= SystemCoreClock / 4)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_4;
|
|
|
|
}
|
|
|
|
else if (configure->max_hz >= SystemCoreClock / 8)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_8;
|
|
|
|
}
|
|
|
|
else if (configure->max_hz >= SystemCoreClock / 16)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_16;
|
|
|
|
}
|
|
|
|
else if (configure->max_hz >= SystemCoreClock / 32)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_32;
|
|
|
|
}
|
|
|
|
else if (configure->max_hz >= SystemCoreClock / 64)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_64;
|
|
|
|
}
|
|
|
|
else if (configure->max_hz >= SystemCoreClock / 128)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_128;
|
|
|
|
}
|
|
|
|
else if (configure->max_hz >= SystemCoreClock / 256)
|
|
|
|
{
|
|
|
|
spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_256;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* min prescaler 512 */
|
|
|
|
spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_512;
|
|
|
|
}
|
|
|
|
|
|
|
|
SPI_Init(spi_cfg->SPIx, &(spi_cfg->spi_initstruct));
|
|
|
|
SPI_Open(spi_cfg->SPIx);
|
|
|
|
LOG_D("%s init done", spi_cfg->name);
|
|
|
|
return RT_EOK;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SPISTEP(datalen) (((datalen) == 8) ? 1 : 2)
|
|
|
|
#define SPISEND_1(reg, ptr, datalen) \
|
|
|
|
do \
|
|
|
|
{ \
|
|
|
|
if (datalen == 8) \
|
|
|
|
{ \
|
|
|
|
(reg) = *(rt_uint8_t *)(ptr); \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
(reg) = *(rt_uint16_t *)(ptr); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
#define SPIRECV_1(reg, ptr, datalen) \
|
|
|
|
do \
|
|
|
|
{ \
|
|
|
|
if (datalen == 8) \
|
|
|
|
{ \
|
|
|
|
*(rt_uint8_t *)(ptr) = (reg); \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
*(rt_uint16_t *)(ptr) = reg; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
static rt_err_t swm_spi_txrx1b(struct swm_spi_device *spi_drv, void *rcvb, const void *sndb)
|
|
|
|
{
|
|
|
|
rt_uint32_t padrcv = 0;
|
|
|
|
rt_uint32_t padsnd = 0xFF;
|
|
|
|
if (!rcvb && !sndb)
|
|
|
|
{
|
|
|
|
return RT_ERROR;
|
|
|
|
}
|
|
|
|
if (!rcvb)
|
|
|
|
{
|
|
|
|
rcvb = &padrcv;
|
|
|
|
}
|
|
|
|
if (!sndb)
|
|
|
|
{
|
|
|
|
sndb = &padsnd;
|
|
|
|
}
|
|
|
|
while (SPI_IsTXFull(spi_drv->spi_cfg->SPIx))
|
|
|
|
;
|
|
|
|
SPISEND_1(spi_drv->spi_cfg->SPIx->DATA, sndb, spi_drv->spi_cfg->spi_initstruct.WordSize);
|
|
|
|
while (SPI_IsRXEmpty(spi_drv->spi_cfg->SPIx))
|
|
|
|
;
|
|
|
|
SPIRECV_1(spi_drv->spi_cfg->SPIx->DATA, rcvb, spi_drv->spi_cfg->spi_initstruct.WordSize);
|
|
|
|
return RT_EOK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static rt_uint32_t swm_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
|
|
|
|
{
|
|
|
|
rt_err_t res;
|
|
|
|
|
|
|
|
RT_ASSERT(device != RT_NULL);
|
|
|
|
RT_ASSERT(device->bus != RT_NULL);
|
|
|
|
RT_ASSERT(device->bus->parent.user_data != RT_NULL);
|
|
|
|
RT_ASSERT(message != RT_NULL);
|
|
|
|
|
|
|
|
struct swm_spi_device *spi_drv = rt_container_of(device->bus, struct swm_spi_device, spi_bus);
|
|
|
|
struct swm_spi_cfg *spi_cfg = spi_drv->spi_cfg;
|
|
|
|
struct swm_spi_cs *cs = device->parent.user_data;
|
|
|
|
|
|
|
|
if (message->cs_take)
|
|
|
|
{
|
|
|
|
GPIO_ClrBit(cs->GPIOx, cs->gpio_pin);
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_D("%s transfer prepare and start", spi_cfg->name);
|
|
|
|
LOG_D("%s sendbuf: %X, recvbuf: %X, length: %d",
|
|
|
|
spi_cfg->name, (uint32_t)message->send_buf, (uint32_t)message->recv_buf, message->length);
|
|
|
|
|
|
|
|
const rt_uint8_t *sndb = message->send_buf;
|
|
|
|
rt_uint8_t *rcvb = message->recv_buf;
|
|
|
|
rt_int32_t length = message->length;
|
|
|
|
|
|
|
|
while (length)
|
|
|
|
{
|
|
|
|
res = swm_spi_txrx1b(spi_drv, rcvb, sndb);
|
|
|
|
if (rcvb)
|
|
|
|
{
|
|
|
|
rcvb += SPISTEP(spi_cfg->spi_initstruct.WordSize);
|
|
|
|
}
|
|
|
|
if (sndb)
|
|
|
|
{
|
|
|
|
sndb += SPISTEP(spi_cfg->spi_initstruct.WordSize);
|
|
|
|
}
|
|
|
|
if (res != RT_EOK)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
length--;
|
|
|
|
}
|
|
|
|
/* Wait until Busy flag is reset before disabling SPI */
|
|
|
|
while (!SPI_IsTXEmpty(spi_cfg->SPIx) && !SPI_IsRXEmpty(spi_cfg->SPIx))
|
|
|
|
;
|
|
|
|
if (message->cs_release)
|
|
|
|
{
|
|
|
|
GPIO_SetBit(cs->GPIOx, cs->gpio_pin);
|
|
|
|
}
|
|
|
|
return message->length - length;
|
|
|
|
}
|
|
|
|
|
2022-08-17 00:43:24 +08:00
|
|
|
static const struct rt_spi_ops swm_spi_ops =
|
2022-08-14 10:29:05 +08:00
|
|
|
{
|
|
|
|
.configure = swm_spi_configure,
|
|
|
|
.xfer = swm_spi_xfer,
|
|
|
|
};
|
|
|
|
|
|
|
|
//cannot be used before completion init
|
|
|
|
rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, GPIO_TypeDef *cs_gpiox, uint32_t cs_gpio_pin)
|
|
|
|
{
|
|
|
|
RT_ASSERT(bus_name != RT_NULL);
|
|
|
|
RT_ASSERT(device_name != RT_NULL);
|
|
|
|
|
|
|
|
rt_err_t result;
|
|
|
|
struct rt_spi_device *spi_device;
|
|
|
|
struct swm_spi_cs *cs_pin;
|
|
|
|
|
|
|
|
GPIO_Init(cs_gpiox, cs_gpio_pin, 1, 1, 0, 0);
|
|
|
|
GPIO_SetBit(cs_gpiox, cs_gpio_pin);
|
|
|
|
|
|
|
|
spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
|
|
|
|
RT_ASSERT(spi_device != RT_NULL);
|
|
|
|
cs_pin = (struct swm_spi_cs *)rt_malloc(sizeof(struct swm_spi_cs));
|
|
|
|
RT_ASSERT(cs_pin != RT_NULL);
|
|
|
|
cs_pin->GPIOx = cs_gpiox;
|
|
|
|
cs_pin->gpio_pin = cs_gpio_pin;
|
|
|
|
|
|
|
|
result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);
|
|
|
|
if (result != RT_EOK)
|
|
|
|
{
|
|
|
|
LOG_E("%s attach to %s faild, %d\n", device_name, bus_name, result);
|
|
|
|
}
|
|
|
|
RT_ASSERT(result == RT_EOK);
|
|
|
|
LOG_D("%s attach to %s done", device_name, bus_name);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int swm_spi_init(void)
|
|
|
|
{
|
|
|
|
rt_err_t result;
|
|
|
|
|
|
|
|
#ifdef BSP_USING_SPI0
|
|
|
|
PORT_Init(PORTM, PIN2, PORTM_PIN2_SPI0_SCLK, 0);
|
|
|
|
PORT_Init(PORTM, PIN4, PORTM_PIN4_SPI0_MISO, 1);
|
|
|
|
PORT_Init(PORTM, PIN5, PORTM_PIN5_SPI0_MOSI, 0);
|
|
|
|
#endif //BSP_USING_SPI0
|
|
|
|
|
|
|
|
#ifdef BSP_USING_SPI1
|
|
|
|
PORT_Init(PORTB, PIN2, PORTB_PIN2_SPI1_SCLK, 0);
|
|
|
|
PORT_Init(PORTB, PIN3, PORTB_PIN3_SPI1_MISO, 1);
|
|
|
|
PORT_Init(PORTB, PIN4, PORTB_PIN4_SPI1_MOSI, 0);
|
|
|
|
#endif //BSP_USING_SPI1
|
|
|
|
|
|
|
|
for (int i = 0; i < sizeof(swm_spi_cfg) / sizeof(swm_spi_cfg[0]); i++)
|
|
|
|
{
|
|
|
|
spi_bus_obj[i].spi_cfg = &swm_spi_cfg[i];
|
|
|
|
spi_bus_obj[i].spi_bus.parent.user_data = &swm_spi_cfg[i];
|
|
|
|
result = rt_spi_bus_register(&spi_bus_obj[i].spi_bus, swm_spi_cfg[i].name, &swm_spi_ops);
|
|
|
|
if (result != RT_EOK)
|
|
|
|
{
|
|
|
|
LOG_E("%s bus register fail.", swm_spi_cfg[i].name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LOG_D("%s bus register success.", swm_spi_cfg[i].name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
INIT_BOARD_EXPORT(swm_spi_init);
|
|
|
|
|
|
|
|
#endif /* BSP_USING_SPI */
|
|
|
|
#endif /* RT_USING_SPI */
|