Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

12 Bit ADC with interfacing SPI in STM32

Status
Not open for further replies.
Actually, I couldn't read the correct values with 'volt' variables.

I want to share my code, input signal, and ADC signals. I miss something because the 'volt' shows different values than input signal amplitudes.

Function code:

Code:
uint8_t i;
uint16_t Sample;
float volt=0;
uint8_t ADC_Buf[2]; 
float testdata_in[16];

uint8_t spidata[2];


void ADC_Conversion_of_Voltage_Transients()
{
		spidata[0]=0x00;       // Set everything up  before you start the exchange
		spidata[1]=0x00;
		HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET);
		HAL_SPI_TransmitReceive(&hspi4,spidata,ADC_Buf,1,100);   // See below
		HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET);
		Sample = (((uint16_t) ADC_Buf[1]) << 8 | ADC_Buf[0]);
		volt = (float)(Sample * (5.0 / 4096.0)); //
		testdata_in[i++]=volt;
		i %= 16;
	
}


ADC SCK (green plot), ADC CS (yellow plot), and ADC SDO (blue plot)

IMG_5727.jpg


Input signal is
IMG_5729.jpg

The 'volt' variable result is
IMG_5730.jpg
 

Attachments

  • IMG_5728.jpg
    IMG_5728.jpg
    511.3 KB · Views: 95

Hi,

From post to post you change multiple things but don´t show us. This makes it really hard to find out what you try to do.

* Now the SCK frequency is about 16MHz. This should be OK.
* But again you work with 5V ADC (data signal voltage). Tis probably violates STM input specifications.
* We can´t find out your sample rate

Btw: Do you recognize how slow the HAL lib works: Just setting NSS LOW takes longer than the complete ADConversion and data transmision. And taking NSS HIGH is really booring slow. It takes three sampling times.

***Scope pictures:
* If you want to verify sampling rate you need a scope view with at least twho falling NSS edges.
* If you want to do a raw chekc on ADC signals your upper scope picture of post#21 is good
* if you want to verify data, then you need a more detailed view with focus on all the clock edges and data

****
Every time when you modify the setup, then please show us the setup code.

****
The yellow signal of the second scope view:
I still don´t know what to do with this. I don´t know if you think this signal is correct or not. ..from timing, waveform, noise and voltage levels.
Please give more detailed information.

****
I miss something because the 'volt' shows different values than input signal amplitudes.
Please tell us what "volt levels" you expect. In numbers. Best if you can give a range.

And .. since the conversion result depends on ADC_VCC.... please measure it with a DVM when ADC is running. Also verify that the VCC signal is very clean during vonversion.

Klaus
 

Let's finalize this post.

I isolate the analog input signal of ADC from the circuit, by then I connect this pin to the function generator output which generates 1kHz with 520mV peak to peak sine wave signal. I also changed supply voltage of ADC to 3.3V.

IMG_5733.jpg

I get the plot for ADC SCK (yellow plot), ADC CS (green plot) and ADC SDO (blue plot).

IMG_5734.jpg

Please tell us what "volt levels" you expect. In numbers. Best if you can give a range.
I expect that the values of 'volt' change between 0 and 0.52V. But in Keil debug mode, the 'volt' values in the 'watch window' is different than this values.

test out 10 11 19.PNG

Here is my code:

Code:
void ADC_Conversion_of_Voltage_Transients()
{

		
		spidata[0]=0x00;       // Set everything up  before you start the exchange
		spidata[1]=0x00;
		HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET);
		HAL_SPI_TransmitReceive(&hspi4,spidata,ADC_Buf,2,100);   // See below
		HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET);
		Sample = (((uint16_t) ADC_Buf[1]) << 8 | ADC_Buf[0]) & 0x0FFF;
		volt = (float)(Sample * (5.0 / 4096.0)); //
		testdata_in[i++]=volt;
		i %= 16;
}


SPI settings:

Code:
/* SPI4 init function */
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_2EDGE;
  hspi4.Init.NSS = SPI_NSS_SOFT;
  hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  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__);
  }

}

I try to implement NSS enable with Hardware output. But I didn't observe any signal at ADC CS. IF you refer any source, I will definitely implement it.

Thank you
 

Hi,

Important: if you choose the ADC supply voltage to 3.3V, then you need to change your mathematics, too:
--> volt = (float)(Sample * (3.3 / 4096.0)); //

I'm no friend of guessing. Now with none of your scope pictures I can verify binary code of the ADC.
Thus I can't be sure at which clock edge tge data transfer occurs.
Change the following line to react on first clock edge (as already suggested in my first post)
--> hspi4.Init.CLKPhase = SPI_PHASE_2EDGE;

