RSS

Archive for the ‘STM8’ Category

Using the UART on the STM8S

Monday, August 27th, 2012

We have previously seen how to configure the STM8S using the STD Peripheral Library using both the high level API and the slightly lower level register access. In this post we will use the definitions in the iostm8s103f3.h and we will also have a look at the registers which control the UART ending with a program which uses the UART to send data to a terminal emulator on a desktop computer (in my case a PC). We must remember that while this article looks at low speed communications with a PC, the UARTs on the STM8S have a variety of uses covering other protocols other than those discussed in this article.

The definition of the problem is simple, allow the STM8S to send debug information to a terminal emulator running at 115200,n,8,1 (for all of those who remember DOS MODE commands for serial communication).

It should be noted that UART1 is not available on the STM8S Discovery board and so UART2 is used instead.

The Registers

In order to set up the UART we will need to perform the following tasks:

  1. set the parity and number of data bits
  2. set the parity
  3. set the number of stop bits
  4. setup the baud rate
  5. set the clock polarity etc.

It is important to remember that transmission and reception must both be disabled before we start to change these registers.

UART_CR1 – Data Bits and Parity

The number of data bits is selected using the M bit of CR1. This can be set to either 8 or 9 bits. We will be using 8 data bits and so will need to set UART_CR1_M to 0 (setting to 1 would enable 9 data bits).

To set the parity we would set PCEN and PS. In our case we are disabling parity so only need to worry about PCEN (Parity Control Enable). Setting this bit to 0 will disable the parity calculation.

UART_CR3 – Number of Stop Bits and Clock Settings

The number of stop bits can be set to 1, 1.5 or 2. This is controlled by setting UART_CR3_STOP to one of the following values:

ValueDescription
001 Stop bit
01Reserved
102 Stop bits
111.5 Stop bits

Next, we need to consider the clock settings.

UART_CR3_CPOL determines the idle state of the clock, 0 sets the clock low when idle, 1 sets it high when idle.

UART_CR3_CPHA determines if the data should be stable on the rising or falling edge of the signal. Setting this to 0 means the data is set on the rising edge of the clock signal. Setting this to 1 means the data is ready on the falling edge of the clock signal.

UART_CR3_LBCL determines if the clock pulse for the last data bit is set to the clock pin. A 0 means the last clock pulse is not generated while 1 means that the pulse is generated.

UART_BRR1 & UART_BRR2 – Baud Rate Registers

The baud rate of the UART is controlled by dividing fmaster by the baud rate divisor. The result gives the clock speed of the serial port. In our case we have a 16 MHz clock speed for the microcontroller and we want the serial port to run at 115200 baud. So some simple rearranging of the formula and the UART divider is given by:

UART Divider= fmaster / baud rate
= 16,000,000 / 115,200
= 138
= 0x008a

Now we need to rearrange the number 0x008a a little in order to get the right bits into BRR1 and BRR2 (Baud Rate Register 1 & 2). This was written as a 32 bit number to illustrate how this is put into the registers. To do this we split the number (represented by d3d2d1d0) into three parts:

  • the first digit (d3) – 0
  • the next two digits (d2d1) – 08
  • the last digit (d0) – a

And set up the registers as follows:

BRR1= d2d1
= 0x08
BRR2= d3d0
= 0x0a

When setting these registers it is important to remember to set BRR2 before setting BRR2.

UART_CR2 & UART_CR3 – Enabling the UART

The first action we would need to take is to disable the UART and the last thing we should do is enable it. This is controlled by three bits in two different registers, namely registers UART_CR3 and UART_CR2. All three bits use 0 for disable and 1 for enable. The bits we been to set are:

UART_CR2_TENEnable/disable transmission
UART_CR2_RENEnable/disable reception
UART_CR3_CKENEnable/disable the clock

In our case we do not need to enable the output of the system clock or reception. The software below will enable these anyway in order to provide a generic serial initialisation method which can be using a variety of circumstances.

Software

Moving on to our software, we will need a standard STM8 project for the microcontroller you are using. I am using the STM8S103F3P3 and the default project I set up in a previous article.

Setting Up the UART

This code makes a fundamental assumption, namely that you have configured the chip and your circuit to run at 16 MHz. I did this by setting the chip to use the internal oscillator as its clock source and using a prescalar of 1 (see the previous article on setting up the system clock for more information). The code for this is in the InitialiseSystemClock method.

The next step is to configure the UART. This is performed in the InitialiseUART method.

//
//  Setup the UART to run at 115200 baud, no parity, one stop bit, 8 data bits.
//
//  Important: This relies upon the system clock being set to run at 16 MHz.
//
void InitialiseUART()
{
    //
    //  Clear the Idle Line Detected bit in the status register by a read
    //  to the UART1_SR register followed by a Read to the UART1_DR register.
    //
    unsigned char tmp = UART1_SR;
    tmp = UART1_DR;
    //
    //  Reset the UART registers to the reset values.
    //
    UART1_CR1 = 0;
    UART1_CR2 = 0;
    UART1_CR4 = 0;
    UART1_CR3 = 0;
    UART1_CR5 = 0;
    UART1_GTR = 0;
    UART1_PSCR = 0;
    //
    //  Now setup the port to 115200,n,8,1.
    //
    UART1_CR1_M = 0;        //  8 Data bits.
    UART1_CR1_PCEN = 0;     //  Disable parity.
    UART1_CR3_STOP = 0;     //  1 stop bit.
    UART1_BRR2 = 0x0a;      //  Set the baud rate registers to 115200 baud
    UART1_BRR1 = 0x08;      //  based upon a 16 MHz system clock.
    //
    //  Disable the transmitter and receiver.
    //
    UART1_CR2_TEN = 0;      //  Disable transmit.
    UART1_CR2_REN = 0;      //  Disable receive.
    //
    //  Set the clock polarity, lock phase and last bit clock pulse.
    //
    UART1_CR3_CPOL = 1;
    UART1_CR3_CPHA = 1;
    UART1_CR3_LBCL = 1;
    //
    //  Turn on the UART transmit, receive and the UART clock.
    //
    UART1_CR2_TEN = 1;
    UART1_CR2_REN = 1;
    UART1_CR3_CKEN = 1;
}

We will need to provide a method of sending a simple string to the serial port. The algorithm is simple:

  1. Set a pointer to the start of the string
  2. If the character pointed to be the pointer is not a null (i.e. 0) character then
    1. Transfer the character pointed to be the pointer into the UART data register
    2. Wait until the data register has been sent (Transmission Empty is true)
    3. Move the pointer on one byte

//
//  Send a message to the debug port (UART1).
//
void UARTPrintf(char *message)
{
    char *ch = message;
    while (*ch)
    {
        UART1_DR = (unsigned char) *ch;     //  Put the next character into the data transmission register.
        while (UART1_SR_TXE == 0);          //  Wait for transmission to complete.
        ch++;                               //  Grab the next character.
    }
}

And finally we need a main program to control the application.

//
//	Main program loop.
//
void main()
{
	__disable_interrupts();
	InitialiseSystemClock()
    InitialiseUART()
	__enable_interrupts();
	while (1)
	{
        UARTPrintF("Hello from my microcontroller....\n\r");
		for (long counter = 0; counter < 250000; counter++);
	}
}

The full application code can be downloaded from here. Simply unzip the files and open the project with IAR. The application can be downloaded to the chip by pressing Ctrl-D. Once downloaded to the microcontroller press F5 to run the application.

This application has been tested on my reference platform, the Variable Labs Protomodule and the STM8S Discovery board.

To check the application is working, connect the Tx line of the STM8S (in my case pin 2) to the Rx line on a PC which can accept 3.3V TTL logic signals. This is important, the port must accept 3.3V TTL and NOT standard RS232 signals. To do this I use a 3.3V FTDI cable and connect this to the STM8S and one of the USB ports on my PC. This gives me a COM port which I can connect to devices running at 3.3V. Now open up a terminal emulator (I used PuTTY) and connect to the com port using the correct protocol. You should see the following output:

Output from the STM8S shown on a PuTTY terminal

Output from the STM8S shown on a PuTTY terminal

