Reading a 16-bit word via SPI [STM32] - Page 1
Forum section: electronics / general

author: XaviPacheco
author_rank: Regular Contributor
timestamp: September 20, 2018, 05:02:11 am
content: To receive data via SPI in polling mode, HAL library has the following definition:HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout).I understand the first parameter. But, I'm confused about the second and third parameter. I've told CubeMx that the data size for SPI1 is 16 bits. Now, the second parameter need a 8 bits pointer. Let's say thatHAL_SPI_Receive(&hspi1, &SPIRx, 1, 1);I've declared SPIRx as uint16_t SPIRx, but the compiler throws a warning saying that"passing argument 2 of 'HAL_SPI_Receive' from incompatible pointer type [-Wincompatible-pointer-types]"So, obviously the pointer has to be 8 bits. So, if I want to read a 16 word data from a current sensor, do I need to declare a 8 bit pointer, and in the third parameter, uint16_t Size, do I say that I want to receive two bytes?I would appreciate anyone help clear my mind.

author: rjp
author_rank: Regular Contributor
timestamp: September 20, 2018, 05:08:00 am
content: normally these api's do everything as a block of bytes and its up to you to reconstruct the specific data types.eg:uint8_t buffer;HAL_SPI_Receive(&hspi1, buffer, 2, 1);uint16_t value = ((uint16_t)buffer << 8 ) | ((uint16_t) buffer);

author: XaviPacheco
author_rank: Regular Contributor
timestamp: September 20, 2018, 05:16:17 am
content: So, isn't it relevant that I told CubeMx the data size to be 16 bits instead of 8? Because I’m using a 8 bits pointer anyways, so I think if I say that my data size is 8 bits, it would be the same.Another question, I set the timeout at 1. But I’ve seen timeouts at 5000. What's the criteria behind?

author: newbrain
author_rank: Super Contributor
timestamp: September 20, 2018, 07:33:37 am
content: Quote from: XaviPacheco on September 20, 2018, 05:16:17 amTimeouts depend very much on the needs and constraints of the application, quite difficult to give a general rule, but yes 1ms seems a bit tight...As for the SPI data transfer size: yes it's extremely relevant.The SPI peripheral in STM32F7 (IIRC that's what you are using?) will send/receive the data differently if it's in <=8bit or >8bit mode.If in 16bit mode, the reconstruction suggested by rjb will not work, as the MSByte will be in buffer and the LSByte in buffer: the data register is treated as an uint16_t and (this) Arm is little endian, so the lower address will hold the least significant part.Note that this is true even if you use direct register access instead of the HAL.If you are sending and receiving 16bit words, the best option IMO is to use a uint16_t variable, and cast its address to an a (uint8_t *) when passing it to the HAL (if you want to silence the warning).The C standard guarantees that the cast gives the expected results: address of the first byte in the object.In this way there's no need for reassembling the received values, assuming, of course, that theyaretransmitted as 16bit cvalues MSb first!The size must still be given in bytes, so it will be something as:Code:uint16_t val;HAL_SPI_Receive(&hspi1, (uint8_t *)&val, 2, 10);Edit: corrected after richardman's finding!

author: richardman
author_rank: Frequent Contributor
timestamp: September 20, 2018, 08:23:24 am
content: Quote from: newbrain on September 20, 2018, 07:33:37 amMinor typo:Code:uint16_t val;HAL_SPI_Receive(&hspi1, (uint8_t *)&val, 2, 10);

author: XaviPacheco
author_rank: Regular Contributor
timestamp: September 20, 2018, 01:53:34 pm
content: Thank you.Actually, my SPI routine is called every 10ms by a timer-based interrupt. That's why I asked about the timeout.

author: XaviPacheco
author_rank: Regular Contributor
timestamp: September 21, 2018, 12:21:57 am
content: I'm having problems to read the sensorThis is my sensor datasheetTLI4970This is my routine every 10ms:HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); //Bring slave select lowHAL_SPI_Receive(&hspi1, (uint8_t *)&SPIRx, 2, 10); //Receive dataHAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); //Bring slave select highHowever, I get random values even when the current is zero. I'm getting values like:0x88000x88030x88010xA7FF0x87FC0x97FE0xA8020xA8010x97FD0x87FD0xA8000xA7FEAccording to the datasheet, these are sensor status messages, but none of them are Sensor Current Messages. The hardware seems okay. This sensor is not so common I think, I haven't found much help.

