STM8 Microcontrollers – the Final Chapters

Hardware

This post is the sequel of the posts on STM8 microcontrollers here and here.

Hardware

Software I2C – DS1307

Software-based I2C is not a big requirement in case of STM8s because STM8 chips have hardware I2C blocks. However, for some reason if we are unable to use hardware I2C blocks, we can implement software-based I2C by bit-banging ordinary GPIOs. I2C protocol is itself slow compared to SPI and other protocols and so implementing software-based I2C will not significantly affect communication speed and overall throughput in most applications. However, extra coding is required and this in turn adds some coding and resource overheads.

Hardware Connection

sw_i2c

Code Example

SW_I2C.h

#include "STM8S.h"


#define SW_I2C_port              GPIOD

#define SDA_pin                  GPIO_PIN_6
#define SCL_pin                  GPIO_PIN_7

#define SW_I2C_OUT()             do{GPIO_DeInit(SW_I2C_port); GPIO_Init(SW_I2C_port, SDA_pin, GPIO_MODE_OUT_PP_LOW_FAST); GPIO_Init(SW_I2C_port, SCL_pin, GPIO_MODE_OUT_PP_LOW_FAST);}while(0)

#define SW_I2C_IN()              do{GPIO_DeInit(SW_I2C_port); GPIO_Init(SW_I2C_port, SDA_pin, GPIO_MODE_IN_FL_NO_IT); GPIO_Init(SW_I2C_port, SCL_pin, GPIO_MODE_OUT_PP_LOW_FAST);}while(0)

#define SDA_HIGH()               GPIO_WriteHigh(SW_I2C_port, SDA_pin)
#define SDA_LOW()                GPIO_WriteLow(SW_I2C_port, SDA_pin)
#define SCL_HIGH()               GPIO_WriteHigh(SW_I2C_port, SCL_pin)
#define SCL_LOW()                GPIO_WriteLow(SW_I2C_port, SCL_pin)

#define SDA_IN()                 GPIO_ReadInputPin(SW_I2C_port, SDA_pin)

#define I2C_ACK                  0xFF
#define I2C_NACK                 0x00

#define I2C_timeout              1000


void SW_I2C_init(void);
void SW_I2C_start(void);
void SW_I2C_stop(void);
unsigned char SW_I2C_read(unsigned char ack);
void SW_I2C_write(unsigned char value);
void SW_I2C_ACK_NACK(unsigned char mode);
unsigned char SW_I2C_wait_ACK(void);

 

SW_I2C.c

#include "SW_I2C.h"


void SW_I2C_init(void)
{
    SW_I2C_OUT();
    delay_ms(10);
    SDA_HIGH();
    SCL_HIGH();
}


void SW_I2C_start(void)
{
    SW_I2C_OUT();
    SDA_HIGH();
    SCL_HIGH();
    delay_us(40);
    SDA_LOW();
    delay_us(40);
    SCL_LOW();
}


void SW_I2C_stop(void)
{
    SW_I2C_OUT();
    SDA_LOW();
    SCL_LOW();
    delay_us(40);
    SDA_HIGH();
    SCL_HIGH();
    delay_us(40);
}


unsigned char SW_I2C_read(unsigned char ack)
{
    unsigned char i = 0x08;
    unsigned char j = 0x00;

    SW_I2C_IN();

    while(i > 0x00)
    {
        SCL_LOW();
        delay_us(20);
        SCL_HIGH();
        delay_us(20);
        j <<= 1;

        if(SDA_IN() != 0x00)
        {
            j++;
        }

        delay_us(10);
        i--;
    };

    switch(ack)
    {
        case I2C_ACK:
        {
            SW_I2C_ACK_NACK(I2C_ACK);;
            break;
        }
        default:
        {
            SW_I2C_ACK_NACK(I2C_NACK);;
            break;
        }
    }

    return j;
}


void SW_I2C_write(unsigned char value)
{
    unsigned char i = 0x08;

    SW_I2C_OUT();
    SCL_LOW();

    while(i > 0x00)
    {

        if(((value & 0x80) >> 0x07) != 0x00)
        {
            SDA_HIGH();
        }
        else
        {
            SDA_LOW();
        }


        value <<= 1;
        delay_us(20);
        SCL_HIGH();
        delay_us(20);
        SCL_LOW();
        delay_us(20);
        i--;
    };
}