Conclusion

Adding these methods to you project should allow you to generate debug output from an application running on the STM8S or communicate with devices which are controlled using a serial communication protocol.

Source Code Compatibility

SystemCompatible?
STM8S103F3 (Breadboard)
Variable Lab Protomodule
STM8S Discovery

External Interrupts on the STM8S

Thursday, August 16th, 2012

In a previous post we looked at the GPIO pins and how we could set the pins to either input or output but we concentrated on output. In this post we will look at using interrupts to detect input from the user and an output to indicate that the input has been detected. Our objective is:

  1. Create a circuit which can register when a button has been pressed.
  2. Toggle a LED when the button is pressed

Should be simple, so let’s give it a go.

Hardware

For the hardware we will need three components:

  1. STM8S Circuit
  2. Switch
  3. LED

STM8S Circuit

For the STM8S we need the STM8S103F3 connected to 3.3V and ground. We will also need to add two ceramic capacitors, a 100nF between Vss and Vdd (pins 7 and 9) and a 1uF between Vss and Vcap (pins 7 and 8).

Switch Circuit

For the switch circuit we need a switch and a pull-up resistor. We will pull the input line to the STM8S high through the pull-up resistor. Pressing the switch will pull the input line low. This part of the circuit looks like this:

This circuit will have the pin held high until the switch is pressed. All we need to do is to detect the falling edge of the input to the STM8S and we have a way to detecting when the user presses the switch.

One thing to note here is that we will be ignoring switch bounce and so we are likely to get the odd spurious message. We will come back to this later.

LED Circuit

This circuit is a simple transistor circuit which is used to switch on a LED. By applying a low current to the base of a transistor we can switch on a larger current to drive the LED. A quick simple circuit looks like this:

By setting a GPIO pin connected to the base of the transistor to high we can turn on a LED.

Putting it all together, we connect the switch to PD4 and the LED to PD3.

Software

The software is a simple extension of the previous post on GPIO pins. Here we are going to use the same port (port D) for both input and output. The full code is:

#include <intrinsics.h>
#include <iostm8s103f3.h>

//
//  Process the interrupt generated by the pressing of the button on PD4.
//
#pragma vector = 8
__interrupt void EXTI_PORTD_IRQHandler(void)
{
    PD_ODR_ODR3 = !PD_ODR_ODR3;     //  Toggle Port D, pin 3.
}

//
//  Main program loop.
//
void main()
{
    //
    //  Initialise the system.
    //
    __disable_interrupt();
    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.
    //
    //  Now configure the input pin.
    //
    PD_DDR_DDR4 = 0;        //  PD4 is input.
    PD_CR1_C14 = 0;         //  PD4 is floating input.
    //
    //  Set up the interrupt.
    //
    EXTI_CR1_PDIS = 2;      //  Interrupt on falling edge.
    EXTI_CR2_TLIS = 0;      //  Falling edge only.
    __enable_interrupt();

    while (1)
    {
        __wait_for_interrupt();
    }
}

So let’s start and break the program down. The first thing you will notice is that we initially configure all of the pins on port D as outputs as we have previously seen:

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 thing we do is to set up the system to allow PD4 as an input pin:

PD_DDR_DDR4 = 0;        //  PD4 is input.
PD_CR1_C14 = 0;         //  PD4 is floating input.

The final step of the configuration is to set the external interrupt for the port to detect the falling edge of the signal:

EXTI_CR1_PDIS = 2;      //  Interrupt on falling edge.
EXTI_CR2_TLIS = 0;      //  Falling edge only.

The final part of the main program is to wait for an interrupt. This is repeated infinitely:

while (1)
{
    __wait_for_interrupt();
}

The interesting part of the problem is the interrupt routine as this is where the real work is actually done. The code is simple enough you just have to make sure that you declare the method correctly:

#pragma vector = 8
__interrupt void EXTI_PORTD_IRQHandler(void)
{
    PD_ODR_ODR3 = !PD_ODR_ODR3;     //  Toggle Port D, pin 3.
}

The first two lines declare the method as an Interrupt Service Routine (ISR). The #pragma vector = 8 tells the compiler which interrupt this method will be servicing. In this case, this method will be called to process the interrupts for Port D. We will look into ISRs a little more in a later post.

Putting it all Together

If we wire all of the hardware together on a breadboard we end up with something like the following:

and a quick video of it working:

Switch Bounce

As noted earlier, mechanical switches are susceptible to switch bounce. This is caused by the mechanical contacts not closing perfectly when the switch is pressed. You can notice an example of bounce in the above video. When the switch is pressed the second time you can see that the LED is switched off but it is then switched on straight away. This is the effect of the switch contacts causing multiple signals. If a scope is hooked up to a switch you see something like the following:

In this case the switch is held down and then released. Notice the spike; this is caused by the switch bounce. If the signal is large enough then the microcontroller will think that the switch has been pressed twice rather than once – in fact that is what happened in the above video. There are a few ways to solve this but I’ll leave that as an exercise to the reader.

You can find the full source for the above project here. This application has been tested on tmy reference platform, the Variable Labs Protomodule and the STM8S Discovery board.

Compatibility

System Compatible?
STM8S103F3 (Breadboard)
Variable Lab Protomodule
STM8S Discovery

Custom IAR Templates for STM8S Projects

Thursday, August 9th, 2012

I have been using the IAR development environment to develop code for the STM8S chip for several months now. One of the things that has been a little bit of an annoyance is the need to keep reconfiguring each new project as it is created. One option would be to create a template project and keep copying the template and just rename the project files and add it to a new workspace. There must be a better way…

This is the thought which started me delving into the way in which IAR stores it’s templates and asking if I could make my own.

Where are the Templates

So the first thing to do is to work out if the templates are stored as files and if so where they are. Breaking out Explorer I found an interesting directory which looked like it might be a candidate, namely:

C:Program Files (x86)\IAR Systems\Embedded Workbench 6.0 Kickstartstm\configtemplateproject

I remembered that when you create a new IAR project you are presented with three options:

  1. asm
  2. C
  3. C++

Guess what, in that directory I found three directories and three files with the extension .projtempl. For each project name in the default projects (asm, C, C++) there was a matching directory and matching file. We may be in luck.

What’s in the Files?

The .projtempl files are pretty small so I guessed they were text files of some sort. Feeding them to Notepad++ confirmed that they were indeed text files. In fact they were XML files. In fact the default C file looks like this:

<?xml version="1.0" encoding="iso-8859-1"?>

<template>
  <displayname>C</displayname>
    <description>Creates a C project.
    </description>
  <files>
    <file>$PROJ_DIR$main.c</file>
  </files>
</template>

So my guess is that the displayname and description fields determine what is shown to the user and the files section tells the environment what files are to be included in the project.

What’s in the Directory?

Next port of call is the directory for the project. So continuing with the C project type we find that the C directory contains two files:

  1. main.c
  2. templproj.ewp

Looking inside the main.c file we find the default code which is placed inside the main.c file which is created for you when you ask for a new C project.

Putting it all together

If we put all of the pieces together, I think that we can create out own project templates using the default C project as an example. Note that I am using the C project type as an example as it is the only high level language version supported by the Kickstart edition of the IAR programming environment for the STM8S processor.

So I think we need to do the following in order to create our own template:

  1. Copy the default C directory and templproj file and give them a consistent name (i.e. the name of the directory matches the root of the file name)
  2. Edit the templproj file and change the displayname, the description and optionally the files section to match our new project type
  3. Add files to the new directory and edit any existing files as required

So let’s give it a go and see what happens.

Creating a New Template

So the first thing to do is to decide on the name of the template we are creating and copy the files. I decided on the imaginative name of My Default C Project for the new template. Catchy isn’t it.

One of the first problems I encountered was the fact that this is a system directory as far as Windows 7 is concerned and so I had to modify the permissions. If you hit any problems with this step of any others you need to make sure that you change the permissions to allow editing and creating files. I came across this on a few occasions. If you don’t know how to do this then Google your OS to find out how to change permissions. I gave Full Control access to myself for the duration of the task and changed the permissions back afterwards.