author: newbrain
author_rank: Super Contributor
timestamp: September 21, 2018, 06:51:40 am
content: Quote from: XaviPacheco on September 21, 2018, 12:21:57 amNothing strange in that code, but those values really look random, not a legit status message.That chip should really return something about 0 (4096) with no current through it, but:HW: Check all the connections are correctHW: If you have a scope, check for signal integrity (and SPI decode, if available).SW: The sensor is good for SPI clock frequencies up to 5MHz, how is the clock chain to the SPI set up?SW: Make sure the SPI is defined with the correct clock polarity and phase.Quote from: XaviPacheco on September 20, 2018, 01:53:34 pmI see, then I would not use a TO of 10ms...Generally speaking, I try not to hang a long time in ISRs.In any case, the maximum update frequency for the sensor is 70Hz, so a 10ms sampling time seems higher than needed (you are bound to get duplicated reads).

author: XaviPacheco
author_rank: Regular Contributor
timestamp: September 21, 2018, 12:15:40 pm
content: I'm now reading the sensor every second:I've captured a new pattern of received data and all of them start at either 8 or A in the MSB position. I don't expect to receive "1" in the MSB bit after the first message which is the status. It's interesting that if I ignore the 3 top bits, the measurement makes sense a little bit. But, I have to receive a zero in the first top bit so that the message is about the current.This is how my SPI is set:/* SPI1 init function */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_16BIT;hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;hspi1.Init.NSS = SPI_NSS_SOFT;hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;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(__FILE__, __LINE__);}}

author: newbrain
author_rank: Super Contributor
timestamp: September 21, 2018, 02:45:13 pm
content: Quote from: XaviPacheco on September 21, 2018, 12:15:40 pmAre you sure you are keeping the SPI clock rate below 5MHz as needed by the sensor?What is your APB2 frequency?On an L432 (is that what you are using? you mentioned it in some other post), it can be up to 80MHz, and with a /2 prescaler that would make 40MHz...slightly higher than what the sensor supports.Clock and polarity look good.

author: XaviPacheco
author_rank: Regular Contributor
timestamp: September 21, 2018, 02:53:00 pm
content: My APB2 frequency is 1 MHz. HCLK is 2 MHz. So, do you think that could be a potential problem? I will try 5 MHz as suggested in the datasheet. I thought 5 MHz was like the max frequency supported. I've misinterpreted that data.

author: newbrain
author_rank: Super Contributor
timestamp: September 21, 2018, 04:29:11 pm
content: No, it's fine, as long as it less than 5MHz, no misinterpretation there!No minimum frequency or maximum times are given, so it should be perfectly ok to drive it at 500kHz.

author: XaviPacheco
author_rank: Regular Contributor
timestamp: September 21, 2018, 04:31:54 pm
content: Ok. I'll continue checking. For some reason I don't get expected values.

author: XaviPacheco
author_rank: Regular Contributor
timestamp: September 21, 2018, 11:11:40 pm
content: Wait... according to the datasheet, I think I should set clock phase =1, which means that I have to modify the following line:hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;CubeMx says that 2 Edge, is clock phase =1

author: richardman
author_rank: Frequent Contributor
timestamp: September 22, 2018, 01:35:13 am
content: Do yourself a HUGE favor, and get something like a LOGIC logic analyzer or something. It's invaluable to make sure your SPI (or I2C etc.) timing and signals are what you expect. For example, you may need some delay between setting and unsetting the chip select, or time for the sensor to respond etc.

author: XaviPacheco
author_rank: Regular Contributor
timestamp: September 22, 2018, 03:49:07 am
content: Indeed. That was my problem. I had SPI mode 0, when the right one is the Mode 1. Everything works fine now.

author: PAULN2
author_rank: Newbie
timestamp: December 18, 2018, 01:47:41 am
content: Hi Xavi, Thanks a lot. I have learned so much with the info you have published. But, I want to ask you about the values you obtain when read the sensor. I am currently using a TLI4970 mounted on Hall Current Click Board of Mikroelectronika and STM32F4 Discovery Board. Apparently, I have done correctly all the config steps for spi port usign STM32CubeMx Tool and Hal Drivers to sense current about 1A but when I read the sensor I obtain erractic values. i.e I obtain positive, zero and negative values. I'd appreciate your help.

