2026-03-13 9:58 PM - last edited on 2026-03-14 2:41 AM by Andrew Neil
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)
{
}
}
#endifadc.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;
}
Solved! Go to Solution.
2026-03-14 9:46 AM
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
2026-03-14 2:10 AM
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.
2026-03-14 2:52 AM
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);
2026-03-14 4:03 AM
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);
2026-03-14 4:08 AM
Against "sprintf" compiler showing error "#error cpstm8 main.c:37(7) missing prototype"
2026-03-14 7:07 AM
This is part of the C programming language. Add this:
#include <stdio.h>
2026-03-14 9:46 AM
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
2026-03-15 12:30 AM
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.
2026-03-15 3:06 AM
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.
2026-03-15 5:00 AM
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);