RSS

Archive for June, 2012

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.