Starting STM8 Microcontrollers

STM8S003K3 Discovery

Analog-to-Digital Converter (ADC)

ADC is a very important peripheral in any modern-day microcontroller. It is used to read analogue outputs from sensors, sense voltage levels and so on. For example, we can use an ADC to read a LM35 temperature sensor. The voltage output from the sensor is proportional to temperature and so we can use the voltage info to back-calculate temperature. STM8S003K3 has four ADC channels associated with one ADC block. Other STM8 micros have more ADC channels and ADC blocks. The ADC of STM8 micros is just as same as the ADCs of other micros. There are a few additional features. Shown below is the block diagram of the STM8’s ADC peripheral:

Block Diagram

A few things must be noted before using the ADC. These enhance performance significantly:

  • Input impedance should be less than 10kΩ.
  • It is better to keep ADC clock within or less than 4MHz.
  • Schmitt triggers must be disabled.
  • Opamp-based input buffer and filter circuits are preferred if possible.
  • If the ADC has reference source pins, they should be connected to a precision reference source like LM336. It is recommended to use a good LDO regulator chip otherwise.
  • Unused ADC pins should not be configured or disabled. This will reduce power consumption.
  • Rather taking single samples, ADC readings should be sampled at fixed regular intervals and averaged to get rid of minute fluctuations in readings.
  • Right-justified data alignment should be used as it is most convenient to use.
  • PCB/wire tracks leading to ADC channels must be short to reduce interference effects.

 

Hardware Connection

ADC_CubeMX

Code Example

#include "STM8S.h"
 
 
void clock_setup(void);
void GPIO_setup(void);
void ADC1_setup(void);
void lcd_print(unsigned char x_pos, unsigned char y_pos, unsigned int value);
 
 
void main()
{
   unsigned int A0 = 0x0000;
                
   clock_setup();
   GPIO_setup();
   ADC1_setup();
                
   LCD_init();  
   LCD_clear_home(); 
                
   LCD_goto(0, 0);
   LCD_putstr("STM8 ADC");
   LCD_goto(0, 1);
   LCD_putstr("A0");
                
   while(TRUE)
   {
       ADC1_StartConversion();
       while(ADC1_GetFlagStatus(ADC1_FLAG_EOC) == FALSE);
                                
       A0 = ADC1_GetConversionValue();
       ADC1_ClearFlag(ADC1_FLAG_EOC);
                                
       lcd_print(4, 1, A0);
       delay_ms(90);
   };
}
 
 
void clock_setup(void)
{
   CLK_DeInit();
                
   CLK_HSECmd(DISABLE);
   CLK_LSICmd(DISABLE);
   CLK_HSICmd(ENABLE);
   while(CLK_GetFlagStatus(CLK_FLAG_HSIRDY) == FALSE);
                
   CLK_ClockSwitchCmd(ENABLE);
   CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV2);
   CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV4);
                
   CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSI, 
   DISABLE, CLK_CURRENTCLOCKSTATE_ENABLE);
                
   CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, DISABLE);
   CLK_PeripheralClockConfig(CLK_PERIPHERAL_I2C, DISABLE);
   CLK_PeripheralClockConfig(CLK_PERIPHERAL_ADC, ENABLE);
   CLK_PeripheralClockConfig(CLK_PERIPHERAL_AWU, DISABLE);
   CLK_PeripheralClockConfig(CLK_PERIPHERAL_UART1, DISABLE);
   CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER1, DISABLE);
   CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER2, DISABLE);
   CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER4, DISABLE);
}
 
 
void GPIO_setup(void)
{
   GPIO_DeInit(GPIOB);
   GPIO_Init(GPIOB, GPIO_PIN_0, GPIO_MODE_IN_FL_NO_IT);
                
   GPIO_DeInit(GPIOC);
                
   GPIO_DeInit(GPIOD);
   GPIO_Init(GPIOD, GPIO_PIN_3, GPIO_MODE_IN_PU_NO_IT);
}
 
 
void ADC1_setup(void)
{
   ADC1_DeInit();         
                
   ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, 
             ADC1_CHANNEL_0,
             ADC1_PRESSEL_FCPU_D18, 
             ADC1_EXTTRIG_GPIO, 
             DISABLE, 
             ADC1_ALIGN_RIGHT, 
             ADC1_SCHMITTTRIG_CHANNEL0, 
             DISABLE);
                                                                                  
   ADC1_Cmd(ENABLE);
}
 
 
void lcd_print(unsigned char x_pos, unsigned char y_pos, unsigned int value)
{
   char chr = 0x00;
                                
   chr = ((value / 1000) + 0x30);     
   LCD_goto(x_pos, y_pos);
   LCD_putchar(chr); 
                
   chr = (((value / 100) % 10) + 0x30);
   LCD_goto((x_pos + 1), y_pos);
   LCD_putchar(chr); 
                
   chr = (((value / 10) % 10) + 0x30);
   LCD_goto((x_pos + 2), y_pos);
   LCD_putchar(chr); 
                
   chr = ((value % 10) + 0x30);
   LCD_goto((x_pos + 3), y_pos);
   LCD_putchar(chr); 
}

 

