Режим энкодера: различия между версиями

Материал из MIK32 микроконтроллер
(Новая страница: «В примере будет продемонстрирован Timer16 в режиме энкодера. == Работа с конфигуратором (В р...»)
 
Нет описания правки
 
(не показано 7 промежуточных версий 2 участников)
Строка 1: Строка 1:
В примере будет продемонстрирован Timer16 в режиме энкодера.
В примере будет продемонстрирован Timer16 в режиме энкодера.  


== Работа с конфигуратором (В разработке) ==
== Работа с конфигуратором (В разработке) ==
Строка 17: Строка 17:
! rowspan="2" |Активный фронт
! rowspan="2" |Активный фронт
! rowspan="2" |Уровень
! rowspan="2" |Уровень
противоположного сиг-нала (lnput1 для lnput2,
противоположного сигнала (lnput1 для lnput2,


lnput2 для lnput1)
lnput2 для lnput1)
Строка 67: Строка 67:
|Вверх
|Вверх
|}
|}
Например, если Input1 установился в 1, а в это время противоположный сигнал Input2 в низком уровне, то таймер считает вверх.
Если Input1 установился в 1, а Input2 в это время в высоком уровне, то таймер считает вниз.
Если Input1 установился в 0, а Input2 в это время в высоком или низком  уровне, то таймер не считает.
Следующая настройка - период. Это верхний предел, до которого считает таймер. В нашем примере таймер будет считать до максимального значения 65535.
Следующая настройка - период. Это верхний предел, до которого считает таймер. В нашем примере таймер будет считать до максимального значения 65535.


Строка 83: Строка 90:
== Использование библиотеки HAL_Timer16 ==
== Использование библиотеки HAL_Timer16 ==
В сгенерированном проекте в файле main.c должна быть функция Timer16_1_Init, в которой будут заданы настройки для Timer16_1. Выглядит она так:
В сгенерированном проекте в файле main.c должна быть функция Timer16_1_Init, в которой будут заданы настройки для Timer16_1. Выглядит она так:
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
static void Timer16_1_Init(void)
{
    htimer16_1.Instance = TIMER16_1;
    /* Настройка тактирования */
    htimer16_1.Clock.Source = TIMER16_SOURCE_INTERNAL_SYSTEM;
    htimer16_1.CountMode = TIMER16_COUNTMODE_INTERNAL;  /* При тактировании от Input1 не имеет значения */
    htimer16_1.Clock.Prescaler = TIMER16_PRESCALER_1;
    htimer16_1.ActiveEdge = TIMER16_ACTIVEEDGE_RISING;  /* Выбирается при тактированиии от Input1 */
    /* Настройка верхнего предела счета */
    htimer16_1.Period = 0xFFFF;
    /* Настрйока режима обновления регистра ARR и CMP */
    htimer16_1.Preload = TIMER16_PRELOAD_AFTERWRITE;
    /* Настройка триггера */
    htimer16_1.Trigger.Source = TIMER16_TRIGGER_TIM1_GPIO1_9;
    htimer16_1.Trigger.ActiveEdge = TIMER16_TRIGGER_ACTIVEEDGE_SOFTWARE;    /* При использовании триггера значение доложно быть отлично от software */
    htimer16_1.Trigger.TimeOut = TIMER16_TIMEOUT_DISABLE;  /* Разрешить повторное срабатывание триггера */
    /* Настройки фильтра */
    htimer16_1.Filter.ExternalClock = TIMER16_FILTER_NONE;
    htimer16_1.Filter.Trigger = TIMER16_FILTER_NONE;
    /* Настройка режима энкодера */
    htimer16_1.EncoderMode = TIMER16_ENCODER_ENABLE;
    HAL_Timer16_Init(&htimer16_1);
}
</syntaxhighlight>
}}
Кроме этого в функции SystemClock_Config приведены настройки для тактирования. Убедитесь что в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_TIMER16_1_M. Сама функция должна выглядеть примерно так:   
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInit = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  RCC_OscInit.OscillatorEnable = RCC_OSCILLATORTYPE_OSC32K | RCC_OSCILLATORTYPE_OSC32M; 
  RCC_OscInit.OscillatorSystem = RCC_OSCILLATORTYPE_OSC32M;                         
  RCC_OscInit.AHBDivider = 0;                           
  RCC_OscInit.APBMDivider = 0;                           
  RCC_OscInit.APBPDivider = 0;                           
  RCC_OscInit.HSI32MCalibrationValue = 0;                 
  RCC_OscInit.LSI32KCalibrationValue = 0;
  HAL_RCC_OscConfig(&RCC_OscInit);
  PeriphClkInit.PMClockAHB = PMCLOCKAHB_DEFAULT;   
  PeriphClkInit.PMClockAPB_M = PMCLOCKAPB_M_DEFAULT | PM_CLOCK_WU_M | PM_CLOCK_PAD_CONFIG_M;   
  PeriphClkInit.PMClockAPB_P = PMCLOCKAPB_P_DEFAULT | PM_CLOCK_UART_0_M | PM_CLOCK_TIMER16_1_M;   
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_NO_CLK;
  PeriphClkInit.RTCClockCPUSelection = RCC_RTCCLKCPUSOURCE_NO_CLK;
  HAL_RCC_ClockConfig(&PeriphClkInit);
}
</syntaxhighlight>
}}
Для демонстрации вывода текста в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_UART_0_M. У вас его может не быть так как UART нужно включить отдельно. Для этого нужно подключить библиотеки uart_lib и xprintf:
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
#include "mik32_hal_rcc.h"
#include "mik32_hal_timer16.h"
#include <gpio.h>
#include "uart_lib.h"
#include "xprintf.h"
</syntaxhighlight>
}}
Для инициализации UART в функции main после функции тактирования SystemClock_Config следует написать:
<syntaxhighlight lang="c" line="1" start="1">
UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0);
</syntaxhighlight>
Скорость UART задается делителем во втором аргументе функции. При такой записи скорость будет 9600 бод.
В начале main.c можно видеть объявление структуры с набором настроек для выбранного таймера, которую использует функция инициализации Timer16_1_Init.   
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
Timer16_HandleTypeDef htimer16_1;
void SystemClock_Config(void);
static void Timer16_1_Init(void);
</syntaxhighlight>
}}
Перед инициализацией таймера (функция Timer16_1_Init) следует настроить вывод Input1 и Input2 в режим третьей функции вывода (последовательный интерфейс или таймер). Для Timer16_1 выводы Input1 - Port0.8 и Input2 - Port0.9. Перевод вывода в третью функцию осуществляется с помощью регистра PORT_0_CFG (PAD_0_CFG). Убедитесь что в функции SystemClock_Config присутствует PMCLOCKAPB_M_DEFAULT или PM_CLOCK_PAD_CONFIG_M, которые отвечают за тактирование контроллера выводов. В дальнейшем вместо записи в регистры будет использоваться библиотека.
Для запуска счета таймера следует воспользоваться функцией HAL_Timer16_StartSingleMode или HAL_Timer16_StartContinuousMode.
Функция main будет выглядеть так:
{{#spoiler:show=Развернуть код|hide=Свернуть код|
<syntaxhighlight lang="c" line="1">
int main()
{   
    SystemClock_Config();
    /**************************Включить вывод Input1 для Timer16_1**************************/
    /* Port0.8 */
    PAD_CONFIG->PORT_0_CFG |= (PORT_AS_TIMER << 2 * TIMER16_1_IN1);
    /***************************************************************************************/
    /**************************Включить вывод Input2 для Timer16_1**************************/
    /* Port0.9 */
    PAD_CONFIG->PORT_0_CFG |= (PORT_AS_TIMER << 2 * TIMER16_1_IN2);
    /***************************************************************************************/
    Timer16_1_Init();
    /*****************Запуск таймера в одиночном или продолжительном режиме*****************/
    HAL_Timer16_StartSingleMode(&htimer16_1);
    //HAL_Timer16_StartContinuousMode(&htimer16_1);
    /***************************************************************************************/
    while (1)
    {   
        /* Вывод значения счетчика */
        xprintf("Counter = %d\n", HAL_Timer16_GetCounterValue(&htimer16_1));
    }
     
}
</syntaxhighlight>
}}

Текущая версия от 10:04, 24 марта 2023

В примере будет продемонстрирован Timer16 в режиме энкодера.

Работа с конфигуратором (В разработке)

