Monday, September 20, 2010

STM8S Interrupt Handling

I have purchased about seven of the STM8S-Discovery development boards. These are high price-performance boards. They cost less than $8. That's incredible for a board that offers an eight bit microcontroller along with I/O, ADC, multiple timers, a hardware UART, SPI, I2C and much more. They are a real blast to work with.

Many of my projects use interrupts. The STM8S interrupt structure is pretty straight forward. However, ST wants you to put all of your interrupt routines in one module called "stm8s_it.c". I prefer to put my interrupt routines in the same C file that contains the other code needed for that routine. This is especially useful when you are writing code to be used as a library routine to be used over and over in the future.

As an example, if I am writing code to support a real time clock calendar (RTCC) I also want the interrupt routines needed for the RTCC module to be in the same source file as the other RTCC code. In this manner, I can build up a library that is easy to use and contains everything needed to support the RTCC in one source file.

To specify an interrupt vector to be used you must put a reference to your interrupt routine into one of the interrupt vectors in the stm8_interrupt_vector.c file. Usually, you would do this by modifying the line of code for the interrupt vector that is already in the file. This can get unwieldy when you have to do it for several different interrupt routines and is just one more thing to remember to get your project working correctly. This is especially true when, during the course of development, you are adding and removing multiple interrupt routines. I do it by using the following as my stm8_interrupt_vector.c file.


/*
The following lines are include files
that declare interrupt vector routines.
*/

#include "Debug.h"
#include "Wait.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 */

  #ifdef IRQ0
  {0x82, (interrupt_handler_t)IRQ0},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ1
  {0x82, (interrupt_handler_t)IRQ1},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ2
  {0x82, (interrupt_handler_t)IRQ2},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ3
  {0x82, (interrupt_handler_t)IRQ3},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ4
  {0x82, (interrupt_handler_t)IRQ4},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ5
  {0x82, (interrupt_handler_t)IRQ5},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ6
  {0x82, (interrupt_handler_t)IRQ6},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ7
  {0x82, (interrupt_handler_t)IRQ7},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ8
  {0x82, (interrupt_handler_t)IRQ8},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ9
  {0x82, (interrupt_handler_t)IRQ9},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ10
  {0x82, (interrupt_handler_t)IRQ10},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ11
  {0x82, (interrupt_handler_t)IRQ11},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ12
  {0x82, (interrupt_handler_t)IRQ12},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ13
  {0x82, (interrupt_handler_t)IRQ13},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ14
  {0x82, (interrupt_handler_t)IRQ14},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ15
  {0x82, (interrupt_handler_t)IRQ15},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ16
  {0x82, (interrupt_handler_t)IRQ16},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ17
  {0x82, (interrupt_handler_t)IRQ17},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ18
  {0x82, (interrupt_handler_t)IRQ18},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ19
  {0x82, (interrupt_handler_t)IRQ19},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ20
  {0x82, (interrupt_handler_t)IRQ20},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ21
  {0x82, (interrupt_handler_t)IRQ21},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ22
  {0x82, (interrupt_handler_t)IRQ22},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ23
  {0x82, (interrupt_handler_t)IRQ23},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ24
  {0x82, (interrupt_handler_t)IRQ24},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ25
  {0x82, (interrupt_handler_t)IRQ25},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ26
  {0x82, (interrupt_handler_t)IRQ26},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ27
  {0x82, (interrupt_handler_t)IRQ27},
  #else
  {0x82, NonHandledInterrupt},
  #endif

  #ifdef IRQ28
  {0x82, (interrupt_handler_t)IRQ28},
  #else
  {0x82, NonHandledInterrupt},
  #endif
};

Now, in the include file for the library module that I am working on I add two statements. The file below "wait.h" is an example.

#ifndef __WAIT_H
#define __WAIT_H

void Wait(unsigned MilliSeconds);
void InitWait(void);
void Debounce(unsigned MilliSeconds);

extern u8 DoWaitUpdate;
extern u8 InDebounce;

@far @interrupt void WaitInterruptHandler(void);
#define IRQ13 WaitInterruptHandler

#endif
 
In the above example, the two added lines are shown in red. The first line is the interrupt routine declaration. The second line defines "IRQ13" which is the interrupt vector I want to use (IRQ13 is used with timer two). These two lines will effectively make the same change in the stm8_interrupt_vector.c file that you used to have to do manually. Note that you do have to include this include file in the stm8_interrupt_vector.c file as seen above in its listing. Here is a partial listing for the Wait.c file that implements the WaitInterruptHandler interrupt routine.
 
        .
        .
        .
unsigned MillCount = 0;
u8 InDebounce = 0;
u8 DoWaitUpdate = 0;

void WaitInterruptHandler(void) {
  TIM2_ClearFlag(TIM2_FLAG_UPDATE);


  if (--MillCount)
    TIM2_Cmd(ENABLE);
  else if (InDebounce)
    InDebounce = 2;
  else
    DoWaitUpdate = 1;
}
        .
        .
        .
 
This can be made even easier by having a separate include file to hold the includes for all of the interrupt routines. As an example, if we have an include file called "Interrupts.h" we can put the two include lines from the stm8_interrupt_vector.c file as show in the first listing into the "Interrupts.h" file. This would look like the listing below.
 
/*
The following lines are include files
that declare interrupt vector routines.
*/

#include "Debug.h"
#include "Wait.h"
/*************************************/

Now, we just put an include for the "Interrupts.h" file into the stm8_interrupt_vector.c file instead of the separate includes for each interrupt routine. The net effect of this is that the stm8_interrupt_vector.c will never have to be changed again and the same stm8_interrupt_vector.c file can be used for all projects that use interrupts. Of course, we still have to remember to create the "Interrupts.h" file for each project.

Let me know what you think of this technique.

Monday, August 23, 2010

First Post


Hi. My name is Rich Goldner. I'm currently retired, but have worked in the computer industry for over 40 years. I'm an old guy, one of the original microcomputer pioneers, although I am no one you would know. I built my first microcomputer in 1975, the year the Altair computer was heralded in the January 1975 issue of "Popular Electronics" as the first home computer. In December of that year I ordered an IMSAI computer kit. It was a knock-off of the Altair, but with some of its basic problems fixed.

The IMSAI came with 256 bytes of memory. Please note that that is not kilobytes, megabytes or gigabytes. It had no ROM and a bootstrap program had to be keyed in from the front panel switches every time you powered it on. Above is a picture of one.
At some time in the future I may get deeper into the IMSAI and what I accomplished with it.

But I have other interests and hobbies that you may be interested in. Firstly, I love to program and I am first and foremost a programmer. I also love what's called embedded hardware or microcontrollers. I am also interested in CNC machines to shape both wood and metal. I have built approximately six CNC machines.

My current projects use the STM8S-Discover boards. These are manufactured by STMicroelectronics. The price performance is amazing. You can get started for $7. More on this later.