440 lines
13 KiB
C
440 lines
13 KiB
C
|
/*
|
||
|
* Copyright (C) 2018 Shanghai Eastsoft Microelectronics Co., Ltd.
|
||
|
*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*
|
||
|
* Change Logs:
|
||
|
* Date Author Notes
|
||
|
* 2019-10-23 yuzrain the first version
|
||
|
*/
|
||
|
|
||
|
#include <rtthread.h>
|
||
|
#include <rtdevice.h>
|
||
|
#include <string.h>
|
||
|
#include <rthw.h>
|
||
|
#include "board.h"
|
||
|
#include "drv_spi.h"
|
||
|
#include "md_spi.h"
|
||
|
#include "md_gpio.h"
|
||
|
|
||
|
#ifdef RT_USING_SPI
|
||
|
|
||
|
#define SPITIMEOUT 0x0FFF
|
||
|
|
||
|
static rt_err_t __spi_send(struct rt_spi_device *device, rt_uint8_t *buf,
|
||
|
rt_int32_t len, rt_uint32_t tmout);
|
||
|
static rt_err_t __spi_recv(struct rt_spi_device *device, rt_uint8_t *buf,
|
||
|
rt_int32_t len, rt_uint32_t tmout);
|
||
|
static rt_err_t __spi_send_recv(struct rt_spi_device *device, rt_uint8_t *tbuf,
|
||
|
rt_uint8_t *rbuf, rt_int32_t len, rt_uint32_t tmout);
|
||
|
|
||
|
/**
|
||
|
* @brief: SPI single line send.
|
||
|
* @param: device, pointer to the SPI device
|
||
|
* @param: buf, send data buffer
|
||
|
* @param: len, the length of buf
|
||
|
* @param: tmout, timeout
|
||
|
* @retval: rt_err_t
|
||
|
*/
|
||
|
static rt_err_t __spi_send(struct rt_spi_device *device, rt_uint8_t *buf,
|
||
|
rt_int32_t len, rt_uint32_t tmout)
|
||
|
{
|
||
|
SPI_TypeDef *hspi;
|
||
|
rt_uint32_t rt_timout;
|
||
|
rt_uint8_t temp_data;
|
||
|
|
||
|
/* Get the SPI port */
|
||
|
hspi = (SPI_TypeDef *)device->bus->parent.user_data;
|
||
|
|
||
|
/* Open SPI if it is disabled */
|
||
|
if (READ_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK) != SPI_CON1_SPIEN_MSK)
|
||
|
SET_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK);
|
||
|
|
||
|
while (len > 0)
|
||
|
{
|
||
|
/* Confirm that no data is being transmitted */
|
||
|
rt_timout = tmout;
|
||
|
while (((hspi->STAT & SPI_STAT_TXE_MSK) == 0) && (--rt_timout));
|
||
|
if (rt_timout == 0)
|
||
|
return RT_ETIMEOUT;
|
||
|
|
||
|
/* Send data */
|
||
|
if (device->config.data_width == 8)
|
||
|
{
|
||
|
hspi->DATA = *(rt_uint8_t *)buf;
|
||
|
buf++;
|
||
|
len--;
|
||
|
}
|
||
|
else if (device->config.data_width == 16)
|
||
|
{
|
||
|
hspi->DATA = *(rt_uint16_t *)buf;
|
||
|
buf += 2;
|
||
|
len -= 2;
|
||
|
}
|
||
|
else
|
||
|
return RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
/* At here, we have transmitted all the data.
|
||
|
* The next step is to clear the IT flag.
|
||
|
*/
|
||
|
for (rt_uint8_t i = 0; i < md_spi_get_stat_rxflv(hspi); i++)
|
||
|
temp_data = hspi->DATA;
|
||
|
UNUSED(temp_data);
|
||
|
hspi->ICR = hspi->RIF;
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief: SPI single line receive.
|
||
|
* @param: device, pointer to the SPI device
|
||
|
* @param: buf, receive data buffer
|
||
|
* @param: len, the length of buf
|
||
|
* @param: tmout, timeout
|
||
|
* @retval: rt_err_t
|
||
|
*/
|
||
|
static rt_err_t __spi_recv(struct rt_spi_device *device, rt_uint8_t *buf,
|
||
|
rt_int32_t len, rt_uint32_t tmout)
|
||
|
{
|
||
|
SPI_TypeDef *hspi;
|
||
|
rt_uint32_t rt_timout;
|
||
|
|
||
|
/* Get the SPI port */
|
||
|
hspi = (SPI_TypeDef *)device->bus->parent.user_data;
|
||
|
|
||
|
/* Open SPI if it is disabled */
|
||
|
if (READ_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK) != SPI_CON1_SPIEN_MSK)
|
||
|
SET_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK);
|
||
|
|
||
|
/* Handle data in __spi_send_recv() function */
|
||
|
if (((device->config.mode & RT_SPI_SLAVE) == 0)
|
||
|
&& ((device->config.mode & RT_SPI_3WIRE) == 0))
|
||
|
__spi_send_recv(device, buf, buf, len, tmout);
|
||
|
|
||
|
while (len > 0)
|
||
|
{
|
||
|
/* Waiting for data */
|
||
|
rt_timout = tmout;
|
||
|
while (((hspi->STAT & SPI_STAT_RXTH_MSK) == 0) && (--rt_timout));
|
||
|
if (rt_timout == 0)
|
||
|
return RT_ETIMEOUT;
|
||
|
|
||
|
/* Send data */
|
||
|
if (device->config.data_width == 8)
|
||
|
{
|
||
|
*(rt_uint8_t *)buf = hspi->DATA;
|
||
|
buf++;
|
||
|
len--;
|
||
|
}
|
||
|
else if (device->config.data_width == 16)
|
||
|
{
|
||
|
*(rt_uint16_t *)buf = hspi->DATA;
|
||
|
buf += 2;
|
||
|
len -= 2;
|
||
|
}
|
||
|
else
|
||
|
return RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
/* At here, we have transmitted all the data.
|
||
|
* The next step is to clear the IT flag.
|
||
|
*/
|
||
|
hspi->ICR = hspi->RIF;
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief: SPI two line transmission.
|
||
|
* @param: device, pointer to the SPI device
|
||
|
* @param: tbuf, send data buffer
|
||
|
* @param: rbuf, receive data buffer
|
||
|
* @param: len, the length of buf
|
||
|
* @param: tmout, timeout
|
||
|
* @retval: rt_err_t
|
||
|
*/
|
||
|
static rt_err_t __spi_send_recv(struct rt_spi_device *device, rt_uint8_t *tbuf,
|
||
|
rt_uint8_t *rbuf, rt_int32_t len, rt_uint32_t tmout)
|
||
|
{
|
||
|
SPI_TypeDef *hspi;
|
||
|
rt_uint32_t rt_timout;
|
||
|
|
||
|
/* Get the SPI port */
|
||
|
hspi = (SPI_TypeDef *)device->bus->parent.user_data;
|
||
|
|
||
|
/* Open SPI if it is disabled */
|
||
|
if (READ_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK) != SPI_CON1_SPIEN_MSK)
|
||
|
SET_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK);
|
||
|
|
||
|
/* return error if SPI is in 1-line mode */
|
||
|
if ((device->config.mode & RT_SPI_3WIRE) == RT_SPI_3WIRE)
|
||
|
return RT_ERROR;
|
||
|
|
||
|
while (len > 0)
|
||
|
{
|
||
|
/* Confirm that no data is being transmitted */
|
||
|
rt_timout = tmout;
|
||
|
while (((hspi->STAT & SPI_STAT_TXE_MSK) == 0) && (--rt_timout));
|
||
|
if (rt_timout == 0)
|
||
|
return RT_ETIMEOUT;
|
||
|
|
||
|
/* Send data */
|
||
|
if (device->config.data_width == 8)
|
||
|
{
|
||
|
hspi->DATA = *(rt_uint8_t *)tbuf;
|
||
|
tbuf++;
|
||
|
len--;
|
||
|
}
|
||
|
else if (device->config.data_width == 16)
|
||
|
{
|
||
|
hspi->DATA = *(rt_uint16_t *)tbuf;
|
||
|
tbuf += 2;
|
||
|
len -= 2;
|
||
|
}
|
||
|
else
|
||
|
return RT_EINVAL;
|
||
|
|
||
|
/* Waiting for data */
|
||
|
rt_timout = tmout;
|
||
|
while (((hspi->STAT & SPI_STAT_RXTH_MSK) == 0) && (--rt_timout));
|
||
|
if (rt_timout == 0)
|
||
|
return RT_ETIMEOUT;
|
||
|
|
||
|
/* Send data */
|
||
|
if (device->config.data_width == 8)
|
||
|
{
|
||
|
*(rt_uint8_t *)rbuf = hspi->DATA;
|
||
|
rbuf++;
|
||
|
}
|
||
|
else if (device->config.data_width == 16)
|
||
|
{
|
||
|
*(rt_uint16_t *)rbuf = hspi->DATA;
|
||
|
rbuf += 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* At here, we have transmitted all the data.
|
||
|
* The next step is to clear the IT flag.
|
||
|
*/
|
||
|
hspi->ICR = hspi->RIF;
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
rt_err_t spi_configure(struct rt_spi_device *device,
|
||
|
struct rt_spi_configuration *cfg)
|
||
|
{
|
||
|
SPI_TypeDef *hspi;
|
||
|
hspi = (SPI_TypeDef *)device->bus->parent.user_data;
|
||
|
|
||
|
/* Close SPI temporarily */
|
||
|
md_spi_disable_con1_spien(hspi);
|
||
|
|
||
|
/* config spi mode */
|
||
|
if (cfg->mode & RT_SPI_SLAVE)
|
||
|
md_spi_set_con1_mstren(hspi, MD_SPI_MODE_SLAVE);
|
||
|
else
|
||
|
md_spi_set_con1_mstren(hspi, MD_SPI_MODE_MASTER);
|
||
|
|
||
|
/* Config data mode */
|
||
|
if (cfg->mode & RT_SPI_3WIRE)
|
||
|
md_spi_set_con1_bidimode(hspi, MD_SPI_HALF_DUPLEX);
|
||
|
else
|
||
|
md_spi_set_con1_bidimode(hspi, MD_SPI_FULL_DUPLEX);
|
||
|
|
||
|
/* Config data width */
|
||
|
if (cfg->data_width == 8)
|
||
|
md_spi_set_con1_flen(hspi, MD_SPI_FRAME_FORMAT_8BIT);
|
||
|
else if (cfg->data_width == 16)
|
||
|
md_spi_set_con1_flen(hspi, SPI_CON1_FLEN_MSK);
|
||
|
|
||
|
/* Config phase */
|
||
|
if (cfg->mode & RT_SPI_CPHA)
|
||
|
md_spi_set_con1_cpha(hspi, MD_SPI_PHASE_2EDGE);
|
||
|
else
|
||
|
md_spi_set_con1_cpha(hspi, MD_SPI_PHASE_1EDGE);
|
||
|
|
||
|
/* Config polarity */
|
||
|
if (cfg->mode & RT_SPI_CPOL)
|
||
|
md_spi_set_con1_cpol(hspi, MD_SPI_POLARITY_HIGH);
|
||
|
else
|
||
|
md_spi_set_con1_cpol(hspi, MD_SPI_POLARITY_LOW);
|
||
|
|
||
|
/* Config if NSS pin is managed by software */
|
||
|
md_spi_disable_con1_ssen(hspi);
|
||
|
|
||
|
/* config spi clock */
|
||
|
if (cfg->max_hz >= SystemCoreClock / 2)
|
||
|
{
|
||
|
/* pclk1 max speed 48MHz, spi master max speed 10MHz */
|
||
|
if (SystemCoreClock / 2 <= 10000000)
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV2);
|
||
|
else if (SystemCoreClock / 4 <= 10000000)
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV4);
|
||
|
else
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV8);
|
||
|
}
|
||
|
else if (cfg->max_hz >= SystemCoreClock / 4)
|
||
|
{
|
||
|
/* pclk1 max speed 48MHz, spi master max speed 10MHz */
|
||
|
if (SystemCoreClock / 4 <= 10000000)
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV4);
|
||
|
else
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV8);
|
||
|
}
|
||
|
else if (cfg->max_hz >= SystemCoreClock / 8)
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV8);
|
||
|
else if (cfg->max_hz >= SystemCoreClock / 16)
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV16);
|
||
|
else if (cfg->max_hz >= SystemCoreClock / 32)
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV32);
|
||
|
else if (cfg->max_hz >= SystemCoreClock / 64)
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV64);
|
||
|
else if (cfg->max_hz >= SystemCoreClock / 128)
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV128);
|
||
|
else
|
||
|
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV256);
|
||
|
|
||
|
/* Enable SPI */
|
||
|
md_spi_enable_con1_spien(hspi);
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
static rt_uint32_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
|
||
|
{
|
||
|
rt_err_t res;
|
||
|
rt_uint32_t *cs;
|
||
|
|
||
|
RT_ASSERT(device != RT_NULL);
|
||
|
RT_ASSERT(device->bus != RT_NULL);
|
||
|
RT_ASSERT(device->bus->parent.user_data != RT_NULL);
|
||
|
RT_ASSERT(message->send_buf != RT_NULL || message->recv_buf != RT_NULL);
|
||
|
|
||
|
cs = (rt_uint32_t *)device->parent.user_data;
|
||
|
|
||
|
/* only send data */
|
||
|
if (message->recv_buf == RT_NULL)
|
||
|
{
|
||
|
if (message->cs_take)
|
||
|
{
|
||
|
rt_pin_write(*cs, 0);
|
||
|
}
|
||
|
res = __spi_send(device, (rt_uint8_t *)message->send_buf, (rt_int32_t)message->length, SPITIMEOUT);
|
||
|
if (message->cs_release)
|
||
|
{
|
||
|
rt_pin_write(*cs, 1);
|
||
|
}
|
||
|
if (res != RT_EOK)
|
||
|
return RT_ERROR;
|
||
|
}
|
||
|
|
||
|
/* only receive data */
|
||
|
if (message->send_buf == RT_NULL)
|
||
|
{
|
||
|
if (message->cs_take)
|
||
|
{
|
||
|
rt_pin_write(*cs, 0);
|
||
|
}
|
||
|
res = __spi_recv(device, (rt_uint8_t *)message->recv_buf, (rt_int32_t)message->length, SPITIMEOUT);
|
||
|
if (message->cs_release)
|
||
|
{
|
||
|
rt_pin_write(*cs, 1);
|
||
|
}
|
||
|
if (res != RT_EOK)
|
||
|
return RT_ERROR;
|
||
|
}
|
||
|
|
||
|
/* send & receive */
|
||
|
else
|
||
|
{
|
||
|
if (message->cs_take)
|
||
|
{
|
||
|
rt_pin_write(*cs, 0);
|
||
|
}
|
||
|
res = __spi_send_recv(device, (rt_uint8_t *)message->send_buf, (rt_uint8_t *)message->recv_buf,
|
||
|
(rt_int32_t)message->length, SPITIMEOUT);
|
||
|
if (message->cs_release)
|
||
|
{
|
||
|
rt_pin_write(*cs, 1);
|
||
|
}
|
||
|
if (res != RT_EOK)
|
||
|
return RT_ERROR;
|
||
|
}
|
||
|
|
||
|
return message->length;
|
||
|
}
|
||
|
|
||
|
const struct rt_spi_ops es32f0_spi_ops =
|
||
|
{
|
||
|
spi_configure,
|
||
|
spixfer,
|
||
|
};
|
||
|
|
||
|
static struct rt_spi_bus _spi_bus1, _spi_bus2;
|
||
|
int es32f0_spi_register_bus(SPI_TypeDef *SPIx, const char *name)
|
||
|
{
|
||
|
struct rt_spi_bus *spi_bus;
|
||
|
|
||
|
if (SPIx == SPI2)
|
||
|
{
|
||
|
/* Open GPIO and SPI clock */
|
||
|
SET_BIT(RCU->APB1EN, RCU_APB1EN_SPI2EN_MSK);
|
||
|
SET_BIT(RCU->AHBEN, RCU_AHBEN_GPBEN_MSK);
|
||
|
|
||
|
/* Config SPI2 GPIO */
|
||
|
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_13, MD_GPIO_MODE_FUNCTION);
|
||
|
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_14, MD_GPIO_MODE_FUNCTION);
|
||
|
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_15, MD_GPIO_MODE_FUNCTION);
|
||
|
md_gpio_set_function8_15 (GPIOB, MD_GPIO_PIN_13, MD_GPIO_AF0);
|
||
|
md_gpio_set_function8_15 (GPIOB, MD_GPIO_PIN_14, MD_GPIO_AF0);
|
||
|
md_gpio_set_function8_15 (GPIOB, MD_GPIO_PIN_15, MD_GPIO_AF0);
|
||
|
|
||
|
/* Remember SPI bus2 */
|
||
|
spi_bus = &_spi_bus2;
|
||
|
}
|
||
|
else if (SPIx == SPI1)
|
||
|
{
|
||
|
/* Open GPIO and SPI clock */
|
||
|
SET_BIT(RCU->APB2EN, RCU_APB2EN_SPI1EN_MSK);
|
||
|
SET_BIT(RCU->AHBEN, RCU_AHBEN_GPBEN_MSK);
|
||
|
|
||
|
/* Config SPI1 GPIO */
|
||
|
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_3, MD_GPIO_MODE_FUNCTION);
|
||
|
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_4, MD_GPIO_MODE_FUNCTION);
|
||
|
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_5, MD_GPIO_MODE_FUNCTION);
|
||
|
md_gpio_set_function0_7 (GPIOB, MD_GPIO_PIN_3, MD_GPIO_AF0);
|
||
|
md_gpio_set_function0_7 (GPIOB, MD_GPIO_PIN_4, MD_GPIO_AF0);
|
||
|
md_gpio_set_function0_7 (GPIOB, MD_GPIO_PIN_5, MD_GPIO_AF0);
|
||
|
|
||
|
/* Remember SPI bus1 */
|
||
|
spi_bus = &_spi_bus1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
spi_bus->parent.user_data = SPIx;
|
||
|
|
||
|
return rt_spi_bus_register(spi_bus, name, &es32f0_spi_ops);
|
||
|
}
|
||
|
|
||
|
int rt_hw_spi_init(void)
|
||
|
{
|
||
|
int result = 0;
|
||
|
|
||
|
#ifdef BSP_USING_SPI2
|
||
|
result = es32f0_spi_register_bus(SPI2, "spi2");
|
||
|
#endif
|
||
|
|
||
|
#ifdef BSP_USING_SPI1
|
||
|
result = es32f0_spi_register_bus(SPI1, "spi1");
|
||
|
#endif
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
INIT_BOARD_EXPORT(rt_hw_spi_init);
|
||
|
|
||
|
#endif
|