STM32F411: implement SPI

add some helpers in pinio.h also
This commit is contained in:
Nico Tonnhofer 2016-09-17 22:32:35 +02:00
parent 7223c9dea8
commit dc9e016e2c
4 changed files with 201 additions and 27 deletions

72
pinio.h
View File

@ -130,32 +130,12 @@
IO ## _PORT->BSRR = MASK((IO ## _PIN + 16)); \ IO ## _PORT->BSRR = MASK((IO ## _PIN + 16)); \
} while (0) } while (0)
/** #define _SET_MODE(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 { \ do { \
IO ## _PORT->PUPDR &= ~(3 << ((IO ## _PIN) << 1)); \ IO ## _PORT->MODER &= ~(0x3 << ((IO ## _PIN) * 2)); \
IO ## _PORT->MODER &= ~(3 << ((IO ## _PIN) << 1)); \ IO ## _PORT->MODER |= (v << ((IO ## _PIN) * 2)); \
} 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)); \
} while (0) } while (0)
#define SET_MODE(IO, v) _SET_MODE(IO, v)
/// Enable pullup resistor. /// Enable pullup resistor.
#define _PULLUP_ON(IO) \ #define _PULLUP_ON(IO) \
@ -169,6 +149,50 @@
IO ## _PORT->PUPDR &= ~(3 << ((IO ## _PIN) << 1)); \ IO ## _PORT->PUPDR &= ~(3 << ((IO ## _PIN) << 1)); \
} while (0) } 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 #elif defined SIMULATOR
#include "simulator.h" #include "simulator.h"

133
spi-stm32.c Normal file
View File

@ -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__ */

2
spi.c
View File

@ -19,7 +19,7 @@
#include "spi-avr.c" #include "spi-avr.c"
// Each ARM needs it's own file // Each ARM needs it's own file
// #include "spi-lpc.c" // #include "spi-lpc.c"
// #include "spi-stm32.c" #include "spi-stm32.c"
#undef TEACUP_C_INCLUDE #undef TEACUP_C_INCLUDE
#endif /* SPI */ #endif /* SPI */

21
spi.h
View File

@ -10,8 +10,8 @@
/** /**
Test configuration. Test configuration.
*/ */
#ifdef __ARMEL__ #ifdef __ARM_LPC1114__
#error SPI (SD_CARD_SELECT_PIN, TEMP_MAX6675, TEMP_MCP3008) not yet supported on ARM. #error SPI (SD_CARD_SELECT_PIN, TEMP_MAX6675, TEMP_MCP3008) not yet supported on LPC1114.
#endif #endif
// Uncomment this to double SPI frequency from (F_CPU / 4) to (F_CPU / 2). // 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 */ #endif /* TEMP_MCP3008 */
#ifdef __AVR__
/** Set SPI clock speed to something between 100 and 400 kHz. /** Set SPI clock speed to something between 100 and 400 kHz.
This is needed for initialising SD cards. We set the whole SPCR register 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; 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 */
#endif /* _SPI_H */ #endif /* _SPI_H */