Skip to main content
Bicer
Associate II
August 8, 2021
Question

Stm32f103 adc stable conversion

  • August 8, 2021
  • 3 replies
  • 2009 views

I am making a ntc thermometer with stm32f103c8 (by writing over registers without using state library). The code works as it is,I did the adc conversion in 7.5 cycles. I used the calibration register and ran the adc in continuous conversion mode. but it is not fully stable, the value in the last step is constantly changing. How can I make adc more stable?.I want the adc value to stay as constant as possible (I used a 10k ntc between 3.3v and a2 pin. I connected a 10k resistor between gnd and a2 pin ).

#include "stm32f10x.h" // Device header
#include "math.h"
float heat = 0;
char analog[10];
char analog2[10];
int temp;
int a;
void int2char(int num, char str[])
{
 char lstr[30];
 int cnt = 0;
 int div = 10;
 int j = 0;
 
 
 
while( num >= div)
{
 lstr[cnt] = num % div + 0x30;
 num /= 10;
 cnt++;
}
 lstr[cnt] = num + 0x30;
for(j= cnt ; j >=0;j--)
{
 str[cnt-j] = lstr[j];
}
 
 
}
 
void DelayUs(unsigned long t)
{
 for(;t>0;t--)
 {
 SysTick->LOAD = 72;
 SysTick->VAL = 0;
 while((SysTick->CTRL & 0x00010000) == 0);
 }
}
 
 
void lcd_cmd(unsigned char data)
{
 //pin_output(0,11);
 GPIOA->ODR &=~0x100;//lcd_rs(LOW);
 GPIOA->ODR &=~0x200;//lcd_rw(LOW);
 DelayUs(10);
 GPIOA->ODR |=0x400;//lcd_e(HIGH);
 DelayUs(5);
 GPIOA->ODR &= 0xff0f;
 GPIOA->ODR |= (data & 0x00f0);
 DelayUs(10);
 GPIOA->ODR &=~0x400;//lcd_e(LOW);
 
 DelayUs(20);
 
 GPIOA->ODR |=0x400;//lcd_e(HIGH);
 DelayUs(5);
 GPIOA->ODR &= 0xff0f;
 GPIOA->ODR |= ((data << 4) & 0x00f0);
 DelayUs(10);
 GPIOA->ODR &=~0x400;//lcd_e(LOW);
}
 
void lcd_data(unsigned char data)
{
 //pin_output(4,7);
 GPIOA->ODR |=0x100;//lcd_rs(HIGH);
 GPIOA->ODR &=~0x200;//lcd_rw(LOW);
 DelayUs(10);
 GPIOA->ODR |=0x400;//lcd_e(HIGH);
 DelayUs(5);
 GPIOA->ODR &= 0xff0f;
 GPIOA->ODR |= (data & 0x00f0);
 DelayUs(10);
 GPIOA->ODR &=~0x400;//lcd_e(LOW);
 
 DelayUs(20);
 
 GPIOA->ODR |=0x400;//lcd_e(HIGH)
 DelayUs(5);
 GPIOA->ODR &= 0xff0f;
 GPIOA->ODR |= ((data << 4) & 0x00f0);
 DelayUs(10);
 GPIOA->ODR &=~0x400;//lcd_e(LOW)
}
 
void lcd_send( char str[])
{
 int i = 0;
 while(str[i])
 {
 lcd_data(str[i]);
 i++;
 DelayUs(100);
 }
 
}
 
 
void lcd_msg(unsigned char line_1_2, unsigned char pos_0_16, char msg[])
{
 short pos = 0;
 if(line_1_2==1)
 {
 pos = 0;
 }
 else if(line_1_2==2)
 {
 pos = 0x40;
 }
 lcd_cmd(0x80 +pos + pos_0_16);
 DelayUs(100);
 lcd_send(msg);
}
 
 
 
