Ардуино миллис () в стм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;
}

Некоторые предостережения, о которых нужно знать.

  1. SysTick - это 24-битный счетчик. Это обернет при переполнении. Помните об этом, когда вы сравниваете значения или внедряете метод задержки.

  2. 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 не совсем точны.

Имея это в виду, вы должны сделать следующее:

  1. Настройте TIM так, чтобы он мог выполнять прерывание каждые 1 мс
  2. Включить прерывание по TIM,
  3. В прерывании TIM увеличьте счетчик (это будет ваше время), который определен как глобальная переменная для этого модуля.

void interrupt TIMX_interrupt(void){ counter++; // this will have the time count in MS. }

  1. Создать функцию с именем millis() что вернуть счетчик что-то вроде:

uint32_t millis(void){ return counter; }

Таким образом, вы можете использовать ту же функцию в этом MCU.

Другие вопросы по тегам