So at this point I have a My Default C Project.templproj file and a My Default C Project directory. The directory contains the same files as the C directory.

Next step, edit the templproj file and change the displayname and the description entries. I made the displayname match the directory and project file names. The description field expands on this a little more explaining what is included in the project.

Moving on to the main.c file in our new directory I edited this to contain a basic C program which included some basic headers and reset the system clock:

#include <intrinsics.h>
#include <iostm8s103f3.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.
}

//
//  Main program loop.
//
void main()
{
    //
    //  Initialise the system.
    //
    __disable_interrupt();
    InitialiseSystemClock();
    __enable_interrupt();
}

So What Does This Do?

Next step, fire up IAR and create a new project. Guess what….

IT’S THERE !

Selecting the new project prompts me to create a new workspace (which I do) and then opens up the main.c file. Sure enough, it matches the code I have entered into the new template.

What’s Next?

Next step is to try and edit the templproj.ewp file in the C directory and set up some of the default options. For instance, I always use ST-Link/V2 to deploy to the STM8S chip. Maybe try adding a few files to the directory and the .projtempl file. The rest, as they say, is left as an exercise for the reader.

Configuring the STM8S System Clock – The Way of the Register

Sunday, July 29th, 2012

This post continues the low level use of the STM8S registers and shows you how to configure the STM8S system clock. In particular we will configure the system clock to use the internal high speed clock.

So let us start with a little background. The STM8S can be driven by three clock sources:

  • High Speed Internal (HSI) RC oscillator running at 16MHz
  • Low Speed Internal (LSI) RC oscillator running at 128KHz
  • External clock (1-24 MHz) which can be either an external crystal or user supplied clock.

We will be using the internal oscillator as this will be satisfactory for our purposes. An external clock may be more accurate but for most of the work I am doing I am happy to sacrifice the accuracy for the lower component count and greater simplicity of my designs.

At start up, the STM8S will be using HSI with a prescalar to bring the initial clock speed down to 2MHz. We will be resetting the clock prescalar to allow the chip to run at the full 16MHz.

The switching algorithm we will use is as follows:

  1. Reset all of the clock registers to the power on state
  2. Select the clock we wish to use
  3. Enable the switching mechanism
  4. Wait until the STM8S indicates that the clock has stabilised

Clock Registers

The description of the clock registers start around page 89 of the STM8S Reference Manual. In this article we will run through the registers concentrating on the values we will be setting. For a fuller description you should refer to the STM8S Reference Manual.

CLK_ICKR – Internal Clock Register

The only bits we will really be interested in for this example bits 1 and 0, this allows us to select the HSI and see if it is ready.

Register NameDescription
REGAHControl the actions of the internal voltage regulator when the chip enters Active-halt mode.
LSIRDYSet by hardware, this bit determines if the LSI is stabilised and ready. 0 indicates the oscillator is not ready, 1 indicates it is stable.
LSIENSet and cleared by the user program, this bit determines if the LSI has been selected as the clock source for the chip.
This can also be set by hardware to indicate that the LSI is required.
FHWUSet and cleared by the user program, this bit indicates if fast wakeup from Halt/Active -halt is enabled. 0 = disabled, 1 = enabled
HSIRDYSet and reset by hardware, this bit determines if the HSI is stable and ready for use.
HSIENSet and cleared by the user program, this bit determines if the HSI has been selected as the clock source for the chip.
This can also be set by hardware to indicate that the HSI is required.
0 indicates HSI is not selected, 1 indicates HSI is selected.

Our example here will be resetting the clock source on the STM8S chip to use the internal oscillator using HSIEN and using the HSIRDY bit to determine when the clock has been setup and is stable.

CLK_ECKR – External Clock Register

This register is used to provide information about the state of the external clock. We will not be using this register for anything other than resetting the register to the power on state.

Register NameDescription
HSERDYExternal high speed clock state, 0 = not ready, 1 = ready.
HSEENEnable/disable the external high speed clock. 1 = enabled, 0 = disabled

CLK_CMSR – Clock Master Status Register

This register is set and cleared by hardware and indicates which clock source is currently selected as the master clock source.

ValueClock Source
0xe1HSI
0xd2LSI
0xb4HSE

CLK_SWR – Clock Master Switch Register

This register is written to by the user program and is used to select the master clock source. The register cannot be written to whilst a clock source switch is on-going. You can test for the switch using the SWBSY in the CLK_SWCR register.

ValueClock Source Selected
0xe1HSI
0xd2LSI
0xb4HSE

Clock Switch Control Register – CLK_SWCR

The Clock Switch Control Register is used to control how and when the clock switch is performed.

Register NameDescription
SWBSYThis bit indicates if a clock switch is in progress, 1 = clock switch is on-going, 0 indicates that no switch is in progress.
This bit is set and cleared by hardware but can also be cleared in software. Clearing this bit resets the clock switch process.
SWENWriting a 1 into this bit starts the clock switching process.
SWIENEnable (1) or disable (0) an interrupt to be generated when a clock switch takes place.
SWIFIndicates if a clock switch is taking place (interrupts enabled) or is the target clock is ready (interrupts disabled)

In our case we will be using this register to start the clock switch process.

CLK_CKDIVR – Clock Divider Register

The system clock can be divided down to allow for lower clock frequencies to be used. This register allows for the HSI prescalar (divider) and the CPU prescalar to be set.

Register NameDescription
HSIDIVHSI prescalar
CPUDIVCPU Prescalar

The following values can be used for the HSI prescalar:

ValuePrescalar
0HSI frequency
1HSI frequency divided by 2
2HSI frequency divided by 4
3HSI frequency divided by 8

The following values are allowed for the CPU prescalar:

ValuePrescalar
0Master clock frequency
1Master clock frequency divided by 2
2Master clock frequency divided by 4
3Master clock frequency divided by 8
4Master clock frequency divided by 16
5Master clock frequency divided by 32
6Master clock frequency divided by 64
7Master clock frequency divided by 128

In this case we will want the CPU to be running at full speed and so no prescalars will be applied to either the master clock or the HSI.

CLK_PCKENR1 & CLK_ PCKENR2 – Peripheral Clock Gating Registers

These two registers enable (set to 1 and disable (set to 0) the clocks used to control the peripherals used in the application. I have fallen foul of these values a few times. If you set these registers or zero then you will turn off the peripheral clocks. This has the effect of turning off the feature you may be trying to use. If in doubt set both of these registers to 0xff. You can always refine the values later by restricting the clock signals to only those which you are using.

Register NamePeripheralDescription
CLK_PCKENR1TIM1Enable/disable timer 1
TIM3Enable/disable timer 2
TIM2/TIM5Enable/disable timer 2/5 (product dependent)
TIM4/TIM6Enable/disable timer 4/6 (product dependent)
UART1/2/3Enable/disable UART 1/2/3
SPIEnable/disable SPI
I2CEnable/disable I2C
CLK_PCKENR2CANEnable/disable the CAN bus
ADCEnable/.disable the analogue to digital converter
AWUEnable/disable the watchdog service

Enabling the service will use the frequency of the master clock. Disabling the clock to the service will turn it off.

The <iosstm8s103f3.h> file does not contain definitions for these bits, you need to define your own constants for these. I have recently taken to just turning them all on as I’m not too worried about the power requirements for the applications I am working on.

CLK_CSSR – Clock Security System Register

We will not be using this register in this example apart for setting the default power on value. Please see the STM8S Reference Manual for more information.

CLK_CCOR – Configurable Clock Output Register

We will not be using this register in this example apart for setting the default power on value. Please see the STM8S Reference Manual or future examples for more information.

CLK_HSITRIMR – HSI Clock Calibration Trimming Register

We will not be using this register in this example apart for setting the default power on value. Please see the STM8S Reference Manual for more information.

CLK_SWIMCCR – SWIM Clock Control Register

We will not be using this register in this example apart for setting the default power on value. Please see the STM8S Reference Manual for more information.

Software

As previously mentioned, this example will simply set the system clock to the maximum speed the HSI can deliver. We will then toggle a GPIO pin and hook this up to a scope to get an idea of the output. We will never see the full 16MHz clock speed of the HSI but we should be able to see that the output frequency changes when we change the clock dividers.

So first thing, let’s get the system set up and running at the full 16 MHz and see what we the scope produces.

#include <intrinsics.h>
#include <iostm8s103f3.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.
}

