Ардуино миллис () в stm32

Я пытаюсь портировать некоторую библиотеку Arduino на stm32. В Arduino millis() возвращает количество миллисекунд с момента загрузки. Есть ли аналогичная функция в stm32? Я использую микроконтроллер stm32f0.


person Community    schedule 22.05.2016    source источник
comment
Почему бы вам не взглянуть на реализацию millis() (github.com/arduino/Arduino/blob/master/hardware/arduino/avr/) и попробуйте сделать что-то подобное.   -  person Ouss4    schedule 22.05.2016
comment
Попробуйте прочитать руководство по драйверу HAL. Это поможет вам сэкономить ваше время.   -  person imbearr    schedule 23.05.2016
comment
установите таймер при загрузке и прочитайте его.   -  person old_timer    schedule 23.05.2016
comment
@dwelch, OP, используйте драйвер HAL и такой таймер с привязкой по времени к 1 мс, который всегда реализован в нем.   -  person imbearr    schedule 23.05.2016
comment
значительно проще просто настроить таймер и не использовать хал, а каждому свое ... у ОП есть этот выбор.   -  person old_timer    schedule 23.05.2016


Ответы (7)


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 также необходимо настроить вручную.

person Andy Brown    schedule 04.06.2016
comment
Аппаратный счетчик, который генерирует ваши тики в 1 мс, может быть 24-битным, но это вряд ли имеет значение. Переменная, которую ваш код использует для их подсчета, представляет собой 32-битное целое число без знака, поэтому оно будет переполняться на основе этого, а не чего-то меньшего. - person Chris Stratton; 04.06.2016
comment
если вы используете реализацию SysTick_Handler по умолчанию, сгенерированную CubeMX, вы можете использовать HAL_GetTick() . - person Guillaume Legrain; 10.09.2018

Вы можете использовать HAL_GetTick(): this function gets current SysTick counter value (incremented in SysTick interrupt) used by peripherals drivers to handle timeouts.

person nbout    schedule 23.05.2016

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

person David.O    schedule 08.11.2017

Я бы посоветовал установить собственный таймер.

Чтобы сделать таймер, вы должны настроить предварительный скаляр и период в CubeMX или вручную.
Конфигурация часов

Поскольку мои часы 72 МГц, это означает, что мне нужен предварительный скаляр 72 МГц / 1 МГц - 1 = 71. Следовательно, мой период будет 1 МГц / 1 кГц - 1 = 999, потому что я хочу, чтобы прерывание срабатывало один раз в 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 */
}
person studentbrad    schedule 24.06.2019

Сначала вам нужно инициализировать SysTick, чтобы установить отметку в 1 мс, инициализировав его с помощью SystemCoreClock (Hz)/1000.

person Danijel    schedule 01.05.2021

В некоторых ситуациях вы не можете использовать SysTick для этой цели. Например, при использовании FreeRTOS, которая уже реализует SysTick. В этом случае запустите свой код в задаче, и вы можете использовать vTaskDelay. В зависимости от того, что вы хотите сделать, программный таймер также может помочь: http://www.freertos.org/FreeRTOS-Software-Timer-API-Functions.html

Если вы не можете использовать SysTick и у вас нет FreeRTOS или аналогичной системы, вы можете настроить таймер с прерыванием и поместить свой код в соответствующий обратный вызов.

person Tom    schedule 18.11.2016

Ну, я думаю, мне нужно больше информации для правильного ответа, но я сделаю все возможное.

Соображение, которое вам необходимо учитывать для вашего приложения, если вы выполняете измерения с высоким разрешением (нужно точное время), вам нужно будет предоставить внешний кристалл для точности, поскольку внутренние часы 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.

person adrianzx    schedule 26.06.2017