Hi,
I'm trying to implement a PRE_KERNEL1 timer (with top priority 0) to toggle an LED.
I’m able to control the LED using registers, and now I want to add a timer layer.
I've tried using both registers and NRFX for this task. Both solutions compiled and flashed successfully, but they don’t seem to do anything. :(
In the NRFX solution, it looks like the program gets stuck during timer initialization (code below).
What do you think might be the issue?
// Base addresses - NRF5340
#define TIMER1_BASE 0x50010000UL
#define CLOCK_BASE 0x50005000UL
#define OSC_BASE 0x50004000UL
#define NRF_P0_BASE 0x50842500UL
// LED definitions
#define NRF_GPIO_DIRSET (*(volatile uint32_t *)(NRF_P0_BASE + 0x018))
#define NRF_GPIO_DIRCLR (*(volatile uint32_t *)(NRF_P0_BASE + 0x01C))
#define NRF_GPIO_OUTSET (*(volatile uint32_t *)(NRF_P0_BASE + 0x008))
#define NRF_GPIO_OUTCLR (*(volatile uint32_t *)(NRF_P0_BASE + 0x00C))
#define NRF_GPIO_IN (*(volatile uint32_t *)(NRF_P0_BASE + 0x010))
#define NRF_GPIO_PIN_CNF(PIN) (*(volatile uint32_t *)(NRF_P0_BASE + 0x700 + PIN * 4))
#define PIN_LED 3 // P0.03
// Timer registers (offsets from TIMER1_BASE)
#define TIMER_TASKS_START (*(volatile uint32_t *)(TIMER1_BASE + 0x000))
#define TIMER_TASKS_STOP (*(volatile uint32_t *)(TIMER1_BASE + 0x004))
#define TIMER_TASKS_CLEAR (*(volatile uint32_t *)(TIMER1_BASE + 0x008))
#define TIMER_TASKS_CAPTURE0 (*(volatile uint32_t *)(TIMER1_BASE + 0x018))
#define TIMER_TASKS_CAPTURE1 (*(volatile uint32_t *)(TIMER1_BASE + 0x01C))
#define TIMER_EVENTS_COMPARE0 (*(volatile uint32_t *)(TIMER1_BASE + 0x140))
#define TIMER_EVENTS_COMPARE1 (*(volatile uint32_t *)(TIMER1_BASE + 0x144))
#define TIMER_SHORTS (*(volatile uint32_t *)(TIMER1_BASE + 0x200))
#define TIMER_INTENSET (*(volatile uint32_t *)(TIMER1_BASE + 0x304))
#define TIMER_INTENCLR (*(volatile uint32_t *)(TIMER1_BASE + 0x308))
#define TIMER_MODE (*(volatile uint32_t *)(TIMER1_BASE + 0x504))
#define TIMER_BITMODE (*(volatile uint32_t *)(TIMER1_BASE + 0x508))
#define TIMER_PRESCALER (*(volatile uint32_t *)(TIMER1_BASE + 0x510))
#define TIMER_CC0 (*(volatile uint32_t *)(TIMER1_BASE + 0x514))
#define TIMER_CC1 (*(volatile uint32_t *)(TIMER1_BASE + 0x518))
// Clock registers (offsets from CLOCK_BASE)
#define CLOCK_TASKS_HFCLKSTART (*(volatile uint32_t *)(CLOCK_BASE + 0x000))
#define CLOCK_HFCLKSTAT (*(volatile uint32_t *)(CLOCK_BASE + 0x40C))
// NVIC_BASE comes from <core_cm33.h>
#define NVIC_ISER0 (*(volatile uint32_t *)(NVIC_BASE + 0x000)) // Interrupt Set Enable Register 0
#define NVIC_ICER0 (*(volatile uint32_t *)(NVIC_BASE + 0x080)) // Interrupt Clear Enable Register 0
#define NVIC_IPR0 (*(volatile uint8_t *)(NVIC_BASE + 0x400)) // Interrupt Priority Register 0 (byte access)
#define SCB_SCR (*(volatile uint32_t *)(SCB_BASE + 0x010)) // System Control Register
//////// Register test /////// - please run just one at the time
// Interrupt handler for TIMER1
void TIMER1_IRQHandler(void)
{
static bool led_status = false;
NRF_GPIO_OUTSET = (1 << PIN_LED);
// Check if COMPARE0 event caused the interrupt
if (TIMER_EVENTS_COMPARE0)
{
// Clear the event
TIMER_EVENTS_COMPARE0 = 0;
// Toggle the LED
led_status = !led_status;
if (led_status)
{
NRF_GPIO_OUTSET = (1 << PIN_LED);
}
else
{
NRF_GPIO_OUTCLR = (1 << PIN_LED);
}
}
}
void timer_init()
{
// Start high-frequency clock (if not already running)
CLOCK_TASKS_HFCLKSTART = 1;
while (!(CLOCK_HFCLKSTAT & 1))
; // Wait for HFCLK to start
// NRF_GPIO_OUTSET = (1 << PIN_LED); // for testing, can be removed
// Configure Timer
TIMER_TASKS_STOP = 1; // Ensure timer is stopped before configuring
TIMER_TASKS_CLEAR = 1; // Clear timer
TIMER_MODE = 0x00; // Timer mode
TIMER_BITMODE = 0x02; // 32-bit timer
TIMER_PRESCALER = 0x04; // Prescaler. 32MHz / (2^4) = 2MHz timer clock. Each tick is 0.5 us.
TIMER_CC0 = 2000000; // Compare value for 1 second (2MHz * 1 second = 2000000)
// Enable shortcut to clear timer on COMPARE0 event
TIMER_SHORTS = (1 << 0);
// Enable interrupt on COMPARE0 event
TIMER_INTENSET = (1 << 16);
NVIC_IPR0 |= (0x40 << (17 % 8) * 8); // Set priority to 0x40 (adjust as needed)
// Enable TIMER1 interrupt in NVIC
NVIC_ISER0 = (1 << 17); // Enable interrupt number 17 (TIMER1)
TIMER_TASKS_START = 1; // Start timer
}
/////// NRFX Test //////// - please run just one at the time
#define TIMER_INSTANCE_ID 1
static const nrfx_timer_t TIMER_INSTANCE = NRFX_TIMER_INSTANCE(TIMER_INSTANCE_ID);
#define TIMER_IRQ_NUM TIMER1_IRQn // IRQ number for TIMER1
#define TIMER_CC_VALUE (16000000 * 5) // 5 seconds (16 MHz clock)
void timer_event_handler(nrf_timer_event_t event_type, void *context)
{
static bool led_status = false;
led_status = !led_status;
if (led_status)
{
NRF_GPIO_OUTSET = (1 << PIN_LED);
}
else
{
NRF_GPIO_OUTCLR = (1 << PIN_LED);
}
// Reset the timer
nrfx_timer_clear(&TIMER_INSTANCE);
}
void timer_init()
{
// Timer configuration structure
nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG(NRF_TIMER_FREQ_16MHz);
timer_cfg.frequency = NRF_TIMER_FREQ_16MHz;
timer_cfg.mode = NRF_TIMER_MODE_TIMER;
timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
timer_cfg.interrupt_priority = 1;
// Initialize Timer
nrfx_timer_init(&TIMER_INSTANCE, &timer_cfg, timer_event_handler);
// The next 2 lines are led testing - if we reach this point.
// It looks like we stuck here, because the led is not turned on,
// but it doest turn on if i place it before 'nrfx_timer_init' line
NRF_GPIO_OUTSET = (1UL << PIN_LED); // for testing, should be removed
return; // for testing, should be removed
// Set CC0 for 5-second interval
nrfx_timer_extended_compare(&TIMER_INSTANCE,
NRF_TIMER_CC_CHANNEL0,
TIMER_CC_VALUE,
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);
// Enable Timer Interrupt
NVIC_ClearPendingIRQ(TIMER_IRQ_NUM);
NVIC_EnableIRQ(TIMER_IRQ_NUM);
// Start Timer
nrfx_timer_enable(&TIMER_INSTANCE);
}
///// Register function to run before kernel 1 - MCUBOOT ///// - This should run all the time
static int gpio_early_read_write(void)
{
// Set LED gpio as output
NRF_GPIO_DIRSET = (1UL << PIN_LED);
NRF_GPIO_OUTCLR = (1UL << PIN_LED);
// LED Test to see if the system powered
NRF_GPIO_OUTSET = (1UL << PIN_LED);
for (volatile int i = 0; i < 1000000; i++){};
NRF_GPIO_OUTCLR = (1UL << PIN_LED);
timer_init();
// Wait for timer
for (;;){};
return 0;
}
SYS_INIT(gpio_early_read_write, PRE_KERNEL_1, 0);