diff --git a/pinio.h b/pinio.h index efe8e06..b4502e8 100644 --- a/pinio.h +++ b/pinio.h @@ -130,32 +130,12 @@ IO ## _PORT->BSRR = MASK((IO ## _PIN + 16)); \ } while (0) - /** - Set pins as input/output. Standard is input. MODER ist 32bit. 2bits for every pin. - */ - /** Set pin as input. - - reset pullup - - set input mode - */ - #define _SET_INPUT(IO) \ + #define _SET_MODE(IO, v) \ do { \ - IO ## _PORT->PUPDR &= ~(3 << ((IO ## _PIN) << 1)); \ - IO ## _PORT->MODER &= ~(3 << ((IO ## _PIN) << 1)); \ - } while (0) - - /** Set pin as output. - - reset Pullup - - reset direction mode - - set output - - set speed - */ - #define _SET_OUTPUT(IO) \ - do { \ - IO ## _PORT->PUPDR &= ~(3 << ((IO ## _PIN) << 1)); \ - IO ## _PORT->MODER &= ~(3 << ((IO ## _PIN) << 1)); \ - IO ## _PORT->MODER |= (1 << ((IO ## _PIN) << 1)); \ - IO ## _PORT->OSPEEDR |= (3 << ((IO ## _PIN) << 1)); \ + IO ## _PORT->MODER &= ~(0x3 << ((IO ## _PIN) * 2)); \ + IO ## _PORT->MODER |= (v << ((IO ## _PIN) * 2)); \ } while (0) + #define SET_MODE(IO, v) _SET_MODE(IO, v) /// Enable pullup resistor. #define _PULLUP_ON(IO) \ @@ -169,6 +149,50 @@ IO ## _PORT->PUPDR &= ~(3 << ((IO ## _PIN) << 1)); \ } while (0) + /// Set alternate function register + #define _SET_AFR(IO, v) \ + do { \ + uint8_t subst; \ + subst = (IO ## _PIN >= 8); \ + IO ## _PORT->AFR[subst] &= ~(0xF << ((IO ## _PIN - (subst * 8)) * 4)); \ + IO ## _PORT->AFR[subst] |= ((v) << ((IO ## _PIN - (subst * 8)) * 4)); \ + } while (0) + #define SET_AFR(IO, v) _SET_AFR(IO, v) + + /// Set output speed register + #define _SET_OSPEED(IO, v) \ + do { \ + IO ## _PORT->OSPEEDR &= ~(0x3 << ((IO ## _PIN) * 2)); \ + IO ## _PORT->OSPEEDR |= ~(v << ((IO ## _PIN) * 2)); \ + } while (0) + #define SET_OSPEED(IO, v) _SET_OSPEED(IO, v) + + /** + Set pins as input/output. Standard is input. MODER ist 32bit. 2bits for every pin. + */ + /** Set pin as input. + - reset pullup + - set input mode + */ + #define _SET_INPUT(IO) \ + do { \ + _PULLUP_OFF(IO); \ + _SET_MODE(IO, 0); \ + } while (0) + + /** Set pin as output. + - reset Pullup + - reset direction mode + - set output + - set speed + */ + #define _SET_OUTPUT(IO) \ + do { \ + _PULLUP_OFF(IO); \ + _SET_MODE(IO, 1); \ + _SET_OSPEED(IO, 3); \ + } while (0) + #elif defined SIMULATOR #include "simulator.h" diff --git a/spi-stm32.c b/spi-stm32.c new file mode 100644 index 0000000..b716808 --- /dev/null +++ b/spi-stm32.c @@ -0,0 +1,133 @@ + +/** \file + \brief SPI routines, STM32F411 specific part. +*/ + +#if defined TEACUP_C_INCLUDE && defined __ARM_STM32F411__ + +/** Initialise SPI subsystem. + +20.3.3 Configuring the SPI in master mode + In the master configuration, the serial clock is generated on the SCK pin. + Procedure + 1. Select the BR[2:0] bits to define the serial clock baud rate (see SPI_CR1 register). + 2. Select the CPOL and CPHA bits to define one of the four relationships between the + data transfer and the serial clock (see Figure 194). This step is not required when the + TI mode is selected. + 3. Set the DFF bit to define 8- or 16-bit data frame format + 4. Configure the LSBFIRST bit in the SPI_CR1 register to define the frame format. This + step is not required when the TI mode is selected. + 5. If the NSS pin is required in input mode, in hardware mode, connect the NSS pin to a + high-level signal during the complete byte transmit sequence. In NSS software mode, + set the SSM and SSI bits in the SPI_CR1 register. If the NSS pin is required in output + mode, the SSOE bit only should be set. This step is not required when the TI mode is + selected. + 6. Set the FRF bit in SPI_CR2 to select the TI protocol for serial communications. + 7. The MSTR and SPE bits must be set (they remain set only if the NSS pin is connected + to a high-level signal). + + In this configuration the MOSI pin is a data output and the MISO pin is a data input. + Transmit sequence + The transmit sequence begins when a byte is written in the Tx Buffer. + The data byte is parallel-loaded into the shift register (from the internal bus) during the first + bit transmission and then shifted out serially to the MOSI pin MSB first or LSB first + depending on the LSBFIRST bit in the SPI_CR1 register. The TXE flag is set on the transfer + of data from the Tx Buffer to the shift register and an interrupt is generated if the TXEIE bit in + the SPI_CR2 register is set. + Receive sequence + For the receiver, when data transfer is complete: + • The data in the shift register is transferred to the RX Buffer and the RXNE flag is set + • An interrupt is generated if the RXNEIE bit is set in the SPI_CR2 register + At the last sampling clock edge the RXNE bit is set, a copy of the data byte received in the + shift register is moved to the Rx buffer. When the SPI_DR register is read, the SPI + peripheral returns this buffered value. + Clearing the RXNE bit is performed by reading the SPI_DR register. + A continuous transmit stream can be maintained if the next data to be transmitted is put in + the Tx buffer once the transmission is started. Note that TXE flag should be ‘1 before any + attempt to write the Tx buffer is made. + Note: When a master is communicating with SPI slaves which need to be de-selected between + transmissions, the NSS pin must be configured as GPIO or another GPIO must be used and + toggled by software. +*/ + +void set_spi_mode(uint8_t mode) { + uint32_t temp_reg; + temp_reg = SPI2->CR1; + switch (mode) { + case 0: + temp_reg &= ~(SPI_CR1_CPHA | SPI_CR1_CPOL); + break; + case 1: + temp_reg &= ~(SPI_CR1_CPHA); + temp_reg |= SPI_CR1_CPOL; + break; + case 2: + temp_reg |= SPI_CR1_CPHA; + temp_reg &= ~(SPI_CR1_CPOL); + break; + case 3: + temp_reg |= SPI_CR1_CPHA | SPI_CR1_CPOL; + break; + } + SPI2->CR1 = temp_reg; +} + +void spi_init() { + /* + Set SCK (clock) and MOSI line to output. + Setting this hard to SPI2. SPI1 and SPI4 are used for JTAG and other things. + SPI2 is APB1-clock. + + SPI1, SPI4, SPI5 -> APB2 -> 100MHz + SPI2, SPI3 -> APB1 -> 50MHz + */ + + // Enable SPI2 clock + RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; + + #define SCK_SPI2 PB_13 + #define MOSI_SPI2 PB_15 + #define MISO_SPI2 PB_14 + + // Alternate function mapping for SPI2 + // DM00115249.pdf datasheet (page 47; table 9) + SET_AFR(SCK_SPI2, 5); + SET_AFR(MOSI_SPI2, 5); + SET_AFR(MISO_SPI2, 5); + + // Alternate function mode + SET_MODE(SCK_SPI2, 2); + SET_MODE(MOSI_SPI2, 2); + SET_MODE(MISO_SPI2, 2); + + // High speed == 3 + SET_OSPEED(SCK_SPI2, 3); + SET_OSPEED(MOSI_SPI2, 3); + SET_OSPEED(MISO_SPI2, 3); + + #ifdef TMC2130 + set_spi_mode(3); + #else + set_spi_mode(0); + #endif + + spi_speed_100_400(); + + // SSM: software slave management (no NSS) + // SSI: internal slave select + // MSTR: master mdoe + // SPE: enable SPI + SPI2->CR1 |= SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR; + SPI2->CR1 |= SPI_CR1_SPE; +} + +void spi_speed_100_400() { + SPI2->CR1 |= SPI_CR1_BR; // 1/256 +} + +void spi_speed_max() { + SPI2->CR1 &= ~(SPI_CR1_BR); // reset BR + SPI2->CR1 |= SPI_CR1_BR_1 | SPI_CR1_BR_0; // 1/16 -> SPI2 = 3.125MHz +} + +#endif /* TEACUP_C_INCLUDE && defined __ARM_STM32F411__ */ diff --git a/spi.c b/spi.c index b634b18..93ccaab 100644 --- a/spi.c +++ b/spi.c @@ -19,7 +19,7 @@ #include "spi-avr.c" // Each ARM needs it's own file // #include "spi-lpc.c" -// #include "spi-stm32.c" +#include "spi-stm32.c" #undef TEACUP_C_INCLUDE #endif /* SPI */ diff --git a/spi.h b/spi.h index 73d3c77..0fc013c 100644 --- a/spi.h +++ b/spi.h @@ -10,8 +10,8 @@ /** Test configuration. */ -#ifdef __ARMEL__ - #error SPI (SD_CARD_SELECT_PIN, TEMP_MAX6675, TEMP_MCP3008) not yet supported on ARM. +#ifdef __ARM_LPC1114__ + #error SPI (SD_CARD_SELECT_PIN, TEMP_MAX6675, TEMP_MCP3008) not yet supported on LPC1114. #endif // Uncomment this to double SPI frequency from (F_CPU / 4) to (F_CPU / 2). @@ -67,6 +67,7 @@ inline void spi_deselect_mcp3008(void) { } #endif /* TEMP_MCP3008 */ +#ifdef __AVR__ /** Set SPI clock speed to something between 100 and 400 kHz. This is needed for initialising SD cards. We set the whole SPCR register @@ -120,6 +121,22 @@ inline uint8_t spi_rw(uint8_t byte) { return SPDR; } +#elif defined __ARM_STM32F411__ + +void spi_speed_100_400(void); +void spi_speed_max(void); + +static uint8_t spi_rw(uint8_t) __attribute__ ((always_inline)); +inline uint8_t spi_rw(uint8_t byte) { + SPI2->DR = byte; + while(!(SPI2->SR & SPI_SR_TXE)); + while(!(SPI2->SR & SPI_SR_RXNE)); + while(SPI2->SR & SPI_SR_BSY); + return SPI2->DR; +} + +#endif /* AVR / STM32F411 */ + #endif /* SPI */ #endif /* _SPI_H */