STM32 Sine Wave works but Audio file produces static from DAC - stm32

I'm trying to play a simple WAV file using the internal DAC on the STM32F466RE. I have the DMA, writing to a dac by an interval set by Timer4. Using a Sine Wave, this works perfectly:
Sine Wav Example (This works fine):
const uint16_t sine_wave_array[32] = {2047, 1648, 1264, 910, 600, 345,
156, 39, 0, 39, 156, 345,
600, 910, 1264, 1648, 2048, 2447,
2831, 3185, 3495, 3750, 3939, 4056,
4095, 4056, 3939, 3750, 3495, 3185,
2831, 2447};
HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)sine_wave_array, 32, DAC_ALIGN_12B_R);
HAL_TIM_Base_Start(&htim4);
//timer config
htim4.Instance = TIM4;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 1136; // (16MHZ /(32 * 440hz))
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
//DAC Setup from Stm32cubeIDE
static void MX_DAC_Init(void)
{
/* USER CODE BEGIN DAC_Init 0 */
/* USER CODE END DAC_Init 0 */
DAC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN DAC_Init 1 */
/* USER CODE END DAC_Init 1 */
/** DAC Initialization
*/
hdac.Instance = DAC;
if (HAL_DAC_Init(&hdac) != HAL_OK)
{
Error_Handler();
}
/** DAC channel OUT1 config
*/
sConfig.DAC_Trigger = DAC_TRIGGER_T4_TRGO;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN DAC_Init 2 */
/* USER CODE END DAC_Init 2 */
}
The DAC is set to use DMA Stream 5. The DMA is set to half-word (16 bit). The MCU is at 16 MHZ. Everything sounds good.
So now I'm trying to replace that sine_wave with an audio file (headers removed) sampled at 44.1khz form here: hello.h
Here's the modified code:
#include "helo.h"
//HELLO_LENGTH is 57890
HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)hello, HELLO_LENGTH, DAC_ALIGN_12B_R);
HAL_TIM_Base_Start(&htim4);
htim4.Init.Period = 362; // (16MHZ /44100)
I also changed the DMA Buffer from Half-word to Byte (8 bit). However, I'm just hearing static now from the speaker. But the static has a repetition to it, almost like the sound is there but all garbled.
I also tried with my own audio file (with headers removed, only the data portion) and I still get the same thing.
Any ideas?

Try to go one step at a time! You have massively ramped up the data rate, and changed the data format and source all at once.
First try the sine wave at the increased rate and verify that you get sensible output. Try different frequencies of sine wave. Start with a low frequency like 1kHz and work up. 44.1kHz sample rate should be able to get close to 20kHz signal.
Each time verify on the oscilloscope that the signal looks correct. If you don't have an oscilloscope then connect up a speaker and listen to the audio with a frequency monitoring app on your phone (eg: this one), or else maybe even an electronic guitar tuner.
Before you hook up a speaker think about how you will filter out the DC bias and amplify the signal. If you don't know what that means then you will have to ask on electronics stack exchange.
I expect that you may find that you are trying to run the sample rate faster that the DAC can go. Internal DACs are not usually meant for audio, also they are very non-linear.
Once you have found a sample rate that works then create a data file in that format. Remember that for wav files the data may be in unsigned or signed integer format. If it is signed then you need to convert to unsigned by adding a bias. Also think about endianness.

Related

LIS3DH sensor do not generate interrupt on pin

I config a LIS3DH sensor to generate interrupt when acceleration in any direction exceeds a certain threshold. I read INT1_SRC register and when I shake my board, IA bit is set and reset again. From this I realized that interrupt has been generated but I can not capture it on pin. I also check INT1 pin with oscilloscope but no signal.
Do any one know where would be the problem?
my configuration for sensor:
/* High-pass filter enabled on interrupt activity 1 */
lis3dh_high_pass_int_conf_set(&dev_ctx, LIS3DH_ON_INT1_GEN);
/* Enable HP filter for wake-up event detection.*/
/* Use this setting to remove gravity on data output */
lis3dh_high_pass_on_outputs_set(&dev_ctx, PROPERTY_ENABLE);
/* Enable AOI1 on int1 pin */
lis3dh_pin_int1_config_get(&dev_ctx, &ctrl_reg3);
ctrl_reg3.i1_ia1 = PROPERTY_ENABLE;
lis3dh_pin_int1_config_set(&dev_ctx, &ctrl_reg3);
/* Interrupt 1 pin latched */
lis3dh_int1_pin_notification_mode_set(&dev_ctx, LIS3DH_INT1_LATCHED);
/* Set full scale to 2 g */
lis3dh_full_scale_set(&dev_ctx, LIS3DH_2g);
/* Set interrupt threshold to 0x10 -> 250 */
lis3dh_int1_gen_threshold_set(&dev_ctx, 0x05);
/* Set no time duration */
lis3dh_int1_gen_duration_set(&dev_ctx, 0);
/* Dummy read to force the HP filter to current acceleration value. */
lis3dh_filter_reference_get(&dev_ctx, &dummy);
/* Configure wake-up interrupt event on all axis */
lis3dh_int1_gen_conf_get(&dev_ctx, &int1_cfg);
int1_cfg.zhie = PROPERTY_ENABLE;
int1_cfg.yhie = PROPERTY_ENABLE;
int1_cfg.xhie = PROPERTY_ENABLE;
int1_cfg.aoi = PROPERTY_DISABLE;
lis3dh_int1_gen_conf_set(&dev_ctx, &int1_cfg);
/* Set device in HR mode */
lis3dh_operating_mode_set(&dev_ctx, LIS3DH_HR_12bit);
/* Set Output Data Rate to 100 Hz */
lis3dh_data_rate_set(&dev_ctx, LIS3DH_ODR_100Hz);

