This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

NRF52840 Timer Registers

Hi all,

I am working on a project with an Adafruit ItsyBitsy that uses and nRF52840 chip, and I am having a bit of trouble with getting the timers to work accurately. Basically what I want to do is to have a task completed once per second, and I was planning on using a software timer to do so. I have attached an image of the sort of sawtooth wave that I want to create:

I have been reading through the data sheet of the NRF52840 and some other examples that people have done. It seems like this should be perfectly doable with Timer2. However, when I try to execute it in code I am getting a strange output. I have attached my code, here, as well (sorry it is in Arduino's IDE, but mostly I am manually setting registers in a very basic C++ kind of way).

#include <bluefruit.h>

/* NRF Timer Demo
 *  Loops the timer at 1 second
 *  Prints a value every 60 seconds
 * 
 */

// Defining Timer2 Registers
static const unsigned int Timer_2_base = 0x4000A000;
enum Registers
{
  TASKS_START = 0x000, // Start Timer
  TASKS_STOP = 0x004, // Stop Timer
  TASKS_COUNT = 0x008, // Increment Timer (Counter mode only)
  TASKS_CLEAR = 0x00C, // Clear time
  TASKS_SHUTDOWN = 0x010, // Shut down timerDeprecated
  TASKS_CAPTURE_0 = 0x040, // Capture Timer value to CC[0] register
  TASKS_CAPTURE_1 = 0x044, // Capture Timer value to CC[1] register
  TASKS_CAPTURE_2 = 0x048, // Capture Timer value to CC[2] register
  TASKS_CAPTURE_3 = 0x04C, // Capture Timer value to CC[3] register
  TASKS_CAPTURE_4 = 0x050, // Capture Timer value to CC[4] register
  TASKS_CAPTURE_5 = 0x054, // Capture Timer value to CC[5] register
  EVENTS_COMPARE_0 = 0x140, // Compare event on CC[0] match
  EVENTS_COMPARE_1 = 0x144, // Compare event on CC[1] match
  EVENTS_COMPARE_2 = 0x148, // Compare event on CC[2] match
  EVENTS_COMPARE_3 = 0x14C, // Compare event on CC[3] match
  EVENTS_COMPARE_4 = 0x150, // Compare event on CC[4] match 
  EVENTS_COMPARE_5 = 0x154, // Compare event on CC[5] match 
  SHORTS = 0x200, // Shortcuts between local events and tasks
  INTENSET = 0x304, // Enable interrupt
  INTENCLR = 0x308, // Disable interrupt
  MODE = 0x504, // Timer mode selection
  BITMODE = 0x508, // Configure the number of bits used by the TIMER
  PRESCALER = 0x510, // Timer prescaler register
  CC_0 = 0x540, // Capture/Compare register 0
  CC_1 = 0x544, // Capture/Compare register 1
  CC_2 = 0x548, // Capture/Compare register 2
  CC_3 = 0x54C, // Capture/Compare register 3
  CC_4 = 0x550, // Capture/Compare register 4
  CC_5 = 0x554 // Capture/Compare register 5
};

// Inline Register functions
inline volatile uint8_t *regAddress (Registers reg){
  return reinterpret_cast<volatile 
        uint8_t*>(Timer_2_base + reg);
}
inline uint8_t regRead(Registers reg){
  return *regAddress(reg);
}
inline void regWrite(Registers reg, uint8_t value){
  *regAddress(reg) = value;
}

// Parameters
const int ledPin =  7;
const uint16_t max_value = 31250;
const uint32_t interupt_activator = 0x0001000;
volatile bool ledState = LOW; 
int samples_taken = 0;
int passes_completed = 0;

void setup() {
  Serial.begin(9600);
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, ledState);

  regWrite(TASKS_STOP, 1);    // Stop TIMER2
  regWrite(MODE, 0);          // Set the timer in Counter Mode
  regWrite(TASKS_CLEAR, 1);   // clear the task first to be usable for later
  regWrite(BITMODE, 0);       // Set counter to 32 bit resolution
  regWrite(PRESCALER, 9);     // Set prescaler (2^n). Prescaller of 9 gives 512
  regWrite(CC_0, max_value);      // set the maximum timer value (31250 = 1 second)
  regWrite(INTENSET, interupt_activator);      // Enabling the interupt for maximum value being reached

  regWrite(TASKS_START, 1);   // Start TIMER2
  Serial.println("Timer configured");
}

void loop(){
  if(regRead(EVENTS_COMPARE_0) == 1){
    regWrite(TASKS_CLEAR, 1);       // Resetting the timer to 0
    regWrite(EVENTS_COMPARE_0, 0);    // Clearing the events compare bit
    ledState = !ledState;          //Set LED
    samples_taken += 1;
  }

  if(samples_taken > 0){
    samples_taken = 0;
    passes_completed += 1;
    Serial.print(passes_completed);
    Serial.print(", ");
    Serial.println(millis());
  }
  digitalWrite(ledPin, ledState);
}

Based on my understanding of the system, this should create a 1 second looping timer, but it does not. Instead, it creates an ~147 ms looping timer. My next step was to try changing the maximum value, but the time duration seems to be random. For example around 5,800 gives 0.8 seconds, and around 63,000 gives 1.5 seconds. Am I misinterpreting what the CC[0] register means? Also, in the data sheet, it says that the INTENSET enables an interrupt. Does anyone know the pointer for that? I have tried setting up a proper ISR, but it doesn't seem to do anything. I may be running into issues with the Arduino IDE mask. Any help, here would be much appreciated! Thank you!!

  • Hi,

    inline volatile uint8_t *regAddress (Registers reg){
      return reinterpret_cast<volatile 
            uint8_t*>(Timer_2_base + reg);
    }
    inline uint8_t regRead(Registers reg){
      return *regAddress(reg);
    }
    

    here you're working with registers as with 8-bit ones, but they're 32-bit wide.

  • Hello,

    I would say as @dmitry already have mentioned: please keep in mind that the registers are 32 bit-wide.
    In the configuration registers you functions will likely work as intended, since only a couple of bits is used, but when you get to the CC registers it will fail.

    For the record, in your configuration you are setting BITMODE to 0, which configures 16 bit resolution, while your comment states you are attempting to set 32 bit resolution - so this is either an oversight, an error or an outdated comment.

    Please let me know if accounting for the registers being 32 bit resolves your issue with the unexpected values and behavior.

    Best regards,
    Karl

Related