//
//  Main program loop.
//
int main(void)
{
    __disable_interrupt();
    //
    //  Initialise Port D.
    //
    PD_ODR = 0;             //  All pins are turned off.
    PD_DDR_DDR4 = 1;        //  Port D, bit 4 is output.
    PD_CR1_C14 = 1;         //  Pin is set to Push-Pull mode.
    PD_CR2_C24 = 1;         //  Pin can run up to 10 MHz.
    //
    //  Now setup the system clock
    //
    InitialiseSystemClock();
    __enable_interrupt();
    while (1)
    {
        PD_ODR_ODR4 = 1;    // Turn Port D, Pin 4 on.
        PD_ODR_ODR4 = 0;    // Turn Port D, Pin 4 off.
    }
}

Running this code resulted in the following trace on the oscilloscope:

GPIO Output when the System Clock is set to HSI with no Divider.

GPIO Output when the System Clock is set to HSI with no Divider.

The frequency of the out was 2.693Mhz.

So now let’s see what happens when we set the prescalar for HSI to be 2 (i.e. half the clock speed. To do this we need to edit the code slightly, so change this:

CLK_CKDIVR = 0;                     //  Ensure the clocks are running at full speed.

to this:

CLK_CKDIVR = 0;                     //  Ensure the clocks are running at full speed.
CLK_CKDIVR_HSIDEV = 1;				//	Set the HSI divider to 2.

The result is the following trace:

GPIO Output when System Clock is set to HSI divided by 2

GPIO Output when System Clock is set to HSI divided by 2

This looks remarkably like the trace above but the oscilloscope shows that the frequency of the signal is now 1.335MHz, or approximately half of the frequency of the first trace.

The full project for this article can be downloaded from here. This code has been tested with my reference platform, the Variable Labs Protomodule and the STM8S Discovery board.

Compatibility

SystemCompatible?
STM8S103F3 (Breadboard)
Variable Lab Protomodule
STM8S Discovery

Simple GPIO – The Way of the Register

Sunday, July 8th, 2012

If you have been following this blog for a while you will be aware that I have recently changed the way I write code for the STM8S from using the STD Peripheral Library to using direct register access. This has required that I go back to basics learning a new way to perform tasks I had only just mastered. The Way of the Register posts will show you have to control the STM8 using register access.

This first post will look at a simple task, controlling GPIO lines. The aim of this first exercise is to simply toggle one of the GPIO lines.

Hardware

For this task we need very little hardware, only the STM8S set up on breadboard. For this we need a minimum of two capacitors and the chip itself. A 1uF capacitor is placed between VSS and VCAP and a 100 nF capacitor is placed between VDD and VSS. An additional 1uF capacitor is also placed across the +3.3V and ground of the power supply. The following shows this set up on breadboard along with the breadboard power supply, connections to the oscilloscope and the ST-Link programmer:

STM8S103 set up on Breadboard

STM8S103 set up on Breadboard

The Registers

Before we can start coding we need to have a look at the registers we will be using. The GPIO pins have five registers used to control the GPIO function and set/read the value of a pin. These are:

Register
Data Direction Register
Control Register 1
Control Register 2
Output Data Register
Input Data Register

The following sections provide a brief description of the registers. For more information you should consult RM0016 – STM8S Reference Manual.

Px_DDR – Data Direction Register

This register determines which or the GPIO pins are outputs and which are inputs. A value of one in a bit sets a port line to output and zero sets the port line to input. You can set the value of the whole register or each individual bit in the register.

To set the whole register in one go you would execute something like the following:

PD_DDR = 0x0f;

This sets bits 0-3 inclusive of port D to output whilst bit 4-7 are inputs.

An alternative to this is to set up each GPIO pin individually, so the equivalent of the above is:

PD_ODR = 0;
PD_DDR_DDR0 = 1;
PD_DDR_DDR1 = 1;
PD_DDR_DDR2 = 1;
PD_DDR_DDR3 = 1;

Px_CR1 & PxCR2 – Control Register 1 and 2

These two registers determine the properties of the pins depending upon the operating mode of the pin. The following table summarises the operating modes:

RegisterModeValueDescription
CR1Input0Floating input
CR1Input1Input with pull-up
CR1Output0Open drain
CR1Output1Push-Pull
CR2Input0Interrupt disabled
CR2Input1Interrupt enabled
CR2Output0Output up to 2 MHz.
CR2Output1Output up to 10 MHz

In our case, we need the output pin to operate in Push-Pull mode and as we do not know the final speed of the output signal we will set this to the maximum, 10 MHz.

As with the data direction register, each bit in the registers maps to a specific GPIO on each port. In the case of these registers the bits are labelled C10-C17 (for Control Register 1) and C20-C27 (for control Register 2). The format appears to be CRB, where C stands for Control Register; R is an integer representing the register number and B is an integer representing the bit to be set.

In our case, this results in the following code:

PD_CR1_C14 = 1;
PD_CR2_C24 = 1;

Px_ODR – Input/Output Data Registers

These two registers allow you to read the value of an input pin and set the value of an output pin. As with the previous registers you can set/read the register in its entirety or bit by bit. So to turn all of the outputs off you would execute something like:

PD_ODR = 0;


In the case we have been using we will be outputting data on pin 4 of port D. So to set the pin high you would execute the following statement:

PD_ODR_ODR4 = 1;

Software

Now we have the theory in place lets put together a simple application which uses it. The following code sets up port D to have pin 4 configured as an output operating to 10 MHz. The main program loop then simply keeps flipping the bit. The result should be a square wave signal.

#include <iostm8S103f3.h>

int main( void )
{
    //
    //  Initialise Port D.
    //
    PD_ODR = 0;             //  All pins are turned off.
    PD_DDR_DDR4 = 1;        //  Port D, bit 4 is output.
    PD_CR1_C14 = 1;         //  Pin is set to Push-Pull mode.
    PD_CR2_C24 = 1;         //  Pin can run upto 10 MHz.
    //
    //  Now lets toggle to IO line.
    //
    while (1)
    {
        PD_ODR_ODR4 = 1;    // Turn Port D, Pin 4 on.
        PD_ODR_ODR4 = 0;    // Turn Port D, Pin 4 off.
    }
}

If we hook an oscilloscope up to PD4 on the STM8 chip we see the following:

Simple GPIO Output

Simple GPIO Output

As you can see, we have a square wave. Note that the wave is not symmetrical. This is due to the branch instruction being executed in the while loop to go back to the start of the loop.

Source Code

Here is the IAR project and source code for this post. This code has been tested on my reference platform, Variable Lab Protomodule and the STM8S Discovery board.

Compatibility

SystemCompatible?
STM8S103F3 (Breadboard)
Variable Lab Protomodule
STM8S Discovery

Using Registers on the STM8S

Saturday, June 23rd, 2012

A few weeks ago I had a rant about the STM8S Standard Peripheral library after it cost me a fair amount of time tracking down what appeared to be a bug in the library. As a result of this I have moved over to accessing the registers on the chip directly to control the operation of the chip. A recent question ion one of the forums I haunt has prompted this post. Here I am going to present a few different ways of achieving the same task, one using the STD Peripheral Library and two examples using the registers directly but in different ways.

The task we are going to be looking at is one I perform as part of my initialisation of the STM8S, namely I set the clock up to a known state. In this case we will be setting the system to use the HSI clock running at 16 MHz with no dividers.

It is important to note that you will need to have a copy of the reference manual for the chip available when using direct register access to control the microcontroller. In this case you should be looking for document RM0016 on ST’s web site.

Using the Standard Peripheral Library

Using the STD Peripheral Library makes this a relatively simple task. We only need to call four methods:

CLK_DeInit();
CLK_SYSCLKConfig(CLK_PRESCALAR_CPUDIV1);    // CPU Prescalar = 1.
CLK_SYSCLKConfig(CLK_PRESCALAR_HSIDIV1);    // Prescalar = 1, 16 MHz.
CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO,  // Automatically switch
                      CLK_SOURCE_HSI,       // Switch to internal timer.
                      DISABLE,              // Disable the clock switch interrupt.
                      CLK_CURRENTCLOCKSTATE_DISABLE);   // Disable the previous clock.

