data_sheet_to_csv.py - GUI to assist in translating Microchip data sheets to pin mappings

This program accepts text copied and pasted from a Microchip data sheet (in PDF format) of the pinout for a given package, then translates it into an Rxy/RPy/ANn/CNm mapping.

To build as an executable:

pyinstaller data_sheet_to_csv.py -y
 
import re
import sys
import csv
import os
 

The excellent PyQt5 library provides the GUI for this package.

from PyQt5 import QtWidgets, QtGui, QtCore, uic

from compact_csv import enumeratePic24Ports


def text_pinout_to_mapping(text):
    pins = text.split()
    RPy = {}
    ANn = {}
    CNm = {}
    last_failure = ''
    last_pin = ''
    for pin in pins:

Look for Rxy. If not found, we can’t do anything with this pin.

        mo = re.search('R([A-K]\d\d?)', pin)
        if mo:
            Rxy = mo.group(1)
        else:

No Rxy pin here, so we can’t map. Do error checking anyway.

            Rxy = ''
 

Look for RPy.

        mo = re.search('RPI?(\d\d?\d?)', pin)
        if mo:
            if not Rxy:
                last_failure = 'Warning: found RPy without finding Rxy in ' + pin
                last_pin = pin
            else:
                RPy[Rxy] = mo.group(1)
 

Look for ANn.

        mo = re.search('AN(\d\d?)', pin)
        if mo:
            if not Rxy:
                last_failure = 'Warning: found ANn without finding Rxy in ' + pin
                last_pin = pin
            else:
                ANn[Rxy] = mo.group(1)
 

Look for CNm.

        mo = re.search('CN(\d\d?)', pin)
        if mo:
            if not Rxy:
                last_failure = 'Warning: found CNm without finding Rxy in ' + pin
                last_pin = pin
            else:
                CNm[Rxy] = mo.group(1)

    return RPy, ANn, CNm, last_failure, last_pin

form_class, base_class = uic.loadUiType('data_sheet_to_csv.ui')

class main_dialog(QtWidgets.QMainWindow, form_class):
    def __init__(self):

Let Qt and PyQt run their init first.

        super().__init__()
        self.setupUi(self)
 

Store portlist, which is used several times.

        self.portlist = enumeratePic24Ports()
        self.portlist.insert(0, 'Device port / pin')

    def parse_gui_text(self):

Read the GUI text fields and parse out pin mapping and procesor name.

        pinout_text = self.pinout_text_edit.toPlainText()
        RPy, ANn, CNm, last_failure, last_pin = text_pinout_to_mapping(pinout_text)
        self.statusBar().showMessage(last_failure)
        if last_pin:

Select failing text, assuming it was unique

            cursor = self.pinout_text_edit.textCursor()
            i = pinout_text.index(last_pin)
            cursor.setPosition(i)
            cursor.setPosition(i + len(last_pin), QtGui.QTextCursor.KeepAnchor)
            self.pinout_text_edit.setTextCursor(cursor)
        processor_names = ' '.join([s for s in self.processors_text_edit.toPlainText().split() if 'PIC' in s])
        return processor_names, RPy, ANn, CNm

    def on_pinout_text_edit_textChanged(self):
        self.update_table()

    def on_processors_text_edit_textChanged(self):
        self.update_table()
 

A helper to create an uneditable QTableWidgetItem

    def QTableWidgetItemUneditable(self, o):
        qtwi = QtWidgets.QTableWidgetItem(o)
        qtwi.setFlags(qtwi.flags() & ~QtCore.Qt.ItemIsEditable)
        return qtwi
 

Display results for the user.

    def update_table(self):
        processor_names, RPy, ANn, CNm = self.parse_gui_text()
        self.results_label.setText('Results: ' + processor_names)
        self.results_table_widget.clearContents()
        self.results_table_widget.setRowCount(128)
        row = 0
        for Rxy in self.portlist[1:]:
            if (Rxy in RPy) or (Rxy in ANn) or (Rxy in CNm):
                self.results_table_widget.setItem(row, 0, self.QTableWidgetItemUneditable(RPy.get(Rxy, '')))

                self.results_table_widget.setItem(row, 1, self.QTableWidgetItemUneditable(ANn.get(Rxy, '')))

                self.results_table_widget.setItem(row, 2, self.QTableWidgetItemUneditable(CNm.get(Rxy, '')))

                self.results_table_widget.setVerticalHeaderItem(row, QtWidgets.QTableWidgetItem(Rxy))

                row += 1

        self.results_table_widget.setRowCount(row)
 

This is called when OK is clicked. Append a CSV entry.

    def on_buttonBox_accepted(self):
        processor_names, RPy, ANn, CNm = self.parse_gui_text()
 

Prepare for appending to the CSV.

        with open('pic24_devices.csv', 'a+', newline='', encoding='utf-8') as outFile:
            csv_dict_writer = csv.DictWriter(outFile, self.portlist)

Write the header only if the file is empty. (Leaving out the seek always reports 0 for the tell).

            outFile.seek(0, os.SEEK_END)
            if outFile.tell() == 0:
                csv_dict_writer.writeheader()
 

Write it to the CSV.

            RPy['Device port / pin'] = processor_names + ' RPy'
            ANn['Device port / pin'] = processor_names + ' ANn'
            CNm['Device port / pin'] = processor_names + ' CNm'
            csv_dict_writer.writerow(RPy)
            csv_dict_writer.writerow(ANn)
            csv_dict_writer.writerow(CNm)
 

Display success, set up for next run.

        self.statusBar().showMessage('CSV updated.', 3000)
        self.processors_text_edit.clear()
        self.pinout_text_edit.clear()
        self.results_label.setText('Results')
 

This routine runs the CodeChat GUI.

def main():

Instantiate the app and GUI.

    app = QtWidgets.QApplication(sys.argv)
    window = main_dialog()

Run the program.

    window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()