The clock of stm32f103 SPI is different from theoretic calculation after initialization - stm32

I am a new to stm32f103c8t6. Now I am trying to learn the SPI function and finished the code from the RCC to SPI initialization. I used oscilloscope to measure the SPI1 SCK signal due to test equipment limitation. Indeed that is clock signal but much lower than my RCC configuration. I tried to search the reason, but still failed. So may I ask if someone knows the answer.
RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
Errsts = RCC_WaitForHSEStartUp();
if (Errsts == SUCCESS)
{
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
RCC_PLLConfig(RCC_PLLSource_HSE_Div2, RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY == RESET))
{
}
FLASH_SetLatency(FLASH_Latency_1);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
while (RCC_GetSYSCLKSource() != 0x08)
{
}
}
The code above shows my configuration about RCC, I used HSE as clock and the final frequency is 8Mhz/2*9=36Mhz. Below is my SPI and GPIO initialization:
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6| GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_WriteBit(GPIOA, GPIO_Pin_4,Bit_SET);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
/* Enable SPI2 */
SPI_Cmd(SPI1, ENABLE);
So SPI_BaudRatePrescaler=4 means my SPI clock is 36Mhz/4=9Mhz, but actually the oscilloscope shows the frequecy is around 200Khz, it is much lower the the expectation. Also I tried to config SPI_BaudRatePrescaler with different value, it seems that the outcome is not linear.
Therefore I would like to ask if someone knows the answer. Thanks a lot!

Double check your prescaler value in your configuration. According to the reference manual, a value of 4 in the baud rate field will result in a divider of 32. To get to a clock of around 200 kHz, you need a value in that register of 6 or 7, which results in a divider of 128 or 256 (281 kHz or 140 kHz). If you want a divider of 4, you'll need to put a value of 1 in there. Check and see if SPI_BaudRatePrescaler_4 results in a value of 4 in the register, or a value of 1.
The outcome is not linear because they are using it as an exponent of 2. A value of 0 is 2^(0+1), while 1 is 2^(1+1), etc, etc.

Related

STM32F0x8 SPI with 25LC256 recieve problem [Peripheral Lib]

