Charliplexing ist eine möglichkeit durch geschickte Verwendung der drei Zustände $Low$, $High$ und $Z$ (= Tri-state) aus durch wenige Pins viele LEDs anzusteuern. Dabei wir ausgenutzt, dass in den hochohmigen Pin, welcher auf $Z$ „liegt“, kein Strom fließen kann und dieser auch keinen Spannungswert definiert.
In Abbildung 1 ist ein Beispiel für Charlieplexing dargestellt. es ist folgendes zu sehen:
Es soll nun die Logik etwas näher beschrieben werden. Dies kann auch durch Reduktion der Simulationsgeschwindigkeit besser in der Simulation sichtbar gemacht werden:
CharlieP.c) Dabei ist für die LED links oben ([0][0]) die Kathode gerade der Pin des Ports $PORTx.0$.portMatrix gefüllt.actCathode für ein LED-Pärchen in Zeile 145 bzw. 148 nieder.Wichtig:
/*
 * CharlieP.h
 *
 * Created: 10.12.2022 22:30:57
 *  Author: Tim Fischer 
 */ 
#ifndef CHARLIEP_H_
#define CHARLIEP_H_
//Makros for bit manipulation
#define SET_BIT(BYTE, BIT)		((BYTE) |= (1<<BIT))							// set    single BIT in BYTE
#define CLR_BIT(BYTE, BIT)		((BYTE) &= ~(1<<BIT))							// clear  single BIT in BYTE
#define CHG_BIT(BYTE, BIT, VAL) ((BYTE) = ((BYTE) & ~(1<<BIT)) | (VAL<<BIT))	// change single BIT in BYTE to VAL
#define TGL_BIT(BYTE, BIT)		((BYTE) ^=  (1 << (BIT)))						// toggle single BIT in BYTE
#define PIN_COUNT		12														// overall number of pins used
#define PORT_COUNT		3														// number of ports used
enum	PORTS	{PORT_B, PORT_C, PORT_D, DDR_B, DDR_C, DDR_D};					// in this example three different Ports are used for output. This can also be change to more or less
enum	P		{    PB,     PC,     PD};										// In this case, the matrizes outputPort, outputPin and portMatrix have to be adapted
#define DONE			1														// return value for finished round over all anodes 
//function prototypes
extern  uint8_t CharlieP_setPortsForLightingLeds(void);
extern void     CharlieP_setPortMatrix(void);
extern uint8_t outputMatrixXY	[PIN_COUNT]		[PIN_COUNT-1];
extern uint8_t portMatrix		[PORT_COUNT*2]	[PIN_COUNT];
#endif /* CHARLIEP-H_H_ */
/*
 * CharlieP.c
 *
 * Created: 10.12.2022 22:30:48
 *  Author: Tim Fischer
 *
 * The following code drives charlieplexed LED arrays. For this, the array shall have the following structure:
 *
 *               colNr->     0   1      2   3        
 *
 *   rowNr       PIN0  ----------------------------
 *     v                     |   |      |   |
 *                           |   |      ^   v   . . . . . . . . . . . . This is LED[0,3]
 *     0                     ^   v      |   |
 *                           |   |      ------PIN2
 *                           |   |    
 *               PIN1  ----------------------------
 *                           |   |      |   |
 *                           |   |      ^   v
 *     1                     ^   v      |   |
 *                           |   |      ------PIN3
 *                           |   |
 *               PIN2  ----------------------------
 *                           |   |
 *                           |   |      ------PIN0
 *     2                     ^   v      |   |
 *                           |   |      ^   v
 *                           |   |      |   |
 *            	 PIN3  -----------------------------
 *
 * This example is explained more: 
 * - The numbers of pins is PIN_COUNT = 4. 
 * - There are groups of independent LEDs. There are as much groups of independent LEDs as numbers of pins.
 * - Example for independent LEDs: 
 *      - assume only PIN0 is HIGH and all other pins are at high-impedance. 
 *      - Then the anode on some LEDs is at 5V.
 *      - For LED[0,1], LED[0,3] and LED[2,3] anode is HIGH. These can independently be activated, by switching their cathode to LOW. 
 * - The active output anode has to be stepped through all PIN_COUNT pins.
 * - The arrangements of the ANODES can be read from the circuit above: 
 *     rowNr, colNr->  0   1      2   3   
 *      0            PIN1 PIN0  PIN2 PIN0
 *      1            PIN2 PIN1  PIN3 PIN1
 *      2            PIN3 PIN2   PIN3 PIN0 
 *
 * - The arrangements of the CATHODE is just the groups of columns rearranged (column 1 with 0, column 3 with 2)
 *   The ANODE becomes the CATHODE ans vice versa.
 * - For PIN_COUNT = 6, the  arrangements of the ANODES shall be as follows:
 *     rowNr, colNr->  0   1      2   3      4   5
 *      0            PIN1 PIN0  PIN2 PIN0  PIN3 PIN0
 *      1            PIN2 PIN1  PIN3 PIN1  PIN4 PIN1
 *      2            PIN3 PIN2  PIN4 PIN2  PIN5 PIN2
 *      3            PIN4 PIN3  PIN5 PIN3   PIN4 PIN0
 *      4            PIN5 PIN4   PIN5 PIN0  PIN5 PIN1
 *
 * - For PIN_COUNT = 8, the  arrangements of the ANODES shall be as follows:
 *     rowNr, colNr->  0   1      2   3      4   5      6   7
 *      0            PIN1 PIN0  PIN2 PIN0  PIN3 PIN0  PIN4 PIN0
 *      1            PIN2 PIN1  PIN3 PIN1  PIN4 PIN1  PIN5 PIN1
 *      2            PIN3 PIN2  PIN4 PIN2  PIN5 PIN2  PIN6 PIN2
 *      3            PIN4 PIN3  PIN5 PIN3  PIN6 PIN3  PIN7 PIN3
 *      4            PIN5 PIN4  PIN6 PIN4  PIN7 PIN4   PIN5 PIN9
 *      5            PIN6 PIN5  PIN7 PIN5   PIN6 PIN0  PIN6 PIN1
 *      6            PIN7 PIN6   PIN7 PIN0  PIN7 PIN1  PIN7 PIN2
 * - The systematic is as following: 
 *		- every EVEN column (for   UPward pointing LED) is a progression of rowNr + colNr + 1 until it                reaches PIN_COUNT-1. 
 *		  After this, the numbers reflect the rowNr+1 
 *      - every  ODD column (for DOWNward pointing LED) is a progression of rowNr             until rowNr + colNr + 1 reaches PIN_COUNT-1. 
 *		  After this, it is counting up again from 0 (= actAnode - PIN_COUNT).
 *
 */  
