Continuing the STM8 Expedition

STM8S105 Discovery

This post is the continuation of the first post on STM8 microcontrollers here.

STM8S105 Discovery

ADC Interrupt

Instead of polling for ADC’s end of conversion (EOC) state, it is wise to use ADC interrupt. Just as with any hardware peripheral, interrupt methods make a system highly responsive. Interrupts like these free up the CPU for other tasks. However, it is up to the coder to determine interrupt priorities and look out for situation that may cause too many interrupts to be processed in a short while.

Hardware Connection

adc_isr

Code Example

 

stm8s_it.h (top part only)

#ifndef __STM8S_IT_H
#define __STM8S_IT_H

@far @interrupt void ADC_IRQHandler(void);

/* Includes ------------------------------------------------------------------*/
#include "stm8s.h"
....

 

stm8s_it.c (top part only)

#include "stm8s.h"
#include "stm8s_it.h"


extern unsigned int adc_value;


void ADC_IRQHandler(void)
{
       adc_value = ADC1_GetConversionValue();
       ADC1_ClearFlag(ADC1_FLAG_EOC);
}
....

 

stm8_interrupt_vector.c (shortened)

#include "stm8s_it.h"


typedef void @far (*interrupt_handler_t)(void);

struct interrupt_vector {
       unsigned char interrupt_instruction;
       interrupt_handler_t interrupt_handler;
};

//@far @interrupt void NonHandledInterrupt (void)
//{
       /* in order to detect unexpected events during development,
          it is recommended to set a breakpoint on the following instruction
       */
//     return;
//}

extern void _stext();     /* startup routine */


struct interrupt_vector const _vectab[] = {
       {0x82, (interrupt_handler_t)_stext}, /* reset */
       {0x82, NonHandledInterrupt}, /* trap  */
       {0x82, NonHandledInterrupt}, /* irq0  */
       ....
       {0x82, (interrupt_handler_t)ADC_IRQHandler}, /* irq22 */
       ....
       {0x82, NonHandledInterrupt}, /* irq29 */
};

 

main.c

#include "STM8S.h"
#include "lcd.h"


unsigned int adc_value;


unsigned char bl_state;
unsigned char data_value;


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(void)
{
       float mv = 0x00000000;

       clock_setup();
       GPIO_setup();
       ADC1_setup();

       LCD_init(); 
       LCD_clear_home();

       LCD_goto(2, 0);
       LCD_putstr("STM8 ADC ISR");
       LCD_goto(0, 1);
       LCD_putstr("A0/mV");

       while(TRUE)
       {
              ADC1_StartConversion();

              mv = (adc_value * 5000.0);
              mv /= 1023.0;

              lcd_print(7, 1, mv);            
              lcd_print(12, 1, adc_value);
              GPIO_WriteReverse(GPIOD, GPIO_PIN_0);
              delay_ms(160);
       };
}


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_HSIDIV4);
       CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);

       CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSI,
       DISABLE, CLK_CURRENTCLOCKSTATE_ENABLE);

       CLK_PeripheralClockConfig(CLK_PERIPHERAL_I2C, ENABLE);
       CLK_PeripheralClockConfig(CLK_PERIPHERAL_ADC, ENABLE);
       CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, DISABLE);
       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_TypeDef)(GPIO_PIN_0 | GPIO_PIN_1)),      GPIO_MODE_IN_FL_NO_IT);

       GPIO_DeInit(GPIOD);
       GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_OD_HIZ_FAST);
}


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_ITConfig(ADC1_IT_EOCIE, ENABLE);
       enableInterrupts();
       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

This example and the first ADC example is all same except for the interrupt part. Note the last three lines below.

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_ITConfig(ADC1_IT_EOCIE, ENABLE);
       enableInterrupts();
       ADC1_Cmd(ENABLE);
}

As can be seen, End-of-Conversion (EOC) interrupt has been enabled along with global interrupt and the ADC itself.

In the vector mapping file, ADC interrupt is set.

{0x82, (interrupt_handler_t)ADC_IRQHandler}, /* irq22 */

When ADC EOC interrupt triggers, the ADC data buffer is read and the EOC flag is cleared.

void ADC_IRQHandler(void)
{
       adc_value = ADC1_GetConversionValue();
       ADC1_ClearFlag(ADC1_FLAG_EOC);
}

 

Demo

ADC ISR (2) ADC ISR (1)

Continue Reading ...

Related Posts

12 comments

  • Hi,
    Thats great work
    Do you have steps for to work ST7735 TFT IN 8 BIT 8080 mode

  • I don’t even understand how I ended up right here, however
    I believed this publish used to be good. I don’t know who you are however definitely
    you’re going to a well-known blogger should you aren’t already.
    Cheers!

  • Sankalp Rai Gambhir

    Thanks Shawon, your blogs are indeed very helpful. Keep Growing.

  • why are you sending the received data back through TX pin?

  • Hi SHAWON SHAHRYIAR

    I am wondering how to get a Max31855 to talk to a STM8s via SPI.

    JP

    • What’s to wonder about it? It is a simple SPI communication and SPI for STM8 is no different from the SPI of other MCUs…. The following lines are taken from the device’s datasheet and the write up there states how to communicate with it:

      “Drive CS low to output the first bit on the SO pin. A complete serial-interface read of the cold-junction compensated thermocouple temperature requires 14 clock cycles. Thirty-two clock cycles are required to read both the thermocouple and reference junction temperatures (Table 2 and Table 3.) The first bit, D31, is the thermocouple temperature sign bit, and is presented to the SO pin within tDV of the falling edge of CS. Bits D[30:18] contain the converted temperature in the order of MSB to LSB, and are presented to the SO pin within tD0 of the falling edge of SCK. Bit D16 is normally low and goes high when the thermocouple input is open or shorted to GND or VCC. The reference junction temperature data begins with D15. CS can be taken high at any point while clocking out con-version data. If T+ and T- are unconnected, the thermocouple temperature sign bit (D31) is 0, and the remainder of the thermocouple temperature value (D[30:18]) is 1.”

Leave a Reply

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