Explanation

First, we need to enable the peripheral clock of the ADC module:

CLK_PeripheralClockConfig(CLK_PERIPHERAL_ADC, ENABLE);

Secondly, we have to set out ADC pin as a floating GPIO with no interrupt capability:

GPIO_Init(GPIOB, GPIO_PIN_0, GPIO_MODE_IN_FL_NO_IT);

ADC setup needs a few info regarding the desired ADC channel:

void ADC1_setup(void)
{
   ADC1_DeInit();         
                
   ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, 
             ADC1_CHANNEL_0,
             ADC1_PRESSEL_FCPU_D18, 
             ADC1_EXTTRIG_GPIO, 
             DISABLE, 
             ADC1_ALIGN_RIGHT, 
             ADC1_SCHMITTTRIG_CHANNEL0, 
             DISABLE);
                                                                                  
   ADC1_Cmd(ENABLE);
}

The second line of the above function states that we are going to use ADC channel 0 (PB0) with no Schmitt trigger. We are also not going to use external triggers from timer/GPIO modules. Since the master clock is running at 8MHz, the ADC prescaler divides the master/peripheral clock to get a sampling frequency of 444 kHz. We are also going to use continuous conversion mode because we want to continually read the ADC input and don’t want to measure it in certain intervals. Lastly right-justified data alignment is used as it is easy to read from such.

In the main loop, we need to start ADC conversion and wait for the conversion to complete. We are not using interrupt methods and so we need to poll if ADC conversion has completed. At the end of conversion, we can read the ADC and clear ADC End of Conversion (EOC) flag.

ADC1_StartConversion();
while(ADC1_GetFlagStatus(ADC1_FLAG_EOC) == FALSE);
                                
A0 = ADC1_GetConversionValue();
ADC1_ClearFlag(ADC1_FLAG_EOC);

The rest of the code is about printing the ADC data on a LCD.

 

Demo

ADC

Video link: https://www.youtube.com/watch?v=rx68zPDEZUU

 

Analog Watchdog (AWD)

The AWD is one additional feature that most microcontrollers in the market do not have. AWD is more like a comparator but with the exception that we can set both the upper and lower limits of this comparator as per our requirement unlike fixed levels in other micros. The region between the upper and lower limits is called guarded zone. Beyond the boundaries of the guarded zone, the AWD unit kicks off.

The AWD unit is very useful in situations where we need to monitor the output of a sensor for example and take quick actions. For instance, consider a temperature controller. We would want the controller to turn on a heater should temperature fall below some level and turn it off when temperature rises to some high value without complex calculations and constant monitoring in our application firmware. In other microcontrollers, we would have accomplished this simple task using conditional IF-ELSE statements.

AWD Block Diagram

AWD


Hardware Connection

AWD_CubeMX

Code Example

