2021-11-29 09:48 AM
Hi everyone,
I have a SPI eeprom on my hands, which has a weird communication protocol. Data is 8 bits wide, except for the 1st frame, which contains 1 start bit, 2 op-code bits, and 9 address bits - so 12 in total. It does not accept this first frame as 2x 8bit transfers - start bit and op codes need to be the MSB part of a 12 clock pulses frame.
While looking at various F0xx and F4xx data sheets, I noticed that there is NO explicit note about changing the DS bits in the SPI CR2 register only when SPI is disabled. So I decided to see if changing this parameter after the first frame would work... Needless to say, it does not do what I expected.
I tested on a STM32F070F6 Cortex-M0 MCU.
Here is my SPI initialization:
static void MX_SPI1_Init(void)
{
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
This was produced by CubeMX. The next snippet is my code to constantly send some test data, so I can capture it with a logic analyzer:
uint8_t data[8] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
uint16_t spiReg = hspi1.Instance->CR2;
uint8_t cmd[2] = { 0x0c, 0x00 }; // start bit and op-code, address shall remain 0
while (1)
{
HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, 0); // chip select low
hspi1.Init.DataSize = SPI_DATASIZE_12BIT;
spiReg = (spiReg & ~SPI_DATASIZE_16BIT) | SPI_DATASIZE_12BIT;
hspi1.Instance->CR2 = spiReg;
HAL_SPI_Transmit(&hspi1, cmd, 2, 5000);
HAL_Delay(2);
spiReg = (spiReg & ~SPI_DATASIZE_16BIT) | SPI_DATASIZE_8BIT;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Instance->CR2 = spiReg;
HAL_SPI_Transmit(&hspi1, data, sizeof(data), 5000);
HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, 1); // chip select high
HAL_Delay(2000);
}
As you can see, the peripheral is initialized with 8bit data frame. When I attempt the switch to 12 bit frames "on the fly", the switching works, but the peripheral is sending the command bytes as 2 separate 12bit frames - my expectation was to see 1 frame with last 4 bits of the first byte and the 8 bits of the second byte.
And then when I attempt to switch back to 8 bit frames, it does not work. It continues using 12bit frames.
Is my code wrong somehow? Or is what I need not possible with STM32 HAL? Would it be possible if I use the registers directly?
Any help would be valued and appreciated! Thx!
2021-11-29 08:25 PM
I used dynamic bit length, you can change it when the spi is idle, no need to be overconservative for prototyping.
2021-11-30 02:51 PM
> uint16_t spiReg = hspi1.Instance->CR2;
> spiReg = (spiReg & ~SPI_DATASIZE_16BIT) | SPI_DATASIZE_12BIT;
This isn't doing anything except changing a variable in memory. It's not modifying an SPI register or changing the data width.
Perhaps you want this:
volatile uint16_t * spiReg = hspi1.Instance->CR2;
*spiReg = (spiReg & ~SPI_DATASIZE_16BIT) | SPI_DATASIZE_12BIT;
Or perhaps a little more readable and less prone to typos:
MODIFY_REG(hspi1.Instance->CR2, SPI_CR2_DS, SPI_DATASIZE_12BIT);
Also:
> uint8_t cmd[2] = { 0x0c, 0x00 }; // start bit and op-code, address shall remain 0
> HAL_SPI_Transmit(&hspi1, cmd, 2, 5000);
This transmits data past the end of the buffer. cmd should be uint16_t if it contains 12-bit values..
2021-11-30 08:26 PM
HAL does not provide access to all HW capabilities.... you need to dig down to hw register manipulation.
2021-11-30 08:55 PM