Repository: incubator-mynewt-core Updated Branches: refs/heads/develop c8713fc81 -> f4f67ef83
stm32f4 hal_timer. Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/6f20b61e Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/6f20b61e Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/6f20b61e Branch: refs/heads/develop Commit: 6f20b61e554f588a2a95648e289e519277e826e2 Parents: c8713fc Author: Marko Kiiskila <ma...@runtime.io> Authored: Tue Oct 11 20:08:24 2016 -0700 Committer: Marko Kiiskila <ma...@runtime.io> Committed: Tue Oct 11 20:08:24 2016 -0700 ---------------------------------------------------------------------- hw/mcu/stm/stm32f4xx/src/hal_timer.c | 589 ++++++++++++++++++++++++++++++ 1 file changed, 589 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6f20b61e/hw/mcu/stm/stm32f4xx/src/hal_timer.c ---------------------------------------------------------------------- diff --git a/hw/mcu/stm/stm32f4xx/src/hal_timer.c b/hw/mcu/stm/stm32f4xx/src/hal_timer.c new file mode 100644 index 0000000..7d572bf --- /dev/null +++ b/hw/mcu/stm/stm32f4xx/src/hal_timer.c @@ -0,0 +1,589 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <string.h> +#include <inttypes.h> +#include <assert.h> + +#include <syscfg/syscfg.h> + +#include <bsp/cmsis_nvic.h> +#include <os/queue.h> +#include <hal/hal_timer.h> + +#include "stm32f4xx.h" +#include "stm32f4xx_hal_dma.h" +#include "stm32f4xx_hal_tim.h" +#include "stm32f4xx_hal_rcc.h" + +#include "mcu/stm32f4xx_mynewt_hal.h" + +#define STM32F4_HAL_TIMER_MAX (2) + +struct stm32f4_hal_tmr { + TIM_TypeDef *sht_regs; /* Pointer to timer registers */ + uint32_t sht_oflow; /* 16 bits of overflow to make timer 32bits */ + TAILQ_HEAD(hal_timer_qhead, hal_timer) sht_timers; +}; + +#if MYNEWT_VAL(TIMER_0) +struct stm32f4_hal_tmr stm32f4_tmr0 = { + .sht_regs = MYNEWT_VAL(TIMER_0_UNIT) +}; +#endif +#if MYNEWT_VAL(TIMER_1) +struct stm32f4_hal_tmr stm32f4_tmr1 = { + .sht_regs = MYNEWT_VAL(TIMER_1_UNIT) +}; +#endif + +static struct stm32f4_hal_tmr *stm32f4_tmr_devs[STM32F4_HAL_TIMER_MAX] = { +#if MYNEWT_VAL(TIMER_0) + &stm32f4_tmr0, +#else + NULL, +#endif +#if MYNEWT_VAL(TIMER_1) + &stm32f4_tmr1, +#else + NULL, +#endif +}; + +static uint32_t hal_timer_cnt(struct stm32f4_hal_tmr *tmr); + +#if (MYNEWT_VAL(TIMER_0) || MYNEWT_VAL(TIMER_1)) +/* + * Call expired timer callbacks, and reprogram timer with new expiry time. + */ +static void +stm32f4_tmr_cbs(struct stm32f4_hal_tmr *tmr) +{ + uint32_t cnt; + struct hal_timer *ht; + + while ((ht = TAILQ_FIRST(&tmr->sht_timers)) != NULL) { + cnt = hal_timer_cnt(tmr); + if (((int32_t)(cnt - ht->expiry)) >= 0) { + TAILQ_REMOVE(&tmr->sht_timers, ht, link); + ht->link.tqe_prev = NULL; + ht->cb_func(ht->cb_arg); + } else { + break; + } + } + ht = TAILQ_FIRST(&tmr->sht_timers); + if (ht) { + tmr->sht_regs->CCR1 = ht->expiry; + } else { + TIM_CCxChannelCmd(tmr->sht_regs, TIM_CHANNEL_1, TIM_CCx_DISABLE); + tmr->sht_regs->DIER &= ~TIM_DIER_CC1IE; + } +} + +/* + * timer irq handler + * + * Generic HAL timer irq handler. + * + * @param tmr + */ +static void +stm32f4_tmr_irq(struct stm32f4_hal_tmr *tmr) +{ + uint32_t sr; + uint32_t clr = 0; + + sr = tmr->sht_regs->SR; + if (sr & TIM_SR_UIF) { + /* + * Overflow interrupt + */ + tmr->sht_oflow += 0x10000; + clr |= TIM_SR_UIF; + } + if (sr & TIM_SR_CC1IF) { + /* + * Capture event + */ + clr |= TIM_SR_CC1IF; + stm32f4_tmr_cbs(tmr); + } + + tmr->sht_regs->SR = ~clr; +} +#endif + +#if MYNEWT_VAL(TIMER_0) +static void +stm32f4_tmr0_irq(void) +{ + stm32f4_tmr_irq(&stm32f4_tmr0); +} +#endif + +#if MYNEWT_VAL(TIMER_1) +static void +stm32f4_tmr1_irq(void) +{ + stm32f4_tmr_irq(&stm32f4_tmr1); +} +#endif + +static void +stm32f4_tmr_reg_irq(IRQn_Type irqn, uint32_t prio, uint32_t func) +{ + NVIC_SetPriority(irqn, prio); + NVIC_SetVector(irqn, func); + NVIC_EnableIRQ(irqn); +} + +static uint32_t +stm32f4_base_freq(TIM_TypeDef *regs) +{ + RCC_ClkInitTypeDef clocks; + uint32_t fl; + uint32_t freq; + + HAL_RCC_GetClockConfig(&clocks, &fl); + + /* + * Assuming RCC_DCKCFGR->TIMPRE is 0. + * There's just APB2 timers here. + */ + switch ((uint32_t)regs) { +#ifdef TIM1 + case (uint32_t)TIM1: +#endif +#ifdef TIM8 + case (uint32_t)TIM8: +#endif +#ifdef TIM9 + case (uint32_t)TIM9: +#endif +#ifdef TIM10 + case (uint32_t)TIM10: +#endif +#ifdef TIM11 + case (uint32_t)TIM11: +#endif + freq = HAL_RCC_GetPCLK2Freq(); + if (clocks.APB2CLKDivider) { + freq *= 2; + } + break; + default: + return 0; + } + return freq; +} + +static void +stm32f4_hw_setup(int num, TIM_TypeDef *regs) +{ + uint32_t func; + uint32_t prio; + + switch (num) { +#if MYNEWT_VAL(TIMER_0) + case 0: + func = (uint32_t)stm32f4_tmr0_irq; + prio = MYNEWT_VAL(TIMER_0_INTERRUPT_PRIORITY); + break; +#endif +#if MYNEWT_VAL(TIMER_1) + case 1: + func = (uint32_t)stm32f4_tmr1_irq; + prio = MYNEWT_VAL(TIMER_1_INTERRUPT_PRIORITY); + break; +#endif + default: + assert(0); + return; + } + +#ifdef TIM1 + if (regs == TIM1) { + stm32f4_tmr_reg_irq(TIM1_CC_IRQn, prio, func); + stm32f4_tmr_reg_irq(TIM1_UP_TIM10_IRQn, prio, func); + __HAL_RCC_TIM1_CLK_ENABLE(); + } +#endif +#ifdef TIM8 + if (regs == TIM8) { + stm32f4_tmr_reg_irq(TIM8_CC_IRQn, prio, func); + stm32f4_tmr_reg_irq(TIM8_UP_TIM13_IRQn, prio, func); + __HAL_RCC_TIM8_CLK_ENABLE(); + } +#endif +#ifdef TIM9 + if (regs == TIM9) { + stm32f4_tmr_reg_irq(TIM1_BRK_TIM9_IRQn, prio, func); + __HAL_RCC_TIM9_CLK_ENABLE(); + } +#endif +#ifdef TIM10 + if (regs == TIM10) { + stm32f4_tmr_reg_irq(TIM1_UP_TIM10_IRQn, prio, func); + __HAL_RCC_TIM10_CLK_ENABLE(); + } +#endif +#ifdef TIM11 + if (regs == TIM11) { + stm32f4_tmr_reg_irq(TIM1_TRG_COM_TIM11_IRQn, prio, func); + __HAL_RCC_TIM11_CLK_ENABLE(); + } +#endif +} + +static void +stm32f4_hw_setdown(TIM_TypeDef *regs) +{ +#ifdef TIM1 + if (regs == TIM1) { + __HAL_RCC_TIM1_CLK_DISABLE(); + } +#endif +#ifdef TIM8 + if (regs == TIM8) { + __HAL_RCC_TIM8_CLK_DISABLE(); + } +#endif +#ifdef TIM9 + if (regs == TIM9) { + __HAL_RCC_TIM9_CLK_DISABLE(); + } +#endif +#ifdef TIM10 + if (regs == TIM10) { + __HAL_RCC_TIM10_CLK_DISABLE(); + } +#endif +#ifdef TIM11 + if (regs == TIM11) { + __HAL_RCC_TIM11_CLK_DISABLE(); + } +#endif +} + +/** + * hal timer init + * + * Initialize (and start) a timer to run at the desired frequency. + * + * @param timer_num + * @param freq_hz + * + * @return int + */ +int +hal_timer_init(int num, uint32_t freq_hz) +{ + struct stm32f4_hal_tmr *tmr; + TIM_Base_InitTypeDef init; + uint32_t prescaler; + + if (num >= STM32F4_HAL_TIMER_MAX || !(tmr = stm32f4_tmr_devs[num])) { + return -1; + } + if (!IS_TIM_CC1_INSTANCE(tmr->sht_regs)) { + return -1; + } + + prescaler = stm32f4_base_freq(tmr->sht_regs) / freq_hz; + if (prescaler > 0xffff) { + return -1; + } + + memset(&init, 0, sizeof(init)); + init.Period = 0xffff; + init.Prescaler = SystemCoreClock / freq_hz; + init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + init.CounterMode = TIM_COUNTERMODE_UP; + + stm32f4_hw_setup(num, tmr->sht_regs); + + /* + * Stop the timers at debugger. XXX Which TIM? + */ + DBGMCU->APB1FZ |= 0x1ff; /* TIM2 - TIM7, TIM12-TIM14 */ + DBGMCU->APB2FZ |= 0x70003; /* TIM1, TIM8-TIM11 */ + + /* + * Set up to count overflow interrupts. + */ + tmr->sht_regs->CR1 = TIM_CR1_URS; + tmr->sht_regs->DIER = TIM_DIER_UIE; + + TIM_Base_SetConfig(tmr->sht_regs, &init); + + tmr->sht_regs->SR = 0; + tmr->sht_regs->CR1 |= TIM_CR1_CEN; + + return 0; +} + +/** + * hal timer deinit + * + * De-initialize a HW timer. + * + * @param timer_num + * + * @return int + */ +int +hal_timer_deinit(int num) +{ + struct stm32f4_hal_tmr *tmr; + int sr; + + if (num >= STM32F4_HAL_TIMER_MAX || !(tmr = stm32f4_tmr_devs[num])) { + return -1; + } + + __HAL_DISABLE_INTERRUPTS(sr); + /* + * Turn off CC1, and then turn off the timer. + */ + tmr->sht_regs->CR1 &= ~TIM_CR1_CEN; + tmr->sht_regs->DIER &= ~TIM_DIER_CC1IE; + TIM_CCxChannelCmd(tmr->sht_regs, TIM_CHANNEL_1, TIM_CCx_DISABLE); + __HAL_ENABLE_INTERRUPTS(sr); + stm32f4_hw_setdown(tmr->sht_regs); + + return 0; +} + +/** + * hal timer get resolution + * + * Get the resolution of the timer. This is the timer period, in nanoseconds + * + * @param timer_num + * + * @return uint32_t + */ +uint32_t +hal_timer_get_resolution(int num) +{ + struct stm32f4_hal_tmr *tmr; + + if (num >= STM32F4_HAL_TIMER_MAX || !(tmr = stm32f4_tmr_devs[num])) { + return -1; + } + return SystemCoreClock / tmr->sht_regs->PSC; +} + +static uint32_t +hal_timer_cnt(struct stm32f4_hal_tmr *tmr) +{ + uint32_t rc; + int sr; + + __HAL_DISABLE_INTERRUPTS(sr); + rc = tmr->sht_oflow + tmr->sht_regs->CNT; + __HAL_ENABLE_INTERRUPTS(sr); + + return rc; +} + +/** + * hal timer read + * + * Returns the timer counter. NOTE: if the timer is a 16-bit timer, only + * the lower 16 bits are valid. If the timer is a 64-bit timer, only the + * low 32-bits are returned. + * + * @return uint32_t The timer counter register. + */ +uint32_t +hal_timer_read(int num) +{ + struct stm32f4_hal_tmr *tmr; + + if (num >= STM32F4_HAL_TIMER_MAX || !(tmr = stm32f4_tmr_devs[num])) { + return -1; + } + return hal_timer_cnt(tmr); +} + +/** + * hal timer delay + * + * Blocking delay for n ticks + * + * @param timer_num + * @param ticks + * + * @return int 0 on success; error code otherwise. + */ +int +hal_timer_delay(int num, uint32_t ticks) +{ + return 0; +} + +/** + * + * Initialize the HAL timer structure with the callback and the callback + * argument. Also initializes the HW specific timer pointer. + * + * @param cb_func + * + * @return int + */ +int +hal_timer_set_cb(int num, struct hal_timer *timer, hal_timer_cb cb_func, + void *arg) +{ + struct stm32f4_hal_tmr *tmr; + + if (num >= STM32F4_HAL_TIMER_MAX || !(tmr = stm32f4_tmr_devs[num])) { + return -1; + } + timer->cb_func = cb_func; + timer->cb_arg = arg; + timer->bsp_timer = tmr; + timer->link.tqe_prev = NULL; + + return 0; +} + +/** + * hal_timer_start() + * + * Start a timer. Timer fires 'ticks' ticks from now. + * + * @param timer + * @param ticks + * + * @return int + */ +int +hal_timer_start(struct hal_timer *timer, uint32_t ticks) +{ + struct stm32f4_hal_tmr *tmr; + uint32_t tick; + + if (!ticks) { + return -1; + } + tmr = (struct stm32f4_hal_tmr *)timer->bsp_timer; + + tick = ticks + hal_timer_cnt(tmr); + return hal_timer_start_at(timer, tick); +} + +/** + * hal_timer_start_at() + * + * Start a timer. Timer fires at tick 'tick'. + * + * @param timer + * @param tick + * + * @return int + */ +int +hal_timer_start_at(struct hal_timer *timer, uint32_t tick) +{ + struct stm32f4_hal_tmr *tmr; + struct hal_timer *ht; + int sr; + + tmr = (struct stm32f4_hal_tmr *)timer->bsp_timer; + + if ((int32_t)(tick - hal_timer_cnt(tmr)) <= 0) { + /* + * Can't schedule events to past. + */ + return -1; + } + timer->expiry = tick; + + __HAL_DISABLE_INTERRUPTS(sr); + + if (TAILQ_EMPTY(&tmr->sht_timers)) { + TAILQ_INSERT_HEAD(&tmr->sht_timers, timer, link); + } else { + TAILQ_FOREACH(ht, &tmr->sht_timers, link) { + if ((int32_t)(timer->expiry - ht->expiry) < 0) { + TAILQ_INSERT_BEFORE(ht, timer, link); + break; + } + } + if (!ht) { + TAILQ_INSERT_TAIL(&tmr->sht_timers, timer, link); + } + } + + if (timer == TAILQ_FIRST(&tmr->sht_timers)) { + TIM_CCxChannelCmd(tmr->sht_regs, TIM_CHANNEL_1, TIM_CCx_ENABLE); + tmr->sht_regs->CCR1 = timer->expiry; + tmr->sht_regs->DIER |= TIM_DIER_CC1IE; + } + __HAL_ENABLE_INTERRUPTS(sr); + + return 0; +} + +/** + * hal_timer_stop() + * + * Cancels the timer. + * + * @param timer + * + * @return int + */ +int +hal_timer_stop(struct hal_timer *timer) +{ + struct stm32f4_hal_tmr *tmr; + struct hal_timer *ht; + int sr; + int reset_ocmp; + + __HAL_DISABLE_INTERRUPTS(sr); + + tmr = (struct stm32f4_hal_tmr *)timer->bsp_timer; + if (timer->link.tqe_prev != NULL) { + reset_ocmp = 0; + if (timer == TAILQ_FIRST(&tmr->sht_timers)) { + /* If first on queue, we will need to reset OCMP */ + ht = TAILQ_NEXT(timer, link); + reset_ocmp = 1; + } + TAILQ_REMOVE(&tmr->sht_timers, timer, link); + timer->link.tqe_prev = NULL; + if (reset_ocmp) { + if (ht) { + tmr->sht_regs->CCR1 = ht->expiry; + } else { + TIM_CCxChannelCmd(tmr->sht_regs, TIM_CHANNEL_1, + TIM_CCx_DISABLE); + tmr->sht_regs->DIER &= ~TIM_DIER_CC1IE; + } + } + } + __HAL_ENABLE_INTERRUPTS(sr); + + return 0; +}