RSS

Connecting Informatica to Twitter

December 29th, 2014 • InformaticaComments Off on Connecting Informatica to Twitter

In the past few months I have started to use Informatica Power Centre at work. During the course the instructor mentioned that Informatica had released an Express version of the full development tool. This is fully feature but limited in the number of records it can process. The limit is 250,000 records per day so this makes it useful for personal, non-commercial use. This has lead me to install the system on my home computer and to start to investigate some of the features I will not have the opportunity to use in my day job.

Informatica Power Centre Express can be connected to several data sources including social media site such as Twitter and Facebook. I am highly unlikely to have the opportunity to use Informatica in this area and so a weekend project came into being, the problem, how to connect Power Centre Express to Twitter, retrieve a list of followers and store this in a local database.

Problem Definition

Informatica Power Centre Express can performed a number of operations with Twitter:

  • Search
  • Lookup a profile
  • Get a list of Followers for the account
  • Get a list of Friends for the account

The two operations of interest are the Get list of Followers and Profile Lookup functions.

The general algorithm is as follows:

  1. Get a list of Followers for the Twitter user
  2. Compare the list to those in the local database.
  3. If the Twitter user ID is found then update the record to indicate that the user is still a follower at the time the list was retrieved.
  4. If the Twitter user is not in the list of Followers in the local database then retrieve information about the Twitter user and add to the local database.

One of the overriding aims of this is to only ever retrieve as much information as necessary and never any more. It is therefore essential that the data is filtered as soon as possible to prevent retrieving information which we already hold locally.

Connecting Power Centre Express to Twitter

Rather then duplicate existing knowledge, I draw your attention to the article Informatica PowerCenter Express – Connecting to Twitter. This is a fairly comprehensive article but it stops short of what will be achieved in this post, namely dynamic lookup of user information.

In order to implement the above article you will need to have the following completed:

  1. Download and install Power Centre Express
  2. Create a Twitter account and also register for a Twitter developer ID

At the end of the article you should have a Power Centre mapping which can retrieve static information from Twitter.

Retrieving Dynamic Data

If you have followed the above article you will be able to retrieve information about a user, a number of posts which match search criteria and lookup a profile. This is all achieved by changing the Query Parameter in the Twitter_Data_Object_Operation and running a mapping through the Developer user interface.

Hidden away in the help system is the key fact which we need to allow the retrieval of multiple user profiles where the user IDs are taken from a dynamic data source, namely a text file.

Twitter Project

The Informatica Twitter Project created following the guidelines in the above article looks like this:

Informatica Twitter Project Treeview

Informatica Twitter Project

This is slightly different from the project in the article as the project is intended to process the data retrieved.

conInformatica

This object is a connection from Informatica to a SQL Server local database.

rdoTwitterUser

This object is a relational database object in the local SQL Server database. It contains the mapping to a database table which can be used to hold information about the Twitter user. The SQL code to create a small table to hold the Twitter user details is as follows:

CREATE TABLE [dbo].[TwitterUser]
(
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[Description] [varchar](512) NULL,
	[TwitterID] [varchar](160) NULL,
	[IdStr] [varchar](160) NULL,
	[ScreenName] [varchar](160) NULL,
	[LastUpdated] [datetime] NULL DEFAULT (getdate())
) ON [PRIMARY]

This table holds a local ID, the Twitter ID (TwitterID and IdStr), Twitter account name (ScreenName), a description (Description) for the account and the date/time the record in the local database was last updated (LastUpdated).

When executing a Twitter Profile Lookup, the Twitter API returns much more information than the above. The following creates a table which would hold all of the information returned from the API:

CREATE TABLE [dbo].[TwitterUser]
(
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[ContributorsEnabled] [bit] NULL,
	[CreatedAt] [datetime] NULL,
	[DefaultProfile] [bit] NULL,
	[DefaultProfileImage] [bit] NULL,
	[Description] [varchar](512) NULL,
	[FavouritesCount] [bigint] NULL,
	[FollowRequestSent] [bit] NULL,
	[FollowersCount] [bigint] NULL,
	[Following] [bit] NULL,
	[FriendsCount] [bigint] NULL,
	[GeoEnabled] [bit] NULL,
	[TwitterID] [varchar](160) NULL,
	[IdStr] [varchar](160) NULL,
	[IsTranslator] [bit] NULL,
	[Lang] [varchar](40) NULL,
	[ListedCount] [bigint] NULL,
	[Location] [varchar](360) NULL,
	[Name] [varchar](220) NULL,
	[Notifications] [bit] NULL,
	[ProfileBackgroundColour] [varchar](120) NULL,
	[ProfileBackgroundImageURL] [varchar](940) NULL,
	[ProfileBackgoundImageURLHttps] [varchar](940) NULL,
	[ProfileBackgroundImageTile] [bit] NULL,
	[ProfileImageURL] [varchar](1600) NULL,
	[ProfileImageURLHttps] [varchar](1640) NULL,
	[ProfileLinkColour] [varchar](120) NULL,
	[ProfileSidebarBorderColour] [varchar](120) NULL,
	[ProfileSidebarFillColour] [varchar](120) NULL,
	[ProfileTextColour] [varchar](120) NULL,
	[ProfileUseBackgroundImage] [bit] NULL,
	[Protected] [bit] NULL,
	[ScreenName] [varchar](160) NULL,
	[ShowAllInlineMedia] [varchar](100) NULL,
	[StatusContributors] [varchar](512) NULL,
	[StatusCoordinates] [varchar](512) NULL,
	[StatusCreatedAt] [datetime] NULL,
	[StatusFavourited] [bit] NULL,
	[StatusGeo] [varchar](512) NULL,
	[StatusID] [varchar](360) NULL,
	[StatusInReplyToScreenName] [varchar](512) NULL,
	[StatusInReplyToStatusID] [varchar](512) NULL,
	[StatusInReplyToUserID] [varchar](512) NULL,
	[StatusPlace] [varchar](512) NULL,
	[StatusPossiblySensitive] [varchar](100) NULL,
	[StatusRetweetCount] [bigint] NULL,
	[StatusRetweeted] [bit] NULL,
	[StatusSource] [varchar](1440) NULL,
	[StatusText] [varchar](1900) NULL,
	[StatusTruncated] [bit] NULL,
	[StatusesCount] [bigint] NULL,
	[TimeZone] [varchar](120) NULL,
	[URL] [varchar](480) NULL,
	[UTCOffset] [bigint] NULL,
	[Verified] [bit] NULL,
	[LastUpdated] [datetime] NULL DEFAULT (getdate())
) ON [PRIMARY]

conNevynTwitterAccount

This connection object connects the Informatica project to a Twitter account.

tdoNevynsTwitter

tdoNevynsTwitter is a Twitter Data Object connected to a Twitter account.

adoGetFollowers

This data object gets a lit of Followers for the Twitter account. It retrieves a list Twitter user IDs and nothing more.

adoGetUserDetails

adoGetUserDetails retrieves the details for one or more users. In the article above it retrieved information about a single user by setting the query parameter to something like user_id=123456 where 123456 represents the Twitter user ID to lookup.

The Query Parameter can be modified to read the IDs from a text file by setting the value to something like user_id=file:///c:\tmp\UserIDs.txt.

