Not sure if this is the right place, because responsibilities seem somewhat mixed.
Anyway, I a m having trouble creating proper output of the SPI peripheral on a stm32C031C6 - the one on the ST Nucleo C031 (MB1717B).
I am configuring the SPI for 8-bit transfers, and debugging shows the corresponding config register (CR2) is set correctly.
However, watching the SPI signals, I can see two two 8-bit transfers generated by each write to SPI->DR.
The first value is a correct byte I intend to transfer, the second one is always zero.
After some searching and experiments, I realized that the actual write instruction to the register was a 32-bit access : str r0, [<SPI->DR>].
And checking the MCU-specific include (stm32c031xx.h), the DR register is defined as 32-bit word:
typedef struct
{
__IO uint32_t CR1; /*!< SPI Control register 1 (not used in I2S mode), Address offset: 0x00 */
...
__IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */
...
} SPI_TypeDef;
This is not correct, I think.
According to the ST MCU reference manual (RM0490 Rev3, ch. 25.9.4, page 798), the DR register is only 16-bit wide.
After changing the DR definition to "uint16_t" (and add another 16-bit padding value afterwards), a 16-bit access is generated by the compiler (strh r0, [>SPI->DR>]).
The access seems to be fine now, though I still want to check it with a scope.
While it is part of the Segger STM32C0 support package, this file seems to come from ST, according the the file header :
* @author MCD Application Team
* Copyright (c) 2022 STMicroelectronics.
I am not sure what is happening in this case either.
Perhaps the hardware serializes the peripheral (32-bit) word write into two consecutive 16-bit accesses ?
For reference, the my functions to setup the SPI peripheral, and the send function.
/* this function sets up SPI 1 (SPI & GPIO);
* - PB.3 = SPI SCLK --> O
* - PB.4 = SPI MISO <-- i
* - PB.5 = SPI MOSI --> O
* - PB.6 = GDO0 <-- i
* - PB.7 = SPI CS --> O
* PB6 & PB7 (GDO0 + CS) remain GPIO, i.e. manual SW control
*/
void cc1101_setupPins (void)
{
uint32_t temp, mask;
// enable power to GPIOB and the SPI peripheral
RCC->IOPENR |= (1 << RCC_IOPENR_GPIOBEN_Pos);
RCC->APBENR2 |= (1 << RCC_APBENR2_SPI1EN_Pos);
// set MODE register bits;
// the SPI pins are set to 0b10 (alt. function), CS and GDO0 remain GPIOs
// PB6 (GDO0) is set to DIN
temp = GPIOB->MODER;
mask = ~(GPIO_MODER_MODE3_Msk + GPIO_MODER_MODE4_Msk + GPIO_MODER_MODE5_Msk + GPIO_MODER_MODE6_Msk + GPIO_MODER_MODE7_Msk);
temp &= mask;
temp |= (GPIO_MODER_MODE3_1 + GPIO_MODER_MODE4_1 + GPIO_MODER_MODE5_1 + GPIO_MODER_MODE7_0); // set 3, 4 and 5 to AF, 7 t output
GPIOB->MODER = temp;
// AF mode for SPI pins; all pins (PB3, PB4, PB5) are AF0,
// i.e. the register default value; this means no need to change anything ...
// setup the SPI unit; CPOL & CPHA = 0, manual SS, MSB first
// BR is set to PCLK / 32 (with PCLK = CoreClk / 1 as default value !) = 0b100
// this equals 48MHz / 32 = 1.5 MHz (max would be 10MHz at most, 6.5MHz for
mask = (SPI_CR1_BIDIOE_Msk + SPI_CR1_MSTR_Msk + SPI_CR1_SSM_Msk + SPI_CR1_SSI_Msk);
mask |= (SPI_CR1_BR_2);
SPI1->CR1 = mask;
// set 8-bit transfers; 8 bits = 0b0111
mask = (SPI_CR2_DS_0 + SPI_CR2_DS_1 + SPI_CR2_DS_2);
SPI1->CR2 = mask;
// enable the unit
SPI1->CR1 |= SPI_CR1_SPE_Msk;
}
/* send uint8_t via HW - SPI;
* does only the SPI byte transfer, slave select handling
* must be done in the context of the calling routine !
* 'value' value to be sent
* Return:
* response received from SPI slave
*/
uint8_t spi_send (uint8_t tx)
{
uint8_t rx = 0;
SPI1->DR = (uint8_t) tx; // write Tx byte
while (!(SPI1->SR & SPI_SR_RXNE)); // wait till finished (sync'ed. Rx in)
rx = (uint8_t) SPI1->DR; // read MISO value
return (rx);
}
Anyway, I a m having trouble creating proper output of the SPI peripheral on a stm32C031C6 - the one on the ST Nucleo C031 (MB1717B).
I am configuring the SPI for 8-bit transfers, and debugging shows the corresponding config register (CR2) is set correctly.
However, watching the SPI signals, I can see two two 8-bit transfers generated by each write to SPI->DR.
The first value is a correct byte I intend to transfer, the second one is always zero.
After some searching and experiments, I realized that the actual write instruction to the register was a 32-bit access : str r0, [<SPI->DR>].
And checking the MCU-specific include (stm32c031xx.h), the DR register is defined as 32-bit word:
typedef struct
{
__IO uint32_t CR1; /*!< SPI Control register 1 (not used in I2S mode), Address offset: 0x00 */
...
__IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */
...
} SPI_TypeDef;
This is not correct, I think.
According to the ST MCU reference manual (RM0490 Rev3, ch. 25.9.4, page 798), the DR register is only 16-bit wide.
After changing the DR definition to "uint16_t" (and add another 16-bit padding value afterwards), a 16-bit access is generated by the compiler (strh r0, [>SPI->DR>]).
The access seems to be fine now, though I still want to check it with a scope.
While it is part of the Segger STM32C0 support package, this file seems to come from ST, according the the file header :
* @author MCD Application Team
* Copyright (c) 2022 STMicroelectronics.
I am not sure what is happening in this case either.
Perhaps the hardware serializes the peripheral (32-bit) word write into two consecutive 16-bit accesses ?
For reference, the my functions to setup the SPI peripheral, and the send function.
/* this function sets up SPI 1 (SPI & GPIO);
* - PB.3 = SPI SCLK --> O
* - PB.4 = SPI MISO <-- i
* - PB.5 = SPI MOSI --> O
* - PB.6 = GDO0 <-- i
* - PB.7 = SPI CS --> O
* PB6 & PB7 (GDO0 + CS) remain GPIO, i.e. manual SW control
*/
void cc1101_setupPins (void)
{
uint32_t temp, mask;
// enable power to GPIOB and the SPI peripheral
RCC->IOPENR |= (1 << RCC_IOPENR_GPIOBEN_Pos);
RCC->APBENR2 |= (1 << RCC_APBENR2_SPI1EN_Pos);
// set MODE register bits;
// the SPI pins are set to 0b10 (alt. function), CS and GDO0 remain GPIOs
// PB6 (GDO0) is set to DIN
temp = GPIOB->MODER;
mask = ~(GPIO_MODER_MODE3_Msk + GPIO_MODER_MODE4_Msk + GPIO_MODER_MODE5_Msk + GPIO_MODER_MODE6_Msk + GPIO_MODER_MODE7_Msk);
temp &= mask;
temp |= (GPIO_MODER_MODE3_1 + GPIO_MODER_MODE4_1 + GPIO_MODER_MODE5_1 + GPIO_MODER_MODE7_0); // set 3, 4 and 5 to AF, 7 t output
GPIOB->MODER = temp;
// AF mode for SPI pins; all pins (PB3, PB4, PB5) are AF0,
// i.e. the register default value; this means no need to change anything ...
// setup the SPI unit; CPOL & CPHA = 0, manual SS, MSB first
// BR is set to PCLK / 32 (with PCLK = CoreClk / 1 as default value !) = 0b100
// this equals 48MHz / 32 = 1.5 MHz (max would be 10MHz at most, 6.5MHz for
mask = (SPI_CR1_BIDIOE_Msk + SPI_CR1_MSTR_Msk + SPI_CR1_SSM_Msk + SPI_CR1_SSI_Msk);
mask |= (SPI_CR1_BR_2);
SPI1->CR1 = mask;
// set 8-bit transfers; 8 bits = 0b0111
mask = (SPI_CR2_DS_0 + SPI_CR2_DS_1 + SPI_CR2_DS_2);
SPI1->CR2 = mask;
// enable the unit
SPI1->CR1 |= SPI_CR1_SPE_Msk;
}
/* send uint8_t via HW - SPI;
* does only the SPI byte transfer, slave select handling
* must be done in the context of the calling routine !
* 'value' value to be sent
* Return:
* response received from SPI slave
*/
uint8_t spi_send (uint8_t tx)
{
uint8_t rx = 0;
SPI1->DR = (uint8_t) tx; // write Tx byte
while (!(SPI1->SR & SPI_SR_RXNE)); // wait till finished (sync'ed. Rx in)
rx = (uint8_t) SPI1->DR; // read MISO value
return (rx);
}