#include "STM8S.h"
 
 
void clock_setup(void);
void GPIO_setup(void);
void ADC1_setup(void);
void lcd_print(unsigned char x_pos, unsigned char y_pos, unsigned int value);
 
 
void main()
{
     unsigned int a1 = 0x0000;
                
     clock_setup();
     GPIO_setup();
     ADC1_setup();
                
     LCD_init();  
     LCD_clear_home(); 
                
     LCD_goto(0, 0);
     LCD_putstr("STM8 AWD");
     LCD_goto(0, 1);
     LCD_putstr("A1");
                
     while (TRUE)
     {
          ADC1_ClearFlag(ADC1_FLAG_EOC);                       
                                
          ADC1_StartConversion();
          while(ADC1_GetFlagStatus(ADC1_FLAG_EOC) == 0);
                                
          a1 = ADC1_GetConversionValue();
          lcd_print(4, 1, a1);
                                
          if(ADC1_GetFlagStatus(ADC1_FLAG_AWD))
          {
               GPIO_WriteReverse(GPIOD, GPIO_PIN_0);
               ADC1_ClearFlag(ADC1_FLAG_AWD);
          }
          else
          {
               GPIO_WriteHigh(GPIOD, GPIO_PIN_0);
          }
                                
          delay_ms(90);
     };
}
 
 
void clock_setup(void)
{
     CLK_DeInit();
                
     CLK_HSECmd(DISABLE);
     CLK_LSICmd(DISABLE);
     CLK_HSICmd(ENABLE);
     while(CLK_GetFlagStatus(CLK_FLAG_HSIRDY) == FALSE);
                
     CLK_ClockSwitchCmd(ENABLE);
     CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV2);
     CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV4);
                
     CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSI, 
     DISABLE, CLK_CURRENTCLOCKSTATE_ENABLE);
                
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_I2C, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_ADC, ENABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_AWU, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_UART1, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER1, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER2, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER4, DISABLE);
}
 
 
void GPIO_setup(void)
{
     GPIO_DeInit(GPIOB);
     GPIO_Init(GPIOB, GPIO_PIN_1, GPIO_MODE_IN_FL_NO_IT);
                
     GPIO_DeInit(GPIOD);
     GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_HIGH_FAST);
     GPIO_Init(GPIOD, GPIO_PIN_3, GPIO_MODE_IN_PU_NO_IT);
}
 
 
void ADC1_setup(void)
{
     ADC1_DeInit();         
     ADC1_Init(ADC1_CONVERSIONMODE_SINGLE, 
               ADC1_CHANNEL_1, 
               ADC1_PRESSEL_FCPU_D10, 
               ADC1_EXTTRIG_GPIO, 
               DISABLE, 
               ADC1_ALIGN_RIGHT, 
               ADC1_SCHMITTTRIG_CHANNEL1, 
               DISABLE);
                
     ADC1_AWDChannelConfig(ADC1_CHANNEL_1, ENABLE);
     ADC1_SetHighThreshold(600);
     ADC1_SetLowThreshold(200);
                
     ADC1_Cmd(ENABLE);
}
 
 
void lcd_print(unsigned char x_pos, unsigned char y_pos, unsigned int value)
{
     char chr = 0x00;
                
     chr = ((value / 1000) + 0x30);     
     LCD_goto(x_pos, y_pos);
     LCD_putchar(chr); 
                
     chr = (((value / 100) % 10) + 0x30);
     LCD_goto((x_pos + 1), y_pos);
     LCD_putchar(chr); 
                
     chr = (((value / 10) % 10) + 0x30);
     LCD_goto((x_pos + 2), y_pos);
     LCD_putchar(chr); 
                
     chr = ((value % 10) + 0x30);
     LCD_goto((x_pos + 3), y_pos);
     LCD_putchar(chr); 
}

 

Explanation

The code for the AWD example is just as the one demonstrated in the ADC example. However, this time the ADC channel is channel 1 (PB1). Setting up the AWD is simple. We just need to set the limits, specify which channel to be monitored and enable the AWD unit.

ADC1_AWDChannelConfig(ADC1_CHANNEL_1, ENABLE);
ADC1_SetHighThreshold(600);
ADC1_SetLowThreshold(200);

Here we have set 600 and 200 ADC counts as upper and lower limits respectively.

In the main function, we are simply polling AWD flag. If an AWD (beyond boundary zone) event on PB1 pin occurs the LED on PD0 starts flashing. If PB1 senses voltage between 200 and 600 ADC counts, the LED is turned off, indicating guarded zone.

if(ADC1_GetFlagStatus(ADC1_FLAG_AWD))
{
    GPIO_WriteReverse(GPIOD, GPIO_PIN_0);
    ADC1_ClearFlag(ADC1_FLAG_AWD);
}
else
{
    GPIO_WriteHigh(GPIOD, GPIO_PIN_0);
}

 

Demo

AWD (1) AWD (2)

Video link: https://www.youtube.com/watch?v=bvVNuVpeFPk

Continue Reading ...

Related Posts