********
I try to implement NSS enable with Hardware output. But I didn't observe any signal at ADC CS. IF you refer any source, I will definitely implement it.
I don't see how you did it...thus I have to guess:
--> Did you change to following line from SOFT(-ware controlled)to HARD(-ware controlled)? (As suggested in my first post)
--> Did you connect a pullup to the NSS line? (As suggested in my first post)

Klaus
 

--> Did you change to following line from SOFT(-ware controlled)to HARD(-ware controlled)? (As suggested in my first post)
--> Did you connect a pullup to the NSS line? (As suggested in my first post)

Yes I enabled NSS with Hardware output in CubeMX and pulled-up CS pin.

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_HARD_OUTPUT;
  hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  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__);
  }

}


spi push pull.PNG

Code:
void ADC_Conversion_of_Voltage_Transients()
{
		spidata[0]=0x00;       // Set everything up  before you start the exchange
		spidata[1]=0x00;
	
		HAL_SPI_TransmitReceive(&hspi4,spidata,ADC_Buf,1,100);   // See below
		
		Sample = (((uint16_t) ADC_Buf[1]) << 8 | ADC_Buf[0]) & 0x0FFF;
		volt = (float)(Sample * (3.3 / 4096.0)); //
		testdata_in[i++]=volt;
		i %= 16;

I also updated the formula. When I run with NSS enabled with Hardware output, ADC CS keeps every time in reset position.
 

Hi,

Yes I enabled NSS with Hardware output in CubeMX and pulled-up CS pin.
You need to externally solder a pull up resistor.
(Enabling pull up with CubeMX is "internal" .... which I think does not work)

Klaus
 

You need to externally solder a pull up resistor.

For make more clarification, I drew the existing connection between the stm32 and ADC. I highlighted the pull up resistor. Do you think its true configuration and resistor value? Now the SPI works full duplex master mode so is it also true?

spi.png
 

Hi,

1k seems to be OK.

But in detail it depends in:
* expected speed
* drive strength of the output pin
* signal trace length, connected devices (= capacitance)

The good thing is:
* the timing critical signal is the falling edge
* the falling edge mainly is determined by the drive strength

Check it with a scope.
If the timing is OK, then it will be even better without the scope probe.
* falling edge
* low level
* rising edge

Now the SPI works ....
The receiving values are OK?

full duplex master mode so is it also true?
It does not depend on pull up resistor, it just depends on setup in your software.

****
To save processing power and/or increase sample rate I recommend to use DMA.

Klaus
 

Thank you, Klaus!

But in detail it depends in:
* expected speed
* drive strength of the output pin
* signal trace length, connected devices (= capacitance)
Just my curiosity, could you briefly describe this part of your message?

I capture the ADC SCK (green), ADC SDO (blue), and ADC CS (yellow) signals. I measured the rising and falling edge for CS signal.

test sck(gree) sdo(blue) and CS (yellow) with rising and falling edge 2019-10-15 12-17-52 0.png

For calculating sampling rate, I added the figure with multiple CS and SCK signal together.

test sck(gree) sdo(blue) and CS (yellow) with freq and period 2019-10-15 12-24-44 0.png

So, based on the graphics, the conversion time (tconv) is 1.2us. The sampling rate is (13.5Mbits/s)/16=843.75ksample/s. So these calculations are true?

The conversion works successfully for a step signal input. I fed 2.6V and I observe 2.57V from 'volt' values during the debug session. Now I will try sine wave and then see it. I will update the post with the results what I get.

test volt 10 15 19.PNG
 
Last edited:

I fed the my main signal to the ADC input. I observe the conversion of the signal of mid level (1.95V) as 'volt' value in the debug session. I don't know how long should I wait to see low level (1.628V) and high level (2.288V) of the signal in the watch window as output of the conversion in 'volt' variable?

Actually, I place two breakpoints on the sequential commands in the while loop. Then I debug it and record each break point debug session time.

time.PNG

I highlighted the time value where I recorded it. Then I subtracted between of them. So I get 1 ms difference. Then, There are 9 functions in the while loop and total numbers of line from these 9 functions is 299. So 299x1ms= 300ms. It means that I should wait 300ms for each sampling?

This is the input signal.

input signal 10 15 19 2019-10-15 14-24-39 0.png
 

Hi,

Your scope picture (especially the upper one of post#29) is awful. Don't you recognize that the sampling rate is much too low for your signals. The SCK looks far away from being a square wave. You can't measure rise and falltime this way.

So, based on the graphics, the conversion time (tconv) is 1.2us. The sampling rate is (13.5Mbits/s)/16=843.75ksample/s. So these calculations are true?
Although I can't follow your idea, I can say it is completely wrong.
The sampling period is somewhere below 600us.
You can simply read the sampling frequency in the scope picture: 1.7311kHz.

Just my curiosity, could you briefly describe this part of your message?
Simply every piece of trace is a capacitance, every input is a capacitance...now you want a to charge/discharge this capacitance.
It needs to be fast enough to meet the specified timing of the ADC.
But the faster ... the more current you need. The current has to be delivered by the pullup (rising edge) or the I/O driver (falling edge).

Klaus

- - - Updated - - -

Hi,

I don't know how long should I wait to see
Sorry, I really don't understand what you tell in post#30.

You ask: "how long should I wait"...
In my eyes it's currently much too slow, you need to get faster rather than slower.

Klaus
 

Hello Klaus,

You can simply read the sampling frequency in the scope picture: 1.7311kHz.
How can I change it?

The SCK looks far away from being a square wave
If I set the SPI baudrate as around 1Mbits/s, the clock signal shape looks rectangular.

Thank you,
 

Hi,

How can I change it?
read my previous posts

If I set the SPI baudrate as around 1Mbits/s, the clock signal shape looks rectangular.
that's the wrong way round

Klaus
 

Hi,

read my previous posts
--> /CS = NSS needs to toggle!

So, I set a timer (TIM4) with internal clock in interrupt mode. I verified it with one of the GPIO pin (TIM_CRTL) which has 10kHz. Then I set a flag which will be '1' when the interrupt happens. I placed this flag in to while loop during calling the conversion function.

Calling function:

Code:
while(1)
{

if(Timer_Flag)
		{
			ADC_Conversion_of_Voltage_Transients();
			Timer_Flag=0;
		}
}

Main function:

Code:
void ADC_Conversion_of_Voltage_Transients()
{
		spidata[0]=0x00;       // Set everything up  before you start the exchange
		spidata[1]=0x00;
		HAL_SPI_TransmitReceive(&hspi4,ADC_Buf,ADC_Buf,1,100);   // See below
		Sample = (((uint16_t) ADC_Buf[1]) << 8 | ADC_Buf[0]) & 0x0FFF;
		volt = (float)(Sample * (3.3 / 4095.0)); //
		testdata_in[i++]=volt;
		i %= 16;
}



Timer IRQ:

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(TIM_CRTL_GPIO_Port, TIM_CRTL_Pin);
	Timer_Flag=1;
	
  /* USER CODE END TIM4_IRQn 1 */
}

But the sampling frequency did not change.
 

Hi,

Please read all my posts...especially those with "sampling rate" and what is meant with "toggle NSS".
It's all written already.

Klaus
 

Hi,

I am sorry I couldn't catch it. If you explain again, I will happy with that. Otherwise, thank you very much so far for your help!

Thank you,
 

Hi Klaus,

It depends on how often you toggle /CS .... and read the conversion data.
But hopefully you don't try to use te run time of the main loop for sampling timing control.
--> You need at least an Interrupt with fixed, known timing. Or let the hardware (timer) directly start the conversion (without tge need fir an ISR)
If you are running low on processing power, then I recommend to use SPI in DMA mode.

If you want a 10kHz sampling rate, then you need a falling edge every 100us.
.. but for sure after the falling /CS edge you need to go on according interface specification:
At least 16 SCK cycles to perform the data transfer.
And after this you need to set /CS high.

Today, I try again to figure out the sampling rate based on your comments. These are all sampling rate related comments of yours. So based on that I would use SPI DMA mode instead of timer interrupt for setting sampling frequency?
 

So based on that I would use SPI DMA mode instead of timer interrupt for setting sampling frequency?
I think you are getting a number of concepts mixed up here.

1) Sampling rate: You will need a timer to reliably trigger the ADC sampling at a regular interval.
The way to tell the ADC to start sampling is to start an SPI exchange - and that means lowering the \CS line.