I use an STM32F0 discovery board.
I am trying to program an EEPROM to save an QR-code and display it on an LCD.
Right now I am working on getting the STM32 to receive the data from the EEPROM, my code works if I short MISO and MOSI (I changed it a bit but not much), but when I try to get it to work with the EEPROM, it doesnt work.
My code below. Header file:
#include "stm32f0xx_conf.h"
#include <string.h>
//Pin definitions
//#define NSSA GPIO_PinSource12
#define SCKA GPIO_PinSource13
#define MISOA GPIO_PinSource14
#define MOSIA GPIO_PinSource15
#define TXPINA GPIO_PinSource9
//#define NSS GPIO_Pin_12
#define CS GPIO_Pin_12
#define SCK GPIO_Pin_13
#define MISO GPIO_Pin_14
#define MOSI GPIO_Pin_15
#define TXPIN GPIO_Pin_9
#define HOLD GPIO_Pin_11
#define GreenLED_Pin GPIO_Pin_9
#define BlueLED_Pin GPIO_Pin_8
//EEPROM INSTRUCTIONS:
uint8_t READMEM = 0x003; //READ - 0000 0011 Read data from memory array beginning at selected address
uint8_t WRITEMEM = 0x002; //WRITE - 0000 0010 Write data to memory array beginning at selected address
uint8_t READSR = 0x005; //RDSR - 0000 0101 Read STATUS register
uint8_t WRITESR = 0x001; //WRSR - 0000 0001 Write STATUS register
uint8_t WRITEDI = 0x004; //WRDI - 0000 0100 Reset the write enable latch (disable write operations)
uint8_t WRITEEN = 0x006; //WREN - 0000 0110 Set the write enable latch (enable write operations)
uint8_t NULLBYTE = 0x000;
SPI initialization:
void SPI_EEPROM()
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_PinAFConfig(GPIOB,SCKA,GPIO_AF_0);
GPIO_PinAFConfig(GPIOB,MISOA,GPIO_AF_0);
GPIO_PinAFConfig(GPIOB,MOSIA,GPIO_AF_0);
GPIO_InitTypeDef SPIGPIO;
SPIGPIO.GPIO_Mode = GPIO_Mode_AF;
SPIGPIO.GPIO_OType = GPIO_OType_PP;
SPIGPIO.GPIO_PuPd = GPIO_PuPd_NOPULL;
SPIGPIO.GPIO_Speed = GPIO_Speed_50MHz;
SPIGPIO.GPIO_Pin = SCK;
GPIO_Init(GPIOB, &SPIGPIO);
SPIGPIO.GPIO_Pin = MISO;
GPIO_Init(GPIOB, &SPIGPIO);
SPIGPIO.GPIO_Pin = MOSI;
GPIO_Init(GPIOB, &SPIGPIO);
SPI_I2S_DeInit(SPI2);
SPI_InitTypeDef SPIEPR;
SPIEPR.SPI_Mode = SPI_Mode_Master;
SPIEPR.SPI_CPHA = SPI_CPHA_1Edge;
SPIEPR.SPI_CPOL = SPI_CPOL_Low;
SPIEPR.SPI_FirstBit = SPI_FirstBit_MSB;
SPIEPR.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; //-> _16~3.2MHZ CLOCK | _32 ~1.6MHZ CLOCK
SPIEPR.SPI_NSS = SPI_NSS_Soft;
SPIEPR.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPIEPR.SPI_DataSize = SPI_DataSize_8b;
SPI_Init(SPI2, &SPIEPR);
SPI_NSSInternalSoftwareConfig(SPI2,SPI_NSSInternalSoft_Set);
//SPI2 -> CR2 |= SPI_CR2_FRXTH;
SPI_Cmd(SPI2,ENABLE);
GPIO initialization:
void GPIO()
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = BlueLED_Pin|GreenLED_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitTypeDef GPIO2_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO2_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO2_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO2_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO2_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO2_InitStructure.GPIO_Pin = HOLD;
GPIO_Init(GPIOB, &GPIO2_InitStructure);
GPIO2_InitStructure.GPIO_Pin = CS;
GPIO_Init(GPIOB, &GPIO2_InitStructure);
The simple send and recieve function:
uint8_t SPISendByte(uint8_t data)
uint8_t databck;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) != RESET);
SPI_SendData8(SPI2,data);
while(SPI_GetReceptionFIFOStatus(SPI2) != 0x0000)
SPI_ReceiveData8(SPI2); //read RXFIFO until empty
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET); //wait until TXFIFO is empty
SPI_SendData8(SPI2,NULLBYTE); //send 0-byte to recive answer from EEPROM
while(SPI_GetReceptionFIFOStatus(SPI2) != 0x0200); //wait until the RXFIFO is a quarter full (8bit)
return SPI_ReceiveData8(SPI2);
Main function:
SystemInit();
SPI_EEPROM();
GPIO();
GPIO_ResetBits(GPIOB, CS); //CS low to enable the EEPROM
SPI_SendData8(SPI2,WRITEEN); //send the WREN instruction to set the write enable latch
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) != RESET);
GPIO_SetBits(GPIOB, CS); //Toggle on and off to set bit in the Status register
GPIO_ResetBits(GPIOB, CS);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) != RESET);
USART_SendData(USART1, SPISendByte(READSR)); //Read Status Reg and print it to USART (should be ..0010)
I set up the EEPROM like its datasheet tells me to. Clock setup from the sheet(should be CPOL and CPHA 0):
Instructions, addresses or data present on the SI pin are latched on the rising edge of the clock input, while data on the SO pin is updated after the falling edge of the clock input.
This is not my entire code but what I use for testing.
It should work; I double checked it with my scope (it returns ..0010 on MISO with the logic mode) and looked at the RXFIFO with the debugger if its empty and receives actual data:
It does, but the code always returns 0.
I also tried a lot of different Flags instead of reading the RXFIFO but it seemed to be the best solution.
So, after bashing my head against my table and my coworker stealing my oscilloscope, it worked, so i found out that the oscilloscope channels pulled my signal down bc i had the inputs in 50 Ohm mode and not 1MOhm. Switching that fixed it.