author: A.Ersoz
author_rank: Regular Contributor
timestamp: October 02, 2019, 03:15:04 pm
content: Hello guys,I have almost same problem with this thread. I try to read 16 bit data from ADC (ADS7886) conversion with SPI. In the debug mode, I read only middle level and upper level of my signal values, but the lower level of signal values are missing. In the attachment, there is result of my code in debug mode and the input signal (yellow) of the ADC. Do you have any idea to solve the problem?Code:void ADC_Conversion_of_Voltage_Transients(){uint16_t ADC_Buf;HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET);HAL_SPI_Receive(&hspi4,(uint8_t *)&ADC_Buf,2,100);volt = (float)(ADC_Buf * 5 / pow(2,12)); //testdata_in[i++]=volt;HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET);}

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 02, 2019, 03:37:24 pm
content: Quote from: A.Ersoz on October 02, 2019, 03:15:04 pmWell, I think so. Take a look at "Figure 1. Interface Timing Diagram" of the datasheet.You'll receive the sample word in a "most significant bit first" manner. But you're storing the result in a 16-bit integer. The thing to know here is that ARM-cortex MCUs are low-endian (at least by default).ADC_Buf will thus contain a word in which the sample bytes are swapped... You need to swap them back.Since you can't take advantage of the fact that the ADC gives you a low-endian value, here it's simpler to declare ADC_Buf as "uint8_t ADC_Buf;", then later you can call "HAL_SPI_Receive(&hspi4, ADC_Buf,2,100);" (no cast needed anymore - besides, you don't need to put a "&" before an array in C, it's a pointer.)You'll reconstruct the value with something like (assuming "Sample" is declared as uint16_t): "Sample = ((uint16_t) ADC_Buf) << 8 | ADC_Buf;", and finally you can write: "volt = (float)(Sample * 5 / pow(2,12));"The DS of the ADC states that the (4) upper bits sent through SPI are 0, but for good measure, since it's a 12-bit ADC, you can write this instead of the above:"Sample = (((uint16_t) ADC_Buf) << 8 | ADC_Buf) & 0x0FFF;"Also, take another good look at the SPI diagram in the DS. Use the correct SPI mode, else you may have a 1-bit shift in the values you read...

author: A.Ersoz
author_rank: Regular Contributor
timestamp: October 02, 2019, 03:59:31 pm
content: Thank you for your response!I set the SPI as receive only master mode. Clock polarity as high and phase is 2edge. Its prescaler is 8 and baudrate is 13.5Mbits/s. Baudrate should be maximum 16 Mbit/s, shouldn't it?The other thing is that should I control the conversion with a timer interrupt? Because of on of my trial is that setting a timer with 10 times bigger frequency than input signal frequency. Then, when the interrupt occurs, the conversion will happen. With this way, I imagine to control the sampling time.

author: rstofer
author_rank: Super Contributor
timestamp: October 02, 2019, 05:38:38 pm
content: I haven't looked at the exact details of the SPI peripheral but on many, you are writing the value to a register. The register then shifts the data. So far, no surprise.... But when you send the byte to the register and raise CS' too early, the transmission is garbled because the sender stops sending.It is sometimes helpful to test if the send operation is actually complete before raising CS'.Now, there is a possibility that the HAL supplied code doesn't return before the transmission is actually complete. Maybe...

