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.

Related