Starting STM8 Microcontrollers

STM8S003K3 Discovery

Serial Peripheral Interface (SPI)

SPI communication is an onboard synchronous communication method and is used by a number of devices including sensors, TFT displays, GPIO expanders, PWM controller ICs, memory chips, addon support devices, etc.

There’s always one master device in a SPI communication bus which generates clock and select slave(s). Master sends commands to slave(s). Slave(s) responds to commands sent by the master. The number of slaves in a SPI bus is virtually unlimited. Except the chip selection pin, all SPI devices in a bus can share the same clock and data pins.

Typical full-duplex SPI bus requires four basic I/O pins:

  • Master-Out-Slave-In (MOSI) connected to Slave-Data-In (SDI).
  • Master-In-Slave-Out (MIS0) connected to Slave-Data-Out (SDO).
  • Serial Clock (SCLK) connected to Slave Clock (SCK).
  • Slave Select (SS) connected to Chip Select (CS).

spi

In general, if you wish to know more about SPI bus here are some cool links:

STM8s have SPI hardware that are more capable than the SPI hardware found in other micros. An additional feature of STM8’s SPI is the hardware CRC. This feature ensures reliable data communication between devices.

 

Hardware Connection

spi_cube

Code Example

main.c

#include "STM8S.h"
#include "MAX72XX.h"
 
 
void clock_setup(void);
void GPIO_setup(void);
void SPI_setup(void);
 
 
void main()
{
    unsigned char i = 0x00;
    unsigned char j = 0x00;
 
    unsigned char temp[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    
    const unsigned char text[96] =
    {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
        0x00, 0x7E, 0x04, 0x08, 0x08, 0x04, 0x7E, 0x00,        //M
        0x00, 0x42, 0x42, 0x7E, 0x7E, 0x42, 0x42, 0x00,        //I
        0x00, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x24, 0x00,        //C
        0x00, 0x7E, 0x1A, 0x1A, 0x1A, 0x2A, 0x44, 0x00,        //R
        0x00, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00,        //O
        0x00, 0x7C, 0x12, 0x12, 0x12, 0x12, 0x7C, 0x00,        //A
        0x00, 0x7E, 0x1A, 0x1A, 0x1A, 0x2A, 0x44, 0x00,        //R
        0x00, 0x7E, 0x7E, 0x4A, 0x4A, 0x4A, 0x42, 0x00,        //E
        0x00, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x00,        //N
        0x00, 0x7C, 0x12, 0x12, 0x12, 0x12, 0x7C, 0x00,        //A
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00   
    };
    
                
    clock_setup();
    GPIO_setup();
    SPI_setup();
    MAX72xx_init();
                
    while(TRUE)
    {
          for(i = 0; i < sizeof(temp); i++)
          {
              temp[i] = 0x00;
          }
                                
          for(i = 0; i < sizeof(text); i++)
          {
              for(j = 0; j < sizeof(temp); j++)
              {
                  temp[j] = text[(i + j)];
                  MAX72xx_write((1 + j), temp[j]);
                  delay_ms(9);
              }
           }
    };
}
 
 
void clock_setup(void)
{
     CLK_DeInit();
                
     CLK_HSECmd(DISABLE);
     CLK_LSICmd(DISABLE);
     CLK_HSICmd(ENABLE);
     while(CLK_GetFlagStatus(CLK_FLAG_HSIRDY) == FALSE);
                
     CLK_ClockSwitchCmd(ENABLE);
     CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
     CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
                
     CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSI, 
     DISABLE, CLK_CURRENTCLOCKSTATE_ENABLE);
                
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, ENABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_I2C, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_ADC, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_AWU, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_UART1, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER1, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER2, DISABLE);
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER4, DISABLE);
}
 
 
void GPIO_setup(void)
{
     GPIO_DeInit(GPIOC);
     GPIO_Init(GPIOC, ((GPIO_Pin_TypeDef)GPIO_PIN_5 | GPIO_PIN_6), 
               GPIO_MODE_OUT_PP_HIGH_FAST);
}
 
 
void SPI_setup(void)
{
     SPI_DeInit();
     SPI_Init(SPI_FIRSTBIT_MSB, 
              SPI_BAUDRATEPRESCALER_2, 
              SPI_MODE_MASTER, 
              SPI_CLOCKPOLARITY_HIGH, 
              SPI_CLOCKPHASE_1EDGE, 
              SPI_DATADIRECTION_1LINE_TX, 
              SPI_NSS_SOFT, 
              0x00);
     SPI_Cmd(ENABLE);
}

 