void SW_I2C_ACK_NACK(unsigned char mode)
{
    SCL_LOW();
    SW_I2C_OUT();

    switch(mode)
    {
        case I2C_ACK:
        {
            SDA_LOW();
            break;
        }
        default:
        {
            SDA_HIGH();
            break;
        }
    }

    delay_us(20);
    SCL_HIGH();
    delay_us(20);
    SCL_LOW();
}


unsigned char SW_I2C_wait_ACK(void)
{
    signed int timeout = 0x0000;

    SW_I2C_IN();

    SDA_HIGH();
    delay_us(10);
    SCL_HIGH();
    delay_us(10);

    while(SDA_IN() != 0x00)
    {
        timeout++;

        if(timeout > I2C_timeout)
        {
            SW_I2C_stop();
            return 1;
        }
    };

    SCL_LOW();

    return 0x00;
}

 

DS1307.h

#include "SW_I2C.h"


#define sec_reg                0x00
#define min_reg                0x01
#define hr_reg                 0x02
#define day_reg                0x03
#define date_reg               0x04
#define month_reg              0x05
#define year_reg               0x06
#define control_reg            0x07

#define DS1307_addr            0xD0
#define DS1307_WR              DS1307_addr
#define DS1307_RD              0xD1


void DS1307_init(void);
unsigned char DS1307_read(unsigned char address);
void DS1307_write(unsigned char address, unsigned char value);
unsigned char bcd_to_decimal(unsigned char value);
unsigned char decimal_to_bcd(unsigned char value);
void get_time(void);
void set_time(void);

 

DS1307.c

#include "DS1307.h"


extern struct
{
   unsigned char s;
   unsigned char m;
   unsigned char h;
   unsigned char dy;
   unsigned char dt;
   unsigned char mt;
   unsigned char yr;
}time;


void DS1307_init(void)
{
    SW_I2C_init();
    DS1307_write(control_reg, 0x00);
}


unsigned char DS1307_read(unsigned char address)
{
    unsigned char value = 0x00;

    SW_I2C_start();
    SW_I2C_write(DS1307_WR);
    SW_I2C_write(address);

    SW_I2C_start();
    SW_I2C_write(DS1307_RD);
    value = SW_I2C_read(I2C_NACK);
    SW_I2C_stop();

    return value;
}


void DS1307_write(unsigned char address, unsigned char value)
{
    SW_I2C_start();
    SW_I2C_write(DS1307_WR);
    SW_I2C_write(address);
    SW_I2C_write(value);
    SW_I2C_stop();
}


unsigned char bcd_to_decimal(unsigned char value)
{
    return ((value & 0x0F) + (((value & 0xF0) >> 0x04) * 0x0A));
}


unsigned char decimal_to_bcd(unsigned char value)
{
    return (((value / 0x0A) << 0x04) & 0xF0) | ((value % 0x0A) & 0x0F);
}


void get_time(void)
{
       time.s = DS1307_read(sec_reg);
       time.s = bcd_to_decimal(time.s);

       time.m = DS1307_read(min_reg);
       time.m = bcd_to_decimal(time.m);

       time.h = DS1307_read(hr_reg);
       time.h = bcd_to_decimal(time.h);

       time.dy = DS1307_read(day_reg);
       time.dy = bcd_to_decimal(time.dy);

       time.dt = DS1307_read(date_reg);
       time.dt = bcd_to_decimal(time.dt);

       time.mt = DS1307_read(month_reg);
       time.mt = bcd_to_decimal(time.mt);

       time.yr = DS1307_read(year_reg);
       time.yr = bcd_to_decimal(time.yr);
}


void set_time(void)
{
       time.s = decimal_to_bcd(time.s);
       DS1307_write(sec_reg, time.s);

       time.m = decimal_to_bcd(time.m);
       DS1307_write(min_reg, time.m);

       time.h = decimal_to_bcd(time.h);
       DS1307_write(hr_reg, time.h);

       time.dy = decimal_to_bcd(time.dy);
       DS1307_write(day_reg, time.dy);

       time.dt = decimal_to_bcd(time.dt);
       DS1307_write(date_reg, time.dt);

       time.mt = decimal_to_bcd(time.mt);
       DS1307_write(month_reg, time.mt);

       time.yr = decimal_to_bcd(time.yr);
       DS1307_write(year_reg, time.yr);
}

 

main.c

#include "STM8S.h"
#include "lcd.h"
#include "SW_I2C.h"
#include "DS1307.h"


#define Button_port      GPIOB
#define Button_pin         GPIO_PIN_7


