Description
An issue has been identified with the use of the Zephyr counter API on the Ambiq Apollo3 microcontroller. When configuring counter0 with clock source 2 CTIMER_CTRL0_TMRA0CLK_HFRC_DIV16
, which yields a timer frequency of 3 MHz, the expected timing resolution should be approximately 334 ns. However, attempts to generate short delays using the standard Zephyr counter sample code revealed that the (ISR) is not invoked for delays shorter than approximately 10 µs. This behavior limits the ability to achieve fine-grained timing control, and the underlying cause of this limitation remains unknown to me.
What target platform are you using?
- Target platform is Ambiq Apollo3 Blue EVB
What have you tried to diagnose or workaround this issue?
- As a workaround, I used (HAL) directly within the Zephyr environment, bypassing the counter API. This approach enabled the successful generation of precise pulses with durations below 1 µs, including those as short as 334 ns, indicating that the hardware is capable of such resolution.
Furthermore, I observed that setting alarm_cfg.flags = 0
results in deteriorated timing accuracy, while using COUNTER_ALARM_CFG_ABSOLUTE
leads to improved precision for delays greater than 50 µs. This result was unexpected, as the default relative alarm mode was initially assumed to provide better accuracy.
To Reproduce
apollo3_evb.overlay:
/ {
aliases {
timer0 = &counter0;
pin0 = &gpio17;
};
my_gpio{
compatible = "gpio-leds";
gpio17: gpio_17 {
gpios = <&gpio0_31 17 GPIO_ACTIVE_HIGH>;
};
};
};
&counter0 {
clock-frequency = <DT_FREQ_M(12)>;
clk-source = <1>;
status = "okay";
};
&gpio0_31 {
status = "okay";
};
code: https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/drivers/counter/alarm
modify main.c as:
#include <zephyr/device.h>
#include <zephyr/sys/printk.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/counter.h>
#include <zephyr/drivers/gpio.h>
// Delay in uS
#define DELAY 10
#define ALARM_CHANNEL_ID 0
#if defined(CONFIG_BOARD_APOLLO3_EVB)
#define TIMER DT_ALIAS(timer0)
#define PIN_GPIO DT_ALIAS(pin0)
#else
#error Unable to find a counter device node in devicetree
#endif
// Hardware related devices
const struct device *const counter_dev = DEVICE_DT_GET(TIMER);
const struct gpio_dt_spec gpio_pin = GPIO_DT_SPEC_GET(PIN_GPIO, gpios);
void test_counter_interrupt_fn(const struct device *counter_dev,
uint8_t chan_id, uint32_t ticks,
void *user_data)
{
if (gpio_pin_toggle_dt(&gpio_pin) < 0)
{
printk("GPIO could not be toggle!\n");
}
}
int main(void)
{
// User Data Struct
struct counter_alarm_cfg alarm_cfg;
// GPIO
if (!gpio_is_ready_dt(&gpio_pin))
{
printk("device not ready.\n");
return -ENODEV;
}
if (gpio_pin_configure_dt(&gpio_pin, GPIO_OUTPUT_ACTIVE) < 0)
{
printk("GPIO Configuration is not supported!\n");
return 0;
}
printk("Counter alarm sample\n\n");
if (!device_is_ready(counter_dev))
{
printk("device not ready.\n");
return -ENODEV;
}
// Free run mode
counter_start(counter_dev);
// alarm_cfg.flags = COUNTER_ALARM_CFG_ABSOLUTE;
alarm_cfg.flags = 0;
alarm_cfg.ticks = counter_us_to_ticks(counter_dev, DELAY);
alarm_cfg.callback = test_counter_interrupt_fn;
alarm_cfg.user_data = &alarm_cfg;
/* Single shot alarm */
int err = counter_set_channel_alarm(counter_dev,
ALARM_CHANNEL_ID,
&alarm_cfg);
if (-EINVAL == err)
{
printk("Alarm settings invalid\n");
}
else if (-ENOTSUP == err)
{
printk("Alarm setting request not supported\n");
}
else if (0 != err)
{
printk("Unknown Error\n");
}
printk("tick = %d\n", alarm_cfg.ticks);
printk("SLEEP FOREVER .... -_-\n");
while (1)
{
k_sleep(K_FOREVER);
}
return 0;
}
prj.conf
CONFIG_LOG=y
CONFIG_GPIO=y
CONFIG_PRINTK=y
CONFIG_COUNTER=y
Expected behavior
With a 3 MHz timer (334 ns resolution), the counter should generate accurate delays. A configured delay of 10 µs should produce a 10 µs pulse, and a 5 µs delay should result in a 5 µs pulse. This precision is expected given the hardware capabilities and timer resolution.
Impact
The lack of precise timing support has effectively blocked further development of my project.
Environment
- OS: Ubuntu 22.04.4 LTS (Linux kernel 6.8.0-52) (inside a docker container)
- Toolchain (Zephyr SDK)
- Commit SHA (1207ccf)
Additional context
Delay of 50 µs when using alarm_cfg.flags = COUNTER_ALARM_CFG_ABSOLUTE;
Delay of 50 µs when using alarm_cfg.flags = 0;