Я пытаюсь портировать некоторую библиотеку Arduino на stm32. В Arduino millis()
возвращает количество миллисекунд с момента загрузки. Есть ли аналогичная функция в stm32? Я использую микроконтроллер stm32f0.
Ардуино миллис () в stm32
Ответы (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;
}
Некоторые предостережения, о которых следует знать.
SysTick — это 24-битный счетчик. Он будет обертываться при переполнении. Помните об этом, когда вы сравниваете значения или реализуете метод задержки.
SysTick основан на тактовой частоте ядра процессора. Если вы возитесь с тактовой частотой ядра, например, замедляете ее для экономии энергии, тогда частоту SysTick также необходимо настроить вручную.
Вы можете использовать HAL_GetTick(): this function gets current SysTick counter value (incremented in SysTick interrupt) used by peripherals drivers to handle timeouts.
Я бы предложил сделать это с помощью таймера. Таким образом, вы также можете получить шаг в 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 также может переполняться, но это детали, которыми можно легко управлять.
Я бы посоветовал установить собственный таймер.
Чтобы сделать таймер, вы должны настроить предварительный скаляр и период в 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 */
}
Сначала вам нужно инициализировать SysTick, чтобы установить отметку в 1 мс, инициализировав его с помощью SystemCoreClock (Hz)/1000.
В некоторых ситуациях вы не можете использовать 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.
millis()
(github.com/arduino/Arduino/blob/master/hardware/arduino/avr/) и попробуйте сделать что-то подобное. - person Ouss4   schedule 22.05.2016