cancel
Showing results for 
Search instead for 
Did you mean: 

STM8: Problem displaying ADC value

ankhola
Associate III

Hi Guys,

Based on some tutorials, I am trying to execute ADC module of STM8S105 and display the ADC result in 16x2 LCD display. The problem is : thousandth digit place is showing alphabetic character right from "A"  when I increase voltage on ADC channel from 0. Please see my code as well as adc.h file. I am eagerly awaiting someone to suggest where I have made the mistake.

 

 #define LCD_RS     GPIOD, GPIO_PIN_2
 #define LCD_EN     GPIOD, GPIO_PIN_3
 #define LCD_DB4    GPIOD, GPIO_PIN_4
 #define LCD_DB5    GPIOD, GPIO_PIN_5
 #define LCD_DB6    GPIOD, GPIO_PIN_6
 #define LCD_DB7    GPIOD, GPIO_PIN_7
 
 #include "STM8S.h"
 #include "lcd.h"
#include "adc1.h"
main()
{
//Variable declarations

unsigned int test_var ;
char d4,d3,d2,d1;
gpio_adc_init();
adc_init();
Lcd_Begin();
Lcd_Clear();
Lcd_Set_Cursor(1,1);
Lcd_Print_String("STM8S103F3P3 LCD");
delay_ms(5000);
Lcd_Clear();
Lcd_Set_Cursor(1,1);
Lcd_Print_String("Circuit Digest");
Lcd_Set_Cursor(2,1);
Lcd_Print_String("Test: ");
while (1)


{ 
test_var = read_adc_value();
d4 = test_var%10 + '0';
d3 = (test_var/10)%10 + '0';
d2 = (test_var/100)%10 + '0';
d1 = (test_var/1000) + '0';
Lcd_Set_Cursor(2,6);
Lcd_Print_Char(d1);
Lcd_Print_Char(d2);
Lcd_Print_Char(d3);
Lcd_Print_Char(d4);
delay_ms(1000);
//test_var++;
}
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
  while (1)
  {
  }
}
#endif

adc.h

 

uint16_t value;

void gpio_adc_init(void) {
	CLK_PeripheralClockConfig(CLK_PERIPHERAL_ADC, ENABLE);
	GPIO_Init(GPIOB, GPIO_PIN_4, GPIO_MODE_IN_FL_NO_IT);
			}
										
void adc_init(void)   {												
ADC1 -> CSR = 0x04;
ADC1 -> CR1 =  0x40;
ADC1 -> CR2 = 0x00;
ADC1 -> TDRH = 0xFF;
ADC1 -> TDRL = 0xFF;
ADC1 -> CR1 |= ADC1_CR1_ADON;
			 }
						  
										
uint16_t read_adc_value(void) {
    // Start the conversion process
    ADC1_StartConversion(); 

    // Wait until the end of conversion (EOC) flag is set
    while(ADC1_GetFlagStatus(ADC1_FLAG_EOC) == FALSE);

    // Read the 10-bit value (clears the EOC flag automatically when the data registers are accessed correctly)
    value = ADC1_GetConversionValue(); 

    ADC1_ClearFlag(ADC1_FLAG_EOC); // Explicitly clear the flag if needed, depending on read method and mode

    return value;
}
1 ACCEPTED SOLUTION

Accepted Solutions
Peter BENSCH
ST Employee

For testing, the suggestion of @AA1 is very good, but it has one disadvantage: functions like printf, snprintf, etc., occupy quite a lot of flash memory, which is usually quite limited in an STM8S105, which only provides 16KB or 32KB. In this respect, the original version by @ankhola is more resource-efficient, it just needs to be adapted to work.

First of all, @AA1 correctly pointed out that the data alignment is left by default. However, it can be very easily changed via CR2:

ADC1->CR2 = 0x08; // RIGHT alignment (ALIGN = 1)

Furthermore, you should include a limit for test_var in your code, e.g. 9999:

while (1)
{
    uint16_t test_var = read_adc_value();

    if (test_var > 9999) { test_var = 9999; } // protection optional

    char d4 = (test_var % 10)       + '0';
    char d3 = (test_var / 10  % 10) + '0';
    char d2 = (test_var / 100 % 10) + '0';
    char d1 = (test_var / 1000)     + '0';

    Lcd_Set_Cursor(2, 6);
    Lcd_Print_Char(d1);
    Lcd_Print_Char(d2);
    Lcd_Print_Char(d3);
    Lcd_Print_Char(d4);

    delay_ms(1000);
}

With this, your example should already work. However, it still shows leading zeros, which can be removed with an additional function:

void Lcd_Print_Uint(uint16_t value)
{
    char buf[6]; // max 5 digits + '\0'
    int i = 0;

    if (value == 0)
    { buf[i++] = '0'; } // special case 0
    else
    {
        while (value > 0 && i < 5) // store digits from back to front
        {
            buf[i++] = (value % 10) + '0';
            value /= 10;
        }
    }
    buf[i] = '\0';

    for (int j=0; j<i/2; j++) // reverse string because we started from the back
    {
        char tmp = buf[j];
        buf[j] = buf[i-j-1];
        buf[i-j-1] = tmp;
    }

    Lcd_Print_String(buf);
}

You could then use this function with:

while (1)
{
    uint16_t test_var = read_adc_value();

    if (test_var > 9999) { test_var = 9999; } // protection optional

    Lcd_Set_Cursor(2, 6);
    Lcd_Print_Uint(test_var);
    delay_ms(1000);
}

Hope that helps?

