STM32F411: implement SPI
add some helpers in pinio.h also
This commit is contained in:
parent
7223c9dea8
commit
dc9e016e2c
72
pinio.h
72
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"
|
||||
|
|
|
|||
|
|
@ -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
2
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 */
|
||||
|
|
|
|||
21
spi.h
21
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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue