PROGCONT.RU

Форма входа







Регистрация Вход/

STM8S инкрементный энкодер подключение к контроллеру и примеры программного кода SPL для его использования.

Во всех контроллерах STM8S имеется готовый аппаратный интерфейс для подключения энкодера, который находится к сожалению только в одном первом таймере(TIM1), поэтому к контроллеру можно подключить только один энкодер и то если свободен нужный таймер.

В статье я выложил готовые примеры для работы с механическим и оптическим инкрементным энкодером, для магнитного аналогичный пример как для оптического. Использовать для примеров я буду контроллер STM8S903K3 и LCD на драйвере MAX7219, примеры подойду так же к другим моделям контроллеров данного семейства.


Начнём с механического энкодера и его схемы подключения к контроллеру.


NO


Для более устойчивой работы на схеме имеются сглаживающие конденсаторы подключённые к выводам 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


И на всякий случай схема подключения к оптическим энкодерам которая подойдёт и к магнитным.


NO

Тут отсутствуют на выходах энкодера конденсаторы как у механических, они здесь могут навредить так как будут задерживать спад или подъём выходящего сигнала, что вызовет пропуски счета на больших оборотах энкодера. Для исключения помех на проводах 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

Комментарии  Только зарегистрированные пользователи могут оставлять комментарии!

NOafs1001   2024-03-25 12:08:06
Счетчик считает только в одном направлении в зависимости от первоначальной настройки таймера TIM1, вне зависимости от направления вращения энкодера. Работоспособность энкодера проверена осциллографом, очередность подачи сигналов меняется в зависимости от направления вращения энкодера. Дело в настройке TIM1? Перепробовал различные варианты настройки таймера с использованием TIM1_EncoderInterfaceConfig.

NOafs1001   2024-04-05 08:25:54
Вопрос решен. На плате STM8S-Discovery не было физического контакта PC1 контроллера с разъемом CN2(pin2).