int main(void)
{
 // initialize the delay function (Must initialize)
 SysTick->CTRL = 0;
 SysTick->LOAD = 0x00FFFFFF;
 SysTick->VAL = 0;
 SysTick->CTRL = 5;
 
 
 //pin_output(4,11);
 RCC->APB2ENR = 0x04; /// port a active
 GPIOA->CRL = 0x33330000; /// pin a4-a7 
 GPIOA->CRH = 0x00003333; /// pin a8-a11
 
 DelayUs(20000);
 GPIOA->ODR |=0x100;//lcd_rs(LOW);
 GPIOA->ODR |=0x200;//lcd_rw(LOW);
 DelayUs(10);
 GPIOA->ODR |=0x400;//lcd_e(HIGH);
 DelayUs(5);
 GPIOA->ODR &= 0xff0f;
 GPIOA->ODR |= 0x30; // 8 bit communication mode 
 DelayUs(10);
 GPIOA->ODR &=~0x400;//lcd_e(LOW);
 
 DelayUs(20000);
 
 
 GPIOA->ODR &=~0x100;//lcd_rs(LOW);
 GPIOA->ODR &=~0x200;//lcd_rw(LOW);
 DelayUs(10);
 GPIOA->ODR |=0x400;//lcd_e(HIGH);
 DelayUs(5);
 GPIOA->ODR &= 0xff0f;
 GPIOA->ODR |= 0x20; // 4 bit communication mode 
 DelayUs(10);
 GPIOA->ODR &=~0x400;//lcd_e(LOW);
 
 
 lcd_cmd(0x2C); // 4 bit communication mode / 2 lines
 DelayUs(5000);
 lcd_cmd(0x0C); // Display ON
 DelayUs(5000);
 lcd_cmd(0x01); // Clear Display
 DelayUs(5000);
 lcd_cmd(0x02); // Get back to initial address
 DelayUs(5000);
 
 lcd_msg(1, 0,"Temp(C)");
 RCC->APB2ENR |= 0x201; //ADC 1 interface clock enable
 ADC1->SQR3 = 2; //first transformation in regular order 
 ADC1->SQR1 &= ~(0xf << 20);//adc channel string length 
 ADC1->SMPR2 |= (0x1); //7.5 loop
 //12mhz: 7.5+12.5 =20 hz ->3.3us(micro s) 
 ADC1->CR2 &= ~(1 << 11); // right alignment 
 ADC1->CR2 |= 2|1; //adc on and continuous
 ADC1->CR2 |= (1<<3);// reset calibration 
 ADC1->CR2 |= (1<<2);// calibration 
 ADC1->CR2 |=1; //adc on 
 while(1)
 {
 heat = log(((40950000 / ADC1->DR) - 10000));
 heat = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * heat * heat)) * heat);
 temp = (heat - 273.15)*100; 
 int2char(temp,analog);
 lcd_msg(2, 2,analog);
 if(temp<1000)
 {
 a=3; 
 }
 else if(temp<10000)
 {
 a=4;
 }
 else
 {
 a=5;
 } 
 temp=temp%100;
 int2char(temp,analog2);
 lcd_msg(2, a,".");
 lcd_msg(2, a+1,analog2);
 lcd_msg(2, a+3," ");
 DelayUs(250000);
 }
 
}

This topic has been closed for replies.

3 replies

TDK
August 8, 2021

10kOhm is a relatively high resistance. You're going to have some noise in there. The biggest and easiest improvement will probably be maxing out the sampling time. Temperature doesn't change quickly, no need to limit it to 7.5 cycles. You could add a bypass cap to give the circuit some capacitance so the switching doesn't affect things as much.

Some more general tips here:

https://www.st.com/resource/en/application_note/cd00211314-how-to-get-the-best-adc-accuracy-in-stm32-microcontrollers-stmicroelectronics.pdf

Edit: also note that taking a longer term averages are how temperature measurements are typically made rather than relying on a single sample.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Bicer
BicerAuthor
Associate II
August 8, 2021

Thanks for your advice. I added a bypass capacitor to the circuit and set the sampling rate to maximum, it got a little better. Do you have a software solution suggestion?

TDK
August 8, 2021

Take many samples and use the average.

"If you feel a post has answered your question, please click ""Accept as Solution""."