author: A.Ersoz
author_rank: Regular Contributor
timestamp: October 02, 2019, 08:14:30 pm
content: The interesting thing is that the ADC SCK has 48 clocks are generated in between CS reset and set changing. Maybe because of that the conversions are not true. For example, I have attached the ADC SCK (yellow), ADC SDO (green), and CS (green in another figure) responses.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 02, 2019, 09:30:36 pm
content: Looks like you have multiple problems here.First, you'll need to fix what I said; your code was interpreting the sample value with byte swapped. That's a guarantee.But from the scope capture, you seem to have some kind of hardware issue there. Why are the clock and data lines looking like they become hi-Z towards the end of the transfer? Check your whole code. Did you really show us everything you do?EDIT: for the MISO line, it's probably perfectly normal. The ADC makes it Hi-Z once 16 clock pulses have passed. As to your SPI clock, you're controlling it, so it looks suspicious. I don't know why you'd configure the CLK line as HI-Z once the transfer is done... doesn't seem to be useful.So now your main problem is why the 48 clock pulses? You probably configured the SPI peripheral incorrectly (like, probably not for 8-bit transfers...), which most likely explains both the 48 clock pulses AND the fact the clock line gets Hi-Z once the transfer is done.Three things to do: 1. Check and fix the configuration of your SPI peripheral. 2. I wouldn't leave the MISO line floating when the ADC is not accessed; for this, I would configure the corresponding MCU I/O enabling the internal pull-down for instance. Floating inputs can increase your power consumption significantly. And 3. Implement the swapped bytes fix I suggested.Oh, a last suggestion. While you're fixing the SPI configuration, you could just configure it as 16-bit, MSB first transfer. Then you wouldn't need to swap the bytes by hand.

author: A.Ersoz
author_rank: Regular Contributor
timestamp: October 03, 2019, 04:22:36 pm
content: Thank you your comments are valuable!Firstly I modified the SPI mode the receive only master to full duplex master. Then, I make it pull-down of MOSI GPIO pin in its configuration window. Then I set the 16 bit data transfer (Motorola -default-). For the prescaler, I set 8 for getting 13.5 Mbits/s. I understand from the datasheet that maximum SCK frequency would be 20 MHz. By the way, the SCK pin shows 16 square waves.This is also the SPI configuration:Code:static void MX_SPI4_Init(void){/* SPI4 parameter configuration*/hspi4.Instance = SPI4;hspi4.Init.Mode = SPI_MODE_MASTER;hspi4.Init.Direction = SPI_DIRECTION_2LINES;hspi4.Init.DataSize = SPI_DATASIZE_16BIT;hspi4.Init.CLKPolarity = SPI_POLARITY_HIGH;hspi4.Init.CLKPhase = SPI_PHASE_1EDGE;hspi4.Init.NSS = SPI_NSS_SOFT;hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB;hspi4.Init.TIMode = SPI_TIMODE_DISABLE;hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;hspi4.Init.CRCPolynomial = 7;hspi4.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;hspi4.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;if (HAL_SPI_Init(&hspi4) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}}Then, I change the main function to do the conversion. I agree that 4 bit of data should be zero but with AND operation, don't we loose data. Instead of the doing AND, can we do shifting four zero bits to the top of the array?Here is my conversion function:Code:void ADC_Conversion_of_Voltage_Transients(){uint8_t ADC_Buf;float Sample;uint8_t i=0;float testdata_in;HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET);HAL_SPI_Receive(&hspi4,ADC_Buf,2,100);HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET);Sample = (((uint16_t) ADC_Buf) << 8 | ADC_Buf) & 0x0FFF;volt = (float)(Sample * (5.0 / 4096.0)); //testdata_in[i++]=volt;i %= 16;}}For testing my code and configuration, I feed the ADC with 2.5V step function signal (yellow line and you can check its amplitude with (DELTA)Y variable). But the results show different values.I have attached the ADC input signal and the results. I also attached the ADC SCK (yellow) and ADC SDO (green) signals.