MAX72xx.h

#include "STM8S.h"
 
 
#define CS_pin                                                GPIO_PIN_4
#define CS_port                                               GPIOC
 
#define NOP                                                   0x00
#define DIG0                                                  0x01
#define DIG1                                                  0x02
#define DIG2                                                  0x03
#define DIG3                                                  0x04
#define DIG4                                                  0x05
#define DIG5                                                  0x06
#define DIG6                                                  0x07
#define DIG7                                                  0x08
 
#define decode_mode_reg                                       0x09
#define intensity_reg                                         0x0A
#define scan_limit_reg                                        0x0B
#define shutdown_reg                                          0x0C
#define display_test_reg                                      0x0F
 
#define shutdown_cmd                                          0x00
#define run_cmd                                               0x01
 
#define no_test_cmd                                           0x00
#define test_cmd                                              0x01
                             
#define digit_0_only                                          0x00
#define digit_0_to_1                                          0x01
#define digit_0_to_2                                          0x02
#define digit_0_to_3                                          0x03
#define digit_0_to_4                                          0x04
#define digit_0_to_5                                          0x05
#define digit_0_to_6                                          0x06
#define digit_0_to_7                                          0x07
                                                  
#define No_decode_for_all                                     0x00
#define Code_B_decode_digit_0                                 0x01
#define Code_B_decode_digit_0_to_3                            0x0F
#define Code_B_decode_for_all                                 0xFF
 
 
void MAX72xx_init(void);
void MAX72xx_write(unsigned char address, unsigned char value);

 

MAX72xx.c

#include "MAX72xx.h"
 
 
void MAX72xx_init(void)
{
    GPIO_Init(CS_port, CS_pin, GPIO_MODE_OUT_PP_HIGH_FAST);
 
    MAX72xx_write(shutdown_reg, run_cmd);                 
    MAX72xx_write(decode_mode_reg, 0x00);
    MAX72xx_write(scan_limit_reg, 0x07);
    MAX72xx_write(intensity_reg, 0x04);
    MAX72xx_write(display_test_reg, test_cmd);
    delay_ms(10);     
    MAX72xx_write(display_test_reg, no_test_cmd);  
}
 
 
void MAX72xx_write(unsigned char address, unsigned char value)
{
    while(SPI_GetFlagStatus(SPI_FLAG_BSY));
    GPIO_WriteLow(CS_port, CS_pin);
                
    SPI_SendData(address);
    while(!SPI_GetFlagStatus(SPI_FLAG_TXE));
                
    SPI_SendData(value);
    while(!SPI_GetFlagStatus(SPI_FLAG_TXE));
                
    GPIO_WriteHigh(CS_port, CS_pin);
}

 

Explanation

This time we are again using the max peripheral and CPU clock:

CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
….
….
CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, ENABLE);

We also need to set the GPIOs:

#define CS_pin                         GPIO_PIN_4
#define CS_port                       GPIOC
….
….
GPIO_DeInit(GPIOC);
GPIO_Init(CS_port, CS_pin, GPIO_MODE_OUT_PP_HIGH_FAST);
GPIO_Init(GPIOC, ((GPIO_Pin_TypeDef)GPIO_PIN_5 | GPIO_PIN_6), 
          GPIO_MODE_OUT_PP_HIGH_FAST);

Note we can use definitions to make things meaningful. The GPIOs should be configured as fast I/Os because SPI communication is faster than simple GPIO operations.

Now for the SPI configuration part. Assuming you know how to interpret timing diagrams and understand device datasheets, SPI configuration should not be a problem. Here in the case of MAX7219, we have configured the SPI port as to send MSB first, we have also selected a fast peripheral clock, we have made the STM8 SPI act like a master with proper SPI mode and we have set the sort of duplex. The last two parameters are not important as we are not using hardware slave select option and CRC feature.

void SPI_setup(void)
{
     SPI_DeInit();
     SPI_Init(SPI_FIRSTBIT_MSB, 
              SPI_BAUDRATEPRESCALER_2, 
              SPI_MODE_MASTER, 
              SPI_CLOCKPOLARITY_HIGH, 
              SPI_CLOCKPHASE_1EDGE, 
              SPI_DATADIRECTION_1LINE_TX, 
              SPI_NSS_SOFT, 
              0x00);
     SPI_Cmd(ENABLE);
}

