So endlich Feierabend :)
Patrick S. schrieb:
> wie hast du das mit dem GSCLK Takt gelöst?
Also, der GSCLK kommt bei mir, wie schon erwähnt, von einem
Quarzoszillator (25Mhz). Der Oszillator hängt gleichzeitig noch am Xmega
und zählt dort einen Timer hoch. Der Timer wiederrum läuft im PWM-Mode
und erzeugt alle 4096 GSCLK-Takte den BLANK-Impuls. Die Datenübertragung
zum TLC5941 läuft ganz Normal über Hardware-SPI nur das ich das XLAT mit
dem BLANK-Impuls syncronisiere. Macht man das nicht flackern die LEDs
beim ändern der Werte manchmal auf weil die Register des TLC5941 nich
gebuffert sind und der Zähler sonst weiter ist als der Wert auf den man
ihn setzt->Folge: Die LED bleibt einen ganzen Zyklus lang an.
So und jetzt zum Code. Ist noch lange nicht fertig, das LOD wird noch
nicht ausgewertet. Außerdem wollte ich das Array mit den GS/DC-Werten
noch ändern und mit nem Getter drauf zugreifen da das so doch ne
ganzschöne Platzverschwendung ist.
Zuerst der Init für den Timer zur Erzeugung des BLANK-signals:
1 | void timer_init(void){
|
2 | //Port für BLANK setzen O->
|
3 | PORTC.DIRSET = TLC5941_BLANK;
|
4 |
|
5 | //Port für GSCK setzen & Triggern 0<-
|
6 | PORTD.DIRCLR = TLC5941_GSCK;
|
7 | PORTD.PIN0CTRL = PORT_ISC_RISING_gc;
|
8 |
|
9 | //Setze PD0 als Eventchannel
|
10 | EVSYS.CH0MUX = EVSYS_CHMUX_PORTD_PIN0_gc;
|
11 |
|
12 | //Clocksource -> Event Channel0
|
13 | TCC0.CTRLA = TC_CLKSEL_EVCH0_gc;
|
14 |
|
15 | //SingleSlope-PWM-mode & OC0A aktivieren
|
16 | TCC0.CTRLB |= TC_WGMODE_SINGLESLOPE_gc | TC0_CCAEN_bm;//0xF3;
|
17 |
|
18 | //Legt Timer Topwert und damit auch fest
|
19 | //bei wievielen Impulsen die Interrupt Routine
|
20 | //ausgeführt wird
|
21 | TCC0.PER = 4095;
|
22 | TCC0.CCA = 1;
|
23 | }
|
Jetzt das SPI-initieren
1 | void SPI_init(void) {
|
2 | //SPI-ports festlegen
|
3 | PORTC.DIRSET = TLC5941_Sin | TLC5941_SCK | TLC5941_XLAT | TLC5941_MODE;
|
4 | PORTC.DIRCLR = TLC5941_Sout | TLC5941_XERR;
|
5 |
|
6 | //SPI konfigurien: MASTER, 8Mhz, Mode0
|
7 | SPIC.CTRL |= SPI_MASTER_bm | SPI_PRESCALER_DIV4_gc | SPI_MODE_0_gc;
|
8 |
|
9 | //Keinen interrupt auslösen,
|
10 | //SPIC.INTCTRL = SPI_INTLVL_MED_gc;
|
11 | SPIC.INTCTRL = SPI_INTLVL_OFF_gc;
|
12 |
|
13 | //SPI aktivieren
|
14 | SPIC.CTRL |= SPI_ENABLE_bm;
|
15 |
|
16 | //XERR aktivieren
|
17 | PORTC.PIN2CTRL = PORT_OPC_PULLUP_gc | PORT_ISC_FALLING_gc; //PIN configurieren Pullup & Fallende Flanke
|
18 | PORTC.INTCTRL |= PORT_INT0LVL_MED_gc; // PortC interrupt0 -> medium-level setzen
|
19 | PORTC.INT0MASK |= PIN2_bm; // Pin2 für Interrupt0 freigeben
|
20 | PMIC.CTRL |= PMIC_MEDLVLEN_bm; // Medium-Level Interrupts aktivieren
|
21 | sei(); // Interrupts global freigeben
|
22 | }
|
und jetzt die Routiene zum senden der DC & GS-Werte.
Die Routine zum Senden der der 6bit und 12bit Werte stammt von Falk
Brunner hier aus dem Forum, Ich hoffe er ist mir nich böse das ich das
hier nochmal veröffentliche.
1 | //THX to Falk Brunner @ microcontroller.net
|
2 | void TLC5941_SET_DC(uint8_t array[]) {
|
3 | PORTC.OUTSET = TLC5941_MODE;//MODE 1 = DC
|
4 |
|
5 | uint8_t bit_count_in; // Zähler für Bits in den Eingangsdaten 6Bit/Segment
|
6 | uint8_t bit_count_out=0; // Zähler für Bits in den Ausgangsdaten 8Bit/SPI Byte
|
7 | uint8_t dot_count; // Zähler für Arrayindex der EIngangsdaten
|
8 | uint8_t byte_in; // Eingangsdaten
|
9 | uint8_t byte_out = 0; // Ausgangsdaten
|
10 | uint8_t mask_out = 0x80; // Bitmaske für Ausgangsdaten
|
11 |
|
12 | for (dot_count = 0; dot_count < 32; dot_count++) { // Schleife über Eingangsarray
|
13 | byte_in = array[31-dot_count]; // nächstes Element laden
|
14 | // Schleife über 6 Bit/Eingangsbyte
|
15 | for (bit_count_in=0; bit_count_in < 6; bit_count_in++) {
|
16 | if (byte_in & 0x20) byte_out |= mask_out; // Prüfe Bit #5, wenn ja in den Ausgangsdaten setzen
|
17 | byte_in <<= 1; // nächstes Bit hinschieben
|
18 | mask_out >>= 1; // Maske auf nächsten Ausgangsbit schieben
|
19 | bit_count_out++; // Bitzähler für Ausgang++
|
20 | if (bit_count_out == 8) { // 8 Bits erreich, Ausgangsbyte voll?
|
21 | bit_count_out=0; // Zähler rücksetzen
|
22 | SPI_send(byte_out); // senden
|
23 | byte_out = 0; // Zähler rücksetzen
|
24 | mask_out = 0x80; // Maske rücksetzen
|
25 | }
|
26 | }
|
27 | }
|
28 | PORTC.OUTSET = TLC5941_XLAT; //XLAT kurz auf 1 um daten zu übernehmen
|
29 | PORTC.OUTCLR = TLC5941_XLAT;
|
30 | PORTC.OUTCLR = TLC5941_MODE; //wieder auf GS-mode zurücksetzten
|
31 | }
|
1 | //THX to Falk Brunner @ microcontroller.net
|
2 | void TLC5941_SET_GS(uint16_t array[]) {
|
3 | PORTC.OUTCLR = TLC5941_MODE;//MODE 0 = GS mode
|
4 |
|
5 | uint8_t bit_count_in; // Zähler für Bits in den Eingangsdaten 6Bit/Segment
|
6 | uint8_t bit_count_out=0; // Zähler für Bits in den Ausgangsdaten 8Bit/SPI Byte
|
7 | uint8_t dot_count; // Zähler für Arrayindex der Eingangsdaten
|
8 | uint16_t byte_in; // Eingangsdaten
|
9 | uint8_t byte_out = 0; // Ausgangsdaten
|
10 | uint16_t mask_out = 0x80; // Bitmaske für Ausgangsdaten
|
11 |
|
12 | for (dot_count = 0; dot_count < 32; dot_count++) { // Schleife über Eingangsarray
|
13 | byte_in = array[31-dot_count]; // nächstes Element laden
|
14 | // Schleife über 6 Bit/Eingangsbyte
|
15 | for (bit_count_in=0; bit_count_in < 12; bit_count_in++) {
|
16 | if (byte_in & 0x0800) byte_out |= mask_out; // Prüfe Bit #12, wenn ja in den Ausgangsdaten setzen
|
17 | byte_in <<= 1; // nächstes Bit hinschieben
|
18 | mask_out >>= 1; // Maske auf nächsten Ausgangsbit schieben
|
19 | bit_count_out++; // Bitzähler für Ausgang++
|
20 | if (bit_count_out == 8) { // 8 Bits erreich, Ausgangsbyte voll?
|
21 | bit_count_out=0; // Zähler rücksetzen
|
22 | SPI_send(byte_out); //senden
|
23 | byte_out = 0; // Zähler rücksetzen
|
24 | mask_out = 0x80; // Maske rücksetzen
|
25 | }
|
26 | }
|
27 | }
|
28 |
|
29 | while(TCC0.CNT < 4095); //auf Blank-Flanke warten
|
30 |
|
31 | PORTC.OUTSET = TLC5941_XLAT; //XLAT kurz auf 1 um daten zu übernehmen
|
32 | PORTC.OUTCLR = TLC5941_XLAT;
|
33 | }
|
Die beiden Arrays sehen übrigens so aus und da steckt auch die
Platzverschwendung ;)
1 | uint8_t DC[32] = {
|
2 | 63, //1
|
3 | 63, //2
|
4 | ...
|
5 | 63};
|
6 | uint16_t GS[32] = {
|
7 | 4095, //1
|
8 | 4095, //2
|
9 | ...
|
10 | 4095};
|
So, das wars erstmal, wie gesagt alles noch Baustelle und die LOD fehlt
auch noch aber du willst ja auch noch was zu tun haben ;)
Ich hoffe das ich dir helfen konnte.