RSS

Archive for June, 2014

STM8S Independent Watchdog

Saturday, June 21st, 2014

The Independent Watchdog (IWDG) allows the developer to detect software failures and reset the STM8S microcontroller restarting the application. This post demonstrates how this feature works and how the developer can change the time window for the watchdog.

Watchdogs offer the application developer a simple to use method of determining if an application has entered a state which means that it is locked or taking too long to perform an operation.

The first task is to define what we mean by too long. This will vary from one application to another but the key point is that this should be longer than the longest operation the application will perform plus a margin. Once we have this time period defined we know that the application must reset the IWDG before the time period has elapsed.

Hardware

Independent Watchdog Key Register – IWDG_KR

The Key Register controls the operation of the IWDG. The allowed values are as follows:

Value Description
0x55 Enable writing to the IWDG_PR and IWDG_RLR registers. These registers are write protected by default.
0xAA Reset the IWDG counter to the reload value. This value must be written to the key register periodically in order to prevent the watchdog from resetting the microcontroller.
0xCC Enable and start the IWDG.
0x00 Disable writing to the IWDG_PR and IWDG_RLR registers.

The final value in the table is not present in revision 9 of RM0016 – Reference Manual but does appear in the header files for the STD Peripheral Library.

In researching this post a key point in the documentation was missed, namely IWDG_PR and IWDG_RLR can only be written when the IWDG has actually been enabled. This means that the IWDG needs to be running before the prescaler and reload registers can be loaded with the values required by the application. If the IWDG is not running then the system will resort to the default reset values for these registers. This gives a watchdog window of 16ms. It also means that the application has 16ms to set the desired time window through the IWDG_PR and IWDG_RLR registers before the microcontroller is reset.

Independent Watchdog Prescaler – IWDG_PR and Independent Watchdog Reload Register – IWDG_RLR

These two registers define the watchdog window and can. The default values are IWDG_PR = 0 and IWDG_RLR = 0xff. This gives a watchdog window of approximately 16ms. To change the size of the watchdog window use the table below to determine the values which are appropriate for your application:

Prescaler Divider Prescaler Value (IWDG_PR) Period when RL = 0 Period when RL = 0xff
4 0 62.5 us 15.90 ms
8 1 125 us 31.90 ms
16 2 250 us 63.70 ms
32 3 500 us 127 ms
64 4 1.00 ms 255 ms
128 5 2.00 ms 510 ms
256 6 4.00 ms 1.02 s

The programming reference for the STM8S gives the following formula for determining the exact reset period:

T = 2 x TLSI x P x R

where:

T Timeout period
TLSI 1 / fLSI
P 2(IWDG_PR + 2)
R IWDG_RLR + 1

Additionally, the time between the last reset of the key register (i.e. writing 0xAA to IWDG_KR) is T + 6 x TLSI.

Software

So lets look at how we can implement this:

//
//  This program demonstrates how to use the Independent Watchdog timer
//  on the STM8S microcontroller in order to detect software failures and
//  reset the microcontroller.
//
//  This software is provided under the CC BY-SA 3.0 licence.  A
//  copy of this licence can be found at:
//
//  http://creativecommons.org/licenses/by-sa/3.0/legalcode
//
#include <iostm8S105c6.h>
#include <intrinsics.h>

//--------------------------------------------------------------------------------
//
//  Setup the system clock to run at 16MHz using the internal oscillator.
//
void InitialiseSystemClock()
{
    CLK_ICKR = 0;                       //  Reset the Internal Clock Register.
    CLK_ICKR_HSIEN = 1;                 //  Enable the HSI.
    CLK_ECKR = 0;                       //  Disable the external clock.
    while (CLK_ICKR_HSIRDY == 0);       //  Wait for the HSI to be ready for use.
    CLK_CKDIVR = 0;                     //  Ensure the clocks are running at full speed.
    CLK_PCKENR1 = 0xff;                 //  Enable all peripheral clocks.
    CLK_PCKENR2 = 0xff;                 //  Ditto.
    CLK_CCOR = 0;                       //  Turn off CCO.
    CLK_HSITRIMR = 0;                   //  Turn off any HSIU trimming.
    CLK_SWIMCCR = 0;                    //  Set SWIM to run at clock / 2.
    CLK_SWR = 0xe1;                     //  Use HSI as the clock source.
    CLK_SWCR = 0;                       //  Reset the clock switch control register.
    CLK_SWCR_SWEN = 1;                  //  Enable switching.
    while (CLK_SWCR_SWBSY != 0);        //  Pause while the clock switch is busy.
}