author: A.Ersoz
author_rank: Regular Contributor
timestamp: October 07, 2019, 05:20:43 pm
content: The ADC requires 20MHz clock rate through SPI configuration (this is what I understand from its datasheet). But in the SPI4 configuration window, I can set only 13 Mbits/s or 26 Mbits/s baudrate. Do you think the problem is related with setting the baudrate issue? The other thing is that the clock shape of the ADC SCK is distorting (meaning not exact square shape) after 1 Mbits/s baudrate settings.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 07, 2019, 07:16:24 pm
content: SPI clock rate is irrelevant as long as it doesn't exceed the max clock rate for the device, so 13MHz should be fine if you don't need any faster. I don't know what your system clock is, and even less so that of the corresponding APB, but your SPI clock can only be a fraction of it (by powers of two prescaler), so obviously you won't be able to generate just any frequency you want. You can adjust the APB clock though, doesn't have to be the same as the CPU clock.As to the transfer, again your scope captures help figuring out what's wrong.Now you can see there are actually TWO 16-bit transfers per call, which is not what you intended. (It will even actually overwrite things on the stack, which is bad. Here is why.)I had to take a look at the HAL source code for HAL_SPI_Receive() to understand (maybe it's documented somewhere, but looking at the code was just much faster).When the SPI peripheral is configured for 16-bit transfers, the Size parameter is actually NOT the size in bytes, but the number of 16-bit transfers.Yeah, ST developers are nice like that, and produce consistent interfaces (ahem.) The fact that the parameter which is a pointer to the buffer is of type "uint8_t *" is completely misleading; it kind of makes you assume the Size parameter would naturally be the size in bytes. But nope. They should have declared the buffer param as "void *" instead IMO to make it less ambiguous (and it would not have required any annoying cast...), and they should have called the Size parameter something else. Oh well...So in your case, you should pass ONLY 1 as the Size parameter, and a pointer to a uint16_t variable (like you did first), because the function actually writes the received data by 16-bit chunks (not sure if Cortex-M cores like unaligned accesses, but I don't really like them myself anyway.)Passing 2 as the Size parameter, as you did, will issue TWO 16-bit transfers (which you can see on your scope). The second one will write somewhere on the stack that may have nasty consequences (basically you're unwillingly having a buffer overflow here.)

author: A.Ersoz
author_rank: Regular Contributor
timestamp: October 08, 2019, 08:31:24 pm
content: I modified a little bit of my code. Now there are 16 clock cycles. I have attached the output result in Keil debug session. The values are correct but for just make sure I want to see all the amplitude values of the signal. So, how can I adjust digitized time period, acquisition time, and plot the digitized signal?Code:uint8_t ADC_Buf;uint16_t Sample;HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET);HAL_SPI_Receive(&hspi4,ADC_Buf,1,100);Sample = (((uint16_t) ADC_Buf) << 8 | ADC_Buf) & 0x0FFF;volt = (float)(Sample * (5.0 / 4096.0)); //testdata_in[i++]=volt;i %= 16;HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET);

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2019, 10:07:27 pm
content: Sampling time can be set using HAL_ADC_ConfigChannel() (from a ADC_ChannelConfTypeDef parameter)For the sampling rate, you can set a timer trigger for the ADC.You can then fill in a buffer of samples, and then read/process the whole buffer once the acquisition is done.

author: JustMeHere
author_rank: Frequent Contributor
timestamp: October 10, 2019, 03:02:32 am
content: People will piss and moan, but I use a union to do this.Something likeunion{uint8_t data;uint16_t result;}I know people will give reasons not to do this, but it works. The objection is "type pruning" but if it works, saves cycles, and saves memory; then in low level C why not? I'm not looking for portability.