The headers for the above methods can be found in the file stm8s_clk.h and the source code for the methods can be found in the file stm8s_clk.c. These source files can be found in the source code folder of the STD Peripheral Library.

Direct Register Access – Method 1

This first method of accessing the registers continues to use the STD Peripheral Library files but we do not make any calls into the library. Instead we use the definitions for the registers to access the chip directly.

So let’s start by breaking the above four methods down into their direct register equivalents.

CLK_DeInit

The first thing we need to do is to reset all of the registers to the default values:

CLK->ICKR = CLK_ICKR_RESET_VALUE;
CLK->ECKR = CLK_ECKR_RESET_VALUE;
CLK->SWR  = CLK_SWR_RESET_VALUE;
CLK->SWCR = CLK_SWCR_RESET_VALUE;
CLK->CKDIVR = CLK_CKDIVR_RESET_VALUE;
CLK->PCKENR1 = CLK_PCKENR1_RESET_VALUE;
CLK->PCKENR2 = CLK_PCKENR2_RESET_VALUE;
CLK->CSSR = CLK_CSSR_RESET_VALUE;
//
//  The following set has to be performed twice.
//
CLK->CCOR = CLK_CCOR_RESET_VALUE;
while ((CLK->CCOR & CLK_CCOR_CCOEN) != 0);
CLK->CCOR = CLK_CCOR_RESET_VALUE;
CLK->HSITRIMR = CLK_HSITRIMR_RESET_VALUE;
CLK->SWIMCCR = CLK_SWIMCCR_RESET_VALUE;

As you can see, the library certainly hides a large amount of work from you. Most of the above code is simply a case of setting up the registers to default values.

To find out how this all works you need to start looking in the stm8s.h file. A quick search for CLK soon leads you to the following type declaration:

typedef struct CLK_struct
{
  __IO uint8_t ICKR;     /*!> Internal Clocks Control Register */
  __IO uint8_t ECKR;     /*!> External Clocks Control Register */
  uint8_t RESERVED;      /*!> Reserved byte */
  __IO uint8_t CMSR;     /*!> Clock Master Status Register */
  __IO uint8_t SWR;      /*!> Clock Master Switch Register */
  __IO uint8_t SWCR;     /*!> Switch Control Register */
  __IO uint8_t CKDIVR;   /*!> Clock Divider Register */
  __IO uint8_t PCKENR1;  /*!> Peripheral Clock Gating Register 1 */
  __IO uint8_t CSSR;     /*!> Clock Security System Register */
  __IO uint8_t CCOR;     /*!> Configurable Clock Output Register */
  __IO uint8_t PCKENR2;  /*!> Peripheral Clock Gating Register 2 */
  uint8_t RESERVED1;     /*!> Reserved byte */
  __IO uint8_t HSITRIMR; /*!> HSI Calibration Trimmer Register */
  __IO uint8_t SWIMCCR;  /*!> SWIM clock control register */
}
CLK_TypeDef;

If you have a look at the technical reference sheet for the chip, you will find section which shows the memory layout for the registers. These are at fixed locations in memory and should map to the above layout. The __IO is defined as volatile and will prevent the compiler from optimising out any references to the variables.

The next thing to note is that we still do not have a definition for CLK. A little more searching in the same file will lead you to the following statement:

#define CLK ((CLK_TypeDef *) CLK_BaseAddress)

So this defines CLK for us as a pointer to a location in memory. Some more searching (again in the same file) leads us to the following line of code:

#define CLK_BaseAddress         0x50C0

So the code CLK->ICKR = 0; will set the register at location 0x50C0 to zero.

One point to note is the statement while ((CLK->CCOR & CLK_CCOR_CCOEN) != 0);. This illustrates the use of another type of declaration you will find in stm8s.h, namely, CLK_CCOR_CCOEN. This declaration allows you to mask off certain bits within a register and use this to set or check values in a register. The name is made up of three parts:

NameDescription
CLKClock registers are being access.
CCORThis relates to the CCOR register.
CCOENMask the CCOEN bits in the register.

CLK_SYSCLKConfig

The next task is to set the prescalar for the system clock. This is being set to 1 to ensure the system runs at 16 MHz.

CLK->CKDIVR  &= (uint8_t) (~CLK_CKDIVR_HSIDIV);
CLK->CKDIVR |= (uint8_t) ((uint8_t) CLK_PRESCALAR_HSIDIV1 & (uint8_t) CLK_CKDIVR_HSIDIV);

The first line resets the prescalar to a known value whilst the second selects the divider which will be used.

CLK_ClockSwitchConfig

The final operation is to switch the system clock to the HSI and this is achieved with the following code:

CLK->SWCR |= CLK_SWCR_SWEN;
CLK->SWCR &= (uint8_t) (~CLK_SWCR_SWIEN);
CLK->SWR = (uint8_t) CLK_SOURCE_HSI;
uint16_t downCounter = CLK_TIMEOUT;
while ((((CLK->SWCR & CLK_SWCR_SWBSY) != 0 ) && (downCounter != 0)))
{
    downCounter--;
}

Direct Register Access – Method 2

This method uses the register declarations found in the header files provided by IAR. So for the STM8S103F3 we will be looking in the file .

CLK_DeInit

As before, the first thing we will do is to reset the registers to a known set of values:

CLK_ICKR = 0;
CLK_ECKR = 0;
CLK_SWR = 0xe1;
CLK_SWCR = 0;
CLK_CKDIVR = 0x10;
CLK_PCKENR1 = CLK_PCKENR1_SPI | CLK_PCKENR1_TIM2;   //  Enable the peripheral clocks we need.
CLK_PCKENR2 = 0;
CLK_CSSR = 0;
CLK_CCOR = 0;
while (CLK_CCOR_CCOEN != 0);
CLK_CCOR = 0;
CLK_HSITRIMR = 0;
CLK_SWIMCCR = 0;

The first thing you will notice is that by using this method we are not using the pointer dereferencing operator. Instead the application is accessing the registers directly. So let’s have a look at the header file and dissect the reset of the ICKR register. Searching for CLK_ICKR leads us to the following code:

typedef struct
{
  unsigned char HSIEN       : 1;
  unsigned char HSIRDY      : 1;
  unsigned char FHW         : 1;
  unsigned char LSIEN       : 1;
  unsigned char LSIRDY      : 1;
  unsigned char REGAH       : 1;
} __BITS_CLK_ICKR;
__IO_REG8_BIT(CLK_ICKR,    0x50C0, __READ_WRITE, __BITS_CLK_ICKR);

The first things we see is the definition of the structure which maps on to the format of the ICKR register. Each bit field is broken out and maps on to the sections of the register as defined in the data sheet.

The final line of code in the above snippet uses the __IO_REG8_BIT macro to map the data structure onto the address 0x50C0 and create a new name with bit level access.

The next thing to note is the while loop which checks the CCOOEN bit in the CCOR register – while (CLK_CCOR_CCOEN != 0);. As above, this uses a three part notation to form a reference, this time it is to a particular bit in a register. This is not a mask as in the previous example. This is broken down as follows:

NameDescription
CLKClock registers are being access.
CCORThis relates to the CCOR register.
CCOENCCOEN bits in the CCOR register.

Some more digging in the file leads to the following definition:

#define CLK_CCOR_CCOEN           CLK_CCOR_bit.CCOEN

The CLK_CCOR_bit declaration was created by the __IO_REG8_BIT macro. This is the name which has been given to the location in memory of the ICKR register.

CLK_SYSCLKConfig

The next task is to set the prescalar for the system clock. This is being set to 1 to ensure the system runs at 16 MHz. Note that a prescalar of 1 maps to the prescalar bits in the register being set to zero.

CLK_CKDIVR = 0;

CLK_ClockSwitchConfig

