Charlieplexing and examples

I came across Dave’s 2019 KiCon talk and it seemed interesting. I want to set up a charlieplexed circuit with approximately 50 button inputs connected to an Arduino, and thinking about it, SKiDL looks like it was made for this kind of circuit. Lots of repeated logic, parts and connections in a circuit.

As an added requirement I’d want to make some of these inputs optional as not all 50 buttons will be needed, but it’d be nice to be able to optionally use it. I’d only solder the needed diodes. So diode naming should include the expansion. Each additional pin allows for a network effect, essentially n(n − 1):

  • 2 = 2
  • 3 = 6
  • 4 = 12
  • 5 = 20
  • 6 = 30
  • 7 = 42
  • 8 = 56

So I guess prefixing the diode name with the number of pins would help. D3-1 through D3-6, instead of D3 through D9.

I’m sure I could figure it out. Was looking for a library of example circuits so I can see how the code works as a system, instead of just individual features. Are there examples like that available?

My apologies for the late reply. I was so shocked that anyone had posted to this forum that I lost consciousness.

I have a “complete” example of a microcontroller with charlieplexed LEDs and pushbuttons at the end of this post. It doesn’t have enough comments, but ask me questions and I’ll explain what’s going on if it isn’t clear. (Note that I didn’t build or simulate this circuit. I was just getting a sense of how complicated the code would be.)

You point out another issue that needs addressing: examples of complete designs. I have a list of example designs that I’ve been trying to create. These are mainly subsystems like power supplies, ADC/DAC interfaces, USB interfaces, etc. that can be assembled into larger systems. Unfortunately, this has remained an unrealized goal as I keep going off on other things. I’ll probably publish my list in another post and see if anyone has ideas for moving this forward.

Another useful resource would be a page of complete SKiDL designs done by others, similar to this page for MyHDL projects. Unfortunately, I can’t make a page like that without designs from other users and these have been hard to find. Probably a page of completed designs would help to generate more SKiDL projects from others, but that’s a chicken-and-egg problem.

Any way, here’s the promised complete charlieplexing example. (It was done a while ago, so I’d probably do some things differently today.)

from skidl import *

@subcircuit
def chplx(b, diode_t, two_pin_t=None):
    for hi in b:
        for lo in b:
            if hi != lo:
                diode = diode_t()
                hi += diode['A']
                if two_pin_t != None:
                    two_pin = two_pin_t()
                    two_pin[:] += diode['K'], lo
                else:
                    lo += diode['K']


@subcircuit
def chplx_leds(b, 
        led_t=Part('device', 'LED', TEMPLATE, footprint="KiCad_V5/LED_SMD.pretty:LED_0805_2012Metric_Castellated")):

    chplx(b,led_t)


@subcircuit
def chplx_switches(b,
        diode_t=Part('device','D',TEMPLATE,footprint="KiCad_V5/Diode_SMD.pretty:D_0805_2012Metric"),
        switch_t=Part('Switch', 'SW_SPST', TEMPLATE,footprint="KiCad_V5/Button_Switch_SMD.pretty:SW_SPST_CK_RS282G05A3")):

    chplx(b, diode_t, switch_t)


vdd, gnd = Net('VDD'), Net('GND') # power & ground nets.
vdd.drive = POWER
gnd.drive = POWER

c = Part('Device','C', TEMPLATE, footprint="KiCad_V5/Capacitor_SMD.pretty:C_0805_2012Metric")  # capacitor template.

uc = Part('MCU_Microchip_PIC16',
          'PIC16F83-XXSO', footprint="KiCad_V5/Package_SO.pretty:SOIC-18W_7.5x11.6mm_P1.27mm")        # Microcontroller.
uc['VDD, VSS'] += vdd, gnd        # Attach pwr, gnd to uC.
c_byp = c(value='10uF')           # Add bypass capacitor.
c_byp[1,2] += vdd, gnd

xtal = Part('Device','Crystal', footprint="KiCad_V5/Crystal.pretty:Crystal_SMD_0603-2Pin_6.0x3.5mm")   # Crystal.
uc['OSC1, OSC2'] += xtal[1,2]     # Attach crystal to uC.
c1, c2 = c(2, value='10pF')       # Crystal trim caps.
c1[1,2] += xtal[1], gnd           # Connect trim caps.
c2[1,2] += xtal[2], gnd

chplx_leds(uc['RB[3:0]'])         # 12 charlieplexed LEDs.
chplx_switches(uc['RA[3:0]'])          # 12 charlieplexed switches.