Chip Enable not setting at right moment using SPI

Am trying to set a Chip Enable (CE) of a pin to go high before a SPI transfer and go back low upon accomplishing of the transfer (either TX or RX). Am using the NRF24L01 and Nucleo-F303. It is said that when the CE is high, thats when i can do a real SPI transfer to the NRF24L01. However, the CE pin goes momentarily low and then high again even before the real transfer hasnt been accomplished
The rest of the pins seem to be synching accordingly. How do i get the CE pin to exactly go high prior to making a SPI transfer and then Low when am done or not doing any more transfers.
Here is how i've attempted to solve the problem
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
HAL_Delay(5);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
uint8_t data[6] = {0x5C, 0xBA, 0xBB, 0x4D, 0x5E, 0xFB,};
uint8_t data1[6] = {0};
while (1)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
if(HAL_SPI_TransmitReceive(&hspi1, data, data1, 6, HAL_MAX_DELAY) == HAL_OK)
{
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
}
}
}
and this is how i have configured GPIO_PIN_3 to function
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // digital Output
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
Usually the Slave Select signal of SPI is active low but you can change this behaviour through bit SSIOP (it is the name of the bit on STM32H7 , might be different on other STM32).
About your other question, the Slave Select signal going inactive between two bytes, you can change that behaviour thanks to bit SSOM.
The different SPI configurations are usually pretty well described in the Reference Manual.
I ended up going bare-metal because the libraries were giving a lot of delay and had so many abstract layers

Is the sampling data missing or incorrect in DMA memory buffer, when using ADC with DMA circle mode?

My purpose is sampling signal by ADC channel with DMA data moving in STM32Fx board. Generate a square wave to ADC channel. If using DMA mode, some data is out of order or called mess. Same result happened on STM32F207 and STM32F373 board.
(1) When I collect converted data by using ADC EOC interrupt, the data array looks like a square wave. This is OK.
(2) I would like to try DMA circle instead of EOC IRQ, but the data array seems to mess up, some data missing or incorrect. It could worse if sampling rate was increasing. Below are my test results.
The picture shows my test result: EOC IRQ vs DMA circle mode
EOC IRQ vs DMA with sampling 62.5KHz The waveform in DMA became shorter.
The picture shows very worse DMA with sampling 200KHz The data on DMA mode mess up, but it's consistent by using ADC EOC IRQ.
enter code here
<<<< ADC config >>>>
/* ADC Common Init */
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay =
ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
/* ADC1 DeInit */
ADC_StructInit(&ADC_InitStructure);
/* Configure the ADC1 in continuous mode */
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channels 6 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1,
ADC_SampleTime_480Cycles);
ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
#ifdef __DMA_ENABLE__
/* Enable DMA request after last transfer (Single-ADC mode) */
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
/* Enable ADC1 DMA since ADC1 is the Master*/
ADC_DMACmd(ADC1, ENABLE);
#else
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
ADC_ITConfig(ADC1, ADC_IT_OVR, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
<<<< DMA config >>>>
/* DMA1 clock enable */
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_DMA2, ENABLE );
/* DMA1 Channel1 Config */
DMA_DeInit(DMA2_Stream0);
DMA_DoubleBufferModeConfig(DMA2_Stream0, (uint32_t)ADCValB, DMA_Memory_1);
DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE);
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ADCValA;
DMA_InitStructure.DMA_PeripheralBaseAddr = ((uint32_t) ADC1) + 0x4C;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = NUM_OF_ADC; // 512 buffer size
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* DMA1 Channel1 enable */
DMA_Cmd(DMA2_Stream0, ENABLE);
Finally, I expect the result should be same as that using ADC EOC IRQ.
I have encountered the same problem, as a part of my complicated application, the ADC reads continuously an input PWM data and configured with DMA request in circular mode.
First, I tried to store the converted data in a memory buffer at each End Of Conversion Interrupt. This works well, so I am confident that the ADC is correctly converting the data.
Second, I started the ADC with DMA request in circular mode, (for each End Of Conversion Interrupt, the DMA handler get the ADC converted data and store in a memory buffer). At this stage, when I check the memory buffer via the Debugger - It seams that the data are messed and it's like the DMA was skipping values).
Third, I just wanted to verify, if it's a DMA problem or it's a Debugger one. I started the ADC with DMA request in "normal" mode. And all of a sudden, when opening the Debugger, the memory buffer data are correctly stored.
To summarize, the main difference between the second and the third method when opening the Debugger, is that the DMA handler is still running (in circular mode) and as a result the Debugger can't be able to show correctly the memory buffer data, due to the speed of the ADC conversion time/ DMA Handler requests (~1 µs). And in the other hand (in normal mode), the DMA handlers stops once it has completed filling the buffer.
To conclude, the DMA handler works fine and you can output the square wave with the DAC using your buffer. And if you want to view correctly the data using the Debugger you need to stop the DMA (after a period of time; for example).
HAL_ADC_Stop_DMA(&hadc);
Based on the tabular view of the data you provided, it appears that the DMA is configured properly as the data looks nearly identical to the data obtained from ADC EOC IRQ.
The only variable between relying on DMA and an IRQ is that there may be unexpected bus "collisions" between the DMA Controller and the CPU as, unlike when using an IRQ, they are both running concurrently, potentially resulting in wait states.
From the STM32 Reference Manual Section 13.4:
The DMA controller performs direct memory transfer by sharing the system bus with the Cortex-M4 ® F core. The DMA request may stop the CPU access to the system bus for some bus cycles, when the CPU and DMA are targeting the same destination (memory or peripheral).
And your observed sampling rate-dependent degradation in performance certainly corroborates this hypothesis, as the busmatrix must arbitrate more frequent accesses between the DMA Controller and the CPU.
Without seeing the rest of your code that sets up and reads from the buffer, it is hard to say what aspect of your application code may be causing this issue.