The final operation is to switch the system clock to the HSI and this is achieved with the following code:

CLK_SWCR_SWEN = 1;
CLK_SWR = 0xe1;                     //  Use HSI as the clock source.
while (CLK_SWCR_SWBSY != 0);        //  Pause while the clock switch is busy.

Conclusion

So there you have it, three different ways of performing the same task. The method used will be down to individual preference. Happy experimenting.

Using the UART on the STM8S

Friday, June 8th, 2012

If you have been reading my recent posts you will have noticed that I have had some problems setting up the UART on the STM8S. IN fact I spent several days getting this working. This post gives a couple of code snippets which I have used to get around this problem. You may need to refer to the data sheet on the processor for more detail on how this code works.

Setting Up the UART

This code makes a fundamental, namely that you have configured the chip and your circuit to run at 16 MHz. I did this by setting the chip to use the internal oscillator as it’s clock source and using a prescalar of 1. If you have this set up then the following method will allow you access to the UART running at 115200 baud, 8 data bits, 1 stop bit and no parity.

Firstly, we need to make sure we are using the right include files:

#include "stm8s.h"
#include "stm8s_uart1.h"

Next we need to set up the port:

void SetupSerialPort()
{
    //
    //  Clear the Idle Line Detected bit in the status register by a read
    //  to the UART1_SR register followed by a Read to the UART1_DR register.
    //
    (void) UART1->SR;
    (void) UART1->DR;

    UART1->CR2 = UART1_CR2_RESET_VALUE;
    UART1->CR4 = UART1_CR4_RESET_VALUE;
    UART1->CR5 = UART1_CR5_RESET_VALUE;
    UART1->GTR = UART1_GTR_RESET_VALUE;
    UART1->PSCR = UART1_PSCR_RESET_VALUE;
    //
    //  Setup the port.
    //
    UART1->CR1 = (u8) UART1_WORDLENGTH_8D | (u8) UART1_PARITY_NO;   // Word length = 8, no parity.
    UART1->CR3 = (u8) UART1_STOPBITS_1;                             // 1 stop bit.

    UART1->BRR1 &= (uint8_t) (~UART1_BRR1_DIVM);
    UART1->BRR2 &= (uint8_t) (~UART1_BRR2_DIVM);
    UART1->BRR2 &= (uint8_t) (~UART1_BRR2_DIVF);
    //
    //  Set the clock prescaler for 11520 baud.  This assumes a 16 MHz clock speed.
    //
    UART1->BRR2 = 0x0b;
    UART1->BRR1 = 0x08;
    //
    //  Disable the Transmitter and Receiver before seting the LBCL, CPOL and CPHA bits
    //
    UART1->CR2 &= (u8) ~(UART1_CR2_TEN | UART1_CR2_REN);
    //
    //  Clear the Clock Polarity, lock Phase, Last Bit Clock pulse
    //
    UART1->CR3 &= (u8)~(UART1_CR3_CPOL | UART1_CR3_CPHA | UART1_CR3_LBCL);
    //
    //  Set the Clock Polarity, lock Phase, Last Bit Clock pulse
    //
    UART1->CR3 |= (u8)((u8) UART1_SYNCMODE_CLOCK_ENABLE &
                       (u8) (UART1_CR3_CPOL | UART1_CR3_CPHA | UART1_CR3_LBCL));
    //
    //  Set the Tx and Rx state
    //
    UART1->CR2 |= (u8) ((u8) UART1_CR2_TEN | (u8) UART1_CR2_REN);
    UART1->CR3 &= (u8) (~UART1_CR3_CKEN);
}

Having set up the UART we need a method to write data to the port. The following method will send simple text strings to the UART:

//
//  Send a message to the debug port (UART1).
//
void Printf(char *message)
{
    char *ch = message;
    while (*ch)
    {
        UART1->DR = (u8) (*ch);
        while ((UART1->SR & (u8) UART1_FLAG_TXE) == RESET);
        ch++;
    }
}

Using the UART

The use of this code can be illustrated by the following program:

void main()
{
    SetupSerialPort()
    Printf("Hello from my program");
}

ST’s Standard Peripheral Library – A Day in my Life I’ll Never Get Back

Wednesday, June 6th, 2012

A couple of days ago I was working on a project and was having some problems debugging. It was nothing to do with the debugging environment more to do with the high speed interaction between two devices. So I needed some debug output – enter UART1. I decided that I would send some debug information to UART1 and capture this in PuTTY. At least I would be able to see the sequence of events and hopefully work out where my code is going wrong.

This is where my problems started…

UART Examples

So I started looking through the Standard Peripheral Library example programs. Sure enough there is a program there in the directory UART1_Printf Sounds ideal, I should be able to throw some text data at this and have it appear in PuTTY. Great.

Quick check of the readme.txt in this directory and it looks like this program is compatible:

Hardware and Software environment

This example runs on STM8S High density and Low density devices and on STM8A High density devices.

So I am running on the STM8S103F3 which is one of the low density STM8 devices so I should be fine with this code. A little further down the file it gives the specification for the serial comms as:

Hyperterminal configuration:

  • Word Length = 8 Bits
  • One Stop Bit
  • No parity
  • BaudRate = 115200 baud
  • flow control: None

Great – we are in business!

So what happened?

Creating a project was simple and I had the code compiling within a few minutes. Next job is to hook up PuTTY and send some data through down the serial connection and see it appear on the PC. This is where life got a little messy. Nothing appeared. Double checked all of the settings in both the program and Putty – they both match. Ahhh – should Tx and Rx be crossed? No that did not work.

Hmmmmmmmm….. This could be a long day.

Next step, break out the trusty logic analyser from Saleae. Connected this up and managed to show that data was in fact being output from the application but at 53 baud. This is a long way from the 115200 baud promised.

Hmmmm, so lets change the baud rate requested – 9600 baud is normally safe. No change, still 53 baud.

Starts clutching at straw, lets just make sure the system clock is running using the HSI 16 MHz signal. Still no joy, 53 baud is all I seem to be getting.

The Problem

I finally broke out the data sheet for the chip and started single stepping through the STD Peripheral library code. The example program contains the following line of code:

UART1_Init((uint32_t)115200, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, UART1_PARITY_NO,
UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);

This should set up the port to operate at the rates as per the read me file. If you have a look at this method, about half way down you will see the following code:

/* Set the UART1 BaudRates in BRR1 and BRR2 registers according to UART1_BaudRate value */
BaudRate_Mantissa    = ((uint32_t) CLK_GetClockFreq() / (BaudRate << 4));
BaudRate_Mantissa100 = (((uint32_t) CLK_GetClockFreq() * 100) / (BaudRate << 4));
/* Set the fraction of UART1DIV  */
UART1->BRR2 |= (uint8_t)((uint8_t)(((BaudRate_Mantissa100 - (BaudRate_Mantissa * 100)) << 4) / 100) & (uint8_t)0x0F);
/* Set the MSB mantissa of UART1DIV  */
UART1->BRR2 |= (uint8_t)((BaudRate_Mantissa >> 4) & (uint8_t) 0xF0);
/* Set the LSB mantissa of UART1DIV  */
UART1->BRR1 |= (uint8_t) BaudRate_Mantissa;

So I am single stepping through the code and notice something odd, the Baud Rate Registers (BRR1 and BRR2) are not being set correctly. By my calculations these should be 0x68 and 0x03 respectively (for 9600 baud).

A quick modification to the source file to put these explicit changes in and….

It Works !

I now have PuTTY talking to my STM8 at 9600 baud and so my debugging life should be a little easier. On the downside, I have lost a little faith in the Standard Peripheral Library. I think that in future I will be using this more for guidance rather than as a method of programming this family of processors. Coupled with the data sheet for the chip is is going to be an invaluable tool. Going forward I will be building up my own set of methods working with the registers on the processor. This may be slower but it will bring me closer to the chip and I may occasionally come across the odd gem in the data sheet which I may not otherwise have become aware of.

Now, back to the original problem.

4 Digit, 7 Segment Display

Thursday, May 17th, 2012