Nevyn's Twitter Data Object

Nevyn’s Twitter Data Object

Informatica will now use the file as the data source for the query.

ffUserIDs

ffUserIDs is a flat file object which will contain the Twitter IDs of the Followers to look up. This will be the same file specified in the Query Parameter of the adoGetUserDetails object.

Mappings

Two mappings are required to achieve the project goals, the first mapGetFollowers retrieves the full list of Followers for the Twitter account. The second mapping, mapUpdateTwitterUsersTable, looks up the new Followers and adds them to the local database.

mapGetFollowers

mapGetFollowers retrieves the full list of Followers for the Twitter account.

Get Followers Mapping

Get Followers Mapping

The User IDs are split into two groups by the mapping, the first group contains the IDs of the new Followers. These IDs are written to the flat file object ffUserIDs. The second group are used to update the local database to say that the IDs stored are still Followers on the date the mapping was run.

adoGetFollowers retrieves the full list of Followers for the Twitter account.

The list of Follower IDs are then used in lkpUser to see if the FollowerIDs retrieved exist in the local database. The IdStr field in the lookup will contain the ID of the Twitter user for Followers whose IDs are in the local database and will be null for new Followers.

expAddLastUpdatedField simply adds the current date/time to the record set.

rtrSplitExistingFromNewFollowers splits the records into two groups, new Followers and existing Followers. The IDs of new Followers are written to the flat file ffUserIDs. The IDs for existing followers are passed to the update strategy updExistingFollowers, this will update the local database by matching the Follower ID and updating the LastUpdated column in the database.

mapUpdateTwitterUsersTable

mapUpdateTwitterUsersTable reads the Follower IDs from the flat file ffUserIDs and passes them to Twitter in order to get the profile for the users. Twitter will send back the profile for each of the user IDs in the file.

Update Twitter Users Table Mapping

Update Twitter Users Table Mapping

The Required data is then extracted and converted from Twitter data (strings etc.) into SQL data types by the expression expConverTwitterDataToSQLDataTypes.

wfUpdateFollowersInformation

This workflow allows the two mappings to be executed:

Update Followers Information Workflow

Update Followers Information Workflow

The command tasks in the workflow simply audit the execution of the mappings recording start times, number of records etc. into a log file.

Data Types and Conversions

The current project simply looks at the number of Followers and the Twitter account names. Along the way the full data set returned by Twitter was examined and conversions for the various fields put together. If you wish to extend this project then you may find the following conversions useful.

Data TypeConversion/Expression
BooleanIIF(LOWER(boolean_field) = ‘true’, 1, 0)
BigIntIIF(IS_NUMBER(big_int_field), TO_BIGINT(big_int_field), 0)
Date / TimeIIF(ISNULL(date_field), NULL, to_date(substr(date_field, length(date_field) – 3) || ‘-‘ || substr(date_field, 5, 3) || ‘-‘ || substr(date_field, 9, 2) || ‘ ‘ || substr(date_field, 12, 8)))

Conclusion

The free Express edition of Informatica Powercentre allows the home user to experiment with the full power of Informatica. The social media connectors allow data to be retrieved and analysed locally as in this case.

If you’ve found this useful and wish to use the above you should remember to read the terms and conditions of the Twitter API and remember to respect the privacy of Twitter users.

Teensy 3.1 and Visual Micro

October 14th, 2014 • Electronics, Software DevelopmentComments Off on Teensy 3.1 and Visual Micro

September saw the end of an agonising few months (nearly a year in truth) working with a 32 x 32 LED matrix and smart LEDs (WS2811, Neopixels etc.). I’ve been trying to reliably control these LEDs with the STM32 Discovery board. It’s not that I cracked the problem with the STM32 but that I came across a cheap ready made solution to the problem, namely the Teensy 3.1. It has proved to be a painful reminder of something a software engineer should know, use libraries if you can. In the case of hardware prototyping the use of libraries also extends in part to the choice of hardware.

One of the main reasons I have resisted going down the route of using boards like the Teensy 3.1 is the Arduino IDE. I think that I have been spoiled by the richness of the Visual Studio IDE. Even IDES should as the IAR IDE for the STM8S leave something to be desired when you compare it to Visual Studio. This is the story of how I got the LEDs working and solved the IDE issue by using Visual Micro.

Problem Hardware

There are two pieces of hardware causing me problems at the moment:

  1. 32 x 32 LED Matrix
  2. 5mm Digital Addressable LEDs

These were purchased in the UK from Cool Components.

32 x 32 LED Matrix/Panel

The 32 x 32 LED matrix is often a component in larger displays.

LED Matrix

LED Matrix

The displays are easily chained together although the power requirements quickly increase. Each panel can consume up to 2A depending upon the number of LEDs illuminated as any time.

These boards are designed to be controlled by FPGAs allowing for high frame rates. Sparkfun and Adafruit have tutorials on running these boards and they have found that you can run a single panel at 16MHz (i.e. you can run it on an Arduino but only just). Running two or more panels requires more power, both RAM and processor speed.

Digitally Addressable LEDs

The LEDs in question use the WS2811 constant current IC to drive RGB LEDs. These controllers use their own one-wire protocol (not to be confused with the Dallas one-wire protocol) to determine the PWM characteristics of the red, green and blue components of the light output. Over the years these controllers have shown up in LED strings, LED strips and more recently as the Adafruit Neopixel and now as individual through hole LEDs.

The main problem I have found with these LEDs, or more specifically the controllers, is the one-wire protocol. This requires very precise timing and this is often difficult to obtain on the hobbyist microcontrollers without a lot of very precise control.

Solving the problem

I decided to solve this problem using the Teensy 3.1. The thing which makes the Teensy 3.1 very attractive for this type of project is the add-on board, the SmartMatrix Shield. This board allows you to connect a Teensy directly on to the IDC headers of the LED Matrix.

In addition to the hardware, the Teensy 3.1 library contains ports of the equivalent Arduino libraries for the 32 x 32 LED matrix as well as LEDs controlled by the WS2811.

Teensy 3.1

The Teensy has been through several iterations, the Teensy 3.1 being the latest at the time of writing. The board is small, only 35mm x 18mm, and I was not really prepared for how small it actually is!

TeensyAndRuler

Small does not mean low powered though, the board packs a Cortex-M4 processor, 256K flash and 64K RAM. The 72MHz processor can also be overclocked to over 90MHz. Other features include:

  • 34 Digital I/O (3.3V but 5V tolerant)
  • 21 analog inputs
  • 12 PWM
  • SPI, I2C, CAN bus
  • ARM DSP extension

The foot print of the board allows it to be easily added to a breadboard.

The Teensy 3.1 board is also supplied with it’s own libraries in the form of Teensyduino. This is an add-on for the Arduino IDE and is compatible with many of the Arduino libraries. The Arduino compatible libraries provide the ability to work with the LED matrix and the addressable LEDs mentioned above as well as a wide variety of other devices.

Software

As I have stated earlier, I am not really a fan of the Arduino IDE. Do not misinterpret me, it represents a great piece of work, however the amount of time and money Microsoft have poured into Visual Studio over the years means that Visual Studio really does take some beating. This is where Visual Micro comes in.

