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 }; }