author: A.Ersoz
author_rank: Regular Contributor
timestamp: October 10, 2019, 03:27:31 pm
content: I modified my code for interrupt mode:Main Code:Code:void ADC_Conversion_of_Voltage_Transients(){HAL_SPI_Receive(&hspi4,ADC_Buf,16,100);Sample = (((uint16_t) ADC_Buf) << 8 | ADC_Buf) & 0x0FFF;volt = (float)(Sample * (5.0 / 4096.0)); //testdata_in[i++]=volt;i %= 16;}Intterrupt handler function:Code:void TIM4_IRQHandler(void){/* USER CODE BEGIN TIM4_IRQn 0 *//* USER CODE END TIM4_IRQn 0 */HAL_TIM_IRQHandler(&htim4);/* USER CODE BEGIN TIM4_IRQn 1 */HAL_GPIO_TogglePin(ADC_CS_GPIO_Port, ADC_CS_Pin);Timer_Flag=1;/* USER CODE END TIM4_IRQn 1 */}Call function:Code:if (Timer_Flag){ADC_Conversion_of_Voltage_Transients();Timer_Flag=0;}But the problem is ADC SCK is continous without realted CS position which is controlled timer.Result. ADC SCK (green plot) and ADC CS (yellow plot).I will try 'union' function.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2019, 06:59:06 pm
content: Ignore my last post, I was suddenly thinking about the internal ADC for some reason.Regarding your IRQ handler, I think you're on the right track, but you'll have to think it through a little bit more.You're toggling /CS each time the interrupt fires, and setting a flag to start the SPI transfer outside of the IRQ. This isn't right, as this will trigger an SPI transfer when /CS is low, but ALSO when /CS is high!Think about it...

author: A.Ersoz
author_rank: Regular Contributor
timestamp: October 10, 2019, 07:50:32 pm
content: Is it like this?Code:void TIM4_IRQHandler(void){/* USER CODE BEGIN TIM4_IRQn 0 *//* USER CODE END TIM4_IRQn 0 */HAL_TIM_IRQHandler(&htim4);/* USER CODE BEGIN TIM4_IRQn 1 */HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET);Timer_Flag=1;HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET);/* USER CODE END TIM4_IRQn 1 */}Code:void ADC_Conversion_of_Voltage_Transients(){Timer_Flag=1;HAL_SPI_Receive(&hspi4,ADC_Buf,16,100);Sample = (((uint16_t) ADC_Buf) << 8 | ADC_Buf) & 0x0FFF;volt = (float)(Sample * (5.0 / 4096.0)); //testdata_in[i++]=volt;i %= 16;Timer_Flag=0;}

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2019, 08:21:52 pm
content: Not yet.In this case you're making ultra short /CS pulses.Set /CS low in your IRQ handler as you already do. (This makes sense to do this here as this is what starts the conversion cycle.)Then set /CS high at the end of your ADC_Conversion_of_Voltage_Transients() function only.

author: A.Ersoz
author_rank: Regular Contributor
timestamp: October 10, 2019, 09:32:07 pm
content: Thank you for your help! But something I miss. ADC CS keep high every time. Then, number of clock cycles are true but between of the each cycles has distortion.Code:void TIM4_IRQHandler(void){/* USER CODE BEGIN TIM4_IRQn 0 *//* USER CODE END TIM4_IRQn 0 */HAL_TIM_IRQHandler(&htim4);/* USER CODE BEGIN TIM4_IRQn 1 */HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET);/* USER CODE END TIM4_IRQn 1 */}Code:void ADC_Conversion_of_Voltage_Transients(){HAL_SPI_Receive(&hspi4,ADC_Buf,16,100);Sample = (((uint16_t) ADC_Buf) << 8 | ADC_Buf) & 0x0FFF;volt = (float)(Sample * (5.0 / 4096.0)); //testdata_in[i++]=volt;i %= 16;HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET);}Code:while(1){ADC_Conversion_of_Voltage_Transients();}

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 11, 2019, 01:22:16 pm
content: This time you removed the "flag" mechanism, making this unsynchronized...You still need the flag.(You could also issue the whole transfer from the IRQ handler, but it's usually not recommended to spend too much time inside IRQ handlers, so the flag mechanism is better.)Last note: don't forget to declare the flag global variable with a "volatile" qualifier; otherwise, it may not work as expected with compiler optimizations. This is a very common issue and has been discussed here many times.

author: A.Ersoz
author_rank: Regular Contributor
timestamp: October 11, 2019, 04:32:45 pm
content: Actually, I didn't get SCK, CS, and SDO signals. I defined the Timer_Flag as a global variable and connect to the main function with 'extern'.Code:void TIM4_IRQHandler(void){/* USER CODE BEGIN TIM4_IRQn 0 *//* USER CODE END TIM4_IRQn 0 */HAL_TIM_IRQHandler(&htim4);/* USER CODE BEGIN TIM4_IRQn 1 */HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET);Timer_Flag=1;/* USER CODE END TIM4_IRQn 1 */}Code:void ADC_Conversion_of_Voltage_Transients(){HAL_SPI_Receive(&hspi4,ADC_Buf,2,100);HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET);Sample = (((uint16_t) ADC_Buf) << 8 | ADC_Buf) & 0x0FFF;volt = (float)(Sample * (5.0 / 4096.0)); //testdata_in[i++]=volt;i %= 16;}Code:if(Timer_Flag){ADC_Conversion_of_Voltage_Transients();Timer_Flag=0;}