Visual Micro is a Visual Studio 2008-2013 add-in which allows the development of Arduino sketches within the Visual Studio framework. This gives you all the power of Visual Studio (code refactoring etc.) while still allowing the development of Arduino sketches. The add-in is free until the end of 2014 with the option to purchase the debugger support for a nominal fee.

The add-in supports a good number of boards out of the box including the Teensy 3.1.

Setup is simple and the documentation is really good. There are a few additional steps for the Teensy 3.1 but this is cover in the Tips for Teensy page.

Once configured it was a simple task to create a new Teensy 3.1 project using the Neopixel sketch template. A little code reformatting gives the following code:

#include <Adafruit_NeoPixel.h>

//
//	Parameter 1 = number of pixels in strip
//	Parameter 2 = pin number (most are valid)
//	Parameter 3 = pixel type flags, add together as needed:
//		NEO_RGB     Pixels are wired for RGB bitstream
//		NEO_GRB     Pixels are wired for GRB bitstream
//		NEO_KHZ400  400 KHz bitstream (e.g. FLORA pixels)
//		NEO_KHZ800  800 KHz bitstream (e.g. High Density LED strip)
//
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, 6, NEO_RGB + NEO_KHZ800);

//
//	Input a value 0 to 255 to get a color value.
//	The colours are a transition r - g - b - back to r.
//
uint32_t Wheel(byte WheelPos)
{
	if (WheelPos < 85)
	{
		return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
	}
	else if (WheelPos < 170)
	{
		WheelPos -= 85;
		return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
	}
	else
	{
		WheelPos -= 170;
		return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
	}
}

//
//	Fill the dots one after the other with a colour
//
void colorWipe(uint32_t c, uint8_t wait)
{
	for (uint16_t i = 0; i < strip.numPixels(); i++)
	{
		strip.setPixelColor(i, c);
		strip.show();
		delay(wait);
	}
}

//
//  Set all LEDs to the same colour.
//
void rainbow(uint8_t wait)
{
	uint16_t i, j;

	for (j = 0; j < 256; j++)
	{
		for (i = 0; i < strip.numPixels(); i++)
		{
			strip.setPixelColor(i, Wheel((i + j) & 255));
		}
		strip.show();
		delay(wait);
	}
}

//
//	Slightly different, this makes the rainbow equally distributed throughout
//
void rainbowCycle(uint8_t wait)
{
	uint16_t i, j;

	for (j = 0; j < 256 * 5; j++)
	{ // 5 cycles of all colors on wheel
		for (i = 0; i < strip.numPixels(); i++)
		{
			strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
		}
		strip.show();
		delay(wait);
	}
}

//
//	Executed once at startup to set up the board.
//
void setup()
{
	strip.begin();
	strip.show(); // Initialize all pixels to 'off'
}

//
//	The actual main program loop.
//
void loop()
{
	rainbow(20);
	rainbowCycle(20);
}

This code creates an Adafruit_NeoPixel object with the pixels connected to pin 6 of the Teensy 3.1.

Connecting some LEDs top the Teensy and uploading the above code results in the following:

Modifying the code to add a counter will allow testing of the debugging features. The loop code is changed to have a counter variable to test the conditional breakpoint feature:

int count = 0;

//
//	The actual main program loop.
//
void loop()
{
	count++;
	digitalWrite(LED_PIN, HIGH);
	delay(500);
	digitalWrite(LED_PIN, LOW);
	delay(500);
	// Some example procedures showing how to display to the pixels:
	rainbow(20);
	rainbowCycle(20);
}

Adding a breakpoint after the counter increment and making the breakpoint conditional results in the following display:

Sample code

Sample Code With Breakpoint

Nothing too strange here, pretty much standard for Visual Studio. The breakpoint window shows the breakpoint and the condition:

Debugger Breakpoint Window

Debugger Breakpoint Window

Running the code brings up the debugger expression window:

Debugger Expression Window

Debugger Expression Window

The one thing which experienced users of Visual Studio will find unfamiliar is the way in which single stepping works. Unlike say Win32 development where single stepping takes you to the next line of code and then waits, with Visual Micro, single stepping runs the code to the next breakpoint.

Conclusion

Visual Studio is without doubt one of the best software development environments available. The addition of Visual Micro brings the power of Visual Studio to the Arduino world. Whilst the debugging features (single stepping) may a little unfamiliar to Visual Studio users they are not so strange as to be unusable.

Both the Teensy 3.1 and Visual Micro are a welcome addition to the development tools and it is highly likely that they will appear in future posts.

Using the Electric Imp

September 1st, 2014 • Electronics, Internet of Things, Software DevelopmentComments Off on Using the Electric Imp

A few weeks ago I acquired and Electric Imp as I was interested in how this could be used to prototype and connect a device to the Internet. Such a device would become part of the Internet of Things.

In order to investigate the possibility of using this device I decided to monitor the temperate of my office and log the data on the Internet.

Electric Imp

Electric Imp offers a starting point for hardware and software engineers wishing to develop for the Internet of Things. The simplest format for prototyping is probably the Imp001 and a breakout board. The Imp001 is an Electric Imp in SD format. The card is fully FCC certified and so offers a simple way of using a wireless network to connect a project to the Internet. The SD card contains a Cortex-M3 processor, 802.11b/g/n transceiver and antenna and once connected to a WiFi network with internet connectivity can be programmed using the cloud based development environment.

The Electric Imp is connected to a WiFi network using an application called BlinkUp. This application is a free download for iPhone and Android. The phone application takes details about the local WiFi network (name, password etc.) and sends this to the Imp001 by blinking the screen (hence the name BlinkUp) whilst the phone is held against the LED on the SD card.

Software for the Electric Imp is developed using the online IDE provided by Electric Imp. The development environment offers the ability to develop code which runs on the Electric Imp (Device code) itself as well as a component which runs in the cloud (Agent code). A user account is setup by following the link to the Log in page from the Electric Imp web site.

Sparkfun Data Logging Service

Sparkfun have recently started to provide a cloud based data logging service which is free to use for a limited amount of data. The system uses a circular 50 MByte data store for each data stream. Sign up is simple, just follow the Create link from the main page. The data streams can be both public and private. If the amount of data storage required is greater than 50MB or the application requires a greater level of privacy then the source is freely available for download by following the DFeploy link from the main page.

Once created, the data stream is accessed using public and private keys, the private keys allow data to be written to the data stream. The public key allows the public data stream to be viewed or data to be retrieved for use in say charting.

Temperature Logging

The principles involved in linking the local hardware to the Sparkfun cloud server will be illustrated by logging the local temperature using a temperature sensor and then sending the data to Sparkfun’s servers. The data will be retrieved and displayed on a web page.

Hardware

The bill of materials (BOM) for this project is as follows:

  1. Electric Imp (Imp001)
  2. Electric Imp Breakout board
  3. LM35 Temperature Sensor
  4. LED and current limiting resistor
  5. Connectors
  6. Breadboard and miscellaneous wire
  7. USB cable and power supply (your computer can act as a power supply if necessary)

Solder the connectors to the breakout board and insert the connectors into the breadboard.

Next, connect the current limiting resistor to Pin 9 on the breakout board and the LED. Connect the other leg of the LED through to ground.

Finally connect the LM35 temperature sensor to Vcc, Ground and the sensor output to Pin 2 of the breakout board.