#include <avr/io.h>				// lib for I/O config
#include <stdbool.h>			// lib for Bit variables
#include <avr/interrupt.h>		// lib for standard interrupts
#include "charlieP.h"			// lib for charlie plexing
uint8_t outputPort[PIN_COUNT]={  DDR_C,  DDR_C,  DDR_D,  DDR_D,  DDR_D,  DDR_D,  DDR_D,  DDR_D,  DDR_D,  DDR_D,  DDR_B,  DDR_B};	// This array shows the PORTs used for driving the charlieplexed LEDs
uint8_t outputPin [PIN_COUNT]={    PC2,    PC3,    PD0,    PD1,    PD2,    PD3,    PD4,    PD5,    PD6,    PD7,    PB0,    PB1};	// This array shows the  PINs used for driving the charlieplexed LEDs
uint8_t outputMatrixXY[PIN_COUNT][PIN_COUNT-1]={};																					// this array reflects the LEDs which have to be activated 
																																	// The dimensions are the same as in the geometry: [column][row]
uint8_t portMatrix [PORT_COUNT*2][PIN_COUNT]={																						
	//Definition of Port Nr		0       1       2       3       4       5       6       7       8       9       10      11	//	the portMatrix is used to set the wanted ports to output HIGH or low
	//																														//	When, no output is wanted only the anodes are set to output HIGH
	//																														//	and the cathodes are set to high impedance (bit in DDRx = 0)
	/*PORT_B*/	            {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 1<<PB0, 1<<PB1},		//PORT_B
	/*PORT_C*/	            { 1<<PC2, 1<<PC3,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0},		//PORT_C
	/*PORT_D*/	            {      0,      0, 1<<PD0, 1<<PD1, 1<<PD2, 1<<PD3, 1<<PD4, 1<<PD5, 1<<PD6, 1<<PD7,      0,      0},		//PORT_D
	/* DDR_B*/	            {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 1<<PB0, 1<<PB1},		// DDR_B
	/* DDR_C*/	            { 1<<PC2, 1<<PC3,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0},		// DDR_C
	/* DDR_D*/	            {      0,      0, 1<<PD0, 1<<PD1, 1<<PD2, 1<<PD3, 1<<PD4, 1<<PD5, 1<<PD6, 1<<PD7,      0,      0}		// DDR_D
};
/********************************************************************* 
 *
 * CharlieP_setPortsForLightingLeds 
 *
 * - IMPORTANT: this function has to be in a cycle of about 10ms in order to have a tradeoff between dynamic changes of the output but long persistence for visibility
 * - Sets the ports for one anode (= one pin) active 
 * - multiple independent LEDs can be activated
 * - function outputs DONE, when all anodes were set to active. otherwise it outputs 0.
 *
 *********************************************************************/