STM32L4xx USART Low temperature

I have got a problem with an electronic board based on a TSM32L4xx microcontroler.
I am using HAL driver for all peripherals initilization.
I use USART3 to communicate in RS232 with a computer
When the microcontroler rise under -6°C (in a freeze), the transmission on the microcontroler works, but not the reception (interruption doesn't rise).
The USART is initialized with SYSCLOCK :
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
[...]
USARTx_RCC_CONFIG(RCC_USART3CLKSOURCE_SYSCLK);
[...]
}
I read that under 0°C the PLL can stop to work but I don't use it...
Here is my System Clock initialisation :
void InitSystemClock(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
RCC_OSCILLATORTYPE_MSI|RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI
| RCC_OSCILLATORTYPE_MSI
| RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = 0;
RCC_OscInitStruct.MSIClockRange = CLOCK_RANGE;
RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_OFF;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
DWT->CTRL |= 1; //CYCCNTENA
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
/* GPIO Ports Clock Enable */
__GPIOA_CLK_ENABLE();
__GPIOB_CLK_ENABLE();
__GPIOC_CLK_ENABLE();
__GPIOD_CLK_ENABLE();
__GPIOE_CLK_ENABLE();
}
Edit :
I made an interresting test :
I've configured a PWM (using timer2 channel 3 and 4).
The PWM generates a 375Hz frequency.
After putting the electronic board into the freezer, the PWM decrease to 345Hz at -18°C
I made the measures with a laboratory oscilloscope
Anyone has encountered this issue ?
Anyone has got an idea about this problem ?
Thanks a lot by advance,
Baptiste
Assuming you are using the USART as UART:
If you have an accurate reference frequency such as LSE, you could perform a RC calibration according to the temperature, see the RCC_ICSCR register. An internal temperature sensor is available.
This application note shows how to calibrate the internal RCs.
At 30°C, the HSI16 oscillator has an accuracy of ±0.5%, the MSI
oscillator has an accuracy of ± 0.6% and the HSI48 oscillator has an
accuracy of ±4%. But in the temperature range of -40°C to 105°C, the
accuracy decreases.
UARTs are very sensitive to the clock variations especially if you send more data in one go.
You need to use crystal oscillator with good temperature stability or send very small chunks of data or check the internal temperature sensor and change the baud settings accordingly