Follow the instructions on the Electric Imp web site for downloading the BlinkUp software and configuring the Electric Imp. Configure the Imp and connect to the local WiFi network.

Electric Imp Software

The Electric Imp software is split into two components:

  1. Device code running on the Electric Imp hardware
  2. Agent code which runs on the Electric Imp cloud servers

The first step is to register with the Electric Imp web site for a developer account. Once completed you will be presented with the developer IDE.

To record the temperature the Electric Imp will record the temperature every 5 seconds. These readings will be summed and averaged over a one minute period. The average will sent to the Electric Imp servers, the average cleared and the whole process will restart. This will provide a continuous stream of temperature readings while the Imp is powered.

The Electric Imp servers will run Agent code which will listen for data/commands from the Electric Imp device code. The Agent on the server will then post the data to the Sparkfun server.

The code for the Device and the Agent is written in a C like language called Squirrel.

Device Code

The analog port on the Electric Imp returns a value in the range 0 to 65,535. The maximum value represents a voltage of 3.3V. The temperature sensor selected outputs 10mv per degree C. The maximum range of the values for this sensor is 0V to 1.55V given the operating range for the LM35.

The LED has been added to demonstrate when the board is taking a temperature reading. The LED will flash each time a reading is taken.

Firstly, some space in needed for the supporting variables:

//
//  Create a global variable to allow control of the LED.
//
led <- hardware.pin9;
//
//  Create a global variable for the temperature sensor.
//
temperatureSensor <- hardware.pin2;
// 
//  Configure led to be a digital output.
//
led.configure(DIGITAL_OUT);
//
//  Configure the temperature sensor to be an analog input.
//
temperatureSensor.configure(ANALOG_IN);
//
//  This name will be sent to the stream each update:
//
local impName = "Imp%20" + imp.getmacaddress();
//
//  Variables related to averaging the temperature.
//
readingCount <- 0;
readingSum <- 0.0;
// 
//  Create a global variables for the sensor readings.
//
temperature <- 0.0;
sensorValue <- 0;

Next, the main application loop (function), the first thing this should do is to turn on the LED to show that the application is active:

//
//  Main program loop.
//
function main()
{
    led.write(1);

Next, take the temperature sensor reading and convert to centigrade and add to the ongoing sum:

    sensorValue = temperatureSensor.read();
    temperature = ((sensorValue * 3.3) / 65535) * 100;
    readingCount++;
    readingSum += temperature;

Next, check if the number of readings has reached 12 (60 seconds). If we have 12 readings then take the average and send this to the Electric Imp Agent:

    if (readingCount == 12)
    {
        local average = readingSum / readingCount;
        server.log("Average temperature = " + average);
        local data = "";
        data = "Temperature=" + average;
        agent.send("postData", data);
        readingCount = 0;
        readingSum = 0.0;
    }

The server.log statement sends the logging information to the servers. This is not used anywhere, simply logged. The data is sent to the Agent in the agent.send(“postData”, data) statement.

Next, pause and then turn the LED off:

    imp.sleep(0.5);
    led.write(0);

The whole process should be repeated 4.5 seconds later (remember there is a 0.5 seconds pause above) to take readings every 5 seconds.

    //
    //  Schedule imp to wakeup and repeat.
    //
    imp.wakeup(4.5, main);
}

Finally the main loop should be executed:

//
//  Start the main program loop.
//
main();

Agent Code

The Agent code is responsible for listening for data from the device. The code used is actually provided by Sparkfun and is produced here more or less unaltered:

/*****************************************************************
Phant Imp (Agent)
Post data to SparkFun's data stream server system (phant) using
an Electric Imp
Jim Lindblom @ SparkFun Electronics
Original Creation Date: July 7, 2014

Description

Before uploading this sketch, there are a number of vars that need adjusting:
1. Phant Stuff: Fill in your data stream's public, private, and 
data keys before uploading!

This code is beerware; if you see me (or any other SparkFun 
employee) at the local, and you've found our code helpful, please 
buy us a round!

Distributed as-is; no warranty is given.
*****************************************************************/

//
//  Phant Stuff configuration information.
//
local publicKey = "Your-Public-Key-Goes-Here";      // Your Phant public key
local privateKey = "Your-Private-Key-Goes-Here";    // Your Phant private key
local phantServer = "data.sparkfun.com";            // Your Phant server, base URL, no HTTP

//
//  When the agent receives a "postData" string from the device, use the
//  dataString string to construct a HTTP POST, and send it to the server.
//
device.on("postData", function(dataString)
    {
        server.log("Sending " + dataString); // Print a debug message
        //
        //  Construct the base URL: https://data.sparkfun.com/input/PUBLIC_KEY:
        //
        local phantURL = "https://" +  phantServer + "/input/" + publicKey;
        //
        //  Construct the headers: e.g. "Phant-Private-Key: PRIVATE_KEY"
        //
        local phantHeaders = {"Phant-Private-Key": privateKey, "connection": "close"};
        //
        //  Send the POST to phantURL, with phantHeaders, and dataString data.
        //
        local request = http.post(phantURL, phantHeaders, dataString);
        //
        //  Get the response from the server, and send it out the debug window:
        //
        local response = request.sendsync();
        server.log("Phant response: " + response.body);
    }
);

The code is clearly commented and self explanatory.

Results

Building the above in the IDE should deploy the device code to the Electric Imp should result in the temperature being collected by the device, sent to the Electric Imp server and then from there on to Sparkfun’s servers. The data can be viewed in it’s raw form by browsing to your data stream using a URL such as: https://data.sparkfun.com/streams/Your-Public-Key-Here. This URL will have been supplied on the account creation page for the Sparkfun data service.

The data can also be retrieved using Javascript:

<!DOCTYPE html>
<html>
  <head>
    <!-- EXTERNAL LIBS-->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="https://www.google.com/jsapi"></script>

    <!-- EXAMPLE SCRIPT -->
    <script>

      // onload callback
      function drawChart() {

        var public_key = 'Your-Public-Key-Here';

        // JSONP request
        var jsonData = $.ajax({
          url: 'https://data.sparkfun.com/output/' + public_key + '.json',
          data: {page: 1},
          dataType: 'jsonp',
        }).done(function (results) {

          var data = new google.visualization.DataTable();

          data.addColumn('datetime', 'Time');
          data.addColumn('number', 'Temperature');

          $.each(results, function (i, row) {
            data.addRow([
              (new Date(row.timestamp)),
              parseFloat(row.Temperature),
            ]);
          });

          var chart = new google.visualization.LineChart($('#chart').get(0));

          chart.draw(data, {
            title: 'Room Temperature', height: 500, is3D: true
          });

        });

      }

      // load chart lib
      google.load('visualization', '1', {
        packages: ['corechart']
      });

      // call drawChart once google charts is loaded
      google.setOnLoadCallback(drawChart);

    </script>
  </head>
  <body>
    <div id="chart" style="width: 100%;"></div>
  </body>
</html>

The above uses Google’s chart API to generate a chart from the data stored in the Sparkfun servers (thanks for Sparkfun for the code). Save the above page to a web server and browse to the page and you will see something like the following:

Room Temperature Chart

Room Temperature Chart

The chart shows the fall and rise in temperature in a room over a period of 48 hours.