In early May I started working on a module to display a 4 digit number on a 7 segment LED display. I developed this as far as a working proof of concept developing the hardware and software necessary to prove that the module was feasible. At this point I discovered that development was ongoing by two other teams and decided to stop development at this point taking what I had learnt onboard and move on to other projects.

This article presents the work so far as this is a working project and demonstrates some of the principles used in the STM8S articles written so far this month. Namely:

  • GPIO output
  • Timers

As already stated, this project is working but incomplete and there is no intention to take the hardware further than the proof of concept as presented here.

Hardware Design

The system will use four 7-segment LED modules in place of a single 4 digit, 7 segment module. The hardware design requires the following circuit components:

  • Microprocessor controller
  • Power supply to the 7-segment display modules
  • Decoding for the 7-segments

The software will use multiplexing to allow one chip to be used to control the four 7-segment modules. The basic process is as follows:

  1. Turn power to all segments off
  2. Send the data for a digit to the 74LS47 chip
  3. Turn on the power for the digit
  4. Wait a short time
  5. Move on to the next digit and go back to start

This will give the illusion that all of the LEDs are powered all of the time.

Microcontroller

The microcontroller chosen was the STM8S003 as this is supported by the Netduino team for designers who wish to create their own modules. This chip has several free development environments available and the IAR environment has been used in this blog to document several small projects using this family of chips.

The project as it stands requires the use of 9 pins from the STM8S. Four pins will be used for power control, one line per digit. A further four pins will be used to tell the 74LS47 which of the segments are to be lit. The final pin will be used to indicate if the digit zero is to be shown for a zero value. More on this in the decoding section below. The use of this number of pins makes this unsuitable as it stands for use as a Netduino GO! module. Further refinement is required to take this forward as a module.

7-segment Power Control

The LED power is controlled by using a PNP transistor (the 7-segments share a common anode). In this circuit the power is run through a single current limiting resistor. This will mean that numbers with few digits (for example, 1) will be displayed more brightly then those with a large number of segments (say 8). The final circuit would need to refined to have a current limiting resistor per segment or using a constant current driver. This component of the circuit looks like this:

Power control for a single 7-segment LED module.

The resistor R5 is connected to the output of the microcontroller and the output of R9 going through to the power pin on the 7-segment display.

Decoding the 7-segments

The decoding of the output from the microcontroller is performed by the 74LS47 chip. This is a dedicated decoder circuit for common anode 7-segment LED modules. The system takes a binary coded decimal number and then sinks the current from the appropriate segments of the display. The result is that the module will display the appropriate digit.

We will also take advantage of one final line, the RBI line. By turning this line on and off we can determine if the digit will display the digit 0 when the input to the decoder is zero. So why would we change this line? We we could fix the line so that a digit will always be displayed. So when the microcontroller wants to show 0 it will actually show either a blank display or 0000. The blank display is obviously not desirable as the user will not be able to determine if the display is showing 0 or if it is turned off. Showing four zeroes will give the user the correct information. A nicer solution is for the display to show a single zero when the microcontroller outputs 0.

The resulting decoder wiring looks something like the following:

7-segment decoder circuit

Where pins A, B, C and D will output the binary version of the digit to be displayed.

Full Circuit

This PDF File contains the full circuit diagram for the proof of concept module. The fully assembled circuit looks like this:

Prototype Circuit

Software

In order to test the circuit we will write a small program which will start at 0 and count to 9999 before returning to 0 and starting counting again.

The first thing we need to do is to initialise the system. The following code resets the system clock source, configures the GPIO lines and then starts the timer which contain the code which will perform the multiplexing.

//
//  Initialise the clock
//
CLK_DeInit();
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);                // CPU Prescaler = 1.
CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1);                // Prescaler = 1, 16 MHz.
CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO,              // Automatically switch
					  CLK_SOURCE_HSE,                   // Switch to internal timer.
					  DISABLE,                          // Disable the clock switch interrupt.
					  CLK_CURRENTCLOCKSTATE_DISABLE);   // Disable the previous clock.
//
//  Initialise GPIOs
//
GPIO_DeInit(GPIOC);
GPIO_Init(DIGIT0_BI_PORT, DIGIT0_BI_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT1_BI_PORT, DIGIT1_BI_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT2_BI_PORT, DIGIT2_BI_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT3_BI_PORT, DIGIT3_BI_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT_A0_PORT, DIGIT_A0_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT_A1_PORT, DIGIT_A1_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT_A2_PORT, DIGIT_A2_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT_A3_PORT, DIGIT_A3_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(RBI_PORT, RBI_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
//
//  Setup timer 2 to interrupt every 2048 clock pulses.
//
TIM2_DeInit();
TIM2_TimeBaseInit(TIM2_PRESCALER_2048,      // Interrupt every 2048 clock pulses.
				  1);                       // Period is one.
TIM2_ITConfig(TIM2_IT_UPDATE, ENABLE);      // Enable the overflow interrupt.
TIM2_Cmd(ENABLE);

The next thing we need to do is to look at displaying a digit. To do this we will turn off all of the digits and then convert the digit into binary outputting the value on the four pins connected to the 74LS47 decoder.

GPIO_WriteHigh(DIGIT0_BI_PORT, DIGIT0_BI_PIN);
GPIO_WriteHigh(DIGIT1_BI_PORT, DIGIT1_BI_PIN);
GPIO_WriteHigh(DIGIT2_BI_PORT, DIGIT2_BI_PIN);
GPIO_WriteHigh(DIGIT3_BI_PORT, DIGIT3_BI_PIN);
if (digit & 0x01)
{
	GPIO_WriteHigh(DIGIT_A0_PORT, DIGIT_A0_PIN);
}
else
{
	GPIO_WriteLow(DIGIT_A0_PORT, DIGIT_A0_PIN);
}
if (digit & 0x02)
{
	GPIO_WriteHigh(DIGIT_A1_PORT, DIGIT_A1_PIN);
}
else
{
	GPIO_WriteLow(DIGIT_A1_PORT, DIGIT_A1_PIN);
}
if (digit & 0x04)
{
	GPIO_WriteHigh(DIGIT_A2_PORT, DIGIT_A2_PIN);
}
else
{
	GPIO_WriteLow(DIGIT_A2_PORT, DIGIT_A2_PIN);
}
if (digit & 0x08)
{
	GPIO_WriteHigh(DIGIT_A3_PORT, DIGIT_A3_PIN);
}
else
{
	GPIO_WriteLow(DIGIT_A3_PORT, DIGIT_A3_PIN);
}
GPIO_WriteLow(port, pin);

The final statement will turn on the PNP transistor connected to the selected digit by connecting the base of the transistor to ground.

The final piece of code we need (before the main program loop) is the interrupt handler for the timer. This will turn off all of the power to the LED modules and then work out which digit to show and write the data to the 74LS47 decoder. This interrupt service routine can be found in the file stm8s_it.c. See previous STM8S post on interrupts for further information. Look for the following code in this file:

INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13)
{
}

And replace with the following code:

INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13)
{
    if (_currentDigit == 0)
    {
        DisplayDigit(_displayValue[0], DIGIT0_BI_PORT, DIGIT0_BI_PIN);
        GPIO_WriteHigh(RBI_PORT, RBI_PIN);
        _currentDigit++;
    }
    else
    {
        if (_currentDigit == 1)
        {
            DisplayDigit(_displayValue[1], DIGIT1_BI_PORT, DIGIT1_BI_PIN);
            if ((_displayValue[2] != 0) || (_displayValue[3] != 0))
            {
                GPIO_WriteHigh(RBI_PORT, RBI_PIN);
            }
            else
            {
                GPIO_WriteLow(RBI_PORT, RBI_PIN);
            }
            _currentDigit++;
        }
        else
        {
            if (_currentDigit == 2)
            {
                DisplayDigit(_displayValue[2], DIGIT2_BI_PORT, DIGIT2_BI_PIN);
            if (_displayValue[3] != 0)
            {
                GPIO_WriteHigh(RBI_PORT, RBI_PIN);
            }
            else
            {
                GPIO_WriteLow(RBI_PORT, RBI_PIN);
            }
                _currentDigit++;
            }
            else
            {
                DisplayDigit(_displayValue[3], DIGIT3_BI_PORT, DIGIT3_BI_PIN);
                GPIO_WriteLow(RBI_PORT, RBI_PIN);
                _currentDigit = 0;
            }
        }
    }
    TIM2->SR1 = (uint8_t) (~(uint8_t) TIM2_IT_UPDATE);  // Clear the interrupt.
}