//--------------------------------------------------------------------------------
//
//  Initialise the ports.
//
//  Configure all of Port D for output.
//
void InitialisePorts()
{
    PD_ODR = 0;             //  All pins are turned off.
    PD_DDR = 0xff;          //  All pins are outputs.
    PD_CR1 = 0xff;          //  Push-Pull outputs.
    PD_CR2 = 0xff;          //  Output speeds up to 10 MHz.
}

The code above should be pretty familiar to anyone who has been following The Way of the Register series of posts. This sets up the system clock to run at 16MHz and sets port D as an output port.

//--------------------------------------------------------------------------------
//
//  Initialise the Independent Watchdog (IWDG)
//
void InitialiseIWDG()
{
    IWDG_KR = 0xcc;         //  Start the independent watchdog.
    IWDG_KR = 0x55;         //  Allow the IWDG registers to be programmed.
    IWDG_PR = 0x02;         //  Prescaler is 2 => each count is 250uS
    IWDG_RLR = 0x04;        //  Reload counter.
    IWDG_KR = 0xaa;         //  Reset the counter.
}

We follow this up by setting up the IWDG. The prescaler and counter values should give a 1ms window.

//--------------------------------------------------------------------------------
//
//  Main program loop.
//
int main()
{
    //
    //  Initialise the system.
    //
    __disable_interrupt();
    InitialiseSystemClock();
    InitialisePorts();
    InitialiseIWDG();
    __enable_interrupt();
    __halt();
}

The main program configures the system and then halts the microcontroller. Without the watchdog this would the end of the application. With the watchdog we should see the microcontroller continuously resetting. This can be verified by connecting an oscilloscope to Port D. If we do this we see the output on the port rise as the microcontroller is reset and the InitialisePorts method is called.

Independent Watchdog

Independent Watchdog

Now replace the __halt(); statement with the following code:

while (1)
{
    IWDG_KR = 0xaa;
}

This code continuously writes the I am alive message to the IWDG key register. Doing this forces the IWDG to reset the counters with the reload values and start again. Running this application we see a flat line following the initial reset of the microcontroller. The flat line indicates that the microcontroller is not being reset.

Conclusion

The Independent Watchdog provides a simple method for detecting software failures merely writing a reset value into the key register.

Earlier the point was made that it appears that the IWDG must be enabled (and hence running) before the prescaler and reload registers can be modified. This point is critical as the microcontroller will resort to the reset values otherwise.

Although not demonstrated here it is possible for the application to determine if the application has been started through the application of power to the microcontroller or because of a reset by the IWDG. This can be determined by checking the value in RST_SR_IWDGF. This will be set to 1 if the IWDG has caused the microcontroller to be reset or 0 otherwise. This is left as an exercise for the reader.

Download

The source code for this post is available for download.

Auto-Wakeup on the STM8S

Friday, June 20th, 2014

A few days ago I was asked for advice about pausing the STM8S for a long time period (in this case 30 seconds). I had to admit that I was not sure how to achieve this without using a timer and counting interrupts until the 30 time period had expired. A quick examination of the STM8S Programming Reference reveals that there is a simpler way of doing this. This post examines the Auto-Wakeup (AWU) feature of the STM8S and shows how this feature can be used to pause for a time period which can range from 15.625uS to 30.720s, assuming the clock source is accurate.

Auto-Wakeup (AWU) Feature

The AWU feature wakes the STM8S after a predefined time period following the microcontroller going into the active halt state. This feature can only be used when the microcontroller is halted and the accuracy is dependent upon the clock source.

The clock source is fed into a prescalar. The output from the prescalar is then used as a clock for a counter. The counter will cause an interrupt to be generated when the preset counter value has been reached.

It is also possible to determine the accuracy of the clock source by using the capture compare feature of TIM1 or TIM3 to measure the clock frequency.

Auto-Wakeup Registers

The function of the AWU is determined by the values in the AWU control registers.

Auto-Wakeup Enable – AWU_CSR1_AWUEN

Setting this bit to 1 enables the AWU function. Setting this to 0 disables the function.

Auto-Wakeup Asynchronous Prescalar Divider – AWU_APR_APR and Auto-Wakeup Timebase Selection Register – AWU_TBR_AWUTB

These two registers together control the duration of the AWU. The exact duration of the wakeup period is determined by the AWU_APR_APR and AWU_TBR_AWUTB values from the following table.

fLS = f fLS = 128 kHz AWU_TBR_AWUTB APRDIV Formula AWU_APR_APR Range
2/f – 64/f 0.015625 ms – 0.5 ms 0001 APRDIV/fLS 2 to 64
2×32/f – 2x2x32/f 0.5 ms – 1.0 ms 0010 2 x APRDIV/fLS 32 to 64
2×64/f – 2x2x64/f 1 ms – 2 ms 0011 22 x APRDIV/fLS 32 to 64
22×64/f – 22×128/f 2 ms – 4ms 0100 23 x APRDIV/fLS 32 to 64
23×64/f – 23×128/f 4 ms – 8 ms 0101 24 x APRDIV/fLS 32 to 64
24×64/f – 24×128/f 8 ms – 16 ms 0110 25 x APRDIV/fLS 32 to 64
25×64/f – 25×128/f 16 ms – 32 ms 0111 26 x APRDIV/fLS 32 to 64
26×64/f – 26×128/f 32 ms – 64 ms 1000 27 x APRDIV/fLS 32 to 64
27×64/f – 27×128/f 64 ms – 128 ms 1001 28 x APRDIV/fLS 32 to 64
28×64/f – 28×128/f 128 ms – 256 ms 1010 29 x APRDIV/fLS 32 to 64
29×64/f – 29×128/f 256 ms – 512 ms 1011 210 x APRDIV/fLS 32 to 64
210×64/f – 210×128/f 512 ms – 1.024 s 1100 211 x APRDIV/fLS 32 to 64
211×64/f – 211×128/f 1.024 s – 2.048 s 1101 212 x APRDIV/fLS 32 to 64
211×130/f – 211×320/f 2.080 s – 5.120 s 1110 5 x 211 x APRDIV/fLS 26 to 64
211×330/f – 212×960/f 5.280 s – 30.720s 1111 30 x 211 x APRDIV/fLS 11 to 64

Where fLS = f is the formula which should be used when the inbuilt LSI is not being used.

It may not be possible to obtain an exact time period and the application may have to use the values which give the closest period.

When not in use, AWU_TBR_AWUB should be set to 0 in order to reduce power consumption.

Auto-Wakeup Flag – AWU_CSR1_AWUF

This flag is set when the AWU interrupt has been generated. The flag is cleared by reading the AWU control/status register (AWU_CSR1).

Auto-Wakeup Measurement Enable – AWU_CSR1_MSR

Setting this flag to 1 connects the output from the prescalar to one of the internal timers. This allows the application to accurately determine the clock frequency connected to the AWU prescalar. By measuring the clock frequency the application can adjust the values used for the prescalar divider and the timebase.

Software

Now we have the theory it is time to break out the STM8S Discovery board and the IAR compiler. The application starts pretty much the same as previous examples in this series, by setting the system clock and configuring a port for output:

//
//  This program demonstrates how to use the Auto-Wakeup feature of the STM8S
//  microcontroller.
//
//  This software is provided under the CC BY-SA 3.0 licence.  A
//  copy of this licence can be found at:
//
//  http://creativecommons.org/licenses/by-sa/3.0/legalcode
//
#include <iostm8S105c6.h>
#include <intrinsics.h>

//--------------------------------------------------------------------------------
//
//  Setup the system clock to run at 16MHz using the internal oscillator.
//
void InitialiseSystemClock()
{
    CLK_ICKR = 0;                       //  Reset the Internal Clock Register.
    CLK_ICKR_HSIEN = 1;                 //  Enable the HSI.
    CLK_ECKR = 0;                       //  Disable the external clock.
    while (CLK_ICKR_HSIRDY == 0);       //  Wait for the HSI to be ready for use.
    CLK_CKDIVR = 0;                     //  Ensure the clocks are running at full speed.
    CLK_PCKENR1 = 0xff;                 //  Enable all peripheral clocks.
    CLK_PCKENR2 = 0xff;                 //  Ditto.
    CLK_CCOR = 0;                       //  Turn off CCO.
    CLK_HSITRIMR = 0;                   //  Turn off any HSIU trimming.
    CLK_SWIMCCR = 0;                    //  Set SWIM to run at clock / 2.
    CLK_SWR = 0xe1;                     //  Use HSI as the clock source.
    CLK_SWCR = 0;                       //  Reset the clock switch control register.
    CLK_SWCR_SWEN = 1;                  //  Enable switching.
    while (CLK_SWCR_SWBSY != 0);        //  Pause while the clock switch is busy.
}

//--------------------------------------------------------------------------------
//
//  Initialise the ports.
//
//  Configure all of Port D for output.
//
void InitialisePorts()
{
    PD_ODR = 0;             //  All pins are turned off.
    PD_DDR = 0xff;          //  All pins are outputs.
    PD_CR1 = 0xff;          //  Push-Pull outputs.
    PD_CR2 = 0xff;          //  Output speeds up to 10 MHz.
}

The next step is to provide a method to initialise the AWU function:

//--------------------------------------------------------------------------------
//
//  Initialise the Auto Wake-up feature.
//
//
//
void InitialiseAWU()
{
    AWU_CSR1_AWUEN = 0;     // Disable the Auto-wakeup feature.
    AWU_APR_APR = 38;
    AWU_TBR_AWUTB = 1;
    AWU_CSR1_AWUEN = 1;     // Enable the Auto-wakeup feature.
}

Firstly, the AWU function is disabled. The prescalar is set followed by the timebase. The final step is to re-enable the AWU.

//--------------------------------------------------------------------------------
//
//  Auto Wakeup Interrupt Service Routine (ISR).
//
//  Pulse PD0 for a short time.  The NOP instructions generate a more stable
//  pulse on the oscilloscope.
//
#pragma vector = AWU_vector
__interrupt void AWU_IRQHandler(void)
{
    volatile unsigned char reg;

    PD_ODR = 1;
    asm("nop;");
    asm("nop;");
    PD_ODR = 0;
    reg = AWU_CSR1;     // Reading AWU_CSR1 register clears the interrupt flag.
}

This interrupt handler is triggered at the end of the AWU period. This handler simply pulses pin 0 on port D.

//--------------------------------------------------------------------------------
//
//  Main program loop.
//
int main(void)
{
    //
    //  Initialise the system.
    //
    __disable_interrupt();
    InitialiseSystemClock();
    InitialisePorts();
    InitialiseAWU();
    __enable_interrupt();
    //
    //  Main program loop.
    //
    while (1)
    {
        __halt();
    }
}

The AWU function is turned on when the microcontroller enters the active halt state. A halt instruction is generated by the __halt() method.

Running the above code on the STM8S Discovery board results in the following trace on the oscilloscope:

AWU 3208Hz Signal

AWU 3208Hz Signal

and:

AWU Expanded Pulse

AWU Expanded Pulse

Examining the table above and the values used for the precalar and timebase we should be seeing a pulse with a frequency of approximately 3,368Hz. The actual value measured on the oscilloscope is 3,208Hz. The measurement on the oscilloscope will alway be slightly out due to the time it takes for the ISR to be setup and called. An ISR on the STM8S takes 9 clock cycles to be established, this equates to about 560nS on a system running at 16MHz. The actual difference is 6.25mS. The remainder of the difference comes from the fact that the LSI has an accuracy of +/-12.5%. A quick calculation shows the the LSI was running at about 121KHz at the time this post was written.

Conclusion

AWU offers a simple way of triggering processing at predetermined time periods. The active halt state puts the microcontroller into a low power mode whilst the microcontroller is waiting for the time period to elapse.

It should also be noted that using the LSI results in a slightly variable time period.

Bare Metal GPIO on the Raspberry Pi

Monday, June 16th, 2014

The Raspberry Pi is classically used as a single board computer running Linux but it also offers the possibility of using the board without Linux (known as bare metal development). Add to this the availability of free ARM development tools and we have the opportunity to develop on a high speed ARM processor at a reasonable cost.

This post explores the steps necessary to toggle a GPIO pin on the Raspberry Pi by directly accessing the registers without the presence of an operating system.

Project Description

For this simple project the software will need to perform three operations:

  • Configure the GPIO port
  • Set the GPIO pins for the selected port
  • Reset the GPIO pins for the selected port

The application here will toggle two GPIO pins, one connected to one of the LEDs on the board (GPIO16 connected to the OK/ACT LED) and the second connected to a pin on the header (GPIO18 connected to Pin 12 on header P1).

Resources

There are already a number of resources out on the internet which cover this topic including a large number references in the Bare Metal forum on the Raspberry Pi web site.

As well as the Raspberry Pi web site there are a number of other sites discussing OS development for the Raspberry Pi. This post was going to be a comprehensive write up of the tools and various scripts required to put together a bare metal example but I have since discovered an excellent tutorial on this subject on the OSDev web site. I would heartily recommend that you visit this web site and review the material on the Raspberry Pi C Tutorial page.

Hardware

Before we look at the registers we need to take a look at the first chapter of the Broadcom BCM2835 ARM Peripheral document. Particularly the following two paragraphs:

Physical addresses range from 0x20000000 to 0x20FFFFFF for peripherals. The bus addresses for peripherals are set up to map onto the peripheral bus address range starting at 0x7E000000. Thus a peripheral advertised here at bus address 0x7Ennnnnn is available at physical address 0x20nnnnnn.

and

The peripheral addresses specified in this document are bus addresses. Software directly accessing peripherals must translate these addresses into physical or virtual addresses, as described above. Software accessing peripherals using the DMA engines must use bus addresses.

The remainder of the document discusses the registers and uses addresses in the 0x7Ennnnnn-0x7EFFFFFF memory range. This means that any software written should translate the 0x7Ennnnnn range down to the 0x20nnnnnn range.

GPIO Configuration Register

The software will need to set the function for GPIO pins 16 and 18 using the function select registers. The description of these registers and the GPIO pins which they relate to can be found on page 92 of the Broadcom BCM2835 ARM Peripheral manual. The port is configured using three bits in the register. The three bits have the following meaning:

Bit Field Description
000 GPIO Pin is an input
001 GPIO Pin is an output
100 GPIO Pin configured for alternate function 0
101 GPIO Pin configured for alternate function 1
110 GPIO Pin configured for alternate function 2
111 GPIO Pin configured for alternate function 3
011 GPIO Pin configured for alternate function 4
010 GPIO Pin configured for alternate function 5

The two pins the software will be accessing are pins 16 and 18. These pins are configured using bits 24-26 for GPIO 18 and 18-20 for GPIO 16 of the GPFSEL1 register at 0x7E200004. This maps to the address 0x20200004.

Set GPIO Pin High

The GPIO pins are set by setting a bit in the GPSETn registers. Both GPIO16 and GPIO18 are set through the GPSET0 register (see page 95 of the Broadcom BCM2835 ARM Peripheral manual). The address of this register is 0x7E20001C (0x2020001C).

Set GPIO Pin Low (Clear the GPIO Pin)

The GPIO pins are reset by setting a bit in the GPCLRn registers. Both GPIO16 and GPIO18 are set through the GPCLR0 register (see page 95 of the Broadcom BCM2835 ARM Peripheral manual). The address of this register is 0x7E200028 (0x20200028).

Software

The application is a relatively simple one, toggling a GPIO pin continuously. The following should suffice:

//
//  Flags used to configure the GPIO port.
//
#define FLAG_GPIO_RESET     ((7 << 18) | (7 << 24))
#define FLAG_GPIO_CONFIG    ((1 << 18) | (1 << 24))
//
//  Bits which will allow control of GPIO0 pins for the status
//  LED and the header on the Pi.
//
#define GPIO_PINS           ((1 << 16) | (1 << 18))
//
//  The following register definitions are used to access the GPIO
//  pins on the BCM2835.  The register definitions are taken from
//  section 6 of the BCM manual and the mapping is described in
//  section 1 of the same.
//
//  Address of the register which will configure the GPIO pins.
//
volatile unsigned int *gpsel1 = (unsigned int *) 0x20200004;
//
//  Address of the register which will set the GPIO pins.
//
volatile unsigned int *gpset0 = (unsigned int *) 0x2020001C;
//
//  Address of the register which will clear the GPIO pins.
//
volatile unsigned int *gpclr0 = (unsigned int *) 0x20200028;
//
//  Contents of the GPIO configuration register.
//
unsigned int gpioConfig;
//
//  Main program loop.
//
int main()
{
    //
    //  Reconfigure GPIO 16 and 18 to be outputs.  Clear any
    //  previous GPIO configuration for these pins.
    //
    gpioConfig = *gpsel1;
    //
    //  Firstly, remove any configuration for GPIO 16 & 18.
    //
    gpioConfig &= ~FLAG_GPIO_RESET;
    //
    //  Now configure GPIO 16 & 18 to be plain GPIO outputs.
    //
    gpioConfig |= FLAG_GPIO_CONFIG;
    *gpsel1 = gpioConfig;
    //
    //  Toggle GPIO 16 and 18 forever.
    //
    while (1)
    {
        *gpclr0 = GPIO_PINS;
        *gpset0 = GPIO_PINS;
    }
    return(0);
}

The makefile I used to build the application looks as follows:

#
#   Root directory/name of the ARM tools used to build the application.
#
ARMTOOLSROOT = e:\utils\gcc-arm\bin\arm-none-eabi

#
#   C Compiler options.
#
OPTIONS = -nostdlib -ffreestanding -O3

#
#   What do we need to make to build everything?
#
all: kernel.img
    copy /y /d kernel.img j:\

HelloWorldBlinky.o : HelloWorldBlinky.c
	$(ARMTOOLSROOT)-gcc $(OPTIONS) -c HelloWorldBlinky.c -o HelloWorldBlinky.o

HelloWorldBlinky.elf : memmap HelloWorldBlinky.o 
	$(ARMTOOLSROOT)-ld HelloWorldBlinky.o -T memmap -o HelloWorldBlinky.elf
	$(ARMTOOLSROOT)-objdump -D HelloWorldBlinky.elf > HelloWorldBlinky.list

kernel.img : HelloWorldBlinky.elf
	$(ARMTOOLSROOT)-objcopy HelloWorldBlinky.elf -O binary kernel.img

#
#   Delete any previously built files.
#
clean :
	del -f HelloWorldBlinky.o
	del -f HelloWorldBlinky.elf
	del -f HelloWorldBlinky.list
	del -f kernel.img

Tools

The software present here was compiled using the version 4.8.3 of the ARM eabi tools.

Conclusion

Compiling the above code and executing the application results in the following output when the oscilloscope is connection to GPIO18:

Raspberry Pi GPIO Output

Raspberry Pi GPIO Output

It is necessary to review the documents in the resources section of this post in order to fully understand the theory behind this application.

And now it is time to take on some more complex topics.

Learning Objective-C by Developing iPhone Games

Wednesday, June 4th, 2014

In the last few weeks I have been reading Learning Objective-C by Developing iPhone Games in order to improve my iPhone development skills. A topical subject given this week is the start of Apple’s WWDC.

Learning Objective-C by Developing iPhone Games offers an insight into the iPhone development platform from a games programmers perspective. The book covers the fundamentals of the Objective-C programming language and the development environment.

The early chapters cover the installation of the tools (XCode) and the process of applying for a development certificates along with an insight into the basic language constructs.

The book moves on from the basic environment and gets started with the development of a basic Space Invaders game. The text covers the basic theory of games development including state machines and frame rates putting this into perspective discussing the resource constrained environment of the iPhone.

Later chapters cover the topics of sound and sprite animation and uses three other games (Simon Says, Mini Golf and Galaxy) to cover these topics.

The final chapters gives advice on publishing your game through the Apple store with detailed instructions on the process. I will be reaching for this book when I finally get around to publishing an app through the store.

Conclusion

The title of the book suggests this is suitable for a beginner and I believe that it is suitable for an experienced developer who wants to use this book as an introduction to the iPhone platform. I also think that a new/novice programmer should also invest in a book dedicated to Objective-C and programming theory as the book is a little light in these areas. There are also some inconsistencies which will annoy an experienced developer and risk confusing a novice. Having said this it is still a good introduction to this topic.

Form more information on this title follow this link: Learning Objective-C by Developing iPhone Games