diff --git a/aclocal.m4 b/aclocal.m4
index 3aee821..b58d8d3 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.16.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.16.5 -*- Autoconf -*-
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2021 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -14,13 +14,13 @@
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
-m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
-[m4_warning([this file was generated for autoconf 2.69.
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.71],,
+[m4_warning([this file was generated for autoconf 2.71.
You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
-# Copyright (C) 2002-2018 Free Software Foundation, Inc.
+# Copyright (C) 2002-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -35,7 +35,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.16'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
-m4_if([$1], [1.16.1], [],
+m4_if([$1], [1.16.5], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
@@ -51,14 +51,14 @@ m4_define([_AM_AUTOCONF_VERSION], [])
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.16.1])dnl
+[AM_AUTOMAKE_VERSION([1.16.5])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -110,7 +110,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd`
# AM_COND_IF -*- Autoconf -*-
-# Copyright (C) 2008-2018 Free Software Foundation, Inc.
+# Copyright (C) 2008-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -147,7 +147,7 @@ fi[]dnl
# AM_CONDITIONAL -*- Autoconf -*-
-# Copyright (C) 1997-2018 Free Software Foundation, Inc.
+# Copyright (C) 1997-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -178,7 +178,7 @@ AC_CONFIG_COMMANDS_PRE(
Usually this means the macro was only invoked conditionally.]])
fi])])
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -369,7 +369,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
# Generate code to set up dependency tracking. -*- Autoconf -*-
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -408,7 +408,9 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
done
if test $am_rc -ne 0; then
AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
- for automatic dependency tracking. Try re-running configure with the
+ for automatic dependency tracking. If GNU make was not used, consider
+ re-running the configure script with MAKE="gmake" (or whatever is
+ necessary). You can also try re-running configure with the
'--disable-dependency-tracking' option to at least be able to build
the package (albeit without support for automatic dependency tracking).])
fi
@@ -435,7 +437,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
# Do all the work for Automake. -*- Autoconf -*-
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -463,6 +465,10 @@ m4_defn([AC_PROG_CC])
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.65])dnl
+m4_ifdef([_$0_ALREADY_INIT],
+ [m4_fatal([$0 expanded multiple times
+]m4_defn([_$0_ALREADY_INIT]))],
+ [m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
@@ -499,7 +505,7 @@ m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
[_AM_SET_OPTIONS([$1])dnl
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
m4_if(
- m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
+ m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]),
[ok:ok],,
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
@@ -551,6 +557,20 @@ AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
[m4_define([AC_PROG_OBJCXX],
m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
])
+# Variables for tags utilities; see am/tags.am
+if test -z "$CTAGS"; then
+ CTAGS=ctags
+fi
+AC_SUBST([CTAGS])
+if test -z "$ETAGS"; then
+ ETAGS=etags
+fi
+AC_SUBST([ETAGS])
+if test -z "$CSCOPE"; then
+ CSCOPE=cscope
+fi
+AC_SUBST([CSCOPE])
+
AC_REQUIRE([AM_SILENT_RULES])dnl
dnl The testsuite driver may need to know about EXEEXT, so add the
dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This
@@ -632,7 +652,7 @@ for _am_header in $config_headers :; do
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -653,7 +673,7 @@ if test x"${install_sh+set}" != xset; then
fi
AC_SUBST([install_sh])])
-# Copyright (C) 2003-2018 Free Software Foundation, Inc.
+# Copyright (C) 2003-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -674,7 +694,7 @@ AC_SUBST([am__leading_dot])])
# Check to see how 'make' treats includes. -*- Autoconf -*-
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -717,7 +737,7 @@ AC_SUBST([am__quote])])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
-# Copyright (C) 1997-2018 Free Software Foundation, Inc.
+# Copyright (C) 1997-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -738,12 +758,7 @@ AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
if test x"${MISSING+set}" != xset; then
- case $am_aux_dir in
- *\ * | *\ *)
- MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
- *)
- MISSING="\${SHELL} $am_aux_dir/missing" ;;
- esac
+ MISSING="\${SHELL} '$am_aux_dir/missing'"
fi
# Use eval to expand $SHELL
if eval "$MISSING --is-lightweight"; then
@@ -756,7 +771,7 @@ fi
# Helper functions for option handling. -*- Autoconf -*-
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -785,7 +800,7 @@ AC_DEFUN([_AM_SET_OPTIONS],
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -832,7 +847,7 @@ AC_LANG_POP([C])])
# For backward compatibility.
AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -851,7 +866,7 @@ AC_DEFUN([AM_RUN_LOG],
# Check to make sure that the build environment is sane. -*- Autoconf -*-
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -932,7 +947,7 @@ AC_CONFIG_COMMANDS_PRE(
rm -f conftest.file
])
-# Copyright (C) 2009-2018 Free Software Foundation, Inc.
+# Copyright (C) 2009-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -992,7 +1007,7 @@ AC_SUBST([AM_BACKSLASH])dnl
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
])
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1020,7 +1035,7 @@ fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
-# Copyright (C) 2006-2018 Free Software Foundation, Inc.
+# Copyright (C) 2006-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1039,7 +1054,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
-# Copyright (C) 2004-2018 Free Software Foundation, Inc.
+# Copyright (C) 2004-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/configure.ac b/configure.ac
index 9994cb9..ca36413 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,6 +21,7 @@ AC_CONFIG_FILES([
examples/0button/Makefile
examples/0hello/Makefile
examples/0ledctrl/Makefile
+ examples/0timerint/Makefile
examples/0timgalarm/Makefile
examples/0uart/Makefile
examples/0udma/Makefile
diff --git a/examples/0timerint/Makefile.am b/examples/0timerint/Makefile.am
new file mode 100644
index 0000000..6ef24d8
--- /dev/null
+++ b/examples/0timerint/Makefile.am
@@ -0,0 +1,36 @@
+include $(top_srcdir)/scripts/elf2bin.mk
+include $(top_srcdir)/ld/flags.mk
+
+noinst_HEADERS = defines.h
+
+if WITH_BINARIES
+AM_LDFLAGS += \
+ -T $(top_srcdir)/ld/esp32.rom.ld \
+ -T $(top_srcdir)/ld/esp32.rom.libgcc.ld \
+ -T $(top_srcdir)/ld/esp32.rom.newlib-data.ld \
+ -T $(top_srcdir)/ld/esp32.rom.newlib-locale.ld \
+ -T $(top_srcdir)/ld/esp32.rom.newlib-nano.ld \
+ -T $(top_srcdir)/ld/esp32.rom.newlib-time.ld \
+ -T $(top_srcdir)/ld/esp32.rom.redefined.ld \
+ -T $(top_srcdir)/ld/esp32.rom.syscalls.ld
+else
+AM_LDFLAGS += \
+ -T $(top_srcdir)/ld/esp32.rom.ld \
+ -T $(top_srcdir)/ld/esp32.rom.libgcc.ld \
+ -T $(top_srcdir)/ld/esp32.rom.redefined.ld \
+ -T $(top_srcdir)/ld/esp32.rom.syscalls.ld
+endif
+
+AM_CFLAGS = -std=c11 -flto
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(srcdir)
+LDADD = $(top_builddir)/src/libesp32basic.a
+
+bin_PROGRAMS = \
+ timer.elf
+
+if WITH_BINARIES
+CLEANFILES = \
+ timer.bin
+endif
+
+BUILT_SOURCES = $(CLEANFILES)
diff --git a/examples/0timerint/README.md b/examples/0timerint/README.md
new file mode 100644
index 0000000..3020f0d
--- /dev/null
+++ b/examples/0timerint/README.md
@@ -0,0 +1,28 @@
+### Example on CCOUNT / CCOMPARE interrupt usage
+
+In this example we show how to use TIMER.n (CCOUNT / CCOMPAREn) interrupts.
+We use the timer interrupt to take precise periodic measuements.
+The provided ISR reads current time from default timer and stores the value.
+Also, after a given number of invocations the ISR detaches itself.
+
+#### Control
+
+* `0`, `1`,`2` selects `ccompare0`, `ccompare1` or `ccompare2` for measurement.
+* `[`,`]` for decrease/increase sampling length by `1`.
+* `{`,`}` for decrease/increase sampling length by `10`.
+* `,`,`.` for decrease/increase interrupt period by `1`.
+* `<`,`>` for decrease/increase interrupt period by `10`.
+* `s` starts the measurement.
+* `r` prints the result of the last measurement.
+* `C` prints some register and pointer information for debugging.
+
+#### Experience
+
+* `TIMER.0` interrupt period gets stable around 264 ccompare increment (and above), (i.e., 1.65 µs, running at 160MHz).
+* `TIMER.1` interrupt period gets stable around 230 ccompare increment (and above), (i.e., 1.44 µs, running at 160MHz).
+* `TIMER.2` interrupt period gets stable around 484 ccompare increment (and above), (i.e., 3 µs, running at 160MHz).
+
+Using hardwired RSR()/WSR() macros, at 184 ccompare increment the interrupt period is stable (meaning 1.15 µs).
+
+Note, the highest interrupt frequency we get with `TIMER.0` or `TIMER.1` is almost twice as high as the best frequency we get
+using [TIMG ALARM interrupt](../0timgalarm)).
diff --git a/examples/0timerint/defines.h b/examples/0timerint/defines.h
new file mode 100644
index 0000000..d61d4d7
--- /dev/null
+++ b/examples/0timerint/defines.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2024 SZIGETI János
+ *
+ * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3.
+ * See LICENSE or for full license details.
+ */
+#ifndef DEFINES_H
+#define DEFINES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ // TIMINGS
+ // const -- do not change this value
+#define APB_FREQ_HZ 80000000U // 80 MHz
+
+ // variables
+#define TIM0_0_DIVISOR 2U
+#define START_APP_CPU 0U
+#define SCHEDULE_FREQ_HZ 1000U // 10KHz
+
+ // derived invariants
+#define CLK_FREQ_HZ (APB_FREQ_HZ / TIM0_0_DIVISOR) // 40 MHz
+#define TICKS_PER_MS (CLK_FREQ_HZ / 1000U) // 40000
+#define TICKS_PER_US (CLK_FREQ_HZ / 1000000U) // 40
+
+#define MS2TICKS(X) ((X) * TICKS_PER_MS)
+#define HZ2APBTICKS(X) (APB_FREQ_HZ / (X))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DEFINES_H */
+
diff --git a/examples/0timerint/timer.c b/examples/0timerint/timer.c
new file mode 100644
index 0000000..d7a61fd
--- /dev/null
+++ b/examples/0timerint/timer.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2024 - 2025 SZIGETI János
+ *
+ * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3.
+ * See LICENSE or for full license details.
+ */
+#include
+#include
+#include
+#include
+#include
+
+#include "esp_attr.h"
+#include "dport.h"
+#include "gpio.h"
+#include "iomux.h"
+#include "main.h"
+#include "defines.h"
+#include "romfunctions.h"
+#include "typeaux.h"
+#include "uart.h"
+#include "utils/uartutils.h"
+#include "timg.h"
+
+// =================== Hard constants =================
+// #0: timings
+#define UART_PERIOD_MS 100U ///< for polling UART0 RX for incoming commands
+#define UART_FLUSH_PERIOD_MS 10U ///< for flushing out multiple lines to UART0
+#define UART_FREQ_HZ 115200U
+
+// #1: limits
+#define COMPAREINC_VAL_MIN 20U
+#define COMPAREINC_VAL_MAX 10000U
+#define COMPAREINC_VAL_INIT 200U
+#define SAMPLE_MIN 10U
+#define SAMPLE_MAX 200U
+#define SAMPLE_INIT 50U
+
+// #2: Channels
+#define TIMER_N 1
+#define TIMER_INT 15
+
+// #3: Sizes
+#define SAMPLE_SIZE SAMPLE_MAX
+
+// ============= Local types ===============
+
+/// The results of the last measurement are stored in such structure.
+
+typedef struct {
+ uint32_t u32cycPeriod;
+ uint32_t u32SampleLen;
+ uint64_t au64tckSample[SAMPLE_SIZE];
+} Result;
+
+/// Measurement runtime state settings and variables.
+
+typedef struct {
+ bool bOngoing; ///< true: there is measurement process.going on.
+ TimerId sTimer; ///< Reference clock to read out current time value
+ uint32_t u32CcompareIdx;
+ uint32_t u32cycPeriod;
+ uint32_t u32SampleLen; ///< Total number of samples to take.
+ uint32_t u32SampleIdx; ///< How many samples are still to take.
+ Result *psResult;
+} MeasurementState;
+
+
+// ================ Local function declarations =================
+static void _ccompare_isr_attach();
+static void _ccompare_stop();
+static void _ccompare_isr(void *pvParam);
+
+static void _measurement_start(MeasurementState * psParam);
+static void _print_resultline(uint32_t u32Idx, const Result *psResult);
+
+static void _uart_init();
+static void _uart_cycle(uint64_t u64tckNow);
+
+// =================== Global constants ================
+const bool gbStartAppCpu = START_APP_CPU;
+const uint16_t gu16Tim00Divisor = TIM0_0_DIVISOR;
+const uint64_t gu64tckSchedulePeriod = (CLK_FREQ_HZ / SCHEDULE_FREQ_HZ);
+
+// ==================== Local Data ================
+const uint8_t gau8TimerIntNum[] = {6, 15, 16};
+//const uint8_t gau8TimerIntLvl[] = {1,3,5};
+
+DRAM_ATTR static Result gsResult = {
+ .u32SampleLen = 0
+};
+static bool gbAppCpuStarted = false;
+DRAM_ATTR static MeasurementState gsMeasParam = {
+ .sTimer =
+ {TIMG_0, TIMER0},
+ .u32cycPeriod = COMPAREINC_VAL_INIT,
+ .u32SampleLen = SAMPLE_INIT,
+ .u32SampleIdx = SAMPLE_INIT,
+ .psResult = &gsResult
+};
+
+// ============== Implementation ==============
+// -------------- Internal functions --------------
+
+IRAM_ATTR static void _ccompare_isr(void *pvParam) {
+ MeasurementState *psParam = (MeasurementState*)pvParam;
+
+ uint32_t u32CurrCnt;
+ // note, the xthal_xxx() register access is slower than RSR/WSR access, nevertheless, it is more elastic.
+ // beeing #define macros, in case of RSR/WSR we have to explicitly set CCOMPAREn as first parameter.
+ u32CurrCnt = xthal_get_ccompare(psParam->u32CcompareIdx);
+ // RSR(CCOMPARE1, u32CurrCnt);
+ u32CurrCnt += psParam->u32cycPeriod;
+ xthal_set_ccompare(psParam->u32CcompareIdx, u32CurrCnt);
+ // WSR(CCOMPARE1, u32CurrCnt);
+
+ // storing clock value
+ gsResult.au64tckSample[psParam->u32SampleIdx] = timg_ticks(psParam->sTimer);
+ ++psParam->u32SampleIdx;
+
+ if (psParam->u32SampleIdx == psParam->u32SampleLen) {
+ _ccompare_stop(pvParam);
+ }
+}
+
+IRAM_ATTR static void _ccompare_stop(void *pvParam) {
+ MeasurementState *psParam = (MeasurementState*)pvParam;
+
+ ets_isr_mask(1 << gau8TimerIntNum[psParam->u32CcompareIdx]);
+ _xtos_set_interrupt_handler(gau8TimerIntNum[psParam->u32CcompareIdx], NULL);
+
+ psParam->bOngoing = false;
+ uart_printf(&gsUART0, " Done.\r\n");
+}
+
+IRAM_ATTR static void _ccompare_isr_attach(void *pvParam) {
+ MeasurementState *psParam = (MeasurementState*)pvParam;
+ uint32_t u32Ccount;
+ u32Ccount = xthal_get_ccount();
+ // RSR(CCOUNT, u32Ccount);
+ uint32_t u32Ccompare = u32Ccount;
+ u32Ccompare += psParam->u32cycPeriod;
+ xthal_set_ccompare(psParam->u32CcompareIdx, u32Ccompare);
+ // WSR(CCOMPARE1, u32Ccompare);
+ _xtos_set_interrupt_handler_arg(gau8TimerIntNum[psParam->u32CcompareIdx], _ccompare_isr, (int)pvParam);
+ ets_isr_unmask(1 << gau8TimerIntNum[psParam->u32CcompareIdx]);
+}
+
+static void _measurement_start(MeasurementState * psParam) {
+ uart_printf(&gsUART0, "Starting measurement...");
+ psParam->bOngoing = true;
+ psParam->u32SampleIdx = 1;
+ psParam->psResult->u32cycPeriod = psParam->u32cycPeriod;
+ psParam->psResult->u32SampleLen = psParam->u32SampleLen;
+
+ _ccompare_isr_attach(psParam);
+
+ psParam->psResult->au64tckSample[0] = timg_ticks(psParam->sTimer);
+}
+
+static void _uart_init() {
+ gsUART0.CLKDIV.raw = UART_HZ2CLKDIV(UART_FREQ_HZ, APB_FREQ_HZ);
+}
+
+static void _uart_cycle(uint64_t u64tckNow) {
+ static uint64_t u64tckNext = MS2TICKS(1000);
+ static bool bPrintResult = false;
+ static uint32_t u32PrintNR = 0;
+
+ if (u64tckNext <= u64tckNow) {
+ while (0 < (gsUART0.STATUS & 0xff)) {
+ if (gsMeasParam.bOngoing) break;
+ char cCtrl = gsUART0Mapped.FIFO & 0xff;
+ int i32AlarmValDiff = 0;
+ int i32SampleLengthDiff = 0;
+ switch (cCtrl) {
+ case '0':
+ case '1':
+ case '2': // TIMG_1 TIMER0
+ uart_printf(&gsUART0, "Using ccompare%c\r\n", cCtrl);
+ gsMeasParam.u32CcompareIdx = cCtrl - '0';
+ break;
+ case 'r': // print result
+ u32PrintNR = 0;
+ bPrintResult = true;
+ break;
+ case 's': // start measurement
+ _measurement_start(&gsMeasParam);
+ break;
+ case 'c': // current Reg values
+ break;
+ case 'C':
+ uart_printf(&gsUART0, "ISR addr: %p, dat: %p %p\r\n", _ccompare_isr, &gsResult, &gsMeasParam);
+ break;
+ case 'i':
+ uart_printf(&gsUART0, "Ccompare%u\r\n", gsMeasParam.u32CcompareIdx);
+ break;
+
+ // Alarm value modification
+ case '>':
+ i32AlarmValDiff = 10;
+ break;
+ case '.':
+ i32AlarmValDiff = 1;
+ break;
+ case '<':
+ i32AlarmValDiff = -10;
+ break;
+ case ',':
+ i32AlarmValDiff = -1;
+ break;
+
+ // Samlpling length
+ case '}':
+ i32SampleLengthDiff = 10;
+ break;
+ case ']':
+ i32SampleLengthDiff = 1;
+ break;
+ case '{':
+ i32SampleLengthDiff = -10;
+ break;
+ case '[':
+ i32SampleLengthDiff = -1;
+ break;
+ default:
+ gsUART0.FIFO = '-';
+ }
+ if (i32AlarmValDiff) {
+ gsMeasParam.u32cycPeriod += i32AlarmValDiff;
+ if (gsMeasParam.u32cycPeriod < COMPAREINC_VAL_MIN) gsMeasParam.u32cycPeriod = COMPAREINC_VAL_MIN;
+ if (COMPAREINC_VAL_MAX < gsMeasParam.u32cycPeriod) gsMeasParam.u32cycPeriod = COMPAREINC_VAL_MAX;
+ uart_printf(&gsUART0, "Ccompare increment: %u\r\n", gsMeasParam.u32cycPeriod);
+ }
+ if (i32SampleLengthDiff) {
+ gsMeasParam.u32SampleLen += i32SampleLengthDiff;
+ if (gsMeasParam.u32SampleLen < SAMPLE_MIN)gsMeasParam.u32SampleLen = SAMPLE_MIN;
+ if (SAMPLE_MAX < gsMeasParam.u32SampleLen)gsMeasParam.u32SampleLen = SAMPLE_MAX;
+ uart_printf(&gsUART0, "Sample length: %u\r\n", gsMeasParam.u32SampleLen);
+ }
+ }
+ if (bPrintResult) {
+ _print_resultline(u32PrintNR, &gsResult);
+ ++u32PrintNR;
+ if (u32PrintNR == gsResult.u32SampleLen) {
+ bPrintResult = false;
+ }
+ }
+
+ u64tckNext += MS2TICKS(bPrintResult ? UART_FLUSH_PERIOD_MS : UART_PERIOD_MS);
+ }
+}
+
+static void _print_resultline(uint32_t u32Idx, const Result * psResult) {
+ uint32_t u32Actual = psResult->au64tckSample[u32Idx] - psResult->au64tckSample[0];
+ uint32_t u32Increment = psResult->u32cycPeriod;
+ uint32_t u32Expected = (u32Idx * (u32Increment)) / (2 * TIM0_0_DIVISOR);
+ int32_t i32Diff = u32Actual - u32Expected;
+ if (u32Idx == 0) { // print header
+ uart_printf(&gsUART0, "idx\tact\texp\tdiff\traw\r\n");
+ }
+ uart_printf(&gsUART0, "%u\t%u\t%u\t%d\t%u\r\n",
+ u32Idx, u32Actual, u32Expected, i32Diff, psResult->au64tckSample[u32Idx]);
+}
+
+// -------------- Interface functions --------------
+
+void prog_init_pro_pre() {
+ _uart_init();
+}
+
+void prog_init_app() {
+ gbAppCpuStarted = true;
+}
+
+void prog_init_pro_post() {
+}
+
+void prog_cycle_app(uint64_t u64tckNow) {
+}
+
+void prog_cycle_pro(uint64_t u64tckNow) {
+ _uart_cycle(u64tckNow);
+}
diff --git a/examples/0timgalarm/alarm.c b/examples/0timgalarm/alarm.c
index 0ede722..42167f1 100644
--- a/examples/0timgalarm/alarm.c
+++ b/examples/0timgalarm/alarm.c
@@ -31,7 +31,7 @@
// #1: limits
#define ALARM_DIVISOR TIM0_0_DIVISOR
-#define ALARM_VAL_MIN 50U
+#define ALARM_VAL_MIN 20U
#define ALARM_VAL_MAX 10000U
#define ALARM_VAL_INIT 200U
#define SAMPLE_MIN 10U
@@ -66,7 +66,7 @@ typedef struct {
Isr pfIsr; ///< ISR to invoke in case of ALARM interrupt
uint32_t u32tckPeriod;
uint32_t u32SampleLen; ///< Total number of samples to take.
- uint32_t u32Countdown; ///< How many samples are still to take.
+ uint32_t u32SampleIdx; ///< How many samples are still to take.
Result *psResult;
} MeasurementState;
@@ -84,6 +84,8 @@ static void _uart_cycle(uint64_t u64tckNow);
static void _alarm_reload_isr(void *pvParam);
static void _alarm_inc_isr(void *pvParam);
+static void _print_resultline(uint32_t u32Idx, const Result *psResult);
+
// =================== Global constants ================
const bool gbStartAppCpu = START_APP_CPU;
const uint16_t gu16Tim00Divisor = TIM0_0_DIVISOR;
@@ -106,52 +108,76 @@ DRAM_ATTR static MeasurementState gsAlarmParam = {
.pfIsr = _alarm_reload_isr,
.u32tckPeriod = ALARM_VAL_INIT,
.u32SampleLen = SAMPLE_INIT,
- .u32Countdown = SAMPLE_INIT,
+ .u32SampleIdx = SAMPLE_INIT,
.psResult = &gsResult
};
// ============== Implementation ==============
// -------------- Internal functions --------------
+/**
+ * This ISR is based on gu32ReloadConfig configuration, where the AUTORELOAD (bit29) flag is set.
+ * In this case we do not have to modify the ALARM registers of the timer,
+ * since the timer is automatically reset.
+ * But even in this case we have to set ALARM_EN (bit10) to re-enable interrupt after clearing the interrupt.
+ * @param pvParam ptr to current MeasurementState structure.
+ */
IRAM_ATTR static void _alarm_reload_isr(void *pvParam) {
MeasurementState *psParam = (MeasurementState*)pvParam;
// clear LEVEL interrupt
gapsTIMG[psParam->sAlarm.eTimg]->INT_CLR_TIMERS = 1 << psParam->sAlarm.eTimer;
+ // user section begin
+ // ...
+
// storing clock value
- psParam->psResult->au64tckSample[psParam->u32SampleLen - psParam->u32Countdown] = timg_ticks(psParam->sTimer);
- --psParam->u32Countdown;
+ psParam->psResult->au64tckSample[psParam->u32SampleIdx] = timg_ticks(psParam->sTimer);
+ ++psParam->u32SampleIdx;
+
+ // ...
+ // user section end
// either allow next alarm or terminate the process
- if (0 < psParam->u32Countdown) {
+ if (psParam->u32SampleIdx < psParam->u32SampleLen) {
timg_tregs(psParam->sAlarm)->CONFIG = psParam->u32Config;
} else {
_alarm_stop(psParam, true);
}
}
+/**
+ * This ISR must be used of AUTORELOAD is not enabled in TIMGnTx CONFIG register.
+ * To re-enable interrupt we have to set a new value in the ALARM registers,
+ * as the timer is not reset as the alarm occurs.
+ * Also, the interrupt has to be re-enabled by setting ALARM_EN (bit10) in COMFIG register.
+ * @param pvParam ptr to current MeasurementState structure.
+ */
IRAM_ATTR static void _alarm_inc_isr(void *pvParam) {
MeasurementState *psParam = (MeasurementState*)pvParam;
// clear LEVEL interrupt
gapsTIMG[psParam->sAlarm.eTimg]->INT_CLR_TIMERS = 1 << psParam->sAlarm.eTimer;
+ // user section begin
+ // ...
+
// storing clock value
- psParam->psResult->au64tckSample[psParam->u32SampleLen - psParam->u32Countdown] = timg_ticks(psParam->sTimer);
- --psParam->u32Countdown;
+ psParam->psResult->au64tckSample[psParam->u32SampleIdx] = timg_ticks(psParam->sTimer);
+ ++psParam->u32SampleIdx;
+
+ // ...
+ // user section end
// either allow next alarm or terminate the process
- if (0 < psParam->u32Countdown) {
+ if (psParam->u32SampleIdx < psParam->u32SampleLen) {
uint64_t u64tckAlarm = (((uint64_t)timg_tregs(psParam->sAlarm)->ALARMHI) << 32) | timg_tregs(psParam->sAlarm)->ALARMLO;
u64tckAlarm += psParam->u32tckPeriod;
- timg_tregs(psParam->sAlarm)->ALARMLO = u64tckAlarm & 0xFFFFFFFF;
- timg_tregs(psParam->sAlarm)->ALARMHI = u64tckAlarm >> 32;
+ timg_set_alarm(psParam->sAlarm, u64tckAlarm);
timg_tregs(psParam->sAlarm)->CONFIG = psParam->u32Config;
} else {
_alarm_stop(psParam, false);
}
-
}
IRAM_ATTR static void _alarm_stop(MeasurementState *psParam, bool bReload) {
@@ -160,9 +186,6 @@ IRAM_ATTR static void _alarm_stop(MeasurementState *psParam, bool bReload) {
gapsTIMG[psParam->sAlarm.eTimg]->INT_CLR_TIMERS = 1 << psParam->sAlarm.eTimer;
psParam->psResult->bReload = bReload;
- psParam->psResult->sAlarm = psParam->sAlarm;
- psParam->psResult->u32tckPeriod = psParam->u32tckPeriod;
- psParam->psResult->u32SampleLen = psParam->u32SampleLen;
_alarm_isr_detach(geIsrCpu, psParam->sAlarm);
psParam->bOngoing = false;
@@ -173,16 +196,27 @@ static void _alarm_start(MeasurementState *psParam) {
uart_printf(&gsUART0, "Starting measurement...");
psParam->bOngoing = true;
+ // temporarily disable alarm TIMGnTx (stop running while setting the registers)
+ timg_tregs(psParam->sAlarm)->CONFIG = 0;
+
+ // initialize parameters
+ psParam->u32SampleIdx = 1;
+ psParam->psResult->sAlarm = psParam->sAlarm;
+ psParam->psResult->u32tckPeriod = psParam->u32tckPeriod;
+ psParam->psResult->u32SampleLen = psParam->u32SampleLen;
+
+ // attach ISR to alarm interrupt
_alarm_isr_attach(geIsrCpu, psParam->sAlarm, INT_CH, psParam->pfIsr);
- timg_tregs(psParam->sAlarm)->LOADLO = 0;
- timg_tregs(psParam->sAlarm)->LOADHI = 0;
- timg_tregs(psParam->sAlarm)->LOAD = 0;
- timg_tregs(psParam->sAlarm)->ALARMLO = psParam->u32tckPeriod;
- timg_tregs(psParam->sAlarm)->ALARMHI = 0;
+
+ // set alarm TIMGnTx timer and alarm registers
+ timg_load(psParam->sAlarm, 0ULL);
+ timg_set_alarm(psParam->sAlarm, (uint64_t)psParam->u32tckPeriod);
+
+ // set alarm TIMGnTx interrupt flags
gapsTIMG[psParam->sAlarm.eTimg]->INT_CLR_TIMERS = 1 << psParam->sAlarm.eTimer;
gapsTIMG[psParam->sAlarm.eTimg]->INT_ENA_TIMERS |= 1 << psParam->sAlarm.eTimer;
- psParam->u32Countdown = psParam->u32SampleLen - 1;
+ // get timer TIMGnTx current value (as reference (starting) time) and enable alarm TIMGnTx (with minimal delay)
timg_tregs(psParam->sTimer)->UPDATE = 0;
// start, inc value, autoreload, alarm enabled, generates LVL INT
timg_tregs(psParam->sAlarm)->CONFIG = psParam->u32Config;
@@ -263,16 +297,25 @@ static void _uart_cycle(uint64_t u64tckNow) {
_alarm_start(&gsAlarmParam);
break;
case 'c': // current Reg values
- uart_printf(&gsUART0, "Alarm: {curr: %u, alarm: %u,%u, conf: %08X}, AppCpu: %u\r\n",
- (uint32_t)timg_ticks(gsAlarmParam.sAlarm),
+ {
+ uint64_t u64tckCurr = timg_ticks(gsAlarmParam.sAlarm);
+ uart_printf(&gsUART0, "Alarm(timer%u_%u): {curr: %08X,%08X, alarm: %08X,%08X, conf: %08X}, AppCpu: %u\r\n",
+ gsAlarmParam.sAlarm.eTimg,
+ gsAlarmParam.sAlarm.eTimer,
+ (uint32_t)(u64tckCurr >> 32),
+ (uint32_t)(u64tckCurr),
timg_tregs(gsAlarmParam.sAlarm)->ALARMHI,
timg_tregs(gsAlarmParam.sAlarm)->ALARMLO,
timg_tregs(gsAlarmParam.sAlarm)->CONFIG,
!!gbAppCpuStarted
);
+ }
break;
case 'C':
- uart_printf(&gsUART0, "ISR addr: %p %p, dat: %p %p\r\n", _alarm_reload_isr, _alarm_inc_isr, &gsResult, &gsAlarmParam);
+ uart_printf(&gsUART0, "ISR addr: %p (reload) %p, (inc); dat: %p (result), %p (param)\r\n", _alarm_reload_isr, _alarm_inc_isr, &gsResult, &gsAlarmParam);
+ break;
+ case 'i':
+ uart_printf(&gsUART0, "TIMER %u.%u\r\n", gsAlarmParam.sTimer.eTimg, gsAlarmParam.sTimer.eTimer);
break;
// Alarm value modification
@@ -319,15 +362,9 @@ static void _uart_cycle(uint64_t u64tckNow) {
}
}
if (bPrintResult) {
- uint32_t u32Actual = gsResult.au64tckSample[u32PrintNR] - gsResult.au64tckSample[0];
- uint32_t u32Increment = gsAlarmParam.u32tckPeriod + (gsResult.bReload ? CONST_ALARM_DELAY_TCK : 0);
- if (gsResult.bReload && (u32Increment % 2 == 0)) --u32Increment;
- uint32_t u32Expected = u32PrintNR * (u32Increment);
- int32_t i32Diff = u32Actual - u32Expected;
- uart_printf(&gsUART0, "%u\t%u\t%u\t%d\r\n",
- u32PrintNR, u32Actual, u32Expected, i32Diff);
+ _print_resultline(u32PrintNR, &gsResult);
++u32PrintNR;
- if (u32PrintNR == gsAlarmParam.u32SampleLen) {
+ if (u32PrintNR == gsResult.u32SampleLen) {
bPrintResult = false;
}
}
@@ -335,6 +372,19 @@ static void _uart_cycle(uint64_t u64tckNow) {
}
}
+static void _print_resultline(uint32_t u32Idx, const Result *psResult) {
+ uint32_t u32Actual = psResult->au64tckSample[u32Idx] - psResult->au64tckSample[0];
+ uint32_t u32Increment = psResult->u32tckPeriod + (psResult->bReload ? CONST_ALARM_DELAY_TCK : 0);
+ if (psResult->bReload && (u32Increment % 2 == 0)) --u32Increment;
+ uint32_t u32Expected = u32Idx * (u32Increment);
+ int32_t i32Diff = u32Actual - u32Expected;
+ if (u32Idx == 0) { // print header
+ uart_printf(&gsUART0, "idx\tact\texp\tdiff\traw\r\n");
+ }
+ uart_printf(&gsUART0, "%u\t%u\t%u\t%d\t%u\r\n",
+ u32Idx, u32Actual, u32Expected, i32Diff, psResult->au64tckSample[u32Idx]);
+}
+
// -------------- Interface functions --------------
void prog_init_pro_pre() {
diff --git a/examples/Makefile.am b/examples/Makefile.am
index d8ba741..30fed6c 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1,4 +1,4 @@
AUTOMAKE_OPTIONS =
-SUBDIRS=0blink 0button 0hello 0ledctrl 0timgalarm 0uart 0udma \
+SUBDIRS=0blink 0button 0hello 0ledctrl 0timerint 0timgalarm 0uart 0udma \
1bme280 1i2cssd1306 1rmtblink 1rmtdht 1rmtmorse 1rmtmusic 1rmttm1637 \
3prog1 1rmtws2812
diff --git a/src/romfunctions.h b/src/romfunctions.h
index 0003834..b825298 100644
--- a/src/romfunctions.h
+++ b/src/romfunctions.h
@@ -14,6 +14,10 @@ extern "C" {
#include
#include
+#define RSR(reg, curval) __asm__ volatile ("rsr %0, " #reg : "=r" (curval));
+#define WSR(reg, newval) __asm__ volatile ("wsr %0, " #reg : : "r" (newval));
+
+ void ets_isr_mask(uint32_t mask);
void ets_isr_unmask(uint32_t mask);
void *_xtos_set_interrupt_handler(int irq_number, void* function);
void *_xtos_set_interrupt_handler_arg(int irq_number, void* function, int argument);
@@ -21,6 +25,14 @@ extern "C" {
void gpio_matrix_out(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv);
void gpio_matrix_in(uint32_t gpio, uint32_t signal_idx, bool inv);
+ /* get CCOUNT register (if not present return 0) */
+ extern uint32_t xthal_get_ccount(void);
+
+ /* set and get CCOMPAREn registers (if not present, get returns 0) */
+ extern void xthal_set_ccompare(int iReg, uint32_t u32Value);
+ extern uint32_t xthal_get_ccompare(int iReg);
+ void xthal_set_intclear(uint32_t u32IntMask);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/timg.h b/src/timg.h
index 59bc8a0..0c4622c 100644
--- a/src/timg.h
+++ b/src/timg.h
@@ -73,12 +73,13 @@ extern "C" {
// ============== Global values / References ==============
extern TIMG_Type gsTIMG0;
extern TIMG_Type gsTIMG1;
- static TIMG_Type *gapsTIMG[] = {
+ static TIMG_Type * const gapsTIMG[] = {
&gsTIMG0,
&gsTIMG1
};
// ============== Inline interface functions ==============
+
static inline TimerRegs *timg_tregs(TimerId sTimer) {
return &gapsTIMG[sTimer.eTimg]->T[sTimer.eTimer];
}
@@ -94,10 +95,15 @@ extern "C" {
timg_tregs(sTimer)->LOAD = 0;
}
+ static inline void timg_set_alarm(TimerId sTimer, uint64_t u64Value) {
+ timg_tregs(sTimer)->ALARMLO = u64Value & 0xFFFFFFFF;
+ timg_tregs(sTimer)->ALARMHI = u64Value >> 32;
+ }
+
static inline void timg_init_timer(TimerId sTimer, uint16_t u16Divisor) {
- timg_tregs(sTimer)->CONFIG = (1 << 31) | (1 << 30) | (u16Divisor << 13);
- timg_load(sTimer, 0ULL);
-}
+ timg_tregs(sTimer)->CONFIG = (1 << 31) | (1 << 30) | (u16Divisor << 13);
+ timg_load(sTimer, 0ULL);
+ }
// ============== Interface functions ==============