STM32cubemx PWM + ADC DMA out of sync

I'm trying to get STM32F103 to work with ADC and DMA both in circular mode to perform DSP processing in a basic way (see picture).
DMA fills ADC buffer, on "ADC half full" interrupt we process samples inBuffer[0..half] and put it in outBuf[0..half].
Output buffer is also fed to timer-driven PWM via DMA.
My thought is: TIM1 prescaller is (2-1), period is (1024-1), which with 72MHz timer clock has to make 35156 kHz 10-bit PWM. With buffer length of 512 samples buffer is 14,5ms long. I wait for DMA to sample 256 samples every 7,25ms, pushing out 256 samples to PWM in total pointer-to-pointer sync.
The problem is that I'm not sure if ADC and PWM buffer are syncronised.
With following code i get this:
void dspStart(void){
HAL_TIM_Base_Start(&htim1); // start timer
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2); //start PWM which triggers ADC
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)inBuf, ADC_BUFFER_LEN); // start DMA w/ ADC
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_3, (uint32_t*)outBuf, ADC_BUFFER_LEN); // start PWM output
}
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc1){
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3); // yellow channel
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc1){
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3); // yellow channel
}
void HAL_TIM_PWM_PulseFinishedHalfCpltCallback(TIM_HandleTypeDef *htim1){
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_4); // blue channel
};
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim1){
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_4); // blue channel
}
ADC and PWM buffers are out of sync, although buffer size is same for both DMA channels and they share same TIM1.
What did I do wrong?
STM32cubeMX setting are following:

STM32 Use DMA to generate bit pattern on GPIO PIN

I am trying to generate a bit pattern on a GPIO pin. I have set-up the DMA engine to transfer from an array of GPIO pin states to the GPIO BSRR register
Here is the code I am using to configure the DMA
hdma_tim16_ch1_up.Instance = DMA1_Channel3;
hdma_tim16_ch1_up.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_tim16_ch1_up.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim16_ch1_up.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim16_ch1_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim16_ch1_up.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_tim16_ch1_up.Init.Mode = DMA_NORMAL;
hdma_tim16_ch1_up.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_tim16_ch1_up) != HAL_OK)
{
Error_Handler();
}
/* Several peripheral DMA handle pointers point to the same DMA handle.
Be aware that there is only one channel to perform all the requested DMAs. */
__HAL_LINKDMA(tim_baseHandle,hdma[TIM_DMA_ID_CC1],hdma_tim16_ch1_up);
__HAL_LINKDMA(tim_baseHandle,hdma[TIM_DMA_ID_UPDATE],hdma_tim16_ch1_up);
Here is the code I use to setup the transfer:
uint32_t outputbuffer[] = {
0x0000100,0x01000000,
0x0000100,0x01000000,
0x0000100,0x01000000,
0x0000100,0x01000000,
0x0000100,0x01000000,
0x0000100,0x01000000,
0x0000100,0x01000000
/* ... */
};
if (HAL_DMA_Start_IT(htim16.hdma[TIM_DMA_ID_UPDATE], (uint32_t)outputbuffer, (uint32_t)&GPIOG->BSRR, 14) != HAL_OK)
{
/* Return error status */
return HAL_ERROR;
}
__HAL_TIM_ENABLE_DMA(&htim16,TIM_DMA_UPDATE);
HAL_TIM_Base_Start_IT(&htim16);
I am expecting to see every time the counter overflows, the DMA transfers 32 bits from the array and increments to the next array position until the DMA CNDTR register reads 0.
I set up a GPIO pin to toggle every time the timer over flows and I setup an alternating bit pattern in the array. I would expect the two GPIO pins to be similar in their output shape but I get one longer pulse on the line connected to the DMA. Any tips would be greatly appreciated
configure TIM2 as input capture direct mode (TIM2_CH1)
configure TIM2 DMA direction "memory to peripheral"
configure TIM2 data width Half word / Half word
configure GPIO pins as GPIO_OUTPUT, for example 16 pins GPIOD0..GPIOD15
copy and paste HAL_TIM_IC_Start_DMA() function from HAL library and give it a new name MY_TIM_IC_Start_DMA()
find HAL_DMA_Start_IT() function call in MY_TIM_IC_Start_DMA()
replace (uint32_t)&htim->Instance->CCR1 with (uint32_t)&GPIOD->ODR
if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)&GPIOD->ODR, (uint32_t)pData, Length) != HAL_OK)
Now you can start DMA to GPIO transfer by calling
MY_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_1,(uint32_t*)gpioBuffer,GPIO_BUFFER_SIZE);
Actual transfer must be triggered by providing pulses on TIM2_CH1 input pin (for example, by using output compare pin from other timer channel). Those pulses originally was used to save Timer2 CCR1 register values to DMA buffer. Code was tweaked to transfer DMA buffer value to GPIOD ODR register.
For GPIO to Memory transfer change TIM2 DMA direction to "peripheral to memory", configure GPIO pins as GPIO_INPUT and use GPIOD->IDR instead of ODR in HAL_DMA_Start_IT parameters in modified MY_TIM_IC_Start_DMA() function.

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.

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 !