Запуск Timer16 с тактированием от внешнего источника: различия между версиями
Андрей (обсуждение | вклад) Нет описания правки |
м (Исправил орфографическую ошибку) |
||
(не показано 7 промежуточных версий 2 участников) | |||
Строка 2: | Строка 2: | ||
==Работа с конфигуратором (В разработке)== | ==Работа с конфигуратором (В разработке)== | ||
Для начала | Для начала настроим в конфигураторе тактирование mik32, например, от внешнего кварца 32МГц. Затем настроем делители шины. Так как Timer16 тактируется от шины APB_P_CLK, то зададим делители AHB_DIV и APB_P_DIV. В данном примере оставим делители по умолчанию. В итоге вкладка с тактированием должна выглядеть так: | ||
(Картинка тактирования из конфигуратора. В работе) | (Картинка тактирования из конфигуратора. В работе) | ||
Строка 8: | Строка 8: | ||
Затем перейдем к настройке самого таймера. Для этого откроем вкладку Timer16_1 и включим таймер, выбрав один из режимов. Выберем режим счетчика. | Затем перейдем к настройке самого таймера. Для этого откроем вкладку Timer16_1 и включим таймер, выбрав один из режимов. Выберем режим счетчика. | ||
После этого нужно выбрать источник тактирования, который будет использоваться для подсчета. Выберем, например, тактирование от внешнего вывода Input1. При таком выборе источник синхронизации не имеет значения. Оставим значение по умолчанию. В такой конфигурации Timer16 не нуждается во внутреннем источнике синхронизации тактового сигнала (за исключением случаев, когда включены фильтры glitch). Сигнал, подаваемый на lnput1 Timer16, используется в качестве системного тактового генератора для Timer16. Эта конфигурация подходит для режимов работы, в которых не включен встроенный генератор. При такой конфигурации счетчик Timer16 может обновляться либо по нарастающему, либо по спадающему фронту тактового сигнала lnput1, но не по двум (нарастающему и спадающему фронту) одновременно. Поскольку сигнал, подаваемый на внешний lnput1 Timer16, также используется для тактирования логики ядра | После этого нужно выбрать источник тактирования, который будет использоваться для подсчета. Выберем, например, тактирование от внешнего вывода Input1. При таком выборе источник синхронизации не имеет значения. Оставим значение по умолчанию. В такой конфигурации Timer16 не нуждается во внутреннем источнике синхронизации тактового сигнала (за исключением случаев, когда включены фильтры glitch). Сигнал, подаваемый на lnput1 Timer16, используется в качестве системного тактового генератора для Timer16. Эта конфигурация подходит для режимов работы, в которых не включен встроенный генератор. При такой конфигурации счетчик Timer16 может обновляться либо по нарастающему, либо по спадающему фронту тактового сигнала lnput1, но не по двум (нарастающему и спадающему фронту) одновременно. Поскольку сигнал, подаваемый на внешний lnput1 Timer16, также используется для тактирования логики ядра Timer16, существует некоторая начальная задержка (после включения Timer16) перед началом счета. Точнее, первые пять активных фронтов на внешнем lnput1 Timer16(после включения Timer16) теряются. | ||
Далее следует выбрать нужный вам делитель частоты. | Далее следует выбрать нужный вам делитель частоты. | ||
Строка 29: | Строка 29: | ||
==Использование библиотеки HAL_Timer16== | ==Использование библиотеки HAL_Timer16== | ||
В сгенерированном проекте в файле main.c должна быть функция Timer16_1_Init, в которой будут заданы настройки для Timer16_1. Выглядит она так:<syntaxhighlight lang="c" line="1"> | В сгенерированном проекте в файле main.c должна быть функция Timer16_1_Init, в которой будут заданы настройки для Timer16_1. Выглядит она так: | ||
{{#spoiler:show=Развернуть код|hide=Свернуть код| | |||
<syntaxhighlight lang="c" line="1"> | |||
static void Timer16_1_Init(void) | static void Timer16_1_Init(void) | ||
{ | { | ||
Строка 60: | Строка 62: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Кроме этого в функции SystemClock_Config приведены настройки для тактирования. Убедитесь что в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_TIMER16_1_M. Сама функция должна выглядеть примерно так:<syntaxhighlight lang="c" line="1"> | }} | ||
Кроме этого в функции SystemClock_Config приведены настройки для тактирования. Убедитесь что в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_TIMER16_1_M. Сама функция должна выглядеть примерно так: | |||
{{#spoiler:show=Развернуть код|hide=Свернуть код| | |||
<syntaxhighlight lang="c" line="1"> | |||
void SystemClock_Config(void) | void SystemClock_Config(void) | ||
{ | { | ||
Строка 82: | Строка 87: | ||
HAL_RCC_ClockConfig(&PeriphClkInit); | HAL_RCC_ClockConfig(&PeriphClkInit); | ||
} | } | ||
</syntaxhighlight>Для демонстрации вывода текста в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_UART_0_M. У вас его может не быть так как UART нужно включить отдельно. | </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" | |||
В начале main.c можно видеть объявление структуры с набором настроек для выбранного таймера, | #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; | Timer16_HandleTypeDef htimer16_1; | ||
void SystemClock_Config(void); | void SystemClock_Config(void); | ||
static void Timer16_1_Init(void); | static void Timer16_1_Init(void); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
}} | |||
Перед инициализацией таймера (функция Timer16_1_Init) следует настроить вывод Input1 в режим третьей функции вывода (последовательный интерфейс или таймер). Для Timer16_1 вывод Input1 - Port0.8. Перевод вывода в третью функцию осуществляется с помощью регистра PORT_0_CFG (PAD_0_CFG). Убедитесь что в функции SystemClock_Config присутствует PMCLOCKAPB_M_DEFAULT или PM_CLOCK_PAD_CONFIG_M, которые отвечают за тактирование контроллера выводов. В дальнейшем вместо записи в регистры будет использоваться библиотека. Для запуска таймера в непрерывном режиме нужно вызвать функцию HAL_Timer16_StartContinuousMode и передать в нее адрес на структуру htimer16_1. Функция main | Перед инициализацией таймера (функция Timer16_1_Init) следует настроить вывод Input1 в режим третьей функции вывода (последовательный интерфейс или таймер). Для Timer16_1 вывод Input1 - Port0.8. Перевод вывода в третью функцию осуществляется с помощью регистра PORT_0_CFG (PAD_0_CFG). Убедитесь что в функции SystemClock_Config присутствует PMCLOCKAPB_M_DEFAULT или PM_CLOCK_PAD_CONFIG_M, которые отвечают за тактирование контроллера выводов. В дальнейшем вместо записи в регистры будет использоваться библиотека. Для запуска таймера в непрерывном режиме нужно вызвать функцию HAL_Timer16_StartContinuousMode и передать в нее адрес на структуру htimer16_1. Функция main должна выглядеть примерно так: | ||
{{#spoiler:show=Развернуть код|hide=Свернуть код| | |||
<syntaxhighlight lang="c" line="1"> | <syntaxhighlight lang="c" line="1"> | ||
int main() | int main() | ||
Строка 98: | Строка 124: | ||
SystemClock_Config(); | SystemClock_Config(); | ||
UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0); | |||
/**************************Включить вывод Input1 для Timer16_1**************************/ | /**************************Включить вывод Input1 для Timer16_1**************************/ | ||
Строка 118: | Строка 146: | ||
} | } | ||
</syntaxhighlight>Перед включением контроллера на вывод Port0.8 нужно подать тактирующий сигнал. В нашем случае это сигнал с частотой 1МГц. | </syntaxhighlight> | ||
}} | |||
[[Файл:Timer16. Input1. UART.png|мини|Рисунок 3 - вывод в UART]] | |||
Перед включением контроллера на вывод Port0.8 нужно подать тактирующий сигнал. В нашем случае это сигнал с частотой 1МГц. | |||
Если у вас включен UART, то с помощью функции xprintf и HAL_Timer16_GetCounterValue можно посмотреть текущее значение счетчика. Пример вывода изображен на рисунке 3. | |||
Теперь попробуем задать тактирование от внутреннего источника, а синхронизацию от внешнего вывода Input1. | |||
Вход lnput1 Timer16 дискретизируется внутренним тактовым генератором, подаваемым на Timer16. Следовательно, чтобы не пропустить ни одного события, частота изменений внешнего сигнала lnput1 должна быть не выше четверти частоты внутреннего тактового генератора, подаваемого на Timer16. Кроме того, внутренний тактовый генератор, подаваемый на Timer16, не должен быть предварительно масштабирован (делитель = TIMER16_PRESCALER_1). | |||
Это можно сделать тремя способами: | |||
1) В конфигураторе выбрать источник тактирования, например, системный, а источник синхронизации внешний вывод Input1. В делителе таймера выбрать 1. После чего заново сгенерировать проект; | |||
2) Вручную исправить настройки в функции Timer16_1_Init; | |||
3) После инициализации таймера воспользоваться функциями HAL_Timer16_SetSourceClock, HAL_Timer16_SetCountMode, HAL_Timer16_SetPrescaler, HAL_Timer16_Enable. | |||
Воспользуемся вторым вариантом. Функция Timer16_1_Init должна выглядеть так: | |||
[[Файл:Timer16. Input1. UART.png|мини| | {{#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_EXTERNAL; | |||
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; | |||
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_DISABLE; | |||
HAL_Timer16_Init(&htimer16_1); | |||
} | |||
</syntaxhighlight> | |||
}} | |||
[[Файл:Timer16. Input1. UART вывод.png|мини|Рисунок 4 - вывод в UART]] | |||
Перед включением контроллера на вывод Port0.8 нужно подать тактирующий сигнал. В нашем случае это сигнал с частотой 1МГц. Вывод программы изображен на рисунке 4. |
Текущая версия от 13:43, 10 июня 2024
В примере будет запускаться Timer16_1 в однократном или непрерывном режиме с использованием конфигуратора. При этом тактироваться таймер будет от внешнего источника через вывод Input1.
Работа с конфигуратором (В разработке)
Для начала настроим в конфигураторе тактирование mik32, например, от внешнего кварца 32МГц. Затем настроем делители шины. Так как Timer16 тактируется от шины APB_P_CLK, то зададим делители AHB_DIV и APB_P_DIV. В данном примере оставим делители по умолчанию. В итоге вкладка с тактированием должна выглядеть так:
(Картинка тактирования из конфигуратора. В работе)
Затем перейдем к настройке самого таймера. Для этого откроем вкладку Timer16_1 и включим таймер, выбрав один из режимов. Выберем режим счетчика.
После этого нужно выбрать источник тактирования, который будет использоваться для подсчета. Выберем, например, тактирование от внешнего вывода Input1. При таком выборе источник синхронизации не имеет значения. Оставим значение по умолчанию. В такой конфигурации Timer16 не нуждается во внутреннем источнике синхронизации тактового сигнала (за исключением случаев, когда включены фильтры glitch). Сигнал, подаваемый на lnput1 Timer16, используется в качестве системного тактового генератора для Timer16. Эта конфигурация подходит для режимов работы, в которых не включен встроенный генератор. При такой конфигурации счетчик Timer16 может обновляться либо по нарастающему, либо по спадающему фронту тактового сигнала lnput1, но не по двум (нарастающему и спадающему фронту) одновременно. Поскольку сигнал, подаваемый на внешний lnput1 Timer16, также используется для тактирования логики ядра Timer16, существует некоторая начальная задержка (после включения Timer16) перед началом счета. Точнее, первые пять активных фронтов на внешнем lnput1 Timer16(после включения Timer16) теряются.
Далее следует выбрать нужный вам делитель частоты.
Активный фронт задает фронт, по которому будет происходить увеличение счетчика. Эта настройка используется при внешнем источнике синхронизации. При тактировании от Input1 можно выбрать либо нарастающий фронт, либо спадающий. В данном примере будет выбран нарастающий фронт.
Следующая настройка - период. Это верхний предел, до которого считает таймер. В нашем примере таймер будет считать до максимального значения 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 в режим третьей функции вывода (последовательный интерфейс или таймер). Для Timer16_1 вывод Input1 - Port0.8. Перевод вывода в третью функцию осуществляется с помощью регистра PORT_0_CFG (PAD_0_CFG). Убедитесь что в функции SystemClock_Config присутствует PMCLOCKAPB_M_DEFAULT или PM_CLOCK_PAD_CONFIG_M, которые отвечают за тактирование контроллера выводов. В дальнейшем вместо записи в регистры будет использоваться библиотека. Для запуска таймера в непрерывном режиме нужно вызвать функцию HAL_Timer16_StartContinuousMode и передать в нее адрес на структуру htimer16_1. Функция main должна выглядеть примерно так:
Перед включением контроллера на вывод Port0.8 нужно подать тактирующий сигнал. В нашем случае это сигнал с частотой 1МГц.
Если у вас включен UART, то с помощью функции xprintf и HAL_Timer16_GetCounterValue можно посмотреть текущее значение счетчика. Пример вывода изображен на рисунке 3.
Теперь попробуем задать тактирование от внутреннего источника, а синхронизацию от внешнего вывода Input1.
Вход lnput1 Timer16 дискретизируется внутренним тактовым генератором, подаваемым на Timer16. Следовательно, чтобы не пропустить ни одного события, частота изменений внешнего сигнала lnput1 должна быть не выше четверти частоты внутреннего тактового генератора, подаваемого на Timer16. Кроме того, внутренний тактовый генератор, подаваемый на Timer16, не должен быть предварительно масштабирован (делитель = TIMER16_PRESCALER_1).
Это можно сделать тремя способами:
1) В конфигураторе выбрать источник тактирования, например, системный, а источник синхронизации внешний вывод Input1. В делителе таймера выбрать 1. После чего заново сгенерировать проект;
2) Вручную исправить настройки в функции Timer16_1_Init;
3) После инициализации таймера воспользоваться функциями HAL_Timer16_SetSourceClock, HAL_Timer16_SetCountMode, HAL_Timer16_SetPrescaler, HAL_Timer16_Enable.
Воспользуемся вторым вариантом. Функция Timer16_1_Init должна выглядеть так:
Перед включением контроллера на вывод Port0.8 нужно подать тактирующий сигнал. В нашем случае это сигнал с частотой 1МГц. Вывод программы изображен на рисунке 4.