Conclusion

The Electric Imp offers a simple method for connecting a device to the Electric Imp servers on the Internet. The Agent code can then pass this data on to services provided by additional third parties.

The device options used here would increase the cost of any device produced but as a proof of concept they offer a simple and convenient way of demonstrating how a device can interact with the outside world over the Internet. This example took less than 3 hours to research, build and complete – good going considering how complex this would be if this were completed in a conventional manner (Arduino, WiFly shield etc.).

Intel Galileo – First Impressions

August 21st, 2014 • Electronics, Netduino, Software Development2 Comments »

Yesterday I received my Intel Galileo rev 1 board. I know the rev 2 board is available but recently the Windows on Devices program have release the necessary firmware etc to upgrade a Galileo rev 1 board to enable it to run Windows.

See the end of this article for an update added on 6th Sept 2014.

Upgrading the Firmware

The first step was to check athe firmware and upgrade it if necessary. In my case it was necessary. Intel provide a comprehensive set of instructions on how to do this. The upgrade process took about 10 minutes.

Creating a Windows SD Card

The next step is to write Windows to a micro SD card. This step of the process took the longest to complete, about 25-30 minutes.

Booting to Windows

The next step is to verify that Windows has loaded correctly. Insert the card, power on and then waiting for 2 minutes for Windows to boot. If successful you should be able to telnet to the device. I used PTTYPortable to do this and was presented with the login request.

Testing the Board

One of the first tests I normally perform is to deploy a Blinky application to the board. Once I am happy that I can deploy applications to the board I speed up Blinky by removing any code which would cause a pause. The result should be an indicator of the performance of the board and the software. So let’s give it a go.

The Galileo board offers two methods for deploying Blinky to the board:

  1. Arduino UI
  2. Visual Studio Windows application

The first method uses the board without the Windows SD card image, the second deploys a Windows application to the SD card and runs as a Windows application.

Our starting point for the tests will be the standard Blink application:

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
 
  This example code is in the public domain.
*/
 
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;

// the setup routine runs once when you press reset:
void setup()
{                
    // initialize the digital pin as an output.
    pinMode(led, OUTPUT);     
}

// the loop routine runs over and over again forever:
void loop()
{
    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(1000);               // wait for a second
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    delay(1000);               // wait for a second
}

Arduino UI

Using this application in the Arduino UI for the Galileo is simple. In fact the application is one of the samples (File -> Examples -> 01.Basics -> Blink). Loading this sketch and deploying to the Galileo starts the on board LED blinking at a steadily at 1 Hz.

Visual Studio Windows Application

Microsoft have provided a Wiring API for use in Visual Studio. This allows access to the “Arduino” hardware from a Windows application. An equivalent Windows version of the same application is:

#include "stdafx.h"
#include "arduino.h"

int _tmain(int argc, _TCHAR* argv[])
{
    return RunArduinoSketch();
}

int led = 13;  // This is the pin the LED is attached to.

void setup()
{
    pinMode(led, OUTPUT); // Configure the pin for OUTPUT so you can turn on the LED.
}

// the loop routine runs over and over again forever:
void loop()
{
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    Log(L"LED OFF\n");
    delay(1000);               // wait for a second
    digitalWrite(led, HIGH);    // turn the LED on by making the voltage HIGH
    Log(L"LED ON\n");
    delay(1000);               // wait for a second
}

Much of the application looks the same as the Arduino application. The main differences are the addition of the _tmain method and the Log statement. The _tmain method acts as the entry point for the application and provides a method for running an Arduino sketch (as above) or some other program logic.

The Log statements generate debug information which is displayed in Visual Studio’s Output window.

Deploying this application to the board results in… NOTHING!

Digging a little deeper into the examples section of the web site reveals the On Board LED example:

// Main.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "arduino.h"

int _tmain(int argc, _TCHAR* argv[])
{
    return RunArduinoSketch();
}

//This application flashes the on board LED of the Galileo board by calling GPIO functions directly in the embprpusr.dll instead of using the Arduino layer.

ULONG state = LOW; // keeps track of the state of the on-board LED

void setup()
{
    GpioSetDir(LED_BUILTIN, OUTPUT); // Sets the pin to output
}

void loop()
{
    if (HIGH == state)
    {
	    state = LOW;
	} 
    else
    {
	    state = HIGH;
	}
    GpioWrite(LED_BUILTIN, state); // Writes to the pin, setting its value either HIGH (on) or LOW (off)
    Log(L"LED %s\n", (HIGH == state ? L"ON" : L"OFF"));
    Sleep(1000);
}

Compiling and deploying this application to the board results in the steady flashing of the on board LED.

Reverting to the previous sample and hooking up an oscilloscope reveals that pin 13 is indeed being toggled at 1 Hz it is just not connected directly to the on board LED.

How Fast Can We Go?

Now to find out how fast the board will actually run. Starting with the Arduino example, remove the delay statements, recompile and deploy to the board. Doing this resulted in a square wave with a 50% duty cycle and a frequency of 221Hz. That’s right Hz, not KHz!

Removing the delay and logging statements and deploying the Windows application results in a square wave. This time a 60Hz square wave with a 30% duty cycle is displayed on the oscilloscope.

There must be something wrong. Surely this board with a 400MHz processor should run faster than this.

What About the Netduino?

The Netduino has always had one issue when compared with Arduino and other similar board. Namely it is running interpreted code which is not real time due to the nature of the .NET Microframework and the way the framework runs. I have performed similar tests and I was convinced that it was faster. Only one way to find out, deploy some code to the Netduino Plus 2. This board runs the .NET Microframework on the STM32 family of microcontrollers at 168 MHz. The equivalent code to the two examples above is:

using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;

namespace NetduinoPlus2
{
    public class Program
    {
        public static void Main()
        {
            OutputPort dp = new OutputPort(Pins.GPIO_PIN_D13, false);
            while (true)
            {
                dp.Write(!dp.Read());
            }
        }
    }
}

Deploying this to the Netduino PLus 2 and hooking up the oscilloscope results in a square wave with a 50% duty cycle at 17.8 KHz.

Much faster than the Galileo board.

Update 6th Sept 2014

I have been looking through the schematic for the Galileo Rev 1 board and found that not all of the GPIO pins are connected through the CY8C9540A chip but are in fact connected directly top the Quark processor. These GPIOs should be capable of higher speeds. A quick test shows that these pins (Digital 2, 3 & 10) can all generate a 1.16 KHz square wave for an application compiled in debug mode. Compiling the same applications in release more and running the application on the Galileo increases the frequency from 1.16 KHz to 1.27 KHz.

Conclusion

I’ve only had this board for a few hours but I have deployed a few of the examples. The raw GPIO speed appears lower than the interpreted .NET Microframework equivalent. The Galileo has access to a network port with easy access to the Arduino Wiring API but then so does the .NET Microframework.

Some further investigation is required I believe.

Strictly Yorkshire Photography Exhibition

August 18th, 2014 • PhotographyComments Off on Strictly Yorkshire Photography Exhibition

A few weeks ago I had the opportunity to add some of my photos in a local exhibition of photographs taken in Yorkshire. I don’t know what came over me but I said yes. Never done this sort of thing before but nothing ventured, nothing gained. And so on Saturday 9th and Sunday 10th August I found myself exhibiting seven photographs along with about 30 other members of the Strictly Yorkshire Photography group at the Corn Exchange in Leeds.

The group rules are simple, all photographs must be taken in Yorkshire and the photographs should not contain any offensive or illegal material.

The Exhibition

The group membership is diverse and as with all groups, some members are more active than others. The range of skills and interests are varied. My own particular fields of interest are wildlife and technology as you can see by the photographs I exhibited:

Mark Stevens Display at Yorkshire Photography Exhibition

Mark Stevens Display at Yorkshire Photography Exhibition

Spirit of Yorkshire

This image was taken at a vintage Rolls Royce and Bently gathering at Castle Howard on 1st June 2014. The weather was brilliant and I could not resist taking this image of the Spirit of Ecstasy against and almost clear blue sky.

Spirit of Yorkshire

Spirit of Yorkshire

Three Months

This photograph was taken in April of 2014 in the gardens around the York Museum.

Three Months

Three Months

This is one of the few images I have actually modified to any degree. I wanted to bring out the contrast in the wall at the back of the image whilst highlighting the right-hand headstone. This was achived by using a black and white conversion in Paintshop Pro along with HDR.

Doe at Studley Royal Park

This doe was captured early in the morning on 9th April at Studley Royal Park.

Doe at Studley Royal Park

Doe at Studley Royal Park

Sonny and Charlie

Sonny and Charlie are both resue dogs owned by a friend. I took a series of over 200 images of the two dogs when out walking in mid-April this year.

Sonny in Bluebells

Sonny in Bluebells

Sonny in Bluebells

Where’s the Ball?? WHERE’s THE BALL!!!

Where's the Ball?? WHERE'S THE BALL!!

Where’s the Ball?? WHERE’S THE BALL!!

Tornado Leaving York

Tornado is the last locomotive to be built in the UK as of the time of writing. This locomotive first steamed in 2008.

Tornado Leaving York

Tornado Leaving York

This is also an example of of an image which I manipulated. For this image I converted the image to black and white and then used this to mask off all of the original image except for Tornado itself.

Just Grabbing a Takeout For the Kids

This last image shows a Robin feeding in our garden. This was taken early in the year when the young robins had just hatched.

RobinFeeding

What Did I Learn?

I have always enjoyed photography being an enthusiastic amateur. Looking at the images that were on display gave me some ideas about perspective and angles to improve the quality of the photographs I take.

Looking back I think I over prepared and I could have made my display just as effective but with a simple change such as not using frames and just using mounts.

I felt that the day was an overall success. Would I do it again? Yes, without a doubt.

Finally, I’d just like to thank everyone who took part, a great bunch of people.

Window Watchdog

July 5th, 2014 • Electronics, Software Development, STM8Comments Off on Window Watchdog

Window watchdogs provide a mechanism for detecting software failures in two ways, firstly an early reset of the watchdog and secondly a failure to reset the watchdog in time. In this post we investigate how the window watchdog can be use and illustrate with some examples.

Hardware

Window Watchdog Control Register – WWDG_CR

This register has two components, the timer counter and the enable bit (WWDG_CR_WDGA – see below). The microcontroller will be reset when one of two possible conditions:

  • The counter switches from 0x40 to 0x3f (i.e. bit 6 in the counter changes from 1 to 0)
  • The counter is reset when the counter value is greater than the watchdog window register

Writing 0 to bit 6 will cause the microcontroller to be reset immediately.

Assuming that WWDG_WR contains the default reset value then the time out period (in milliseconds) is defined as follows:

tWWDG = tCPU * 12288 * (WWDG_CR & 0x3f)

where tCPU = 1 / fmaster

On the STM8S running at 16MHz a value of 0x40 represents one count which is equal to 0.768ms. So at 16MHz the time out period is:

tWWDG = 0.768 * (WWDG_CR & 0x3f)

Window Watchdog Enable Register – WWDG_CR_WDGA

Switch the Window Watchdog on (set to 1) or off (set to 0).

Window Watchdog Window Register – WWDG_WR

This register defines a time period where the watchdog counter should not be reset. If the counter (WWDG_CR) is reset when the counter value is greater than the value in this register the microcontroller will be reset. This can be illustrated as follows:

Watchdog Sequence

Watchdog Sequence

We can calculate the value of tWindowStart and ttimeout as follows (assuming a 16MHz clock):

tWindowStart = 0.768 * ((WWDG_CRinitial & 0x3f) – WWDG_WR)

and

ttimeout = 0.768 * (WWDG_CR & 0x3f)

where WWDG_CRinitial is the initial value in the WWDG_CR register.

The default reset value for this register is 0x7f which means that the counter can be reset at any time. In this case, a reset will only be generated if the counter drops below 0x40.

One important point to note is that when the window register is used the value written to the counter (WWDG_CR) must be between 0xc0 and 0x7f. This causes the counter to be reset and the counter value to be reset simultaneously.

Software

The function of the Window Watchdog will be illustrated using the following three examples:

  • WWDG_CR not reset
  • WWDG_CR reset outside the reset window
  • WWDG_CR reset inside the reset window

The first thing we need to do is add some code which will be used in all of the examples.

Common Code

Firstly, lets add the code which will be common to all of the examples:

//
//  This program demonstrates how to use the Window Watchdog on 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.
}

This code has been used many times in The Way of the Register series of posts. It simply sets the system clock to the high speed internal clock and configures Port D for output.

Example 1 – Continuous Reset

This example sets the Windows Watchdog running and then waits for the watchdog to trigger the system reset. We indicate that the application is running by generating a pulse on Port D, pin 2.

//--------------------------------------------------------------------------------
//
//  Initialise the Windows Watchdog.
//
void InitialiseWWDG()
{
    PD_ODR_ODR2 = 1;
    __no_operation();
    __no_operation();
    __no_operation();
    __no_operation();
    PD_ODR_ODR2 = 0;
    WWDG_CR = 0xc0;
}

The __no_operation() instruction in the above code allow the pulse to stabilise on the pin.

The WWDG_CR is set to 0xc0 to both set the value in the counter and enable the watchdog at the same time. This sets bit 6 in the counter to 11 and the remaining bits to 0 (i.e. the counter is set to 0x40). The effect of this is that the first down count event will cause bit 6 to be cleared and the counter to be set to 0x3f. This will trigger the reset event.

The main program simply sets everything up and then “pauses” by simply waiting for an interrupt:

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

If we run this application and connect PD2 and NRST to the oscilloscope we see the following trace:

Initial continuous reset

Initial continuous reset

The yellow trace shows the reset line being pulled low and gradually returning to high. The drop shows where the watchdog has caused the reset pin to be pulled low and the microcontroller to be reset. The blue trace shows the pulse on PD2. If we measure the time difference between the pulse on PD2 and the time that the reset pin is pulled low we find that this is 770uS. This is very close to the time for one count, 768uS.

To verify this we can change the value in the counter to say 0x48. In this case we should see the watchdog running for 9 counts and the system running for 6.912mS. Changing WWDG_CR = 0xc0 to WWDG_CR = 0xc8 gives the following output on the oscilloscope:

Continuous reset

Continuous reset

Measuring the time difference we get a value of 6.9mS.

Example 2 – Reset Outside Watchdog Window

Using the above code as a starting point we will look at the effects of the watch dog window register (WWDG_WR). This defines when the application is allowed to change the value in the counter. The first task is to change the initial value in the control register to 0xc1 and verify that we get a rest every 1.54ms (2 x timer period). So change the InitialiseWWDG method to the following:

void InitialiseWWDG()
{
    PD_ODR_ODR2 = 1;
    __no_operation();
    __no_operation();
    __no_operation();
    __no_operation();
    PD_ODR_ODR2 = 0;
    WWDG_CR = 0xc1;
}

Running this application on the STM8S Discovery board results in the following traces:

Reset Outside window

Reset Outside window

Now we have two counts (1.54mS) in order to change the value in the control register. First task is to modify the InitialiseWWDG method to define the window. We will define this to be 0x40:

void InitialiseWWDG()
{
    PD_ODR_ODR2 = 1;
    __no_operation();
    __no_operation();
    __no_operation();
    __no_operation();
    PD_ODR_ODR2 = 0;
    WWDG_CR = 0xc1;
    WWDG_WR = 0x40;
}

This means that for the first 768uS the control register should not be changed. If the register is changed during this period a reset will be triggered. To demonstrate this we will change the value in the control register immediately after the microcontroller has been initialised:

int main()
{
    //
    //  Initialise the system.
    //
    __disable_interrupt();
    InitialiseSystemClock();
    InitialisePorts();
    InitialiseWWDG();
    __enable_interrupt();
    //
    //  Main program loop.
    //
    while (1)
    {
        WWDG_CR = 0xc1;             //  Trigger a reset.
        __wait_for_interrupt();
    }
}

Deploying this application to the microcontroller results in the following trace on the oscilloscope:

Watchdog immediate reset

Watchdog immediate reset

As you can see, the system is reset almost immediately (there is virtually no time between the pulse on PD2 and the reset line being pulled low).

Example 3 – Reset Within the Watchdog Window

Starting with the common code we initialise the Window Watchdog with the following method:

//--------------------------------------------------------------------------------
//
//  Initialise the Windows Watchdog.
//
void InitialiseWWDG()
{
    WWDG_CR = 0x5b;         //  Approx 70ms total window.
    WWDG_WR = 0x4c;         //  Approx 11.52ms window where cannot reset
    WWDG_CR_WDGA = 1;       //  Enable the watchdog.
}

This code defines a period of 11.52ms where we cannot reset the window watchdog counter followed by a period of 9.216ms during which the watchdog counter must be reset in order to prevent the microcontroller from being reset.

A simple main application loop would look something like this:

//--------------------------------------------------------------------------------
//
//  Main program loop.
//
int main()
{
    //
    //  Initialise the system.
    //
    __disable_interrupt();
    InitialiseSystemClock();
    InitialisePorts();
    PD_ODR_ODR4 = 1;
    __no_operation();
    PD_ODR_ODR4 = 0;
    InitialiseWWDG();
    __enable_interrupt();
    //
    //  Main program loop.
    //
    while (1)
    {
        unsigned char counter = (unsigned char) WWDG_CR;
        if ((counter & 0x7f) < WWDG_WR)
        {
            WWDG_CR = 0xdb;     //  Reset the Window Watchdog counter.
            PD_ODR_ODR2 = !PD_ODR_ODR2;
        }
        //
        //  Do something here.
        //
    }
}

The initial pulse on PD4 indicates that the application has started. We can use this to detect the reset of the microcontroller. In this trivial application the main program loop simply checks to see if the current value of the counter is less than the value in WWDG_WR. If the counter is less than WWDG_WR then the system write a new value into the counter. The value written is 0x5b anded with 0x80, this brings the reset value into the value range (0xc0 – 0xff).

This application also pulses PD2 to indicate that the watchdog counter has been reset.

Deploying this application and hooking up the Saleae logic analyser gives the following trace:

Window watchdog initial trace

Window watchdog initial trace

As you can see, there is an initial pulse showing that the board is reset (top trace) and then a series of pulses showing that the counter is being reset (lower trace). Each up/down transition represents a watchdog counter reset.

This is a relatively trivial example so let’s spice this up and add in a timer.

To the code above add the following code:

//--------------------------------------------------------------------------------
//
//  Setup Timer 2 to generate a 12.5ms interrupt.
//
void SetupTimer2()
{
    TIM2_PSCR = 0x02;       //  Prescaler = 4.
    TIM2_ARRH = 0xc3;       //  High byte of 50,000.
    TIM2_ARRL = 0x50;       //  Low byte of 50,000.
    TIM2_IER_UIE = 1;       //  Enable the update interrupts.
    TIM2_CR1_CEN = 1;       //  Finally enable the timer.
}

This method will set up timer 2 to generate an interrupt every 12.5ms.

Adding the following will catch the interrupt:

//--------------------------------------------------------------------------------
//
//  Timer 2 Overflow handler.
//
#pragma vector = TIM2_OVR_UIF_vector
__interrupt void TIM2_UPD_OVF_IRQHandler(void)
{
    if (_firstTime)
    {
        InitialiseWWDG();
        _firstTime = 0;
    }
    else
    {
        unsigned char counter = (unsigned char) WWDG_CR;
        unsigned char window = WWDG_WR;
        BitBangByte(counter & 0x7f);
        BitBangByte(window);
        WWDG_CR = 0xdb;     //  Reset the Window Watchdog counter.
        counter = (unsigned char) WWDG_CR;
        BitBangByte(counter);
    }
    PD_ODR_ODR2 = !PD_ODR_ODR2;
    TIM2_SR1_UIF = 0;       //  Reset the interrupt otherwise it will fire again straight away.
}

The interrupt will, on first invocation, initialise the window watchdog. Subsequent invocations will output the values of the registers and reset the window watchdog.

We need some code to bit bang the register values:

#define SR_CLOCK            PD_ODR_ODR5
#define SR_DATA             PD_ODR_ODR3

//
//  BitBang the data through the GPIO ports.
//
void BitBangByte(unsigned char b)
{
    //
    //  Initialise the clock and data lines into known states.
    //
    SR_DATA = 0;                    //  Set the data line low.
    SR_CLOCK = 0;                   //  Set the clock low.
    //
    //  Output the data.
    //
    for (int index = 7; index >= 0; index--)
    {
        SR_DATA = ((b >> index) & 0x01);
        SR_CLOCK = 1;               //  Send a clock pulse.
        __no_operation();
        SR_CLOCK = 0;
    }
    //
    //  Set the clock and data lines into a known state.
    //
    SR_CLOCK = 0;                   //  Set the clock low.
    SR_DATA = 0;
}

The main program loop needs to be modified to set up the timer and registers etc. So replace the main program loop with the following:

//--------------------------------------------------------------------------------
//
//  Main program loop.
//
int main()
{
    //
    //  Initialise the system.
    //
    __disable_interrupt();
    InitialiseSystemClock();
    InitialisePorts();
    PD_ODR_ODR4 = 1;
    __no_operation();
    PD_ODR_ODR4 = 0;
    SetupTimer2();
    InitialiseWWDG();
    __enable_interrupt();
    //
    //  Main program loop.
    //
    while (1)
    {
        __wait_for_interrupt();
    }
}