Для начала настроем в конфигураторе тактирование mik32, например, от внешнего кварца 32МГц. Затем настроем делители шины. Так как Timer16 тактируется от шины APB_P_CLK, то зададим делители AHB_DIV и APB_P_DIV. В данном примере оставим делители по умолчанию. В итоге вкладка с тактированием должна выглядеть так:

(Картинка тактирования из конфигуратора. В работе)

Затем перейдем к настройке самого таймера. Для этого откроем вкладку Timer16_1 и включим таймер, выбрав один из режимов. Выберем режим энкодера.

После этого нужно выбрать источник тактирования, который будет использоваться для подсчета. В режиме энкодера не должно быть тактирование от внешнего источника Input1. Выберем, например, тактирование от системной частоты. Для тактирования от внутреннего источника оставим источник синхронизации по умолчанию.

Далее следует выбрать делитель частоты. В режиме энкодера делитель частоты всегда должен быть 1.

Затем следует выбрать подрежим энкодера. Выберем подрежим энкодера 1 - Восходящий фронт.

Подрежимы энкодера
Активный фронт Уровень

противоположного сигнала (lnput1 для lnput2,

lnput2 для lnput1)

Сигнал lnput1 Сигнал lnput2
Подъем Падение Подъем Падение
Восходящий фронт Высокий Вниз Не считать Вверх Не считать
Низкий Вверх Не считать Вниз Не считать
Падающий фронт Высокий Не считать Вверх Не считать Вниз
Низкий Не считать Вниз Не считать Вверх
Оба фронта Высокий Вниз Вверх Вверх Вниз
Низкий Вверх Вниз Вниз Вверх

Например, если Input1 установился в 1, а в это время противоположный сигнал Input2 в низком уровне, то таймер считает вверх.

Если Input1 установился в 1, а Input2 в это время в высоком уровне, то таймер считает вниз.

Если Input1 установился в 0, а Input2 в это время в высоком или низком уровне, то таймер не считает.


Следующая настройка - период. Это верхний предел, до которого считает таймер. В нашем примере таймер будет считать до максимального значения 65535.

Режим обновления регистров задает когда будут обновляться регистры ARR - значение автоматической перезагрузки (верхний предел счета таймера) и CMP - значение сравнения. Выберем, например, обновление после каждого доступа к записи.

Далее идут настройки триггера, которые в этом примере не нужны. Оставляем их значения по умолчанию.

После этого останется настроить цифровой фильтр от помех, если он нужен. При включении фильтра должен использоваться внутренний источник синхронизации.

В итоге настройки таймера в конфигураторе должны выглядеть примерно так:

(Объяснение работы с конфигуратором. В разработке)

Нажимаем кнопку генерации. В итоге у нас появится проект для PlatformIo. Далее работа идет в visual studio code.

Использование библиотеки HAL_Timer16

В сгенерированном проекте в файле main.c должна быть функция Timer16_1_Init, в которой будут заданы настройки для Timer16_1. Выглядит она так:

Кроме этого в функции SystemClock_Config приведены настройки для тактирования. Убедитесь что в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_TIMER16_1_M. Сама функция должна выглядеть примерно так:

Для демонстрации вывода текста в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_UART_0_M. У вас его может не быть так как UART нужно включить отдельно. Для этого нужно подключить библиотеки uart_lib и xprintf:

Для инициализации UART в функции main после функции тактирования SystemClock_Config следует написать:

UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0);

Скорость UART задается делителем во втором аргументе функции. При такой записи скорость будет 9600 бод.

В начале main.c можно видеть объявление структуры с набором настроек для выбранного таймера, которую использует функция инициализации Timer16_1_Init.

Перед инициализацией таймера (функция Timer16_1_Init) следует настроить вывод Input1 и Input2 в режим третьей функции вывода (последовательный интерфейс или таймер). Для Timer16_1 выводы Input1 - Port0.8 и Input2 - Port0.9. Перевод вывода в третью функцию осуществляется с помощью регистра PORT_0_CFG (PAD_0_CFG). Убедитесь что в функции SystemClock_Config присутствует PMCLOCKAPB_M_DEFAULT или PM_CLOCK_PAD_CONFIG_M, которые отвечают за тактирование контроллера выводов. В дальнейшем вместо записи в регистры будет использоваться библиотека.

Для запуска счета таймера следует воспользоваться функцией HAL_Timer16_StartSingleMode или HAL_Timer16_StartContinuousMode.

Функция main будет выглядеть так: