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

Power Profiling Kit RTT Documentation?

We would like to use the PPK for automating some of our tests. Is there any documentation on how to drive the PPK over RTT? If push really comes to shove, we can always read what's in ppk.py, but it would be easier if there's some semi-official documentation.

I know that Nordic is planning on releasing a Python API for the PPK, but we'd like to keep all of our in-house software in C#. We already have a C# wrapper around the jlink dll and would like to use that.

I think I'll keep a running log of what all I've done here.

Initialization

First thing is you have to connect to the PPK with nrfjprog and start RTT. It's pretty straight forward. I'm using the C interface which can be found at (on Windows) Program Files (x86)\Nordic Semiconductor\nrf5x\bin\headers\nrf52_nrfjprogdll.h

  1. Open the nrfjprog library dll by calling NRFJPROG_open_dll
  2. Connect to the Jprog emulator / debugger. NRFJPROG_connect_to_emu_with_snr or NRFJPROG_connect_to_emu_without_snr
  3. Perform a system reset. NRFJPROG_sys_reset
  4. Call NRFJPROG_go
  5. Start the RTT Service. NRFJPROG_rtt_start
  6. Wait 1 second. This is a critical step and things don't work unless you do so.

At this point, you're ready to do your first read. Read 200 characters over RTT by calling NRFJPROG_rtt_read. All reads and writes over RTT use channel 0. Assuming everything worked correctly, the PPK should return something along these lines:

CALIBRATED R1:510.000 R2:28.000 R3:1.800 Board ID 21F3171D

USER SET R1:510.000 R2:28.000 R3:1.800
Refs VDD: 3092 HI: 4941 LO: 27195

I'm trying to work through the startup sequence in the Python code after the calibration values are read back. pms_plotter.start() in ppk.py uses the values read above to setup the windows and controls. From there, it issues the RTT_CMD_TRIGGER_SET, RTT_CMD_RUN, and RTT_CMD_AVG_NUM_SET`. I haven't traced the code further.

I did insert a log-to-file in the rtt.write_stuffed function to log the raw bytes sent to the PPK. Here's what the complete startup command sequence looks like:

02 1f 23 1f 22 00 03 //RTT_CMD_TRIG_WINDOW_SET with value 0x02000
02 01 1f 22 00 03     //RTT_CMD_TRIGGER_SET with value 0x02000
02 06 03                  //RTT_CMD_RUN
02 1f 22 00 01 03     //RTT_CMD_AVG_NUM_SET with value 0x0001
02 0c 00 03              //RTT_CMD_DUT with value 0x00.  I think it disconnects the inputs for calibration?
02 0c 01 03              //RTT_CMD_DUT with value 0x01.  I think it reconnects the inputs after calibration?

The first 5 commands (RTT_CMD_TRIG_WINDOW_SET, RTT_CMD_TRIGGER_SET, RTT_CMD_RUN, RTT_CMD_AVG_NUM_SET, and RTT_CMD_DUT with value 0x00) are sent in rapid succession, one after another. The code waits for 1 second to gather data points with the input disconnected to generate an offset / calibration value. The second RTT_CMD_DUT re-enables the inputs so the PPK can start.

It looks like the PPK is sending back current samples as floats with uA units. Here's the code from the PPK that parses the bytes coming back.

         data = self.nrfjprog.rtt_read(0,10000, encoding=None)
                if data != '':
                    for byte in data:
                        n = byte
                        if self.read_mode == MODE_IDLE:
                            ''' Mode Idle - Not Receiving '''
                            if n == STX:
                                self.read_mode = MODE_RECV

                        elif self.read_mode == MODE_RECV:
                            ''' Mode Receiving - Receiving data '''
                            if n == ESC:
                                self.read_mode = MODE_ESC_RECV
                            elif n == ETX:
                                self.callback(data_buffer)
                                data_buffer[:] = []
                                self.read_mode = MODE_IDLE
                            elif n == STX:
                                data_buffer[:] = []
                            else:
                                data_buffer.append(n)

                        elif self.read_mode == MODE_ESC_RECV:
                            ''' Mode Escape Received - Convert next byte '''
                            data_buffer.append(n ^ 0x20)
                            self.read_mode = MODE_RECV

You'll typically get 4 bytes when self.callback() is called. Those 4 bytes are a float in what I think is little endian format. I'm taking an educated guess that each sample covers 15us, because the documentation says the resolution goes down to 15us.

You'll also see really long byte arrays getting passed to the callback. Those are most likely values for the trigger window. I haven't looked at the trigger-related stuff closely. Sending the RTT_CMD_TRIG_STOP command completely stops those long arrays from showing up.

Parents
  • No, there are no RTT documentation for the PPK interface. It is quite easy to disect the python file, and look at (for instance) the RTT commands:

        RTT_CMD_TRIGGER_SET         = 0x01 
        RTT_CMD_AVG_NUM_SET         = 0x02 
        RTT_CMD_TRIG_WINDOW_SET     = 0x03 
        RTT_CMD_TRIG_INTERVAL_SET   = 0x04
        RTT_CMD_SINGLE_TRIG         = 0x05
        RTT_CMD_RUN                 = 0x06
        RTT_CMD_STOP                = 0x07
        RTT_CMD_RANGE_SET           = 0x08
        RTT_CMD_LCD_SET             = 0x09
        RTT_CMD_TRIG_STOP           = 0x0A
        RTT_CMD_CALIBRATE_OFFSET    = 0x0B
        RTT_CMD_DUT                 = 0x0C
        RTT_CMD_SETVDD              = 0x0D
    

    and how to send them (rtt.py).

    rtt.write([RTT_COMMANDS.RTT_CMD_RUN])
    
    
       def write(self, cmd):
            try:
                s = []
                s.append(STX)
                for byte in cmd:
                    if byte == STX or byte == ETX or byte == ESC:
                        s.append(ESC)
                        s.append(byte ^ 0x20)
                    else:
                        s.append(byte)
                s.append(ETX)
                try:
                    self.nrfjprog.rtt_write(0, s, encoding=None)
                    self.nrfjprog.write_u32(NRF_EGU0_BASE + TASKS_TRIGGER0_OFFSET, 0x00000001, 0)
                    self.nrfjprog.go()
                except Exception as e:
                    raise
    

    Hopefully there will be an API coming soon. If there are any specifics you wonder about, please ask, and I will do my best to answer them.

  • I got it from here: www.nordicsemi.com/.../33444

    I fixed it by bumping up the wait after calling NRFJPROG_rtt_start to 1.5 seconds. I think the 1 second timeout was right on the hairy edge.

Reply Children
No Data
Related