STM32F4 : EEPROM 25LC256 management through SPI

I am trying to drive a EEPROM Chip 25LC256 with a STM32F469I-DISCO but can't achieve it.
I have tried to make my own function with HAL API bases but apparently something is wrong : I don't know if I write datas on the chip since I can't read it. Let me explain more.
So my chip is a DIP 25LC256 (DS is above is you wish). PINs HOLD and WP of EEPROM are tied to VCC (3.3V). PIN CS is connected to PH6 (ARD_D10 on board) and is managed by the software. PIN SI and PIN SO are respectively connected to PB15 (ARD_D11) and PB14 (ARD_D12) with the right alternate function (GPIO_AF5_SPI2). PIN SCK is also connected to PD3 (ADR_D13).
Here is my SPI configuration code :
EEPROM_StatusTypeDef ConfigurationSPI2(SPI_HandleTypeDef *spi2Handle){
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
GPIO_InitTypeDef gpioInit;
//// SCK [PD3]
gpioInit.Pin = GPIO_PIN_3;
gpioInit.Mode = GPIO_MODE_AF_PP;
gpioInit.Pull = GPIO_PULLDOWN;
gpioInit.Speed = GPIO_SPEED_FREQ_HIGH;
gpioInit.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOD, &gpioInit);
//// MOSI [PB15]
gpioInit.Pin = GPIO_PIN_15;
gpioInit.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &gpioInit);
//// MISO [PB14]
gpioInit.Pin = GPIO_PIN_14;
gpioInit.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &gpioInit);
//// CS [PH6]
gpioInit.Pin = GPIO_PIN_6;
gpioInit.Mode = GPIO_MODE_OUTPUT_PP;
gpioInit.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOH, &gpioInit);
HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, GPIO_PIN_SET);
//// SPI2
__HAL_RCC_SPI2_CLK_ENABLE();
spi2Handle->Instance = SPI2;
spi2Handle->Init.Mode = SPI_MODE_MASTER;
spi2Handle->Init.Direction = SPI_DIRECTION_2LINES;
spi2Handle->Init.DataSize = SPI_DATASIZE_8BIT;
spi2Handle->Init.CLKPolarity = SPI_POLARITY_LOW;
spi2Handle->Init.CLKPhase = SPI_PHASE_1EDGE;
spi2Handle->Init.NSS = SPI_NSS_SOFT;
spi2Handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
spi2Handle->Init.FirstBit = SPI_FIRSTBIT_MSB;
spi2Handle->Init.TIMode = SPI_TIMODE_DISABLE;
spi2Handle->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE ;
spi2Handle->Init.CRCPolynomial = 7;
if(HAL_SPI_Init(spi2Handle) != HAL_OK){
return EEPROM_ERROR;
}
return EEPROM_OK;
}
And two functions allowing respectively (and theorically) to WRITE and READ into the the chip :
Write Function :
EEPROM_StatusTypeDef WriteEEPROM(SPI_HandleTypeDef *spi2Handle, uint8_t *txBuffer, uint16_t size, uint16_t addr){
uint8_t addrLow = addr & 0xFF;
uint8_t addrHigh = (addr >> 8);
uint8_t wrenInstruction = WREN_EEPROM; // Value : 0x06
uint8_t buffer[32] = {WRITE_EEPROM, addrHigh, addrLow}; //Value : 0x02
for(uint i = 0 ; i < size ; i++){
buffer[3+i] = txBuffer[i];
}
HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, RESET);
if(HAL_SPI_Transmit(spi2Handle, &wrenInstruction, 1, TIMEOUT_EEPROM) != HAL_OK){
return EEPROM_ERROR;;
}
HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, SET);
HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, RESET);
if(HAL_SPI_Transmit(spi2Handle, buffer, (size + 3), TIMEOUT_EEPROM) != HAL_OK){
return EEPROM_ERROR;
}
HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, SET);
return EEPROM_OK;
}
Read Function :
EEPROM_StatusTypeDef ReadEEPROM(SPI_HandleTypeDef *spi2Handle, uint8_t *rxBuffer, uint16_t size, uint16_t addr){
uint8_t addrLow = addr & 0xFF;
uint8_t addrHigh = (addr >> 8);
uint8_t txBuffer[3] = {READ_EEPROM, addrHigh, addrLow};
HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, RESET);
HAL_SPI_Transmit(spi2Handle, txBuffer, 3, TIMEOUT_EEPROM);
HAL_SPI_Receive(spi2Handle, rxBuffer, size, TIMEOUT_EEPROM);
HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, SET);
return EEPROM_OK;
}
I know my function are not very "beautiful" but it was a first attempt. In my main, I have tried in the first place to write into the chip the data "0x05" at the 0x01 adress then to read this data back :
uint8_t bufferEEPROM[1] = {5};
uint8_t bufferEEPROM2[1] = {1};
WriteEEPROM(&spi2Handle, bufferEEPROM, 1, 0x01);
ReadEEPROM(&spi2Handle, bufferEEPROM2, 1, 0x01);
I have an oscilloscope so since it didn't work (monitoring with STM Studio) I visualized the CLK and SI PINs then CLK and SO PINs (can only see two channel at the same time) :
As you can see, with the first picture that shows CLK (yellow) and SI (or MOSI) in blue, I have all the data expected : The WRite ENable instruction then the WRITE instruction. Following the ADDRESS, then the DATA.
After that, the Read Function starts. First the READ instruction and the ADDRESS where I want to fetch the data. The last 8 bits are supposed to be the data stored at the address (0x01 in this case). Something happens on SI PIN but I guess this is because the HAL_SPI_Receive() function actually calls HAL_SPI_TransmitReceive() with my array bufferEEPROM2 as parameter (that's why we can se 0b00000001). And so it is because of my SPI configuration parameter (Full-duplex).
Anyway, theorically I am supposed to see 0b00000101 on SO PIN but as you can see in the second picture.... nothing.
I have tried to change gpioInit.Pull for SO PIN on PULLUP and PULLDOWN but nothing changed. NOPULL is because that's the last thing I have tried.
The thing is I don't know where to start. My transmission seems to work (but is it actually ?). Is there anything wrong with my initialization ? Acutally my main question would be : why I don't receive any data from my EEPROM ?
Many thanks !
Write operations need some time to complete (your datasheet says 5 ms on page 4), during that time no operation other than read status is possible. Try polling the status register with the RDSR (0x05) opcode to find out when it becomes ready (bit 0). You could also check the status (bit 1) before and after issuing WREN to see if it was successful.
So the problem is now solved. Here are the improvements :
There was actually two issues. The first one and certainly the most important is, as berendi stated, a timing issue. In my WRITE function I didn't let the time for the EEPROM to complete its write cycle (5 ms on datasheet). I added the following code line at the end of all my WRITE functions :
HAL_Delay(10); //10 ms wait for the EEPROM to complete the write cycle
The delay value could be less I think if time is preicous (theorically 5ms). I didn't test below 10 ms though. An other thing. With the oscilloscope I also saw that my Chip Select used to went HIGH in the middle of my last clock edge. I could not say if this could also imply some issues since that's a thing I solved in the first place by adding a code line before HAl_Delay(10). All my SPI transmission functions finishes this way now :
while(HAL_GPIO_ReadPin(CLK_PORT, CLK_PIN) == GPIO_PIN_SET){
}
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);
HAL_Delay(10);
This way I have the proper pattern and I can write in the EEPROM and read back what I wrote.
NB : A last thing that made me goes deeper into my misunderstanding of the events : since my write functions didn't work, I focused on STATUS REGISTER write and read function (in order to solve this step by step). The write function didn't work either and in fact it was because the WRENbit wasn't set. I though (wrong one) that the fact to write into the STATUS REGISTER didn't ask also to set WREN like the WRITE functions into the memory ask to. Actually, it is also necessary.
Thanks for the help !