40 comments

  • Hi please someone tel me where can i find stm8s_delay.h and stm8s_delay.c
    Thanks

  • Hi , please someone tel me . Where can i find stm8s_delay.h and stm8s_delay.c
    Thanks ..

  • i am using stms003f3p6 controller..i have done as u told exactly but tim1 input capture is not displaying any value …according to your article it should display 10…i dont have any errors everything is fine…pulse on tim2 is ok ..it is coming as u said…but capture not working..please help me …thank you…

  • Hi Sir,
    i have just follwed your instruction provied in yout tutorial in main.c

    #include “stm8s.h”

    main()
    {
    while (1);
    }

    This error comes, can you please check why this error come, i am new and learn the controller programming.

    main.c:
    The command: “cxstm8 -i”d:\other datasheet\new folder\lib\stm8s_stdperiph_lib\libraries\stm8s_stdperiph_driver\inc” +debug -pxp -no -l +mods0 -pp -i”C:\Program Files (x86)\COSMIC\FSE_Compilers\CXSTM8\Hstm8″ -clDebug\ -coDebug\ main.c ” has failed, the returned value is: 1
    exit code=1.

    main.o – 2 error(s), 0 warning(s)

    Thanks,

  • Hi there, i solve the previous ADC problem. Anyway, u make some good tutorial on stm8 chips. Nice work

    • What was causing that issue? How did you solve it?

      • because i put all these

        ADC_DeInit(ADC1);
        ADC_SamplingTimeConfig(ADC1, ADC_Group_SlowChannels, ADC_SamplingTime_4Cycles);
        ADC_SchmittTriggerConfig(ADC1, ADC_Channel_7, DISABLE);
        ADC_ChannelCmd(ADC1, ADC_Channel_7, ENABLE);
        ADC_Init(ADC1, ADC_ConversionMode_Continuous, ADC_Resolution_12Bit, ADC_Prescaler_1);
        ADC_Cmd(ADC1, ENABLE);
        ADC_ExternalTrigConfig(ADC1, ADC_ExtEventSelection_None, ADC_ExtTRGSensitivity_All);

        in ‘while(1)’. It keeps looping.

        Once i put it before ‘while (1)’, it becomes normal.

  • Hi there, thanks for sharing. I learn alot thru your example, but I have encounter a problem with adc. Im currently using stm8l151k4t6. I wanna use the pin D7 as to read the voltage from my battery and monitor it (display on LCD). Can u please check where it goes wrong on my code?

    ADC_DeInit(ADC1);
    ADC_SamplingTimeConfig(ADC1, ADC_Group_SlowChannels, ADC_SamplingTime_4Cycles);
    ADC_SchmittTriggerConfig(ADC1, ADC_Channel_7, DISABLE);
    ADC_ChannelCmd(ADC1, ADC_Channel_7, ENABLE);
    ADC_Init(ADC1, ADC_ConversionMode_Continuous, ADC_Resolution_12Bit, ADC_Prescaler_1);
    ADC_Cmd(ADC1, ENABLE);
    ADC_ExternalTrigConfig(ADC1, ADC_ExtEventSelection_None, ADC_ExtTRGSensitivity_All);
    ADC_SoftwareStartConv(ADC1);
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
    battery = ADC_GetConversionValue(ADC1);

  • hi shawon great tutorials it would be nice too if you covered the coide compiler for arm it’s a good free compiler

  • hi shawon thanks for your response and support it turns out that my problem was a license issue I stupidly thought i use one license on multiple pc’s I used the license for a particular computer for mine that’s why I’ve been having compilation issues you have to use the license file stm8 sends you that is generated by your own pc else it won’t compile it’ll jus kip throwing you errors

  • It seems that you didn’t follow my instructions completely…. I found some issues:

    1. You have included header and source files of ADC2, CAN, UART2, etc which are unavailable in STM8S103…. Add only files for the peripherals available in your target MCU…. Exclude the rest….

    2. You have not included stm8s_delay header and source files…. Either disable it from stm8s_conf.h header file if you are not going to use it or add both the header and the source files for it in your project….

    Resolve these and you are good to go….

    Thanks….

  • I haven’t compiled any of the examples yet I just tried to compile the main source file just for test purpose n it just keeps showing me the same errors

  • yes the comic compiler was installed properly i even received a license file from my mail with the instructions to copy the license file to the license folder in the install directory if you fail to do this it will keep popping up for you to put in the license file. iincluded all the source files and header files as instructed i am working with the cheap stm8s103f i uncommented the stm8s103f in the stm8.h header file as you instructed and just to compile the main file it just throws me errors

  • no I just followed your instructions I just compiled it after setup i did not write any code yet just compiled the default main file

    • Need more details….

      1. The version of STVD and Cosmic
      2. Which example is giving this issue?
      3. The chip you are using if it other than STM8S003
      4. Have you tried to compile something else other than my examples?
      5. Are the paths to libraries and other folders properly added?
      6. Is the Cosmic compiler registered properly?

  • it throws this line The command: “cxstm8 -iinc +debug -pxp -no -l +mods0 -pp -i”C:\Program Files (x86)\COSMIC\FSE_Compilers\Hstm8″ -clDebug\ -coDebug\ main.c ” has failed, the returned value is: 1
    exit code=1.

  • hi I’m having compilation issues when I compile it just throws me error about comic compiler located in the program file ialso excluded unwanted header files but my problems still weren’t solved

  • hi thanks for the tutorials but i am having compiler error issues even when I remove unnecessary header files it say comic compiler error

  • I am new to STM8S003k3 Discovery board.I have done UART communication using STM standard peripheral library example. Now I want to define any other GPIO pin for UART communication. Is it possible to use d0 and d1 pin for the UART communication? If yes, then please let me know how?

  • thank you very much for this article!

  • Thank you for your time and effort.

  • Good job bro

  • Thank you for this exceptionally comprehensive article, much appreciated

    Rando!

Leave a Reply

Your email address will not be published. Required fields are marked *