2) SPI clock rate: how fast the MCU exchanges values with the ADC via the SPI interface
Once you lower the \CS line, you need to perform a 16-bit value exchange and that means setting up the SPI module to exchange a single 16-bit value. Once the SPI module has been triggered, it will then send the 16 SCK pulses. Once the value has been exchanged, you need to raise the \CS line

3) Possible interactions between 1) and 2): how to coordinate the \CS line transitions and the SPI exchange
There are two ways you can achieve this coordination (which is necessary to correctly get the ADC value). Firstly you can control the \CS line yourself - that means in your code you:
- lower the \CS line
- start the SPI exchange
- wait for the SPI exchange to complete
- raise the \CS line
The alternative way is to configure the SPI module to control the NSS signal. The SPI module uses the term NSS (Negative Slave Select) but it effectively means the same as the \CS (Negated Chip Select) name we have been using. IN both cases the 'Negative' refers to the fact that you take to line low (logical 0) when it is active.
I don't recommend the alternative way at this stage - you seem to have enough trouble with the basics of SPI and the operation of the ADC and the ST implementation of the NSS signal can (in my experience) be a little tricky to get right. I mention it only so you know what the NSS signal that is mentioned in the HAL is and how to set it correctly - come back to this later once everything else is working.

4) Saving the value: use the value you have received from the ADC
Especially if you are controlling the \CS line yourself (as indicated in the code snippets you have provided), then it is easy to know when the exchange has completed so that you can read the value from the SPI module that has just been received. Basically this is what you have been doing in your code so far.
The alternative is to use DMA which is hardware built into the MCU that transfers the value from the SPI module (in this case) to an array in memory for you. The big advantage of using DMA is that it does not use the main CPU so that your code can be doing something else while the hardware is handling the ADC data.
You can set up the DMA to set a flag/trigger an interrupt when it has transferred a set number of values for you. Only then do you need to look at the values in the array and do something with them.

