204 lines
4.7 KiB
C
204 lines
4.7 KiB
C
/*
|
|
* spi-st.c - SPI driver for STM32F103/GD32F103/GD32VF103
|
|
*
|
|
* Copyright (C) 2019 Flying Stone Technology
|
|
* Author: NIIBE Yutaka <gniibe@fsij.org>
|
|
*
|
|
* This file is a part of Chopstx, a thread library for embedded.
|
|
*
|
|
* Chopstx is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Chopstx is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* As additional permission under GNU GPL version 3 section 7, you may
|
|
* distribute non-source form of the Program without the copy of the
|
|
* GNU GPL normally required by section 4, provided you inform the
|
|
* recipients of GNU GPL by a written offer.
|
|
*
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <chopstx.h>
|
|
#include <mcu/stm32.h>
|
|
|
|
/* Hardware registers */
|
|
struct SPI {
|
|
volatile uint32_t CR1;
|
|
volatile uint32_t CR2;
|
|
volatile uint32_t SR;
|
|
volatile uint32_t DR;
|
|
volatile uint32_t CRCPR;
|
|
volatile uint32_t RXCRCR;
|
|
volatile uint32_t TXCRCR;
|
|
};
|
|
|
|
/* It is named SPI0 in GD32VF103, while it's "SPI1" in RM0008 of STM32F103 */
|
|
#define SPI0_BASE (APB2PERIPH_BASE + 0x3000)
|
|
#define SPI0 ((struct SPI *)SPI0_BASE)
|
|
|
|
#define INTR_REQ_SPI0 54 /* For GD32VF103 */
|
|
|
|
static struct SPI *SPIx = SPI0;
|
|
static struct chx_intr spi_intr;
|
|
static int tx_ready;
|
|
static int rx_done;
|
|
|
|
/*
|
|
* For now, it only support configuration of a specific use case:
|
|
*
|
|
* SPI0 of GD32VF103
|
|
* Master mode
|
|
* Hardware NSS pin not used
|
|
* Two unidirectional lines, full duplex
|
|
* CRC enabled
|
|
* 8-bit data frame
|
|
* MSBit first
|
|
* Clock polarity: High==idle
|
|
*
|
|
* We need to consider what kind of configuration can be specified
|
|
* by the API. Perhaps, it should return an error code when it is
|
|
* not supported by the hardware.
|
|
*/
|
|
|
|
int
|
|
spi_init (void)
|
|
{
|
|
uint32_t cr1_reg = 0;
|
|
|
|
/* Enable SPI module */
|
|
RCC->APB2ENR |= (1 << 12);
|
|
RCC->APB2RSTR = (1 << 12);
|
|
RCC->APB2RSTR = 0;
|
|
|
|
/* Master mode */
|
|
cr1_reg |= (1 << 2); /* In RM0008, it's called MSTR */
|
|
|
|
/* No use of hardware NSS pin */
|
|
cr1_reg |= (1 << 9); /* In RM0008, it's called SSM */
|
|
cr1_reg |= (1 << 8); /* In RM0008, it's called SSI */
|
|
|
|
/* Two unidirectional lines, full duplex: Bit15=0, Bit14=0, Bit10=0 */
|
|
/* Bit11: data-frame-format: 8-bit 0 (16-bit (1<<11)) */
|
|
/* But7: bit-order: MSB first 0, LSB first (1<<7) */
|
|
|
|
/* Enable CRC */
|
|
cr1_reg |= (1 << 13);
|
|
|
|
cr1_reg |= (0x02 << 3); /* prescaler: fPCLK/8 */
|
|
cr1_reg |= (1 << 1); /* clock polarity 1==idle */
|
|
cr1_reg |= (1 << 0); /* second clock data capture edge */
|
|
cr1_reg |= (1 << 6); /* SPI enable */
|
|
|
|
SPIx->CR1 = cr1_reg;
|
|
|
|
/* Don't touch CRCPR: Just use reset default value of 7 */
|
|
/* Don't touch I2SCFGR, I2SPR */
|
|
|
|
chopstx_claim_irq (&spi_intr, INTR_REQ_SPI0);
|
|
tx_ready = 0;
|
|
rx_done = 0;
|
|
|
|
/* Enable notification for Tx empty, Rx done, or Error */
|
|
SPIx->CR2 = (1 << 7) | (1 << 6) | (1 << 5);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
clear_crc_err (void)
|
|
{
|
|
SPIx->SR &= ~(1 << 4);
|
|
}
|
|
|
|
static void
|
|
put_data (uint16_t data)
|
|
{
|
|
SPIx->DR = (uint32_t)data;
|
|
}
|
|
|
|
static uint16_t
|
|
get_data (void)
|
|
{
|
|
return ((uint16_t)SPIx->DR);
|
|
}
|
|
|
|
|
|
#define CHECK_TX 0
|
|
#define CHECK_RX 1
|
|
|
|
static void
|
|
check_transmit (int for_what)
|
|
{
|
|
for (;;)
|
|
{
|
|
uint16_t status;
|
|
|
|
if (for_what == CHECK_TX && tx_ready)
|
|
return;
|
|
|
|
if (for_what == CHECK_RX && rx_done)
|
|
return;
|
|
|
|
if (for_what == CHECK_TX)
|
|
SPIx->CR2 |= (1 << 7);
|
|
|
|
if (for_what == CHECK_RX)
|
|
SPIx->CR2 |= (1 << 6);
|
|
|
|
/* Wait an event */
|
|
chopstx_intr_wait (&spi_intr);
|
|
status = SPIx->SR;
|
|
|
|
if ((status & (1 << 0)))
|
|
{
|
|
rx_done = 1;
|
|
SPIx->CR2 &= ~(1 << 6);
|
|
}
|
|
|
|
if ((status & (1 << 1)))
|
|
{
|
|
tx_ready = 1;
|
|
SPIx->CR2 &= ~(1 << 7);
|
|
}
|
|
|
|
if ((status & (1 << 4)))
|
|
{
|
|
/*FIXME: stat crc_err_count++ */
|
|
clear_crc_err ();
|
|
}
|
|
|
|
#if 0
|
|
/*FIXME: stat err_count++ */
|
|
if ((status & 0x00fc))
|
|
{
|
|
}
|
|
#endif
|
|
|
|
chopstx_intr_done (&spi_intr);
|
|
}
|
|
}
|
|
|
|
void
|
|
spi_send (uint16_t data)
|
|
{
|
|
check_transmit (CHECK_TX);
|
|
put_data (data);
|
|
}
|
|
|
|
uint16_t
|
|
spi_recv (void)
|
|
{
|
|
check_transmit (CHECK_RX);
|
|
return get_data ();
|
|
}
|