Ардуино миллис () в стм32
Я пытаюсь портировать библиотеку Arduino на stm32. В Ардуино millis()
возвращает количество миллисекунд с момента загрузки. Есть ли эквивалентная функция в stm32? Я использую stm32f0 MCU.
8 ответов
SysTick
периферийное устройство ARM, предназначенное для этой цели. Приспособьте это к своим потребностям:
Во-первых, инициализируйте его
// Initialise SysTick to tick at 1ms by initialising it with SystemCoreClock (Hz)/1000
volatile uint32_t counter = 0;
SysTick_Config(SystemCoreClock / 1000);
Предоставить обработчик прерываний. Вашему компилятору могут потребоваться обработчики прерываний для добавления дополнительных атрибутов.
SysTick_Handler(void) {
counter++;
}
Вот твой millis()
функция, не может быть проще:
uint32_t millis() {
return counter;
}
Некоторые предостережения, о которых нужно знать.
SysTick - это 24-битный счетчик. Это обернет при переполнении. Помните об этом, когда вы сравниваете значения или внедряете метод задержки.
SysTick является производным от частоты ядра процессора. Если вы запутались в тактовых частотах ядра, например, замедлили его для экономии энергии, то частоту SysTick также необходимо настроить вручную.
Вы могли бы использовать HAL_GetTick(): this function gets current SysTick counter value (incremented in
SysTick interrupt) used by peripherals drivers to handle timeouts.
Я бы предложил сделать это с таймером. Таким образом, вы также можете получить шаг 1us, просто контролируя размер шага по времени. В любом случае, большинство микроконтроллеров STM32 имеет 8 или более таймеров, поэтому в большинстве случаев вы можете выбрать один из них. Я покажу очень простую основную идею, как это сделать.
Просто создайте таймер:
uint32_t time = 0;
void enable_timer(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM16, ENABLE);
TIM_TimeBaseInitTypeDef timerInitStructure;
/* if MCU frequency 48 MHz, prescaler of value 48 will make 1us counter steps
timerInitStructure.TIM_Prescaler = 48;
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
/*how often you'll get irq*/
timerInitStructure.TIM_Period = 0xFFFF; // will get irq each 65535us on TIMx->CNT overflow
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM16, &timerInitStructure);
TIM_Cmd(TIM16, ENABLE);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM16_IRQn;
/*for more accuracy irq priority should be higher, but now its default*/
NVIC_InitStruct.NVIC_IRQChannelPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
TIM_ClearITPendingBit(TIM16,TIM_IT_Update);
NVIC_Init(&NVIC_InitStruct);
TIM_ITConfig(TIM16,TIM_IT_Update,ENABLE);
}
на IRQ вы должны контролировать переполнение счетчика таймера и обновлять глобальное время
void TIM16_IRQHandler(void){
if (TIM_GetITStatus(TIM16, TIM_IT_Update) != RESET){
TIM_ClearITPendingBit(TIM16, TIM_IT_Update);
time +=65535;
}
}
И в реальном времени будет:
uint32_t real_time_us = time + (uint32_t)TIM16->CNT;
Но если вы можете использовать 32-битный таймер, вы можете даже сделать это без IRQ, просто просто time= TIMx->CNT. Да, это зависит от конфигурации таймера и ваших потребностей, также вы должны знать, что переменная времени uint32_t также может переполниться, но это детали, ею можно легко управлять.
Systick не всегда в миллисекундах. Хотя промежуточные программы, такие как freeRTOS, могут предполагать, что это в миллисекундах. Самое лучшее и элегантное решение - сделать свой таймер!
Чтобы сделать таймер, вы должны настроить прескаляр и точку в CubeMX или вручную. Конфигурация часов
Поскольку мои тактовые частоты равны 100 МГц, это означает, что мне нужен прескаляр 100 МГц / 1000 Гц - 1 = 99 Мой период может оставаться равным 0, потому что я хочу, чтобы прерывание срабатывало раз в 1 миллисекунду. Прескалярный расчет
Вот как это выглядит в CubeMX: Конфигурация таймера
Затем вы хотите добавить приращение счетчика в функцию обратного вызова таймера.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
if (htim->Instance == TIM10) {
counter++;
}
/* USER CODE END Callback 0 */
if (htim->Instance == TIM1) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
Это так просто:)
К вашему сведению, моя версия CubeMX - 5.2.1
Сначала вам нужно инициализировать SysTick, чтобы установить отметку в 1 мс, инициализировав его с помощью SystemCoreClock (Hz)/1000.
Библиотека HAL использует таймер (вы можете выбрать его в STM32CubeMX) для создания счетчика Tick. каждый тик занимает 1 мс. вы можете использовать его для измерения прошедшего времени следующим образом:
uint32_t startTime = HAL_GetTick();
.
.
.
uint32_t endTime = HAL_GetTick();
uint32_t elapsedTime = endTime-startTime; // in ms
В некоторых ситуациях вы не можете использовать SysTick для этой цели. Например, при использовании FreeRTOS, который уже реализует SysTick. В этом случае запустите свой код в задаче, и вы можете использовать vTaskDelay. В зависимости от того, что вы хотели бы сделать, программный таймер также может помочь: http://www.freertos.org/FreeRTOS-Software-Timer-API-Functions.html
Если вы не можете использовать SysTick и у вас нет FreeRTOS или чего-то подобного, вы можете настроить таймер с прерыванием и поместить свой код в соответствующий обратный вызов.
Ну, я думаю, что мне нужен больше информации для правильного ответа, но я сделаю тебе мой лучший выстрел.
Рассмотрение, которое вам необходимо учитывать для вашего приложения, если вы делаете измерения с высоким разрешением (вам нужно точное время), вам нужно будет предоставить внешний кристалл для точности, так как внутренние часы MCU не совсем точны.
Имея это в виду, вы должны сделать следующее:
- Настройте TIM так, чтобы он мог выполнять прерывание каждые 1 мс
- Включить прерывание по TIM,
- В прерывании TIM увеличьте счетчик (это будет ваше время), который определен как глобальная переменная для этого модуля.
void interrupt TIMX_interrupt(void){
counter++; // this will have the time count in MS.
}
- Создать функцию с именем
millis()
что вернуть счетчик что-то вроде:
uint32_t millis(void){
return counter;
}
Таким образом, вы можете использовать ту же функцию в этом MCU.