Deploying and running this application gives the following output on the Saleae logic analyser:

Window watchdog full trace

Window watchdog full trace

Zooming in produces the following

Window watchdog zoomed in

Window watchdog zoomed in

Starting with the top trace and descending we can see the following values:

  • Reset pulse
  • Timer 2 interrupt triggers (up/down transitions)
  • Data (register values)
  • Clock signal

The decoded register values can be seen above the data trace. The first value is the current value of the counter. The second value is the value in the window watchdog register and the final value is the new value in the counter register.

Conclusion

The Window Watchdog provides a mechanism for the developer to detect software faults similar to the Independent Watchdog but further constrains the developer by defining a window where a counter reset by the application is not allowed.

Interesting Book Offer

July 1st, 2014 • GeneralComments Off on Interesting Book Offer

Packt Publishing are celebrating their 10 year anniversary by offering any e-Book for $10 until 5th July.

I’ve read a few of their books recently as I’ve been experimenting with mobile development. Had a look at iOS and currently checking out Xamarin. Interestingly, the special offers also seem to apply. I noticed this afternoon that a couple of books (Raspberry Pi networking plus C Programming for Arduino) when purchased together would only cost £11.80.

I’m currently working my way through the iOS Xamarin Cookbook and that’s looking to be a really useful book. Next step is looking at the Android and Windows Phone equivalents.

STM8S Beep Function

July 1st, 2014 • Electronics, Software Development, STM8Comments Off on STM8S Beep Function

The beep function uses the 128 KHz LSI to generate a beep signal on the beep pin of the STM8S. This post demonstrates how to use this function.

Hardware

The signal is output on the beep pin. On the STM8S discovery board this is an alternative function on PD4 (port D, pin 4).

Setting the Alternative Function

The alternative functions are assigned to a pin by reprogramming the option bytes. This can be performed using the ST Visual Programmer. Start this application and read the option bytes from the STM8S:

Reading the options bytes using ST Visual Developer

Reading the options bytes using ST Visual Developer

  1. Select the STM8S model. On the STM8S Discovery board this is STM8S105C6
  2. Select the OPTION BYTE tab
  3. Read the option bytes from the microcontroller
  4. Check the value of the AFR7 bit

AFR7 needs to be set to Port D4 Alternative Function = BEEP on the STM8S Discovery board. This may be different on your STM8S so check the documentation for your microcontroller. To set the alternative function to beep:

Writing new options bytes using ST Visual Developer

Writing new options bytes using ST Visual Developer

  1. Select Port D4 Alternative Function = BEEP from the list of values in the alternative functions
  2. Write the option bytes back to the microcontroller

The beep function should now be assigned to PD4.

Registers

The beep function is controlled by three registers:

  • BEEP_CSR_BEEPSEL
  • BEEP_CSR_BEEPDIV
  • BEEP_CSR_BEEPEN

Beep Selection – BEEP_CSR_BEEPSEL

These bits set the multiplier for the Beep Divider.

BEEP_CSR_BEEPSELFrequency (KHz)
0fLSI / (8 * BEEPDIV)
1fLSI / (4 * BEEPDIV)
2 or 3fLSI / (2 * BEEPDIV)

Beep Divider – BEEP_CSR_BEEPDIV

Value for the prescalar divider.

BEEPDIV = 2 + BEEP_CSR_BEEPDIV

The documentation states that this should not be left set to the reset value (0x1f) and the value should be in the range 0 – 0x1e.

Beep Enable – BEEP_CSR_BEEPEN

Enable the beep function by setting this to 1, disable by setting this to 0.

Software

We start by providing a method for resetting the system clock to use the HSI:

//
//  This program demonstrates how to use the beep function on 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.
}

The next task is to configure the beep function:

//--------------------------------------------------------------------------------
//
//  Initialise the Beep function.
//
void InitialiseBeep()
{
    BEEP_CSR_BEEPEN = 0;    //  Turn off the beep.
    BEEP_CSR_BEEPSEL = 0;   //  Set beep to fls / (8 * BEEPDIV) KHz.
    BEEP_CSR_BEEPDIV = 0;   //  Set beep divider to 2.
    BEEP_CSR_BEEPEN = 1;    //  Re-enable the beep.
}

The final piece of code is the main program loop. This sets up the clock and the beep function:

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

Compiling this code and deploying to the STM8S results in the following trace on the oscilloscope:

Beep Oscilloscope Output

Beep Oscilloscope Output

As you can see, the output is not exactly 8KHz.

Conclusion

The documentation states that the beep function can generate 1KHz, 2KHz and 4KHz. By changing the values of the prescalar and the selection register it appears that you can also go as low as 500Hz and as high as 32KHz.

STM8S Independent Watchdog

June 21st, 2014 • Electronics, Software Development, STM8Comments Off on STM8S Independent Watchdog

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:

ValueDescription
0x55Enable writing to the IWDG_PR and IWDG_RLR registers. These registers are write protected by default.
0xAAReset 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.
0xCCEnable and start the IWDG.
0x00Disable 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 DividerPrescaler Value (IWDG_PR)Period when RL = 0Period when RL = 0xff
4062.5 us15.90 ms
81125 us31.90 ms
162250 us63.70 ms
323500 us127 ms
6441.00 ms255 ms
12852.00 ms510 ms
25664.00 ms1.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:

TTimeout period
TLSI1 / fLSI
P2(IWDG_PR + 2)
RIWDG_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.

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

June 20th, 2014 • Electronics, Software Development, STM8Comments Off on Auto-Wakeup on the STM8S

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 = ffLS = 128 kHzAWU_TBR_AWUTBAPRDIV FormulaAWU_APR_APR Range
2/f – 64/f0.015625 ms – 0.5 ms0001APRDIV/fLS2 to 64
2×32/f – 2x2x32/f0.5 ms – 1.0 ms00102 x APRDIV/fLS32 to 64
2×64/f – 2x2x64/f1 ms – 2 ms001122 x APRDIV/fLS32 to 64
22×64/f – 22×128/f2 ms – 4ms010023 x APRDIV/fLS32 to 64
23×64/f – 23×128/f4 ms – 8 ms010124 x APRDIV/fLS32 to 64
24×64/f – 24×128/f8 ms – 16 ms011025 x APRDIV/fLS32 to 64
25×64/f – 25×128/f16 ms – 32 ms011126 x APRDIV/fLS32 to 64
26×64/f – 26×128/f32 ms – 64 ms100027 x APRDIV/fLS32 to 64
27×64/f – 27×128/f64 ms – 128 ms100128 x APRDIV/fLS32 to 64
28×64/f – 28×128/f128 ms – 256 ms101029 x APRDIV/fLS32 to 64
29×64/f – 29×128/f256 ms – 512 ms1011210 x APRDIV/fLS32 to 64
210×64/f – 210×128/f512 ms – 1.024 s1100211 x APRDIV/fLS32 to 64
211×64/f – 211×128/f1.024 s – 2.048 s1101212 x APRDIV/fLS32 to 64
211×130/f – 211×320/f2.080 s – 5.120 s11105 x 211 x APRDIV/fLS26 to 64
211×330/f – 212×960/f5.280 s – 30.720s111130 x 211 x APRDIV/fLS11 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.