/* * 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 #include #include #include #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