STM8S инкрементный энкодер подключение к контроллеру и примеры программного кода SPL для его использования.
Во всех контроллерах STM8S имеется готовый аппаратный интерфейс для подключения энкодера, который находится к сожалению только в одном первом таймере(TIM1), поэтому к контроллеру можно подключить только один энкодер и то если свободен нужный таймер.
В статье я выложил готовые примеры для работы с механическим и оптическим инкрементным энкодером, для магнитного аналогичный пример как для оптического. Использовать для примеров я буду контроллер STM8S903K3 и LCD на драйвере MAX7219, примеры подойду так же к другим моделям контроллеров данного семейства.
Начнём с механического энкодера и его схемы подключения к контроллеру.
Для более устойчивой работы на схеме имеются сглаживающие конденсаторы подключённые к выводам A и B, помогающие подавить дребезг и помехи.
Простой пример где вынимаем значение положения энкодера из счётчика таймера.
#include "stm8s.h"
/*Функция настройки входящих фильтров выводов TIM1*/
void Filter_Value(uint8_t value);
uint32_t count;
int main( void ){
/*Устанавливаем максимальную частоту контроллера 16MHz*/
CLK_DeInit();
CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1);
/*Настройка входящих выводов TIM1 и его самого для работы в режиме энкодера*/
GPIO_Init( GPIOC, GPIO_PIN_1, GPIO_MODE_IN_PU_NO_IT);
GPIO_Init( GPIOC, GPIO_PIN_2, GPIO_MODE_IN_PU_NO_IT);
TIM1_TimeBaseInit(0, TIM1_COUNTERMODE_UP, 65535, 0);
TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI12, TIM1_ICPOLARITY_RISING, TIM1_ICPOLARITY_RISING);
/*Устанавливаем максимальную задержку для фильтра выводов TIM1*/
Filter_Value(15);
TIM1_Cmd(ENABLE);
while(1){
/*Вынимаем значение положение энкодера и делим на 4, для фиксации одной риски*/
count=TIM1_GetCounter()/4;
}
return 0;}
/*Функция изменяет значение входящих фильтров, для выводов 1 и 2 TIM1, value от 0 до 15*/
void Filter_Value(uint8_t value){
TIM1->CCMR1|=(uint8_t)(value<<4);
TIM1->CCMR2|=(uint8_t)(value<<4);
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line){while (1){}}
#endif
Не большой ликбез по функциям настройки TIM1.
- TIM1_TimeBaseInit(0, TIM1_COUNTERMODE_UP, 0xFFFF, 0)
- тут можете поменять 65535 на значение от 0 до 65535, это предел после достижения которого счётчик будет сбрасываться или если зайдёт за нуль выставится опять.
- TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI12, TIM1_ICPOLARITY_RISING, TIM1_ICPOLARITY_RISING)
- функция настройки TIM1 для работы в режиме энкодера.
Параметр TIM1_ENCODERMODE_TI12 указывает контроллеру, что нужно фиксировать все события с выводов A и B энкодера, то есть за период четыре значения или например в энкодере 100 рисок а считать будет за один оборот 400. Если изменить его на TIM1_ENCODERMODE_TI1 или TIM1_ENCODERMODE_TI2 то события будут фиксироваться только с вывода TIM1_CH1 или соответственно с TIM1_CH2, за один оборот энкодера будет не 100 рисок а 200.
Параметр TIM1_ICPOLARITY_RISING указывает контроллеру, что сигналы на выводах TIM1_CH1 и TIM1_CH2 не инвертируется, если поменять на TIM1_ICPOLARITY_FALLING то сигнал будет изменён на противоположный, попробуйте в функцию поставить вот такую комбинацию TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI12, TIM1_ICPOLARITY_FALLING, TIM1_ICPOLARITY_RISING) то счёт будет идти в обратном направление.
- Filter_Value(15)
- функция устанавливает задержку для отсеивания ложных срабатываний от дребезга контактов энкодера, тут стоит максимальное значение которое рекомендую для механических энкодеров. Для себя можете подобрать опытным путём от 0 до 15, пока не пропадут ложные срабатывания. Функция сделана мной, не забываем добавлять её код из примера в свою программу.
Теперь код примера, где реализован простой счётчик с выводом информации на LCD.
#include "stm8s.h"
#define Decode_Mode 9
#define Intensity 10
#define Scan_Limit 11
#define Shutdown 12
#define Display_Test 15
/*Функции настройки LCD*/
void LCD_Send_Data( uint8_t address, uint8_t data);
void LCD_Display( uint32_t data_lcd);
void LCD_SPI_Init(void);
/*Функция настройки входящих фильтров выводов TIM1*/
void Filter_Value(uint8_t value);
uint32_t count;
int main( void ){
/*Устанавливаем максимальную частоту контроллера 16MHz*/
CLK_DeInit();
CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1);
/*Настройка LCD*/
LCD_SPI_Init();
/*Настройка входящих выводов TIM1 и его самого для работы в режиме энкодера*/
GPIO_Init( GPIOC, GPIO_PIN_1, GPIO_MODE_IN_PU_NO_IT);
GPIO_Init( GPIOC, GPIO_PIN_2, GPIO_MODE_IN_PU_NO_IT);
TIM1_TimeBaseInit(0, TIM1_COUNTERMODE_UP, 65535, 0);
TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI12, TIM1_ICPOLARITY_RISING, TIM1_ICPOLARITY_RISING);
/*Устанавливаем максимальную задержку для фильтра выводов TIM1*/
Filter_Value(15);
TIM1_Cmd(ENABLE);
while(1){
count=TIM1_GetCounter()/4;
LCD_Display(count);
for( uint32_t e=0; e<100; e++);
}
return 0;}
/*Функция настройки SPI для взаимодействия с LCD*/
void LCD_SPI_Init(){
/*Настройка для SPI выводов*/
GPIO_Init( GPIOC, GPIO_PIN_5, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init( GPIOC, GPIO_PIN_6, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init( GPIOC, GPIO_PIN_7, GPIO_MODE_OUT_PP_HIGH_FAST);
/*Настройка самого SPI для передачи данных*/
SPI_Init( SPI_FIRSTBIT_MSB, SPI_BAUDRATEPRESCALER_2, SPI_MODE_MASTER,
SPI_CLOCKPOLARITY_LOW, SPI_CLOCKPHASE_1EDGE, SPI_DATADIRECTION_1LINE_TX,
SPI_NSS_SOFT, 0x07);
SPI_Cmd(ENABLE);
/*Инициализация MAX7219*/
LCD_Send_Data( Shutdown, 1);
LCD_Send_Data( Display_Test, 0);
LCD_Send_Data( Decode_Mode, 255);
LCD_Send_Data( Intensity, 31);
LCD_Send_Data( Scan_Limit, 7);
}
/*Функция вывода счёта на экран LCD*/
void LCD_Display( uint32_t data_lcd){
uint32_t mask=10000000;
/*Цикл вывода информации на LED модуль*/
for( uint8_t i=8; i>0; i--){
LCD_Send_Data( i, (uint8_t)(data_lcd/mask));
data_lcd=data_lcd-(data_lcd/mask*mask);
mask/=10;
}
}
/*Функция отправки адреса и данных*/
void LCD_Send_Data( uint8_t address, uint8_t data){
GPIO_WriteLow( GPIOC, GPIO_PIN_7);
SPI_SendData(address);
while(!SPI_GetFlagStatus(SPI_FLAG_TXE));
SPI_SendData(data);
while(SPI_GetFlagStatus(SPI_FLAG_BSY));
GPIO_WriteHigh( GPIOC, GPIO_PIN_7);
}
/*Функция изменяет значение входящих фильтров, для выводов 1 и 2 TIM1, value от 0 до 15*/
void Filter_Value(uint8_t value){
TIM1->CCMR1|=(uint8_t)(value<<4);
TIM1->CCMR2|=(uint8_t)(value<<4);
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line){while (1){}}
#endif
И на всякий случай схема подключения к оптическим энкодерам которая подойдёт и к магнитным.
Тут отсутствуют на выходах энкодера конденсаторы как у механических, они здесь могут навредить так как будут задерживать спад или подъём выходящего сигнала, что вызовет пропуски счета на больших оборотах энкодера. Для исключения помех на проводах A и B энкодера они должны быть обязательно экранированы.
В энкодерах оптических и магнитных могут отсутствовать выходные схемы которые выравнивают сигнал, делают его квадратурным, с контроллерами не нужно использовать дополнительные схемы для выравнивания сигналов так как их входы цифровые и имеют встроенный гистерезис.
И не большой пример кода, где изменил настройки для входов таймера(отключил внутренние подтягивающие резисторы) так как напряжение питания энкодера может быть больше контроллерного но не должно превышать +5V и изменил значение настройки фильтра для входов должно быть нулевым(фильтр отключён).
#include "stm8s.h"
/*Функция настройки входящих фильтров выводов TIM1*/
void Filter_Value(uint8_t value);
uint32_t count;
int main( void ){
/*Устанавливаем максимальную частоту контроллера 16MHz*/
CLK_DeInit();
CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1);
/*Настройка входящих выводов TIM1(отключаем подтягивающие резисторы) и его самого для работы в режиме энкодера*/
GPIO_Init( GPIOC, GPIO_PIN_1, GPIO_MODE_IN_FL_NO_IT);
GPIO_Init( GPIOC, GPIO_PIN_2, GPIO_MODE_IN_FL_NO_IT);
TIM1_TimeBaseInit(0, TIM1_COUNTERMODE_UP, 65535, 0);
TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI12, TIM1_ICPOLARITY_RISING, TIM1_ICPOLARITY_RISING);
/*Отключаем фильтр входов*/
Filter_Value(0);
TIM1_Cmd(ENABLE);
while(1){
/*Вынимаем реальное значение положение которое в 4 раза больше чем разрешение энкодера*/
count=TIM1_GetCounter();
}
return 0;}
/*Функция изменяет значение входящих фильтров, для выводов 1 и 2 TIM1, value от 0 до 15*/
void Filter_Value(uint8_t value){
TIM1->CCMR1|=(uint8_t)(value<<4);
TIM1->CCMR2|=(uint8_t)(value<<4);
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line){while (1){}}
#endif