The timing diagram of MAX7219 suggests that CS should be low in order for MAX7219 to receive data.

Timing Diagram

Then it suggests that when idle, clock must be high, data transfer is done on every rising edge of the clock. All these are what required for setting up the SPI hardware.

void MAX72xx_write(unsigned char address, unsigned char value)
{
    while(SPI_GetFlagStatus(SPI_FLAG_BSY));
    GPIO_WriteLow(CS_port, CS_pin);
                
    SPI_SendData(address);
    while(!SPI_GetFlagStatus(SPI_FLAG_TXE));
                
    SPI_SendData(value);
    while(!SPI_GetFlagStatus(SPI_FLAG_TXE));
                
    GPIO_WriteHigh(CS_port, CS_pin);
}

Before sending data to MAX7219, we must check if the SPI hardware is busy for some reason. We set CS low by setting STM8’s slave select pin (PC4) low. Then we send address and data. Every time we send something we must wait until it has completely been sent out. Finally, we set CS high to latch sent data. This function is what we will need to set MAX7219 things up and also to update displays.

The demo here is that of a MAX7219- based scrolling dot-matrix display. Letters of MICROARENA – the name of my Facebook page is scrolled.

Demo

SPI

MAX7219

Related Posts

50 comments

  • Thanks for this article..
    I am looking for microsd card interface with STM8s discovery board. can you please provide the tutorial for microsd card?

  • In relation to disabling unused peripheral clocks as suggested by you:

    I found the following list of peripherals in the STM8S105 Discovery Board: Advanced control timer (TIM1), General-purpose timers (TIM2 and TIM3), Basic timer (TIM4), SPI, I2C, UART2, Window WDG, Independent WDG, ADC1,
    AWU timer, Beeper.

    Is this all?

    I could not find the clock peripheral configuration function for Window WDG, Independent WDG, Beeper in the Standard Peripheral Library.
    Is it not required?

    P.S. I am new to MCU programming with a little experience on Arduino. So please forgive me if the question is foolish.

    • 1. STM8S003K3 Discovery – the one I use for the blog is different from STM8S105…. They have different peripherals and so there will be differences in peripheral clocks…. Please check the device datasheet/STM8CubeMX for available hardware….

      2. WWDG and IWDG are set in option bytes and they don’t use peripheral/CPU clock…. Their oscillator is separate…. Due to these they don’t have clock configurations like other peripherals….

      P.S: No question is a noob question…. every question is allowed no matter how silly it may look….

  • Hi please someone tel me where can i find stm8s_delay.h and stm8s_delay.c
    Thanks

  • Hi , please someone tel me . Where can i find stm8s_delay.h and stm8s_delay.c
    Thanks ..

  • i am using stms003f3p6 controller..i have done as u told exactly but tim1 input capture is not displaying any value …according to your article it should display 10…i dont have any errors everything is fine…pulse on tim2 is ok ..it is coming as u said…but capture not working..please help me …thank you…

  • Hi Sir,
    i have just follwed your instruction provied in yout tutorial in main.c

    #include “stm8s.h”

    main()
    {
    while (1);
    }

    This error comes, can you please check why this error come, i am new and learn the controller programming.

    main.c:
    The command: “cxstm8 -i”d:\other datasheet\new folder\lib\stm8s_stdperiph_lib\libraries\stm8s_stdperiph_driver\inc” +debug -pxp -no -l +mods0 -pp -i”C:\Program Files (x86)\COSMIC\FSE_Compilers\CXSTM8\Hstm8″ -clDebug\ -coDebug\ main.c ” has failed, the returned value is: 1
    exit code=1.

    main.o – 2 error(s), 0 warning(s)

    Thanks,

  • Hi there, i solve the previous ADC problem. Anyway, u make some good tutorial on stm8 chips. Nice work

    • What was causing that issue? How did you solve it?

      • because i put all these

        ADC_DeInit(ADC1);
        ADC_SamplingTimeConfig(ADC1, ADC_Group_SlowChannels, ADC_SamplingTime_4Cycles);
        ADC_SchmittTriggerConfig(ADC1, ADC_Channel_7, DISABLE);
        ADC_ChannelCmd(ADC1, ADC_Channel_7, ENABLE);
        ADC_Init(ADC1, ADC_ConversionMode_Continuous, ADC_Resolution_12Bit, ADC_Prescaler_1);
        ADC_Cmd(ADC1, ENABLE);
        ADC_ExternalTrigConfig(ADC1, ADC_ExtEventSelection_None, ADC_ExtTRGSensitivity_All);

        in ‘while(1)’. It keeps looping.

        Once i put it before ‘while (1)’, it becomes normal.

  • Hi there, thanks for sharing. I learn alot thru your example, but I have encounter a problem with adc. Im currently using stm8l151k4t6. I wanna use the pin D7 as to read the voltage from my battery and monitor it (display on LCD). Can u please check where it goes wrong on my code?

    ADC_DeInit(ADC1);
    ADC_SamplingTimeConfig(ADC1, ADC_Group_SlowChannels, ADC_SamplingTime_4Cycles);
    ADC_SchmittTriggerConfig(ADC1, ADC_Channel_7, DISABLE);
    ADC_ChannelCmd(ADC1, ADC_Channel_7, ENABLE);
    ADC_Init(ADC1, ADC_ConversionMode_Continuous, ADC_Resolution_12Bit, ADC_Prescaler_1);
    ADC_Cmd(ADC1, ENABLE);
    ADC_ExternalTrigConfig(ADC1, ADC_ExtEventSelection_None, ADC_ExtTRGSensitivity_All);
    ADC_SoftwareStartConv(ADC1);
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
    battery = ADC_GetConversionValue(ADC1);

  • hi shawon great tutorials it would be nice too if you covered the coide compiler for arm it’s a good free compiler

  • hi shawon thanks for your response and support it turns out that my problem was a license issue I stupidly thought i use one license on multiple pc’s I used the license for a particular computer for mine that’s why I’ve been having compilation issues you have to use the license file stm8 sends you that is generated by your own pc else it won’t compile it’ll jus kip throwing you errors

  • It seems that you didn’t follow my instructions completely…. I found some issues:

    1. You have included header and source files of ADC2, CAN, UART2, etc which are unavailable in STM8S103…. Add only files for the peripherals available in your target MCU…. Exclude the rest….

    2. You have not included stm8s_delay header and source files…. Either disable it from stm8s_conf.h header file if you are not going to use it or add both the header and the source files for it in your project….

    Resolve these and you are good to go….

    Thanks….

  • I haven’t compiled any of the examples yet I just tried to compile the main source file just for test purpose n it just keeps showing me the same errors

  • yes the comic compiler was installed properly i even received a license file from my mail with the instructions to copy the license file to the license folder in the install directory if you fail to do this it will keep popping up for you to put in the license file. iincluded all the source files and header files as instructed i am working with the cheap stm8s103f i uncommented the stm8s103f in the stm8.h header file as you instructed and just to compile the main file it just throws me errors

  • no I just followed your instructions I just compiled it after setup i did not write any code yet just compiled the default main file

    • Need more details….

      1. The version of STVD and Cosmic
      2. Which example is giving this issue?
      3. The chip you are using if it other than STM8S003
      4. Have you tried to compile something else other than my examples?
      5. Are the paths to libraries and other folders properly added?
      6. Is the Cosmic compiler registered properly?

  • it throws this line The command: “cxstm8 -iinc +debug -pxp -no -l +mods0 -pp -i”C:\Program Files (x86)\COSMIC\FSE_Compilers\Hstm8″ -clDebug\ -coDebug\ main.c ” has failed, the returned value is: 1
    exit code=1.

  • hi I’m having compilation issues when I compile it just throws me error about comic compiler located in the program file ialso excluded unwanted header files but my problems still weren’t solved

  • hi thanks for the tutorials but i am having compiler error issues even when I remove unnecessary header files it say comic compiler error

  • I am new to STM8S003k3 Discovery board.I have done UART communication using STM standard peripheral library example. Now I want to define any other GPIO pin for UART communication. Is it possible to use d0 and d1 pin for the UART communication? If yes, then please let me know how?

  • thank you very much for this article!

  • Thank you for your time and effort.

  • Good job bro

  • Thank you for this exceptionally comprehensive article, much appreciated

    Rando!

Leave a Reply

Your email address will not be published. Required fields are marked *