blob: 093dcfef21e9c491525c9dd79dcfc62f79fa7765 [file] [log] [blame]
/****************************************************************************
* arch/xtensa/src/esp32/esp32_oneshot.c
*
* 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/timers/oneshot.h>
#include <nuttx/clock.h>
#include "hardware/esp32_soc.h"
#include "esp32_tim.h"
#include "esp32_clockconfig.h"
#include "esp32_oneshot.h"
#ifdef CONFIG_ESP32_ONESHOT
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MAX_TIMER_COUNTER UINT64_MAX
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int esp32_oneshot_handler(int irq, void * context, void *arg);
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: esp32_oneshot_handler
*
* Description:
* Oneshot interrupt Handler. When any oneshot timer interrupt
* expires, this function will be triggered. It will forward the call to
* the next level up.
*
* Input Parameters:
* irq - IRQ associated to that interrupt
* arg - A pointer to the argument provided when the interrupt was
* registered.
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static int esp32_oneshot_handler(int irq, void * context, void *arg)
{
int ret = OK;
struct esp32_oneshot_s *oneshot = (struct esp32_oneshot_s *)arg;
oneshot_handler_t handler;
void *handler_arg;
DEBUGASSERT(oneshot != NULL);
DEBUGASSERT(oneshot->handler != NULL);
tmrinfo("Oneshot handler triggered\n");
/* Disable interrupts */
ESP32_TIM_DISABLEINT(oneshot->tim);
/* Detach handler */
ret = ESP32_TIM_SETISR(oneshot->tim, NULL, NULL);
/* Clear the Interrupt */
ESP32_TIM_ACKINT(oneshot->tim);
/* The timer is no longer running */
oneshot->running = false;
/* Forward the event, clearing out any vestiges */
handler = (oneshot_handler_t)oneshot->handler;
oneshot->handler = NULL;
handler_arg = (void *)oneshot->arg;
oneshot->arg = NULL;
/* Call the callback */
handler(handler_arg);
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32_oneshot_initialize
*
* Description:
* Initialize the oneshot timer wrapper.
*
* Input Parameters:
* oneshot Caller allocated instance of the oneshot state structure.
* chan Timer counter channel to be used.
* resolution The required resolution of the timer in units of
* microseconds. NOTE that the range is restricted to the
* range of uint16_t (excluding zero).
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned
* on failure.
*
****************************************************************************/
int esp32_oneshot_initialize(struct esp32_oneshot_s *oneshot, int chan,
uint16_t resolution)
{
uint16_t pre;
int ret = OK;
tmrinfo("chan=%d resolution=%d usecs\n", chan, resolution);
DEBUGASSERT(oneshot != NULL);
DEBUGASSERT(resolution > 0);
oneshot->chan = chan;
oneshot->tim = esp32_tim_init(chan);
if (oneshot->tim == NULL)
{
tmrerr("ERROR: Failed to allocate TIM %d\n", chan);
ret = -EBUSY;
}
else
{
/* Initialize the remaining fields in the state structure. */
oneshot->running = false;
oneshot->handler = NULL;
oneshot->arg = NULL;
oneshot->resolution = resolution;
/* Ensure timer is disabled.
* Change the prescaler divider with the timer enabled can lead to
* unpredictable results.
*/
ESP32_TIM_STOP(oneshot->tim);
/* Calculate the suitable prescaler according to the current apb
* frequency to generate a period equals to resolution.
*/
pre = (esp_clk_apb_freq() * resolution) / USEC_PER_SEC;
/* Configure TIMER prescaler */
ESP32_TIM_SETPRE(oneshot->tim, pre);
}
return ret;
}
/****************************************************************************
* Name: esp32_oneshot_max_delay
*
* Input Parameters:
* oneshot Caller allocated instance of the oneshot state structure. This
* structure must have been previously initialized via a call to
* esp32_oneshot_initialize();
* usec The maximum delay in us.
*
* Returned Value:
* Zero (OK).
*
****************************************************************************/
int esp32_oneshot_max_delay(struct esp32_oneshot_s *oneshot, uint64_t *usec)
{
DEBUGASSERT(oneshot != NULL && usec != NULL);
/* In theory, Maximum delay (us) = resolution (us) * MAX_TIMER_COUNTER
* But if the resolution is bigger than 1 us, the value will not fit
* in a uint64_t. So, this function assumes the max delay using a
* resolution of 1 us.
*/
*usec = MAX_TIMER_COUNTER;
return OK;
}
/****************************************************************************
* Name: esp32_oneshot_start
*
* Description:
* Start the oneshot timer
*
* Input Parameters:
* oneshot Caller allocated instance of the oneshot state structure. This
* structure must have been previously initialized via a call to
* esp32_oneshot_initialize();
* handler The function to call when when the oneshot timer expires.
* arg An opaque argument that will accompany the callback.
* ts Provides the duration of the one shot timer.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned
* on failure.
*
****************************************************************************/
int esp32_oneshot_start(struct esp32_oneshot_s *oneshot,
oneshot_handler_t handler, void *arg,
const struct timespec *ts)
{
uint64_t timeout_us;
int ret = OK;
tmrinfo("handler=%p arg=%p, ts=(%lu, %lu)\n",
handler, arg, (unsigned long)ts->tv_sec,
(unsigned long)ts->tv_nsec);
DEBUGASSERT(oneshot != NULL);
DEBUGASSERT(handler != NULL);
DEBUGASSERT(ts != NULL);
if (oneshot->running)
{
tmrinfo("One shot timer already in use. Cancelling it ...\n");
/* If the oneshot timer was already started, cancel it and then
* restart.
*/
esp32_oneshot_cancel(oneshot, NULL);
}
/* Save the new callback and its argument */
oneshot->handler = handler;
oneshot->arg = arg;
/* Retrieve the duration from timespec in microsecond */
timeout_us = (uint64_t)ts->tv_sec * USEC_PER_SEC +
(uint64_t)(ts->tv_nsec / NSEC_PER_USEC);
/* Verify if it is a multiple of the configured resolution.
* In case it isn't, warn the user.
*/
if ((timeout_us % oneshot->resolution) != 0)
{
tmrwarn("Warning: The interval is not multiple of the resolution.\n"
"Adjust the resolution in your bringup file.\n");
}
/* Set the timer */
/* Ensure timer is stopped */
ESP32_TIM_STOP(oneshot->tim);
/* Configure TIMER mode */
ESP32_TIM_SETMODE(oneshot->tim, ESP32_TIM_MODE_UP);
/* Clear TIMER counter value */
ESP32_TIM_CLEAR(oneshot->tim);
/* Disable autoreload */
ESP32_TIM_SETARLD(oneshot->tim, false);
/* Set the timeout */
ESP32_TIM_SETALRVL(oneshot->tim, timeout_us / oneshot->resolution);
/* Enable TIMER alarm */
ESP32_TIM_SETALRM(oneshot->tim, true);
/* Clear Interrupt Bits Status */
ESP32_TIM_ACKINT(oneshot->tim);
/* Set the interrupt */
/* Register the handler that calls the callback */
ret = ESP32_TIM_SETISR(oneshot->tim, esp32_oneshot_handler, oneshot);
if (ret == OK)
{
ESP32_TIM_ENABLEINT(oneshot->tim);
/* Finally, start the TIMER */
ESP32_TIM_START(oneshot->tim);
oneshot->running = true;
}
return ret;
}
/****************************************************************************
* Name: esp32_oneshot_cancel
*
* Description:
* Cancel the oneshot timer and return the time remaining on the timer.
*
*
* Input Parameters:
* oneshot Caller allocated instance of the oneshot state structure. This
* structure must have been previously initialized via a call to
* esp32_oneshot_initialize();
* ts The location in which to return the time remaining on the
* oneshot timer. A time of zero is returned if the timer is
* not running. ts may be zero in which case the time remaining
* is not returned.
*
* Returned Value:
* Zero (OK) is returned on success. A call to up_timer_cancel() when
* the timer is not active should also return success; a negated errno
* value is returned on any failure.
*
****************************************************************************/
int esp32_oneshot_cancel(struct esp32_oneshot_s *oneshot,
struct timespec *ts)
{
int ret = OK;
uint64_t current_us;
uint64_t remaining_us;
uint64_t timeout_us;
uint64_t counter_value;
uint64_t alarm_value;
DEBUGASSERT(oneshot);
if (oneshot->running == false)
{
tmrinfo("Trying to cancel a non started oneshot timer.\n");
ts->tv_sec = 0;
ts->tv_nsec = 0;
}
else
{
/* Stop timer */
ESP32_TIM_STOP(oneshot->tim);
/* Disable int */
ESP32_TIM_DISABLEINT(oneshot->tim);
/* Detach handler */
ret = ESP32_TIM_SETISR(oneshot->tim, NULL, NULL);
if (ts != NULL)
{
/* Get the current counter value */
ESP32_TIM_GETCTR(oneshot->tim, &counter_value);
/* Get the current configured timeout */
ESP32_TIM_GETALRVL(oneshot->tim, &alarm_value);
current_us = counter_value * oneshot->resolution;
timeout_us = alarm_value * oneshot->resolution;
/* Remaining time (us) = timeout (us) - current (us) */
remaining_us = timeout_us - current_us;
ts->tv_sec = remaining_us / USEC_PER_SEC;
remaining_us = remaining_us - ts->tv_sec * USEC_PER_SEC;
ts->tv_nsec = remaining_us * NSEC_PER_USEC;
}
oneshot->running = false;
oneshot->handler = NULL;
oneshot->arg = NULL;
}
return ret;
}
/****************************************************************************
* Name: esp32_oneshot_current
*
* Description:
* Get the current time.
*
* Input Parameters:
* oneshot Caller allocated instance of the oneshot state structure. This
* structure must have been previously initialized via a call to
* esp32_oneshot_initialize();
* usec The maximum delay in us.
*
* Returned Value:
* Zero (OK).
*
****************************************************************************/
int esp32_oneshot_current(struct esp32_oneshot_s *oneshot, uint64_t *usec)
{
/* Get the current counter value */
ESP32_TIM_GETCTR(oneshot->tim, usec);
*usec = *usec * (uint64_t)oneshot->resolution;
return OK;
}
#endif /* CONFIG_ESP32_ONESHOT */