From e39133daf511c3eabb04a6e41567cd54cfafa822 Mon Sep 17 00:00:00 2001
From: MCUdude <hansibull@gmail.com>
Date: Wed, 28 Sep 2022 21:59:50 +0200
Subject: [PATCH] Initial support for Power Debugger analog reading Voltage and
 current though channel A and B

---
 src/jtag3.c         | 102 +++++++++++++++++++++++++++++++++++++++-----
 src/jtag3.h         |   2 +
 src/jtag3_private.h |  28 +++++++-----
 src/stk500v2.c      |  10 ++---
 src/term.c          |   2 +-
 5 files changed, 117 insertions(+), 27 deletions(-)

diff --git a/src/jtag3.c b/src/jtag3.c
index 04bd1e83..175e0e47 100644
--- a/src/jtag3.c
+++ b/src/jtag3.c
@@ -103,7 +103,7 @@ static int jtag3_read_byte(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM
 static int jtag3_write_byte(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
                                 unsigned long addr, unsigned char data);
 static int jtag3_set_sck_period(const PROGRAMMER *pgm, double v);
-static void jtag3_print_parms1(const PROGRAMMER *pgm, const char *p);
+void jtag3_print_parms1(const PROGRAMMER *pgm, const char *p);
 static int jtag3_paged_write(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m,
                                 unsigned int page_size,
                                 unsigned int addr, unsigned int n_bytes);
@@ -2339,7 +2339,7 @@ int jtag3_read_sib(const PROGRAMMER *pgm, const AVRPART *p, char *sib) {
   return 0;
 }
 
-static int jtag3_set_vtarget(const PROGRAMMER *pgm, double v) {
+int jtag3_set_vtarget(const PROGRAMMER *pgm, double v) {
   unsigned uaref, utarg;
   unsigned char buf[2];
 
@@ -2407,15 +2407,89 @@ static void jtag3_display(const PROGRAMMER *pgm, const char *p) {
 }
 
 
-static void jtag3_print_parms1(const PROGRAMMER *pgm, const char *p) {
-  unsigned char buf[2];
+void jtag3_print_parms1(const PROGRAMMER *pgm, const char *p) {
+  unsigned char buf[3];
 
   if (jtag3_getparm(pgm, SCOPE_GENERAL, 1, PARM3_VTARGET, buf, 2) < 0)
     return;
-
   avrdude_message(MSG_INFO, "%sVtarget         %s: %.2f V\n", p,
     verbose ? "" : "             ", b2_to_u16(buf) / 1000.0);
 
+  // Print features unique to the Power Debugger
+  //if (strncmp("powerdebugger", ldata(lfirst(pgm->id)), strlen("powerdebugger")) == 0)
+  if (*(int *)(ldata(lfirst(pgm->usbpid))) == 0x2144){
+    short analog_raw_data;
+
+    // Read generator set voltage value (VOUT)
+    if (jtag3_getparm(pgm, SCOPE_GENERAL, 1, PARM3_VADJUST, buf, 2) < 0)
+      return;
+    analog_raw_data = b2_to_u16(buf);
+    avrdude_message(MSG_INFO, "%sVout set        %s: %.2f V\n", p,
+      verbose ? "" : "             ", analog_raw_data / 1000.0);
+
+    // Read measured generator voltage value (VOUT)
+    if (jtag3_getparm(pgm, SCOPE_GENERAL, 1, PARM3_TSUP_VOLTAGE_MEAS, buf, 2) < 0)
+      return;
+    analog_raw_data = ((buf[0] & 0x0F) << 8) + buf[1];
+    if ((buf[0] & 0xF0) != 0x30)
+      avrdude_message(MSG_INFO, "%s: jtag3_print_parms1(): invalid PARM3_TSUP_VOLTAGE_MEAS data packet format\n", progname);
+    else {
+      if (analog_raw_data & 0x0800)
+        analog_raw_data |= 0xF000;
+      avrdude_message(MSG_INFO, "%sVout measured   %s: %.02f V\n", p,
+        verbose ? "" : "             ", ((float)analog_raw_data / -200.0));
+    }
+
+    // Read channel A voltage
+    if (jtag3_getparm(pgm, SCOPE_GENERAL, 1, PARM3_ANALOG_A_VOLTAGE, buf, 2) < 0)
+      return;
+    analog_raw_data = ((buf[0] & 0x0F) << 8) + buf[1];
+    if ((buf[0] & 0xF0) != 0x20)
+      avrdude_message(MSG_INFO, "%s: jtag3_print_parms1(): invalid PARM3_ANALOG_A_VOLTAGE data packet format\n", progname);
+    else {
+      if (analog_raw_data & 0x0800)
+        analog_raw_data |= 0xF000;
+      avrdude_message(MSG_INFO, "%sCh A voltage    %s: %.03f V\n", p,
+        verbose ? "" : "             ", ((float)analog_raw_data / -200.0));
+    }
+
+    // Read channel A current
+    if (jtag3_getparm(pgm, SCOPE_GENERAL, 1, PARM3_ANALOG_A_CURRENT, buf, 3) < 0)
+      return;
+    analog_raw_data = (buf[1] << 8) + buf[2];
+    if (buf[0] != 0x90)
+      avrdude_message(MSG_INFO, "%s: jtag3_print_parms1(): invalid PARM3_ANALOG_A_CURRENT data packet format\n", progname);
+    else
+      avrdude_message(MSG_INFO, "%sCh A current    %s: %.3f mA\n", p,
+        verbose ? "" : "             ", ((float)analog_raw_data * 0.003472));
+
+    // Read channel B voltage
+    if (jtag3_getparm(pgm, SCOPE_GENERAL, 1, PARM3_ANALOG_B_VOLTAGE, buf, 2) < 0)
+      return;
+    analog_raw_data = ((buf[0] & 0x0F) << 8) + buf[1];
+    if ((buf[0] & 0xF0) != 0x10)
+      avrdude_message(MSG_INFO, "%s: jtag3_print_parms1(): invalid PARM3_ANALOG_B_VOLTAGE data packet format\n", progname);
+    else {
+      if (analog_raw_data & 0x0800)
+        analog_raw_data |= 0xF000;
+      avrdude_message(MSG_INFO, "%sCh B voltage    %s: %.03f V\n", p,
+        verbose ? "" : "             ", ((float)analog_raw_data / -200.0));
+    }
+
+    // Read channel B current
+    if (jtag3_getparm(pgm, SCOPE_GENERAL, 1, PARM3_ANALOG_B_CURRENT, buf, 3) < 0)
+      return;
+    analog_raw_data = ((buf[0] & 0x0F) << 8) + buf[1];
+    if ((buf[0] & 0xF0) != 0x00)
+      avrdude_message(MSG_INFO, "%s: jtag3_print_parms1(): invalid PARM3_ANALOG_B_CURRENT data packet format\n", progname);
+    else {
+      if (analog_raw_data & 0x0800)
+        analog_raw_data |= 0xF000;
+      avrdude_message(MSG_INFO, "%sCh B current    %s: %.3f mA\n", p,
+        verbose ? "" : "             ", ((float)analog_raw_data * 0.555556));
+    }
+  }
+
   if (jtag3_getparm(pgm, SCOPE_AVR, 1, PARM3_CLK_MEGA_PROG, buf, 2) < 0)
     return;
 
@@ -2444,7 +2518,7 @@ static void jtag3_print_parms1(const PROGRAMMER *pgm, const char *p) {
     return;
 
   if (b2_to_u16(buf) > 0) {
-    avrdude_message(MSG_INFO, "%sPDI/UPDI clock Xmega/megaAVR : %u kHz\n\n", p,
+    avrdude_message(MSG_INFO, "%sPDI/UPDI clock Xmega/megaAVR : %u kHz\n", p,
       b2_to_u16(buf));
   }
 }
@@ -2525,6 +2599,9 @@ void jtag3_initpgm(PROGRAMMER *pgm) {
   pgm->teardown       = jtag3_teardown;
   pgm->page_size      = 256;
   pgm->flag           = PGM_FL_IS_JTAG;
+
+  if (matches(ldata(lfirst(pgm->id)), "powerdebugger"))
+    pgm->set_vtarget  = jtag3_set_vtarget;
 }
 
 const char jtag3_dw_desc[] = "Atmel JTAGICE3 in debugWire mode";
@@ -2556,6 +2633,9 @@ void jtag3_dw_initpgm(PROGRAMMER *pgm) {
   pgm->teardown       = jtag3_teardown;
   pgm->page_size      = 256;
   pgm->flag           = PGM_FL_IS_DW;
+
+  if (matches(ldata(lfirst(pgm->id)), "powerdebugger_dw"))
+    pgm->set_vtarget  = jtag3_set_vtarget;
 }
 
 const char jtag3_pdi_desc[] = "Atmel JTAGICE3 in PDI mode";
@@ -2589,6 +2669,9 @@ void jtag3_pdi_initpgm(PROGRAMMER *pgm) {
   pgm->teardown       = jtag3_teardown;
   pgm->page_size      = 256;
   pgm->flag           = PGM_FL_IS_PDI;
+
+  if (matches(ldata(lfirst(pgm->id)), "powerdebugger_pdi"))
+    pgm->set_vtarget  = jtag3_set_vtarget;
 }
 
 const char jtag3_updi_desc[] = "Atmel JTAGICE3 in UPDI mode";
@@ -2626,11 +2709,8 @@ void jtag3_updi_initpgm(PROGRAMMER *pgm) {
   pgm->unlock         = jtag3_unlock_erase_key;
   pgm->read_sib       = jtag3_read_sib;
 
-  /*
-   * enable target voltage adjustment for PKOB/nEDBG boards
-   */
-  if (matches(ldata(lfirst(pgm->id)), "pkobn_updi")) {
+  if (matches(ldata(lfirst(pgm->id)), "pkobn_updi") ||
+      matches(ldata(lfirst(pgm->id)), "powerdebugger_updi"))
     pgm->set_vtarget  = jtag3_set_vtarget;
-  }
 }
 
diff --git a/src/jtag3.h b/src/jtag3.h
index c871c019..945ddcb2 100644
--- a/src/jtag3.h
+++ b/src/jtag3.h
@@ -38,6 +38,8 @@ int jtag3_setparm(const PROGRAMMER *pgm, unsigned char scope,
 		  unsigned char *value, unsigned char length);
 int jtag3_command(const PROGRAMMER *pgm, unsigned char *cmd, unsigned int cmdlen,
 		  unsigned char **resp, const char *descr);
+void jtag3_print_parms1(const PROGRAMMER *pgm, const char *p);
+int jtag3_set_vtarget(const PROGRAMMER *pgm, double voltage);
 extern const char jtag3_desc[];
 extern const char jtag3_dw_desc[];
 extern const char jtag3_pdi_desc[];
diff --git a/src/jtag3_private.h b/src/jtag3_private.h
index ddc7e2e3..2e414c71 100644
--- a/src/jtag3_private.h
+++ b/src/jtag3_private.h
@@ -184,16 +184,24 @@
  * precedes each parameter address.  There are distinct parameter
  * sets for generic and AVR scope.
  */
-#define PARM3_HW_VER      0x00  /* section 0, generic scope, 1 byte */
-#define PARM3_FW_MAJOR    0x01  /* section 0, generic scope, 1 byte */
-#define PARM3_FW_MINOR    0x02  /* section 0, generic scope, 1 byte */
-#define PARM3_FW_RELEASE  0x03  /* section 0, generic scope, 1 byte;
-                                 * always asked for by Atmel Studio,
-                                 * but never displayed there */
-#define PARM3_VTARGET     0x00  /* section 1, generic scope, 2 bytes, in millivolts */
-#define PARM3_VBUF        0x01  /* section 1, generic scope, 2 bytes, bufferred target voltage reference */
-#define PARM3_VUSB        0x02  /* section 1, generic scope, 2 bytes, USB voltage */
-#define PARM3_VADJUST     0x20  /* section 1, generic scope, 2 bytes, set voltage */
+#define PARM3_HW_VER            0x00  /* section 0, generic scope, 1 byte */
+#define PARM3_FW_MAJOR          0x01  /* section 0, generic scope, 1 byte */
+#define PARM3_FW_MINOR          0x02  /* section 0, generic scope, 1 byte */
+#define PARM3_FW_RELEASE        0x03  /* section 0, generic scope, 1 byte;
+                                       * always asked for by Atmel Studio,
+                                       * but never displayed there */
+
+#define PARM3_VTARGET           0x00  /* section 1, generic scope, 2 bytes, in millivolts */
+#define PARM3_VBUF              0x01  /* section 1, generic scope, 2 bytes, bufferred target voltage reference */
+#define PARM3_VUSB              0x02  /* section 1, generic scope, 2 bytes, USB voltage */
+#define PARM3_ANALOG_A_CURRENT  0x10  /* section 1, generic scope, 2 bytes, Ch A current in milliamps,  Powerdebugger only */
+#define PARM3_ANALOG_A_VOLTAGE  0x11  /* section 1, generic scope, 2 bytes, Ch A voltage in millivolts, Powerdebugger only */
+#define PARM3_ANALOG_B_CURRENT  0x12  /* section 1, generic scope, 2 bytes, Ch B current in milliamps,  Powerdebugger only */
+#define PARM3_ANALOG_B_VOLTAGE  0x13  /* section 1, generic scope, 2 bytes, Ch V voltage in millivolts, Powerdebugger only */
+#define PARM3_TSUP_VOLTAGE_MEAS 0x14  /* section 1, generic scope, 2 bytes, target voltage measurement in millivolts */
+#define PARM3_USB_VOLTAGE_MEAS  0x15  /* section 1, generic scope, 2 bytes, USB voltage measurement in millivolts */
+#define PARM3_VADJUST           0x20  /* section 1, generic scope, 2 bytes, set voltage in millivolts */
+#define PARM3_ANALOG_STATUS     0x30  /* section 1, generic scope, 2 bytes, analog status */
 
 #define PARM3_DEVICEDESC  0x00  /* section 2, memory etc. configuration,
                                  * 31 bytes for tiny/mega AVR, 47 bytes
diff --git a/src/stk500v2.c b/src/stk500v2.c
index a7477030..09f4a4ca 100644
--- a/src/stk500v2.c
+++ b/src/stk500v2.c
@@ -3226,11 +3226,8 @@ static void stk500v2_print_parms1(const PROGRAMMER *pgm, const char *p) {
   } else if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE3) {
     PROGRAMMER *pgmcp = pgm_dup(pgm);
     pgmcp->cookie = PDATA(pgm)->chained_pdata;
-    jtag3_getparm(pgmcp, SCOPE_GENERAL, 1, PARM3_VTARGET, vtarget_jtag, 2);
+    jtag3_print_parms1(pgmcp, p);
     pgm_free(pgmcp);
-    avrdude_message(MSG_INFO, "%sVtarget         : %.1f V\n", p,
-	    b2_to_u16(vtarget_jtag) / 1000.0);
-
   } else {
     stk500v2_getparm(pgm, PARAM_VTARGET, &vtarget);
     avrdude_message(MSG_INFO, "%sVtarget         : %.1f V\n", p, vtarget / 10.0);
@@ -3282,7 +3279,7 @@ static void stk500v2_print_parms1(const PROGRAMMER *pgm, const char *p) {
       if (stk500v2_jtag3_send(pgm, cmd, 1) >= 0 &&
 	  stk500v2_jtag3_recv(pgm, cmd, 4) >= 2) {
 	unsigned int sck = cmd[1] | (cmd[2] << 8);
-	avrdude_message(MSG_INFO, "%sSCK period      : %.2f us\n", p,
+	avrdude_message(MSG_INFO, "%sSCK period                   : %.2f us\n", p,
 		(float)(1E6 / (1000.0 * sck)));
       }
     }
@@ -4681,4 +4678,7 @@ void stk500v2_jtag3_initpgm(PROGRAMMER *pgm) {
   pgm->setup          = stk500v2_jtag3_setup;
   pgm->teardown       = stk500v2_jtag3_teardown;
   pgm->page_size      = 256;
+
+  if (strcmp(ldata(lfirst(pgm->id)), "powerdebugger_isp") == 0)
+    pgm->set_vtarget  = jtag3_set_vtarget;
 }
diff --git a/src/term.c b/src/term.c
index 512e5a3c..46e8d52a 100644
--- a/src/term.c
+++ b/src/term.c
@@ -854,7 +854,7 @@ static int cmd_parms(PROGRAMMER * pgm, struct avrpart * p,
     return -1;
   }
   pgm->print_parms(pgm);
-
+  terminal_message(MSG_INFO, "\n");
   return 0;
 }