ERC()

generate_netlist()

Ha, I figured you didn’t check this forum much, so I had a note in my calendar to track you down today and ask you to look. Just wanted to let you know I saw this. I’m going to run it and see how it works. Then get back to you.

My son understands Python better than I do - things like uc[‘OSC1, OSC2’] look like they should be uc[‘OSC1’, ‘OSC2’]. I figure he would understand what that’s about. I also think that OSC1-2 are pin numbers labeled in the model?

I actually get email when something happens on the forum. I was just away taking care of sick relatives.

uc['OSC1, OSC2'] works the same as uc['OSC1', 'OSC2'] because SKiDL splits strings with embedded commas. This saves you from having to surround each pin name with quotes.

OSC1 and OSC2 are names for the oscillator I/O pins of the microcontroller. They could also be referenced using their pin numbers, but that would be less clear as to their function when you read the code.

OK I worked from your example, and SKiDL goes right to the PCB editor. I’m not seeing how to turn the netlist into a schematic, is that possible?

Charlieplexing is difficult/check to see without being able to see the schematic. The ratsnest connects to the nearest component without making it obvious how all these components/diodes logically line up.

I’m working on having SKiDL output schematics, but I’ll tell you up front that you won’t like the results. I’m using netlistsvg and the results aren’t great even with a lot of fiddling. And for charlieplexing, there’s just no hope for outputing a sensible schematic (unless one of those deep learning apps suddenly appears).

Instead of tediously arranging the schematic and then using it to place the parts, you can assign the part references to reflect their connection in the charlieplexing array. I’ve re-done the example such that each part in the array now has the row and column appended to its part reference. Then when you do the layout, you can see which parts go in which position of the array.

from skidl import *

def reref(part, r, c):
    """Rename a part reference given the row, column it's in."""
    part.ref = '{part.name}-{r}-{c}'.format(**locals())

@subcircuit
def chplx(b, diode_t, two_pin_t=None):
    # Enumerate the bus so we know what row, column we're in as we iterate.
    for r, hi in enumerate(b, 1):
        for c, lo in enumerate(b, 1):
            if hi != lo:
                diode = diode_t()
                reref(diode, r, c) # Part reference now contains row & column.
                hi += diode['A']
                if two_pin_t != None:
                    two_pin = two_pin_t()
                    reref(two_pin, r, c)  # Part reference now contains row & column.
                    two_pin[:] += diode['K'], lo
                else:
                    lo += diode['K']


@subcircuit
def chplx_leds(b, 
        led_t=Part('Device', 'LED', TEMPLATE, footprint="KiCad_V5/LED_SMD.pretty:LED_0805_2012Metric_Castellated")):

    chplx(b,led_t)


@subcircuit
def chplx_switches(b,
    diode_t=Part('Device','D',TEMPLATE,footprint="KiCad_V5/Diode_SMD.pretty:D_0805_2012Metric"),
    switch_t=Part('Switch', 'SW_SPST', TEMPLATE,footprint="KiCad_V5/Button_Switch_SMD.pretty:SW_SPST_CK_RS282G05A3")):

    chplx(b, diode_t, switch_t)


vdd, gnd = Net('VDD'), Net('GND') # power & ground nets.
vdd.drive = POWER
gnd.drive = POWER

c = Part('Device','C', TEMPLATE, footprint="KiCad_V5/Capacitor_SMD.pretty:C_0805_2012Metric")  # capacitor template.

uc = Part('MCU_Microchip_PIC16',
          'PIC16F83-XXSO', footprint="KiCad_V5/Package_SO.pretty:SOIC-18W_7.5x11.6mm_P1.27mm")        # Microcontroller.
uc['VDD, VSS'] += vdd, gnd        # Attach pwr, gnd to uC.
c_byp = c(value='10uF')           # Add bypass capacitor.
c_byp[1,2] += vdd, gnd

xtal = Part('Device','Crystal', footprint="KiCad_V5/Crystal.pretty:Crystal_SMD_0603-2Pin_6.0x3.5mm")   # Crystal.
uc['OSC1, OSC2'] += xtal[1,2]     # Attach crystal to uC.
c1, c2 = c(2, value='10pF')       # Crystal trim caps.
c1[1,2] += xtal[1], gnd           # Connect trim caps.
c2[1,2] += xtal[2], gnd

chplx_leds(uc['RB[3:0]'])         # 12 charlieplexed LEDs.
chplx_switches(uc['RA[3:0]'])          # 12 charlieplexed switches.

ERC()

generate_netlist()