Now, the DMA *can* do something else for you as well and this is where some of the confusion about the sample timing can come in. The DMA controller can also be set up to trigger the SPI module to stat an exchange. There are a couple if things you need to understand about this way of working. The first is that you MUST set up the SPI module to control the NSS single itself as the hardware has to do everything - no software is involved and so you cannot control the \CS line yourself.
The other is how the DMA controller knows to trigger the next transfer. Typically the DMA controller is used to transfer values from one place to another as quickly as possible which means that it will normally trigger the next transfer as soon as the last one finishes. In your case this is NOT what you want as you want the ADC to be triggered by the SPI exchange at a specific interval and not just as fast as it can possibly go.
I've not looked in detail at the DMA controller on your MCU but sometimes you can set the DMA controller to be triggered by some external source - such as your timer.
I've left this part to last because, while it is all possible, I would strongly recommend that you get the basics of communicating with the ADC running in code first. Once that is working then you can build up the rest of the application code. If/When you are dong that, you find that there is too much else going on that upsets the proper timing of the ADC reads, then you can (step by step) mode to a more hardware-managed way of doing things.
Susan
 

Hi,

Maybe it's time to start from the beginning ...
An engineer should start with specifying the requirements (forget about all the ADC and SPI stuff)

You show several different input signals. They very widely in repeating frequency (at least 900Hz up to 14kHz)
Now you need to know that this repeating frequency is also called "fundamental frequency".
But regarding nyquist (and sampling frequency) this fundamental frequency is not what you need to focus on.
You need to focus on the overtones.
You - as the designer - need to decide up to which overtone you are interested in.
Mainly this depends on what information of the signal you are interested in.
Informations can be:
* average voltage
* RMS voltage
* minimum / maximum (@ which time resolution)
* rise / fall time
* SNR
* frequency analysis
* scope like picture
* or many other..

And then (still before thinking about ADC and SPI) you need to install a suitable low pass filter, also called "anti aliasing filter" to suppress the unwanted high frequencies.
If you don't suppress them they will cause artefacts on the digital values that can't be safely filtered out....causing fluctuations in your calculated output value. Your result becomes unreliable.

I assume you need at least a sampling frequency of 15 x fundamental frequency, but maybe even 100 x fundamental frequency is too low, if you are interested in the
* HF ringing (shown in the scope pictures) or
* rise/fall rates
* SNR
* peak values
...

Btw: most (all?) STM microcontrollers have built in 12 bit ADCs. Why don't you use them?

Klaus
 

1MHz Timer Settings at STM32F767

Hello,

I want to setup a 1MHz timer with STM32F767. I picked TIM4 (APB1 Timer Clock=108MHz; APB1 Peripheral Clock=54MHz) for this purpose, based on my calculation prescaler should be 108-1, and a counter period 2-1. But I get maximum 113.64kHz. So, does anyone have an idea to set as 1MHz?

Thank you,

Code:
/* TIM4 init function */
static void MX_TIM4_Init(void)
{

  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;

  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 108-1;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 10-1;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top