Regards
/Peter

 

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

View solution in original post

11 REPLIES 11
AA1
Senior III

Try this:

char str[8];
sprintf(str, "%04u", test_var);
Lcd_Print_String(str);

If the problem continues, the problem should be Data alignment. By default Data alignment is left. Change it to right.

 

ankhola
Associate III

I have inserted the snippet of code just below the "read_adc_value()" function in while loop and deleted the rest. Code did not build. Errors are attached.

 

#error cpstm8 main.c:35(0+4) misplaced local declaration
#error cpstm8 main.c:36(7) missing prototype
#error cpstm8 main.c:37(17+3) incompatible argument type
#error cpstm8 main.c:36(8+3) str undefined

 

while (1)


{ 
test_var = read_adc_value();
char str[8];
sprintf(str, "%04u", test_var);
Lcd_Print_String(str);

Variables definition must be before any other code. Try this:

while (1)
{ 
char str[8];
test_var = read_adc_value();
sprintf(str, "%04u", test_var);
Lcd_Print_String(str);

 

ankhola
Associate III

Against "sprintf"  compiler showing error "#error cpstm8 main.c:37(7) missing prototype"

This is part of the C programming language. Add this:

#include <stdio.h>

 

Peter BENSCH
ST Employee

For testing, the suggestion of @AA1 is very good, but it has one disadvantage: functions like printf, snprintf, etc., occupy quite a lot of flash memory, which is usually quite limited in an STM8S105, which only provides 16KB or 32KB. In this respect, the original version by @ankhola is more resource-efficient, it just needs to be adapted to work.

First of all, @AA1 correctly pointed out that the data alignment is left by default. However, it can be very easily changed via CR2:

ADC1->CR2 = 0x08; // RIGHT alignment (ALIGN = 1)

Furthermore, you should include a limit for test_var in your code, e.g. 9999:

while (1)
{
    uint16_t test_var = read_adc_value();

    if (test_var > 9999) { test_var = 9999; } // protection optional

    char d4 = (test_var % 10)       + '0';
    char d3 = (test_var / 10  % 10) + '0';
    char d2 = (test_var / 100 % 10) + '0';
    char d1 = (test_var / 1000)     + '0';

    Lcd_Set_Cursor(2, 6);
    Lcd_Print_Char(d1);
    Lcd_Print_Char(d2);
    Lcd_Print_Char(d3);
    Lcd_Print_Char(d4);

    delay_ms(1000);
}

With this, your example should already work. However, it still shows leading zeros, which can be removed with an additional function:

void Lcd_Print_Uint(uint16_t value)
{
    char buf[6]; // max 5 digits + '\0'
    int i = 0;

    if (value == 0)
    { buf[i++] = '0'; } // special case 0
    else
    {
        while (value > 0 && i < 5) // store digits from back to front
        {
            buf[i++] = (value % 10) + '0';
            value /= 10;
        }
    }
    buf[i] = '\0';

    for (int j=0; j<i/2; j++) // reverse string because we started from the back
    {
        char tmp = buf[j];
        buf[j] = buf[i-j-1];
        buf[i-j-1] = tmp;
    }

    Lcd_Print_String(buf);
}

You could then use this function with:

while (1)
{
    uint16_t test_var = read_adc_value();

    if (test_var > 9999) { test_var = 9999; } // protection optional

    Lcd_Set_Cursor(2, 6);
    Lcd_Print_Uint(test_var);
    delay_ms(1000);
}

Hope that helps?

Regards
/Peter

 

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
ankhola
Associate III

Thanks both of you. Right aligning the ADC result, the problem is over. As stated by Peter, "However, it still shows leading zeros", it is mentioned that the leading character, i.e. thousandth digit was showing alphabets starting from "A" to special characters. I could not understand this weird behavior.

 

Regards. 

Your last comment with the strange characters refers to the previous case with left-aligned results, right?

Well, just imagine that you’re getting results in the upper range. Then you work out the corresponding value, e.g. 0x3ff = 1023, look at section 24.8. data alignment in RM0016 and work out what d1 gives in your calculation. And finally, you check the character table for the 16x2 LCD to see what is displayed with that hex value.

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
ankhola
Associate III

Thanks Peter. I got the point. actually I thought that whatever alignment settings is configured in code, it will be handled by peripheral library function which shows as follows. However, I shall take the liberty to ask another query to you. While printing the value (d1 to d4), Cursor is set once. But the column is advancing as usual. How it is advancing?

 

Regards.

 

Library Function:

uint16_t ADC1_GetConversionValue(void)
{
  uint16_t temph = 0;
  uint8_t templ = 0;
  
  if ((ADC1->CR2 & ADC1_CR2_ALIGN) != 0) /* Right alignment */
  {
    /* Read LSB first */
    templ = ADC1->DRL;
    /* Then read MSB */
    temph = ADC1->DRH;
    
    temph = (uint16_t)(templ | (uint16_t)(temph << (uint8_t)8));
  }
  else /* Left alignment */
  {
    /* Read MSB first*/
    temph = ADC1->DRH;
    /* Then read LSB */
    templ = ADC1->DRL;
    
    temph = (uint16_t)((uint16_t)((uint16_t)templ << 6) | (uint16_t)((uint16_t)temph << 8));
  }
  
  return ((uint16_t)temph);
}

 

My Query:

 Lcd_Set_Cursor(2, 6);
    Lcd_Print_Char(d1);
    Lcd_Print_Char(d2);
    Lcd_Print_Char(d3);
    Lcd_Print_Char(d4);