From 4e458fd6fb5fc77c72ab25ffdc4d932b9337e77c Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Fri, 9 Dec 2016 17:47:30 +0200 Subject: [PATCH] Initial Timers driver Implementation for Timers 0-4 (no locks yet) --- cores/esp32/esp32-hal-timer.c | 295 ++++++++++++++++++++++++++++++++++ cores/esp32/esp32-hal-timer.h | 72 +++++++++ cores/esp32/esp32-hal.h | 1 + 3 files changed, 368 insertions(+) create mode 100644 cores/esp32/esp32-hal-timer.c create mode 100644 cores/esp32/esp32-hal-timer.h diff --git a/cores/esp32/esp32-hal-timer.c b/cores/esp32/esp32-hal-timer.c new file mode 100644 index 00000000..a06b4f36 --- /dev/null +++ b/cores/esp32/esp32-hal-timer.c @@ -0,0 +1,295 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed 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 "esp32-hal-timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "rom/ets_sys.h" +#include "soc/timer_group_struct.h" +#include "soc/dport_reg.h" +#include "esp_attr.h" +#include "esp_intr.h" + +#define HWTIMER_INUM 10 +#define HWTIMER_LOCK() portENTER_CRITICAL(timer->lock) +#define HWTIMER_UNLOCK() portEXIT_CRITICAL(timer->lock) + +typedef struct { + union { + struct { + uint32_t reserved0: 10; + uint32_t alarm_en: 1; /*When set alarm is enabled*/ + uint32_t level_int_en: 1; /*When set level type interrupt will be generated during alarm*/ + uint32_t edge_int_en: 1; /*When set edge type interrupt will be generated during alarm*/ + uint32_t divider: 16; /*Timer clock (T0/1_clk) pre-scale value.*/ + uint32_t autoreload: 1; /*When set timer 0/1 auto-reload at alarming is enabled*/ + uint32_t increase: 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter decrement.*/ + uint32_t enable: 1; /*When set timer 0/1 time-base counter is enabled*/ + }; + uint32_t val; + } config; + uint32_t cnt_low; /*Register to store timer 0/1 time-base counter current value lower 32 bits.*/ + uint32_t cnt_high; /*Register to store timer 0 time-base counter current value higher 32 bits.*/ + uint32_t update; /*Write any value will trigger a timer 0 time-base counter value update (timer 0 current value will be stored in registers above)*/ + uint32_t alarm_low; /*Timer 0 time-base counter value lower 32 bits that will trigger the alarm*/ + uint32_t alarm_high; /*Timer 0 time-base counter value higher 32 bits that will trigger the alarm*/ + uint32_t load_low; /*Lower 32 bits of the value that will load into timer 0 time-base counter*/ + uint32_t load_high; /*higher 32 bits of the value that will load into timer 0 time-base counter*/ + uint32_t reload; /*Write any value will trigger timer 0 time-base counter reload*/ +} hw_timer_reg_t; + +typedef struct hw_timer_s { + hw_timer_reg_t * dev; + uint8_t num; + uint8_t group; + uint8_t timer; + portMUX_TYPE lock; +} hw_timer_t; + +static hw_timer_t hw_timer[4] = { + {(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE),0,0,0,portMUX_INITIALIZER_UNLOCKED}, + {(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x0024),1,0,1,portMUX_INITIALIZER_UNLOCKED}, + {(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x1000),2,1,0,portMUX_INITIALIZER_UNLOCKED}, + {(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x1024),3,1,1,portMUX_INITIALIZER_UNLOCKED} +}; + +typedef void (*voidFuncPtr)(void); +static voidFuncPtr __timerInterruptHandlers[4] = {0,}; + +void IRAM_ATTR __timerISR(void * arg){ + uint32_t s0 = TIMERG0.int_st_timers.val; + uint32_t s1 = TIMERG1.int_st_timers.val; + TIMERG0.int_clr_timers.val = s0; + TIMERG1.int_clr_timers.val = s1; + uint8_t status = (s1 & 3) << 2 | (s0 & 3); + uint8_t i = 4; + //restart the timers that should autoreload + while(i--){ + hw_timer_reg_t * dev = hw_timer[i].dev; + if((status & (1 << i)) && dev->config.autoreload){ + dev->load_high = 0; + dev->load_low = 0; + dev->reload = 1; + dev->config.alarm_en = 1; + } + } + i = 4; + //call callbacks + while(i--){ + if(status & (1 << i)){ + if(__timerInterruptHandlers[i]){ + __timerInterruptHandlers[i](); + } + } + } +} + +uint64_t timerRead(hw_timer_t *timer){ + timer->dev->update = 1; + uint64_t h = timer->dev->cnt_high; + uint64_t l = timer->dev->cnt_low; + return (h << 32) | l; +} + +uint64_t timerAlarmRead(hw_timer_t *timer){ + uint64_t h = timer->dev->alarm_high; + uint64_t l = timer->dev->alarm_low; + return (h << 32) | l; +} + +void timerWrite(hw_timer_t *timer, uint64_t val){ + timer->dev->load_high = (uint32_t) (val >> 32); + timer->dev->load_low = (uint32_t) (val); + timer->dev->reload = 1; +} + +void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload){ + timer->dev->alarm_high = (uint32_t) (alarm_value >> 32); + timer->dev->alarm_low = (uint32_t) alarm_value; + timer->dev->config.autoreload = autoreload; +} + +void timerSetConfig(hw_timer_t *timer, uint32_t config){ + timer->dev->config.val = config; +} + +uint32_t timerGetConfig(hw_timer_t *timer){ + return timer->dev->config.val; +} + +void timerSetCountUp(hw_timer_t *timer, bool countUp){ + timer->dev->config.increase = countUp; +} + +bool timerGetCountUp(hw_timer_t *timer){ + return timer->dev->config.increase; +} + +void timerSetAutoReload(hw_timer_t *timer, bool autoreload){ + timer->dev->config.autoreload = autoreload; +} + +bool timerGetAutoReload(hw_timer_t *timer){ + return timer->dev->config.autoreload; +} + +void timerSetDivider(hw_timer_t *timer, uint16_t divider){//2 to 65536 + if(!divider){ + divider = 0xFFFF; + } else if(divider == 1){ + divider = 2; + } + int timer_en = timer->dev->config.enable; + timer->dev->config.enable = 0; + timer->dev->config.divider = divider; + timer->dev->config.enable = timer_en; +} + +uint16_t timerGetDivider(hw_timer_t *timer){ + return timer->dev->config.divider; +} + +void timerStart(hw_timer_t *timer){ + timer->dev->config.enable = 1; +} + +void timerStop(hw_timer_t *timer){ + timer->dev->config.enable = 0; +} + +void timerRestart(hw_timer_t *timer){ + timer->dev->config.enable = 0; + timer->dev->config.enable = 1; +} + +bool timerStarted(hw_timer_t *timer){ + return timer->dev->config.enable; +} + +void timerAlarmEnable(hw_timer_t *timer){ + timer->dev->config.alarm_en = 1; +} + +void timerAlarmDisable(hw_timer_t *timer){ + timer->dev->config.alarm_en = 0; +} + +bool timerAlarmEnabled(hw_timer_t *timer){ + return timer->dev->config.alarm_en; +} + + + +hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){ + if(num > 3){ + return NULL; + } + hw_timer_t * timer = &hw_timer[num]; + if(timer->group) { + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_TIMERGROUP1_CLK_EN); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_TIMERGROUP1_RST); + TIMERG1.int_ena.val &= ~BIT(timer->timer); + } else { + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_TIMERGROUP_CLK_EN); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_TIMERGROUP_RST); + TIMERG0.int_ena.val &= ~BIT(timer->timer); + } + timer->dev->config.enable = 0; + timerSetDivider(timer, divider); + timerSetCountUp(timer, countUp); + timerSetAutoReload(timer, false); + timerAttachInterrupt(timer, NULL, false); + timerWrite(timer, 0); + timer->dev->config.enable = 1; + return timer; +} + +void timerEnd(hw_timer_t *timer){ + timer->dev->config.enable = 0; + timerAttachInterrupt(timer, NULL, false); +} + +void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){ + static bool initialized = false; + ESP_INTR_DISABLE(HWTIMER_INUM); + if(fn == NULL){ + timer->dev->config.level_int_en = 0; + timer->dev->config.edge_int_en = 0; + timer->dev->config.alarm_en = 0; + if(timer->num & 2){ + TIMERG1.int_ena.val &= ~BIT(timer->timer); + } else { + TIMERG0.int_ena.val &= ~BIT(timer->timer); + } + __timerInterruptHandlers[timer->num] = NULL; + } else { + if(!initialized){ + xt_set_interrupt_handler(HWTIMER_INUM, &__timerISR, NULL); + initialized = true; + } + __timerInterruptHandlers[timer->num] = fn; + timer->dev->config.level_int_en = edge?0:1;//When set, an alarm will generate a level type interrupt. + timer->dev->config.edge_int_en = edge?1:0;//When set, an alarm will generate an edge type interrupt. + int intr_source = 0; + if(!edge){ + if(timer->group){ + intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer->timer; + } else { + intr_source = ETS_TG0_T0_LEVEL_INTR_SOURCE + timer->timer; + } + } else { + if(timer->group){ + intr_source = ETS_TG1_T0_EDGE_INTR_SOURCE + timer->timer; + } else { + intr_source = ETS_TG0_T0_EDGE_INTR_SOURCE + timer->timer; + } + } + intr_matrix_set(xPortGetCoreID(), intr_source, HWTIMER_INUM); + if(timer->group){ + TIMERG1.int_ena.val |= BIT(timer->timer); + } else { + TIMERG0.int_ena.val |= BIT(timer->timer); + } + } + ESP_INTR_ENABLE(HWTIMER_INUM); +} + +void timerDetachInterrupt(hw_timer_t *timer){ + timerAttachInterrupt(timer, NULL, false); +} + +uint64_t timerReadMicros(hw_timer_t *timer){ + uint64_t timer_val = timerRead(timer); + uint16_t div = timerGetDivider(timer); + return timer_val * div / 80; +} + +double timerReadSeconds(hw_timer_t *timer){ + uint64_t timer_val = timerRead(timer); + uint16_t div = timerGetDivider(timer); + return (double)timer_val * div / 80000000; +} + +uint64_t timerAlarmReadMicros(hw_timer_t *timer){ + uint64_t timer_val = timerAlarmRead(timer); + uint16_t div = timerGetDivider(timer); + return timer_val * div / 80; +} + +double timerAlarmReadSeconds(hw_timer_t *timer){ + uint64_t timer_val = timerAlarmRead(timer); + uint16_t div = timerGetDivider(timer); + return (double)timer_val * div / 80000000; +} diff --git a/cores/esp32/esp32-hal-timer.h b/cores/esp32/esp32-hal-timer.h new file mode 100644 index 00000000..7624fc5a --- /dev/null +++ b/cores/esp32/esp32-hal-timer.h @@ -0,0 +1,72 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_ESP32_HAL_TIMER_H_ +#define MAIN_ESP32_HAL_TIMER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" +#include "freertos/FreeRTOS.h" + +struct hw_timer_s; +typedef struct hw_timer_s hw_timer_t; + +hw_timer_t * timerBegin(uint8_t timer, uint16_t divider, bool countUp); +void timerEnd(hw_timer_t *timer); + +void timerSetConfig(hw_timer_t *timer, uint32_t config); +uint32_t timerGetConfig(hw_timer_t *timer); + +void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge); +void timerDetachInterrupt(hw_timer_t *timer); + +void timerStart(hw_timer_t *timer); +void timerStop(hw_timer_t *timer); +void timerRestart(hw_timer_t *timer); +void timerWrite(hw_timer_t *timer, uint64_t val); +void timerSetDivider(hw_timer_t *timer, uint16_t divider); +void timerSetCountUp(hw_timer_t *timer, bool countUp); +void timerSetAutoReload(hw_timer_t *timer, bool autoreload); + +bool timerStarted(hw_timer_t *timer); +uint64_t timerRead(hw_timer_t *timer); +uint64_t timerReadMicros(hw_timer_t *timer); +double timerReadSeconds(hw_timer_t *timer); +uint16_t timerGetDivider(hw_timer_t *timer); +bool timerGetCountUp(hw_timer_t *timer); +bool timerGetAutoReload(hw_timer_t *timer); + +void timerAlarmEnable(hw_timer_t *timer); +void timerAlarmDisable(hw_timer_t *timer); +void timerAlarmWrite(hw_timer_t *timer, uint64_t interruptAt, bool autoreload); + +bool timerAlarmEnabled(hw_timer_t *timer); +uint64_t timerAlarmRead(hw_timer_t *timer); +uint64_t timerAlarmReadMicros(hw_timer_t *timer); +double timerAlarmReadSeconds(hw_timer_t *timer); + + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_TIMER_H_ */ diff --git a/cores/esp32/esp32-hal.h b/cores/esp32/esp32-hal.h index 99153cea..2296bb2b 100644 --- a/cores/esp32/esp32-hal.h +++ b/cores/esp32/esp32-hal.h @@ -57,6 +57,7 @@ void vPortYield( void ); #include "esp32-hal-i2c.h" #include "esp32-hal-ledc.h" #include "esp32-hal-sd.h" +#include "esp32-hal-timer.h" #include "esp_system.h" uint32_t micros();