diff --git a/ChangeLog b/ChangeLog index a486d6b..695bd21 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2019-12-03 NIIBE Yutaka + * contrib/spi-st.c: New. + * chopstx.c (chx_timer_expired): When no timer, explicitly call chx_systick_reload with 0. diff --git a/contrib/spi-st.c b/contrib/spi-st.c new file mode 100644 index 0000000..7dd9930 --- /dev/null +++ b/contrib/spi-st.c @@ -0,0 +1,203 @@ +/* + * spi-st.c - SPI driver for STM32F103/GD32F103/GD32VF103 + * + * Copyright (C) 2019 Flying Stone Technology + * Author: NIIBE Yutaka + * + * 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 . + * + * 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 +#include +#include +#include + +/* 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 (); +}