struct
{
   unsigned char s;
   unsigned char m;
   unsigned char h;
   unsigned char dy;
   unsigned char dt;
   unsigned char mt;
   unsigned char yr;
}time;

bool set = FALSE;

unsigned char menu = 0;
unsigned char data_value;


void clock_setup(void);
void GPIO_setup(void);
void show_value(unsigned char x_pos, unsigned char y_pos, unsigned char value);
void display_time(void);
void setup_time(void);
unsigned char adjust(unsigned char x_pos, unsigned char y_pos, signed char value_max, signed char value_min, signed char value);


void main(void)
{
       clock_setup();
       GPIO_setup();

       DS1307_init();

       LCD_init();
       LCD_clear_home();

       LCD_goto(0, 0);
       LCD_putstr("STM8 SW-I2C Test");

    while(1)
    {
       if((GPIO_ReadInputPin(Button_port, Button_pin) == FALSE) && (set == FALSE))
       {
              delay_ms(1000);
              if(GPIO_ReadInputPin(Button_port, Button_pin) == FALSE)
              {
                     while(GPIO_ReadInputPin(Button_port, Button_pin) == FALSE);
                     delay_ms(1000);

                     menu = 0;
                     set = TRUE;
              }
       }

       if(set)
       {
              setup_time();
       }
       else
       {
              get_time();
              display_time();
       }
    };
}


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_HSIDIV2);
       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(GPIOB);
       GPIO_Init(Button_port, Button_pin, GPIO_MODE_IN_PU_NO_IT);
}


void show_value(unsigned char x_pos, unsigned char y_pos, unsigned char value)
{
   char ch[0x03] = {0x20, 0x20, '\0'};

   ch[0] = (((value / 10) % 10) + 0x30);
   ch[1] = ((value % 10) + 0x30);

   LCD_goto(x_pos, y_pos);
   LCD_putstr(ch);
}


void display_time(void)
{     
       show_value(4, 1, time.h);
       show_value(7, 1, time.m);
       show_value(10, 1, time.s);

       LCD_goto(6, 1);
       LCD_putchar(':');
       LCD_goto(9, 1);
       LCD_putchar(':');  
       delay_ms(100);
}


void setup_time(void)
{
       switch(menu)
       {
              case 0:
              {
                     time.h = adjust(4, 1, 23, 0, time.h);
                     break;
              }            
              case 1:
              {
                     time.m = adjust(7, 1, 59, 0, time.m);
                     break;
              }
              case 2:
              {
                     time.s = adjust(10, 1, 59, 0, time.s);
                     break;
              }
       }
}


unsigned char adjust(unsigned char x_pos, unsigned char y_pos, signed char value_max, signed char value_min, signed char value)
{
       if(GPIO_ReadInputPin(Button_port, Button_pin) == FALSE)
       {
              delay_ms(900);

              if(GPIO_ReadInputPin(Button_port, Button_pin) == FALSE)
              {
                     while(GPIO_ReadInputPin(Button_port, Button_pin) == FALSE);
                     menu++;

                     if(menu >= 3)
                     {
                           LCD_goto(3, 1);
                           LCD_putchar(' ');
                           LCD_goto(12, 1);
                           LCD_putchar(' ');  
                           set_time();
                           set = FALSE;

                           return;
                     }
              }
       }

       else
       {
              value++;

              if(value > value_max)
              {
                  value = value_min;
              }
       }

       LCD_goto(3, 1);
       LCD_putchar('<');
       LCD_goto(12, 1);
       LCD_putchar('>');  

       LCD_goto(x_pos, y_pos);
       LCD_putstr("  ");
       delay_ms(90);

       show_value(x_pos, y_pos, value);
       delay_ms(90);

       return value;
}

 

Explanation

SW_I2C.h and SW_I2C.c files translate the entire I2C communication operations with the manipulation of ordinary GPIOs. Any GPIO pin pair can be used for software I2C. Just define the software I2C port, and the serial data (SDA) and serial clock (SDA) pins:

#define SW_I2C_port              GPIOD

 

#define SDA_pin                  GPIO_PIN_6
#define SCL_pin                  GPIO_PIN_7

The functions in these files are self-explanatory and so I’m not going to explain them here.

The rest of the code is about using the software I2C library with DS1307 real time clock (RTC) chip to make a real time clock.

 

Demo

SW-I2C

Continue Reading ...

Related Posts

2 comments

Leave a Reply

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