One interesting thing to look at is the setting of the RBI_PORT. Note that this is set to high for the first digit (i.e. rightmost digit) and low for all of the other digits. This is the piece of magic which ensure that we always display a digit for zero in the rightmost digit but only display a value for no zero digits on the remaining three digits.

If this were to be taken forward then the code above would need to be optimised to ensure that the ISR completed in the shortest time possible.

Now we have all of the elements in place we can start to write our main program loop. The code will loop from 0 to 9999 displaying each digit and the restarting the count. The individual digits will be put into an array of four integers, one for each LED in the display. This gives us the following code:

enableInterrupts();                         // Make sure interrupts are enabled.
int value = 0;
while (1)
{
	_displayValue[0] = value % 10;
	_displayValue[1] = (value / 10) % 10;
	_displayValue[2] = (value / 100) % 10;
	_displayValue[3] = (value / 1000) % 10;
	if (value == 9999)
	{
		value = 0;
	}
	else
	{
		value++;
	}
	//
	//  Now delay otherwise we'll be counting too fast for anything
	//  to appear on the display.
	//
	for (int outerDelay = 0; outerDelay < 5; outerDelay++)
	{
		for (int delay = 0; delay < 0xffff; delay++)
		{
			nop();
		}
	}
}

The full source code for this project can be downloaded here.

Conclusion

This was an interesting project to put together and took about a day to work out and get everything working. To take this further would take a lot more work, many times the amount already put into the project. As two other groups are working on similar ideas I have decided to archive this for now and move on to other projects.

STM8S SPI

Wednesday, May 16th, 2012

In this latest in the series I am taking a look at communication between two systems (namely a Netduino Mini and a STM8S103) using SPI.

As with previous articles the system will use the IAR compiler and the STD Peripheral Library. The Netduino Mini will use Visual Studio and .NET to act as the master for the SPI bus.

I ran into some problems along the way and had to ask for help in the Netduino Forums. Special mention to Chris Walker and CW2 for their help.

Netduino Mini Application

SPI is one of the protocols supported by the NETMF framework on the Netduino platform. As such there is little work to do. The Mini will have to act as the SPI master as this is the only role which is supported on the Netduino for this bus. We simply need to set up the SPI bus with the correct parameters and then start writing data to the bus.

To remove as many possible problems as possible we will perform a simple task, have the Netduino Mini write some data to the bus on a regular basis. In this case we will write three bytes with the values 1, 2 and 3. The code on the Mini is as simple as the following:

public static void Main()
{
	SPI.Configuration config = new SPI.Configuration(SPI_mod: SPI.SPI_module.SPI1,
							   ChipSelect_Port: Pins.GPIO_PIN_5,                        // Enable pin is D5.
							   ChipSelect_ActiveState: false,                           // Active Low
							   ChipSelect_SetupTime: 0,                                 // 0 uS setup time.
							   ChipSelect_HoldTime: 0,                                  // 0 uS hold after data transmission has completed.
							   Clock_IdleState: false,                                  // Clock line is low when the device is inactive.
							   Clock_Edge: false,                                       // Data is sampled on the clock falling edge.
							   Clock_RateKHz: 500);                                     // 500 KHz clock speed.

	SPI spi = new SPI(config);

	byte[] buffer = new byte[] { 1, 2, 3 };
	while (true)
	{
		spi.Write(buffer);
		Thread.Sleep(1000);
	}
}

This code configures the bus as follows:

  • Chip select will be D5 on the Netduino Mini
  • Chip select will be active low
  • Clock is low when the device is inactive
  • Data is sampled on the falling edge of the clock signal
  • Clock rate is 500 KHz

The selection of these values is deliberate as they match the values used on the GO! bus.

STM8S Code

This is where it got tricky and where I need some help. We have a simple application on the Netduino Mini which simply sends out three bytes, waits a second and then repeats. In order to know that we have received this data OK, the application on the STM8S will read these bytes from the SPI bus, add 100 and then send the result back to the Mini.

The first thing we need to do is to set up a project similar to the first article in this series. The main STD Peripheral Library module we will be using here is the SPI library which can be found in stm8s_spi.c. Add this to the project and then reference the stm8s_spi.h in the main program file.

Now we have the SPI library referenced and the library file added to the application we need to start using it. So the first step we need to take is to configure the library ready for use. The following code should achieve this matching the parameters we have set up in Netduino Mini project above.

SPI_DeInit();
SPI_Init(SPI_FIRSTBIT_MSB,                      // MSB first.
		 SPI_BAUDRATEPRESCALER_32,              // For slave comms this does not matter as the clock is controlled by the master.
		 SPI_MODE_SLAVE,                        // SPI Slave
		 SPI_CLOCKPOLARITY_LOW,                 // Clock is high when the bus is idle.
		 SPI_CLOCKPHASE_2EDGE,                  // Falling clock edge clocks the first data bit.
		 SPI_DATADIRECTION_2LINES_FULLDUPLEX,   // Full duplex transmission.
		 SPI_NSS_HARD,                          // Disable slave management by software.
		 0x07);                                 // CRC style.
SPI_ITConfig(SPI_IT_RXNE, ENABLE);              // Interrupt when the Rx buffer is not empty.
SPI_ITConfig(SPI_IT_TXE, ENABLE);               // Interrupt when the Tx buffer is empty.

The first this we do is call SPI_DeInit to reset any SPI bus configuration.

Next we call SPI_Init to configure the system, as a SPI slave using the same parameters as the SPI master (i.e. the Netduino Mini). You should not that although the baud rate prescalar parameter is set here it will be ignored. The master controls the clock on the SPI bus.

The final two statements (SPI_ITConfig statements) determine which actions will generate an interrupt. In this case we are instructing the system to generate an interrupt in the following cases:

  • When the Rx buffer is not empty (i.e. data has been received)
  • When the Tx buffer is empty (i.e. the system is ready to send more data)

Interrupt Handler

The data will be handled by an interrupt handler. This handler will be called when we need to send data or when we have data ready for processing. As before, the handlers can be found in the file stm8s_it.c. The handler we will be looking for is the SPI_IRQHandler. Locate this handler and replace the code with the following:

INTERRUPT_HANDLER(SPI_IRQHandler, 10)
{
    if (SPI->SR & SPI_SR_RXNE)
    {
        //  u8 ch = SPI_ReceiveData();
        u8 ch = SPI->DR;
        //  SPI_SendData(ch + 100);
        SPI->DR = ch + 100;
    }
    SPI->SR = (u8) 0;
}

The two lines commented out are the equivalent calls into the SPI library. Remember that ISR’s should be as fast as possible. Replacing the calls with the appropriate expansions will decrease the amount of time required to execute the ISR.

So, here we check to see if the ISR is being called because the Rx buffer is not empty. If the buffer is not empty then we extract the value from the buffer, add 100 to it and then put this in the Tx buffer.

The final thing we do is to clear the interrupt from the status register.

Main Program Loop

The code in the main program queues up a byte ready for transmission, enables the interrupts and then repeatedly waits for more interrupts. The code looks like this:

int main(void)
{
    Initialise();
    SPI_SendData('*');                          // Ready to send before master can initiate transfer.
    enableInterrupts();                         // Make sure interrupts are enabled.
    while (1)
    {
        wfi();
    }
}

So What Happened?

After spending a fair amount of time with the logic analyser and the debugger I hit a brick wall. The interrupts we not getting fired as I expected. This is where Chris Walker and CW2’s help was required. The point I was missing regarded the SPI_NSS signal. On the STM8S103 chip I was using this function is an alternative function for the labelled pin. In order to allow this pin to act as the chip select for the SPI bus it was necessary to set the alternative function register on the chip. Once this was set correctly the system started to generate interrupts as expected.