Continuing the STM8 Expedition

STM8S105 Discovery

Encoder Interface

In applications like motor control, it is often required to know how much a motor has rotated, how fast it is rotating and in which direction. It is possible to deduce these feedback data with interrupts, timers and even with tricky GPIO coding. However, using methods employing GPIOs, interrupts and timers will add some resource overhead and will also add up extra coding efforts. STM8s have in-built encoder decoding interface that can be used in such scenarios without extra coding or hardware addition.

Rotary Click Binary Clock

The applications of encoder decoding interface are not only limited to acquiring motor feedbacks. The encoder interface can also be used to interface rotary encoders, linear actuators, digital volume control, etc. Here in this example, I demonstrated this encoder interface with a rotary encoder.

Hardware Connection

enc

 

Code Example

#include "STM8S.h"
#include "lcd.h"


#define LED_Port                               GPIOD
#define LED_Pin                                GPIO_PIN_0


unsigned char bl_state;
unsigned char data_value;


void clock_setup(void);
void GPIO_setup(void);
void TIM1_setup(void);
void lcd_print(unsigned char x_pos, unsigned char y_pos, unsigned int value);


void main()
{     
       unsigned int present_value = 0x0000;
       unsigned int previous_value = 0x0001;

       clock_setup();
       GPIO_setup();
       TIM1_setup();

       LCD_init(); 
       LCD_clear_home();

       LCD_goto(1, 0);
       LCD_putstr("STM8S QEI Test");
       LCD_goto(0, 1);
       LCD_putstr("Value:");

       while(TRUE)
       {
              present_value = TIM1_GetCounter();
              if(present_value != previous_value)
              {
                     lcd_print(12, 1, present_value);
                     GPIO_WriteReverse(LED_Port, LED_Pin);
              }

              previous_value = present_value;
       };
}


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_HSIDIV4);                                         CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);

       CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSI, DISABLE,
                             CLK_CURRENTCLOCKSTATE_ENABLE);

       CLK_PeripheralClockConfig(CLK_PERIPHERAL_I2C, ENABLE);
       CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER1, ENABLE);
       CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, DISABLE);
       CLK_PeripheralClockConfig(CLK_PERIPHERAL_ADC, DISABLE);
       CLK_PeripheralClockConfig(CLK_PERIPHERAL_AWU, DISABLE);
       CLK_PeripheralClockConfig(CLK_PERIPHERAL_UART1, 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_1 | GPIO_PIN_2)),
                 GPIO_MODE_IN_PU_NO_IT);

       GPIO_DeInit(GPIOD);
       GPIO_Init(LED_Port, LED_Pin, GPIO_MODE_OUT_OD_HIZ_FAST);
}


void TIM1_setup(void)
{
       TIM1_DeInit();

       TIM1_TimeBaseInit(10, TIM1_COUNTERMODE_UP, 1000, 1);

       TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI12,
                                  TIM1_ICPOLARITY_FALLING,
                                  TIM1_ICPOLARITY_FALLING);

       TIM1_Cmd(ENABLE);
}


void lcd_print(unsigned char x_pos, unsigned char y_pos, unsigned int value)
{
       char tmp[5] = {0x20, 0x20, 0x20, 0x20, '\0'} ;

       tmp[0] = ((value / 1000) + 0x30);
       tmp[1] = (((value / 100) % 10) + 0x30);
       tmp[2] = (((value / 10) % 10) + 0x30);
       tmp[3] = ((value % 10) + 0x30);

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

 

Explanation

A rotary/quadrature encoder gives two outputs that are slightly out of phase:

rotary

Thus, to interface such an encoder, we will need two timer inputs. TI12 are those inputs. TI12 are mapped to TIM1_CH1 and TIM1_CH2 respectively. These inputs need to be pulled up unless external pull-up resistors are used.

GPIO_DeInit(GPIOC);
GPIO_Init(GPIOC,
          ((GPIO_Pin_TypeDef)(GPIO_PIN_1 | GPIO_PIN_2)),
          GPIO_MODE_IN_PU_NO_IT);
 
The peripheral and CPU clocks are set as follows:
 
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV4);
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);

CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSI, DISABLE,
                       CLK_CURRENTCLOCKSTATE_ENABLE);

CLK_PeripheralClockConfig(CLK_PERIPHERAL_I2C, ENABLE);
CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER1, ENABLE);

Since TIM1’s encoder interface is used, TIM1 peripheral clock is enabled and the I2C clock is enabled for the I2C LCD module.

Then, the main thing to explain here is the way the timer is set for encoder interface:

void TIM1_setup(void)
{
       TIM1_DeInit();

       TIM1_TimeBaseInit(10, TIM1_COUNTERMODE_UP, 1000, 1);

       TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI12,
                                  TIM1_ICPOLARITY_FALLING,
                                  TIM1_ICPOLARITY_FALLING);

       TIM1_Cmd(ENABLE);
}

The 1000 in the time base setup is maximum encoder interface count, i.e. the encoder interface’s output will span from 0 to 1000.

Encoder modes

Since we are using two inputs, the encoder interface is setup according to the third option shown above.

To get current encoder count, TIM1’s counter is read as follows:

present_value = TIM1_GetCounter();

 

Demo

Encoder Interface (1) Encoder Interface (2)

Related Posts

2 comments

  • Hi SHAWON SHAHRYIAR

    I am wondering how to get a Max31855 to talk to a STM8s via SPI.

    JP

    • What’s to wonder about it? It is a simple SPI communication and SPI for STM8 is no different from the SPI of other MCUs…. The following lines are taken from the device’s datasheet and the write up there states how to communicate with it:

      “Drive CS low to output the first bit on the SO pin. A complete serial-interface read of the cold-junction compensated thermocouple temperature requires 14 clock cycles. Thirty-two clock cycles are required to read both the thermocouple and reference junction temperatures (Table 2 and Table 3.) The first bit, D31, is the thermocouple temperature sign bit, and is presented to the SO pin within tDV of the falling edge of CS. Bits D[30:18] contain the converted temperature in the order of MSB to LSB, and are presented to the SO pin within tD0 of the falling edge of SCK. Bit D16 is normally low and goes high when the thermocouple input is open or shorted to GND or VCC. The reference junction temperature data begins with D15. CS can be taken high at any point while clocking out con-version data. If T+ and T- are unconnected, the thermocouple temperature sign bit (D31) is 0, and the remainder of the thermocouple temperature value (D[30:18]) is 1.”

Leave a Reply

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