uint8_t CharlieP_setPortsForLightingLeds(void)
{
	static uint8_t actOutputAnode = 0;								// set variable as local and persistent
	PORTB = portMatrix[PORT_B][actOutputAnode];						// set PORTB output values (HIGH or LOW)
	PORTC = portMatrix[PORT_C][actOutputAnode];						// set PORTC output values (HIGH or LOW)
	PORTD = portMatrix[PORT_D][actOutputAnode];						// set PORTD output values (HIGH or LOW)
	DDRB  = portMatrix[DDR_B ][actOutputAnode];						// set  DDRB output values (high-omhic or PORTB value)
	DDRC  = portMatrix[DDR_C ][actOutputAnode];						// set  DDRC output values (high-omhic or PORTD value)
	DDRD  =	portMatrix[DDR_D ][actOutputAnode];						// set  DDRD output values (high-omhic or PORTC value)
	
	actOutputAnode++;												// next anode
	if (actOutputAnode < PIN_COUNT) return 0;						// when not the last anode -> exit with returning 0
	actOutputAnode = 0;												// when the last anode -> set back to first
	return DONE;													// when the last anode -> exit with returning "DONE"
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Mapping des SignalOutput Arrays  ==========================================================
//mapped die Matrix auf den SignalOutout, um so die LEDs in Gruppen einzuteilen
/********************************************************************* 
 *
 * CharlieP_setPortMatrix
 *
 * - maps the outputMatrixXY (xy-matrix of activatable LEDs) to the necessary port and pins
 *
 *********************************************************************/
void CharlieP_setPortMatrix()
{
	uint8_t actCathode = 0, actPortCathode = 0, actPinCathode = 0, bitInOutputMatrixForLedUpwards	= 0;
	uint8_t actAnode   = 0, actPortAnode   = 0, actPinAnode   = 0, bitInOutputMatrixForLedDownwards = 0;
	
	for		(uint8_t rowNr = 0; rowNr <PIN_COUNT-1	; rowNr++)							// loop over all rows
		for (uint8_t colNr = 0; colNr <PIN_COUNT/2 	; colNr++)							// loop over half the columns, since each 2nd column has antiparallel LEDs (cathode and anode switched)
		{																				// 'rowNr' and 'colNr' define two antiparallel LEDs
			bitInOutputMatrixForLedUpwards		= outputMatrixXY	[colNr*2]  [rowNr];		// read, whether   UPward pointing LED (LED in even column) has to be activated
			bitInOutputMatrixForLedDownwards	= outputMatrixXY	[colNr*2+1][rowNr];		// read, whether DOWNward pointing LED (LED in  odd column) has to be activated
			actAnode			= colNr + rowNr + 1;										// sets the pin of the actual   anode. For details see comment in the beginning of CharlieP.c
			actCathode			= rowNr;													// sets the pin of the actual cathode. For details see comment in the beginning of CharlieP.c
			if (actAnode>= PIN_COUNT)														// once the change of the charlie plexing type in the row is reached 
			{																					// 
				actCathode		= actAnode - PIN_COUNT;											// recalculate the pin of the actual cathode. For details see comment in the beginning of CharlieP.c
				actAnode		= rowNr + 1;													// recalculate the pin of the actual   anode. For details see comment in the beginning of CharlieP.c
			};
		
			actPortCathode			= outputPort[actCathode];								// get cathode PIN / PORTs for  upward pointing LED, which has to be (de)activated
			actPinCathode			= outputPin [actCathode];
			actPortAnode			= outputPort[actAnode  ];								// get   anode PIN / PORTs for  upward pointing LED, which has to be (de)activated
			actPinAnode				= outputPin [actAnode  ];
		
			CHG_BIT(portMatrix[actPortCathode][actAnode  ], actPinCathode, bitInOutputMatrixForLedUpwards  );	// change the DDR for the pin and port for the   UPward pointing LED
			CHG_BIT(portMatrix[actPortAnode  ][actCathode],   actPinAnode, bitInOutputMatrixForLedDownwards);	// change the DDR for the pin and port for the DOWNward pointing LED
		};
}