2017-02-23 10:53 AM
Hello,
I have need to bring up SPI1 (PA4,PC5,PC6,PC7) on an STM32F103 in master mode using the least code possible. I have a function that (is supposed to) initialize the SPI. I am using the bit-band region to manipulate bits atomically. It is divided into three sections:
RCC (Enable SPI1 and reset device)GPIO (Set MOSI/SCK to Output_ALT_PP and MISO to INPUT)SPI (Master, 8-bit, MSB-first, divide-by-256, etc)void InitializeSPI () {
*((volatile long *)(0x42000000 + (0x40021018 - 0x40000000)*32 + 12*4)) = 1; // RCC_APB2ENR.SPI1EN p.112 *((volatile long *)(0x42000000 + (0x4002100c - 0x40000000)*32 + 12*4)) = 1; // RCC_APB2RSTR.SPI1RST p.106 *((volatile long *)(0x42000000 + (0x4002100c - 0x40000000)*32 + 12*4)) = 0; // RCC_APB2RSTR.SPI1RST p.106 SetMode (PA5, OUTPUT_ALT_PUSHPULL | MODE_OUTPUT_50MHZ); // p.166 SetMode (PA6, INPUT_FLOAT | MODE_INPUT); // p.166 SetMode (PA7, OUTPUT_ALT_PUSHPULL | MODE_OUTPUT_50MHZ); // p.166 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 15*4)) = 0; // SPI1_CR1.BIDIMODE p.745 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 14*4)) = 0; // SPI1_CR1.BIDIOE p.745 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 13*4)) = 0; // SPI1_CR1.CRCEN p.745 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 12*4)) = 0; // SPI1_CR1.CRCNEXT p.745 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 11*4)) = 0; // SPI1_CR1.DFF p.745 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 10*4)) = 0; // SPI1_CR1.RXONLY p.746 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 9*4)) = 0; // SPI1_CR1.SSM p.746 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 8*4)) = 0; // SPI1_CR1.SSI p.746 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 7*4)) = 0; // SPI1_CR1.LSBFIRST p.746 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 5*4)) = 1; // SPI1_CR1.BR[2] \ *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 4*4)) = 1; // SPI1_CR1.BR[1] |--- 111 means FPCLK/256 p.746 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 3*4)) = 1; // SPI1_CR1.BR[0] / *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 2*4)) = 1; // SPI1_CR1.MSTR p.747 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 1*4)) = 0; // SPI1_CR1.CPOL p.747 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 0*4)) = 0; // SPI1_CR1.CPHA p.747 *((volatile long *)(0x42000000 + (0x40013000 - 0x40000000)*32 + 6*4)) = 1; // SPI1_CR1.SPE p.746}The problem is, using a SALEAE logic analyzer, SCLK does not oscillate. I assume the labels out to the right are self-explanatory as far as what I am trying to do with each register access. What am I missing? Thanks in advance!
2017-02-23 11:17 AM
I am using the bit-band region to manipulate bits atomically.
Why? Do you expect these registers to be accessed concurrently, i.e. from an interrupt? That's the only reason for this form of 'atomicity'.
If you are convinced you absolutely must do this, please use the names and symbols from the devices header rather than magic numbers - these are too easy to get wrong. Write a macro to simplify the repeated formula.
using the least code possible. I have a function that (is supposed to) initialize the SPI.
Writing a register at once is surely less code, both memory- and execution-time-wise (and provides more 'atomicity', should that be needed at all, btw.).
JW
PS. Use a debugger or any other debugging technique to read back the registers and see what did you actually do.
PS2. SPI master needs to have either a NSS pin being enabled and pulled up, or switched to software NSS and that set to 1, otherwise it turns to slave
2017-02-23 11:31 AM
Using absolute 32 bit addresses are probably generating poor op-codes, and Bit banding seems not so fashionable today (and on M7 cores too). It would be interesting to see how the compiler reformats the asm code for an equivalent result. Without volatile, the compiler might just generate a surprise asm code.
2017-02-23 03:28 PM
Actually, I figured it out. For my needs, I have PA4 as chip-select and have it under software control. I was missing SSM_ENABLE (bit 9 of SPI1_CR1) and INT_SLAVE_SELECT (bit 8 of SPI1_CR1)
My bare-bones initialization of SPI is as follows:
void InitializeSPI () {
RCC_APB2ENR |= SPI1EN; SetMode (PA5, OUTPUT_ALT_PUSHPULL | MODE_OUTPUT_50MHZ); // Table 25 p.166 SetMode (PA6, INPUT_PULLUP | MODE_INPUT); // Table 25 p.166 SetMode (PA7, OUTPUT_ALT_PUSHPULL | MODE_OUTPUT_50MHZ); // Table 25 p.166 SPI1_CR1 = BIDIMODE_2LINE | BIDIOE_OUTPUT_DISABLED | CRCEN_DISABLED | NO_CRCNEXT_CRC| DFF_8BIT | NO_RXONLY_OUTPUT_DISABLED | SSM_ENABLE | INT_SLAVE_SEL | MSB_FIRST |
SPI_ENABLE | BR_FCLK16 | MSTR | CPOL_CK_0_IDLE | CPHA_1ST_CLK_EDGE;}Enable the SPI1 peripheral in RCC_APB2ENR (bit 12)
Make PA5 and PA7 output. alternate function, 50MHz
Make PA6 be input, pullup.
Lastly, set SPI1_CR1 to the logically-OR'ed litany of #defines above, which hopefully are self-explanatory. I started from bit 15 (BIDIMODE) and worked down to bit 0 (CPHA)
Hope this helps someone else!
2017-02-23 03:45 PM
Bit-banding each bit seems an overly brutal way to do it, and doesn't provide the atomic action you think it does. The CPU and Peripheral registers operate independently, the use on TIM status registers being a particular hazard.
2017-02-24 12:42 AM
,
,
Lastly, set SPI1_CR1 to the logically-OR'ed litany of ♯ defines above, which hopefully are self-explanatory.
I use explicitly explained litany, and using the ST-provided CMSIS-mandated device headers (augmented with multi-bit bitfield constants as ST still did not add them to the headers), to achieve some degree of portability across various STM32 families:
, , , SPI2->,CR1 = 0
,
, , , , , OR ( 1 , , , , * SPI_CR1_CPHA , , , ) , , , , /* Clock Phase -- shift, then sample */,
, , , , , OR ( 0 , , , , * SPI_CR1_CPOL , , , ) , , , , /* Clock Polarity , -- CKL idle is 0 */,
, , , , , OR ( 1 , , , , * SPI_CR1_MSTR , , , ) , , , , /* Master Selection */,
, , , , , OR ( SPI_CR1_BR__8 * SPI_CR1_BR_0 ) , , , , /* Baudrate -- HCLK=160MHz ->, P2CLK=80MHz ->, baud=10MHz */,
, , , , , OR ( 1 , , , , * SPI_CR1_SPE , , , , ) , , , , /* SPI Enable */,
, , , , , OR ( 0 , , , , * SPI_CR1_LSBFIRST) , , , , /* Frame Format -- 0 ->, MSB first ('normal' SPI) */,
, , , , , OR ( 0 , , , , * SPI_CR1_SSI , , , , ) , , , , /* Internal slave select */,
, , , , , OR ( 0 , , , , * SPI_CR1_SSM , , , , ) , , , , /* Software slave management */,
, , , , , OR ( 0 , , , , * SPI_CR1_RXONLY , ) , , , , /* Receive only */,
, , , , , OR ( SPI_CR1_DFF__16 * SPI_CR1_DFF ) , , , , /* Data Frame Format - 16-bit */,
, , , , , OR ( 0 , , , , * SPI_CR1_CRCNEXT ) , , , , /* Transmit CRC next */,
, , , , , OR ( 0 , , , , * SPI_CR1_CRCEN , , ) , , , , /* Hardware CRC calculation enable */,
, , , , , OR ( 0 , , , , * SPI_CR1_BIDIOE , ) , , , , /* Output enable in bidirectional mode */,
, , , , , OR ( 0 , , , , * SPI_CR1_BIDIMODE) , , , , /* Bidirectional data mode enable */,
, , , , , OR ( 1 , , , , * SPI_CR1_SPE , , , , ) , , , , /* SPI Enable */
, , , ,Yes, I do Pascalize my C... ,-)
JW