Starting STM8 Microcontrollers

STM8S003K3 Discovery

External Interrupt (EXTI)

External interrupt is an additional feature of GPIOs in input mode. It makes a micro to respond instantly to changes done on it GPIO input pin(s) by an external events/triggers, skipping other tasks. External interrupts are useful in many scenarios. For instance, an emergency button. Consider the case of a treadmill. You are running at a speed and suddenly you get an ache in one of your ankles. You’ll surely want to stop running immediately. Rather decreasing speed in small steps, you can hit the emergency button to quickly stop the treadmill. The emergency button will interrupt all other processes and immediately instruct the treadmill’s CPU to decrease speed faster than otherwise possible. Thus, it has high priority over other processes.

In most 8-bit micros, there are few external interrupt pins and very limited options are available for them but that’s not the case with STM’s micros. In STM8s, almost all GPIO pins have independent external interrupt capability with input Schmitt triggers. Additionally, there’s interrupt controller to set interrupt priority.

Schematic

 

Hardware Connection

CubeMX

Code Example

We will do the same variable flash rate LED blinking example as in the GPIO example but this time with the DISCO board’s button in external interrupt mode. The code here needs special attention as now we will see how interrupts are configured and coded. This code is way different than anything I saw before. I’m saying so because you’ll need to follow certain steps unlike other compilers. In other compilers, all we do is create the interrupt function and tell the compiler the interrupt vector address. Same here too but too many steps involved.

Now check the interrupt vector table of STM8S003 below:

Vector Table

You’ll find this table not in the reference manual but in the device’s datasheet. This table varies with devices and so be sure of correct datasheet. The DISCO board’s button is connected to PB7 and so clearly, we will need IRQ4, i.e. EXTI1 or PORTB external interrupts. All external interrupts on GPIOB pin are masked in this vector address.

Please note that codes that use peripheral interrupts need stm8s_it.h and stm8s_it.c files. Therefore, add them if you are to use interrupts.

main.c

#include "stm8s.h"
 
 
bool state = FALSE;
 
 
void GPIO_setup(void);
void EXTI_setup(void);
void clock_setup(void);
 
 
void main(void)
{
    GPIO_setup();
    EXTI_setup();
    clock_setup();
                
    do
    {
         GPIO_WriteReverse(GPIOD, GPIO_PIN_0);
         if(state == TRUE)
         {
               delay_ms(100);
         }
         else
         {
               delay_ms(1000);
         }
    }while (TRUE);
}
 
 
void GPIO_setup(void)
{
    GPIO_DeInit(GPIOB);    
    GPIO_Init(GPIOB, GPIO_PIN_7, GPIO_MODE_IN_PU_IT);
                
    GPIO_DeInit(GPIOD);
    GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST);
}
 
 
void EXTI_setup(void)
{
    ITC_DeInit();
    ITC_SetSoftwarePriority(ITC_IRQ_PORTB, ITC_PRIORITYLEVEL_0);
                
    EXTI_DeInit();
    EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOB, EXTI_SENSITIVITY_FALL_ONLY);
    EXTI_SetTLISensitivity(EXTI_TLISENSITIVITY_FALL_ONLY);
                
    enableInterrupts();
}
 
 
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_HSIDIV1);
    CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
                
    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, 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);
}

 

stm8_interrupt_vector.c

/*              BASIC INTERRUPT VECTOR TABLE FOR STM8 devices
 *              Copyright (c) 2007 STMicroelectronics
 */
 
#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, NonHandledInterrupt}, /* irq1  */
                {0x82, NonHandledInterrupt}, /* irq2  */
                {0x82, NonHandledInterrupt}, /* irq3  */
                {0x82, (interrupt_handler_t)EXTI1_IRQHandler}, /* irq4  */
                {0x82, NonHandledInterrupt}, /* irq5  */
                {0x82, NonHandledInterrupt}, /* irq6  */
                {0x82, NonHandledInterrupt}, /* irq7  */
                {0x82, NonHandledInterrupt}, /* irq8  */
                {0x82, NonHandledInterrupt}, /* irq9  */
                {0x82, NonHandledInterrupt}, /* irq10 */
                {0x82, NonHandledInterrupt}, /* irq11 */
                {0x82, NonHandledInterrupt}, /* irq12 */
                {0x82, NonHandledInterrupt}, /* irq13 */
                {0x82, NonHandledInterrupt}, /* irq14 */
                {0x82, NonHandledInterrupt}, /* irq15 */
                {0x82, NonHandledInterrupt}, /* irq16 */
                {0x82, NonHandledInterrupt}, /* irq17 */
                {0x82, NonHandledInterrupt}, /* irq18 */
                {0x82, NonHandledInterrupt}, /* irq19 */
                {0x82, NonHandledInterrupt}, /* irq20 */
                {0x82, NonHandledInterrupt}, /* irq21 */
                {0x82, NonHandledInterrupt}, /* irq22 */
                {0x82, NonHandledInterrupt}, /* irq23 */
                {0x82, NonHandledInterrupt}, /* irq24 */
                {0x82, NonHandledInterrupt}, /* irq25 */
                {0x82, NonHandledInterrupt}, /* irq26 */
                {0x82, NonHandledInterrupt}, /* irq27 */
                {0x82, NonHandledInterrupt}, /* irq28 */
                {0x82, NonHandledInterrupt}, /* irq29 */
};

 

Now check the top parts of the stm8s_it.h and stm8s_it.c files respectively.

stm8s_it.h

stm8s_it.h_file

 

stm8s_it.c

stm8s_it.c_file

These must be coded.

 

Explanation

Most part of the code is same as previous codes and so I won’t be going through them again. However, there’s something new:

void EXTI_setup(void)
{
   ITC_DeInit();
   ITC_SetSoftwarePriority(ITC_IRQ_PORTB, ITC_PRIORITYLEVEL_0);
                
   EXTI_DeInit();
   EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOB, EXTI_SENSITIVITY_FALL_ONLY);
   EXTI_SetTLISensitivity(EXTI_TLISENSITIVITY_FALL_ONLY);
                
   enableInterrupts();
}

This function is where we are setting up the external interrupt. The first two lines deinitiate the interrupt controller and set priority while initiating it. It is not mandatory unless you want to set interrupt priority. Then we configure the external interrupt on PORTB pins. We also set the edge that will invoke an interrupt. Finally, we enable global interrupt. There goes the main.c file

Now it’s time to explain the stm8_interrupt_vector.c file. The top part of this file must include this line #include “stm8s_it.h”. It must also have the following section commented out:

//@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;
//}

We need to let our compiler know the name of the function that it should call when a particular interrupt is triggered. There are two parts for that. Firstly, the interrupt vector address and secondly the name of the function. This is reason for this line:

{0x82, (interrupt_handler_t)EXTI1_IRQHandler}, /* irq4  */

Lastly, the stm8s_it.h and stm8s_it.c files contain the prototype and the function that will execute the interrupt service routine (ISR). In our case, the ISR will change the logic state of the Boolean variable state. This will alter that flashing rate in the main loop.

Demo

Video link: https://www.youtube.com/watch?v=P6qdmWgH-Ls

Continue Reading ...

Related Posts

5 comments

Leave a Reply

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