Interesatno rešenje kontrole intenziteta sijanja 8 LED kanala dato je u ovom primeru. Kao osnova korišćeno je TI TLC5916 kolo, 8-kanalni LED sink drajver konstantne struje u rasponu od 3 do 120 mA po kanalu.

TKC5916 pretvara serijsku komandu preko SDI protokola u paralelnu komadu kojom se upravlja svakim od 8 LED kanala. Struje po kanalu su iste i nije moguće menjati intenzitet pojedinačnih struja, već se to radi za sve kanale odjednom. Moguće je samo svaki kanal paliti i gasiti.

Upravljanje rada ovog kola vrši se uz pomoć TI MSP430FR2311 mikrokontrolera, koji je u ovoj aplikaciji korišćen u vidu TI LaunchPad razvojnog sistema MSP-EXP430FR2311. Šema povezivanja je krajnje jednostavna i data je ispod. Treba samo primetiti da je na izlazu R-EXT postavljen otpornik od 720 oma. Napon VLED može biti onaj koji je potreban za nesmetano sijanje LED kanala, sa jednom ili više redno vezanih dioda u svakom od njih. Treba samo paziti da ukupna struja po kanalu ne prelazi 120mA. Treba paziti i prilikom odabira otpornika na R-EXT izlazu kako se ne bi prekoračila maksimalna snaga.

Kolo TLC5916/7 ima u sebi ugrađen senzor temperature na svakom od kanala, koji u slučaju prekoračenja temperature od 120C gasi dati kanal dok se on ne ohladi. Nakon toga ga automatski pali. Posebnom funkcijom readErrorStatusCode(), koja je takođe ovde data može se pročitati koji od kanala je u nedozvoljenom režimu. Signalizacija stanja je ostavljena za neki drgi ilustrativni projekat.

Standardno povezivanje MSP i TLC

Programiranje je urađeno u TI Code Composer Studio V10.0 u C-u, bez upotrebe DriverLib-a.

Kaskadna veza

Prilikom pisanja programa pazilo se na modularnost i lakoću korišćenja u budućnosti, pa su posebno napisane funkcije, koje se u glavnom (main.c) programu pozivaju prilikom podešavanja. MSP430FR2311 nakon toga ulazi u sleep režim u kome je potrošnja minimalizovana iz koga se pritiskom na dugme, interrupt-om na portu 2.0 vrši ponovno setovanje intenziteta sijanja LED kanala TLC5916.

void SetToSpecialMode (void)
{
    P1OUT &= ~BIT5;

// 1 clk
    P1OUT &= ~BIT1;                                 // LE deactive
    __delay_cycles(30);

    P1OUT |= BIT0;                                   // OE active
    __delay_cycles(30);

    P1OUT ^= BIT5;                                  // CLK
    __delay_cycles(30);
    P1OUT ^= BIT5;
    __delay_cycles(30);

// 2 clk
    P1OUT &= ~BIT0;                              // OE deactive
    __delay_cycles(30);

    P1OUT ^= BIT5;                                 // CLK
    __delay_cycles(30);
    P1OUT ^= BIT5;
    __delay_cycles(30);

// 3 clk
        P1OUT |= BIT0;                              // OE active
        __delay_cycles(30);

        P1OUT ^= BIT5;                             // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

// 4 clk
        P1OUT |= BIT1;                              // LE active
        __delay_cycles(30);

        P1OUT ^= BIT5;                              // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

// 5 clk
        P1OUT &= ~BIT1;                            // LE deactive
        __delay_cycles(30);

      P1OUT ^= BIT5;                                 // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);
    }
setToSpecialMode()

Funkcija setToSpecialMode()
U ovoj fukciji TLC5916 prelazi u specijalni režim u kome može izvršiti dve komande. Jedna je za podešavanje intenziteta sijanja LED kanala, koja se kodira po originalnom datasheet-u (http://www.ti.com/lit/gpn/TLC5916) – writeConfigurationCode(code).

writeConfigurationCode(code)
void writeConfigurationCode (unsigned int code)   // writeConfigurationCode (code=CM|HC|CC0...CC5) function
{

    P1OUT &= ~BIT5;

    unsigned int i;
        i=8;
        P1OUT &= ~BIT1;                                  // LE deactive
        __delay_cycles(30);
        P1OUT |= BIT0;                                     // OE active
        __delay_cycles(30);

        while(i--)
        {
             if ((code & (0x01<<(7-i))) != 0 )
             {
                 P1OUT |= BIT7;
             }
             else
             {
                 P1OUT &= ~BIT7;
             }
             __delay_cycles(30);
             P1OUT ^= BIT5;                                 // CLK
             __delay_cycles(30);
            P1OUT ^= BIT5;
            __delay_cycles(30);
            if (i == 1) {P1OUT |= BIT1; }                // LE active
        }

        P1OUT &= ~BIT1;                                  // LE deactive
        __delay_cycles(30);
}

Druga je za čitanje eventualne greške prilikom rada readErrorStatusCode().

int readErrorStatusCode (void) {

    unsigned int i=8;
    unsigned int code=0x00;

    P1OUT &= ~BIT5;                          // CLK low
    P1OUT &= ~BIT1;                          // LE deactive
    __delay_cycles(30);
    P1OUT |= BIT0;                              // OE active
    __delay_cycles(30);

    // 1 clk
    P1OUT &= ~BIT0;                          // OE deactive
    __delay_cycles(30);
        P1OUT ^= BIT5;                         // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

    // 2 clk
        P1OUT ^= BIT5;                         // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

    // 3 clk
            P1OUT ^= BIT5;                      // CLK
            __delay_cycles(30);
            P1OUT ^= BIT5;
            __delay_cycles(30);

     // 2 us from 1clk!!!
      // read Error Status Code
            while(i--)
             {
                P1OUT ^= BIT5;                     // CLK
                __delay_cycles(30);
                P1OUT |= BIT1;                      // LE active
                __delay_cycles(30);

                if ((P2OUT & 0x01) != 0 )
                  {
                      code |= (0x01<<i) ;
                  }
                  else
                  {
                      code &= !(0x01<<i) ;
                  }
                  __delay_cycles(30);
                  P1OUT ^= BIT5;                     // CLK
                  __delay_cycles(30);
             }
    return(code);
}

Funkcija setToNormalMode()
Ova funkcija prebacuje TLC5916 u normalni režim rada u kome se pozivanjem funckije setLED(code) kontroliše koji od 8 kanala je upaljen, a koji ugašen.

void SetToNormalMode (void)
{
    P1OUT &= ~BIT5;                                     // CLK to Low

// 1 clk
    P1OUT &= ~BIT1;                                    // LE deactive
    __delay_cycles(30);

    P1OUT |= BIT0;                                      // OE active
    __delay_cycles(30);

    P1OUT ^= BIT5;                                     // CLK
    __delay_cycles(30);
    P1OUT ^= BIT5;
    __delay_cycles(30);

// 2 clk
    P1OUT &= ~BIT0;                                 // OE deactive
    __delay_cycles(30);

    P1OUT ^= BIT5;                                   // CLK
    __delay_cycles(30);
    P1OUT ^= BIT5;
    __delay_cycles(30);

// 3 clk
        P1OUT |= BIT0;                               // OE active
        __delay_cycles(30);

        P1OUT ^= BIT5;                              // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

// 4 clk
        P1OUT ^= BIT5;                              // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);


// 5 clk
       P1OUT ^= BIT5;                                 // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

    }
setToNormalMode()

Funkcija setLED(buffer, duration)
Funkcija ima dva parametra. Prvi je sam data paket u vidu binarnog osmobitnog broja u kome setovani bit uključuje dati LED kanal. Duration je vrednost koja ukazuje koliko taktova će funkcija „zakočiti“ izvršavanje programa, kako bi se u nekim aplikacijama podesilo trajanje sijanja LED izlaza.

setLED(buffer,duration)
void setLED (unsigned int buffer, unsigned long duration)   // SetLED (LED output, duration) function
{
unsigned int i;
        i=8;
        while(i--)
        {
             if ((buffer & (0x01<<(i))) != 0 )
             {
                 P1OUT |= BIT7;
             }
             else
             {
                 P1OUT &= ~BIT7;
             }
             __delay_cycles(30);
             P1OUT ^= BIT5;                                   // CLK
             __delay_cycles(30);
             P1OUT ^= BIT5;
             __delay_cycles(30);
        }
        P1OUT ^= BIT1;                                       // LE active
        __delay_cycles(30);
        P1OUT ^= BIT1;
        __delay_cycles(30);
        P1OUT ^= BIT0;                                      // OE active
        while ((duration--)>0)
        __delay_cycles(1);
}

Interrupt rutina je pisana za port 2.0, i to kao najjednostaviji pull-up handler za pritisak na prekidač postavljen na tom portu. Napisana je u osnovnoj verziji samo radi demonstracije u ovom primeru. Bolje bi bilo da se switch komanda nalazi u main() delu, a ne u interrupt hendleru, ali u ovom primeru to nema neki uticaj na samo izvršavanje.

Kompletan projekat možete preuzeti sa GitHub-a.

https://github.com/magazinMehatronika/TLC5916-LED-Sink-Driver

KOD:

//  MSP430FR2311 komunikacija sa Ti TLC5961  LED 8 kanalni sink drajver
//  konstante struje 3mA do 120 mA po kanalu
//  Dva radna moda: normalni i specijalni (detekcija greške i promena intenziteta struje diode)
//  Implementirane funkcije:
//  setLED (buffer, duration)
//                   - buffer: 0b******** - pozicija bita pali jedan od 8 kanala
//                   - duration: dužina trajanja sijanja LED kanala
//  SetToNormalMode ()
//                   - uvodi TLC5961 u normalni mod rada
//  SetToSpecialMode ()
//                   - uvodi TLC5961 u specijalni mod rada
//  writeConfigurationCode (code)
//                   - code: 0b******** -  CM|HC|CCO...CC7
//                   - značenje koda definisano u TLC5916 Datasheetu
//  readErrorStatusCode (code)
//                   - code: ox**
//                   - značenje koda definisano u TLC5916 Datasheetu
//
//  MSP430FR2311 - use sdi communication with TLC5916 LED Sink Driver
//  2 working modes: Normal and Special( Error Detection and Current Gain)
//
//                  MSP430FR2311
//                -----------------
//            /|\|                 |
//             | |                 |
//             --|RST         |
//               |             P1.0|-> ~OE(ED2)
//               |             P1.1|-> LE(ED1)
//               |             P1.5|-> CLK
//               |             P1.7|-> SDI
//    BTN->|P2.1      P2.0|<- SDO
//
// OPIS: Klikom na prekidač BTN, MSP izlazi iz sleep moda i
//           Interrupt rutinom menja intenzitet sijanja LED dioda
//
//
//   magazinMehatronika @ nespojivo
//   April 2020
//******************************************************************************
#include <msp430.h>

unsigned int m=0;



void setLED (unsigned int buffer, unsigned long duration)   // SetLED (LED output, duration) function
{
unsigned int i;
        i=8;
        while(i--)
        {
             if ((buffer & (0x01<<(i))) != 0 )
             {
                 P1OUT |= BIT7;
             }
             else
             {
                 P1OUT &= ~BIT7;
             }
             __delay_cycles(30);
             P1OUT ^= BIT5;                                   // CLK
             __delay_cycles(30);
             P1OUT ^= BIT5;
             __delay_cycles(30);
        }
        P1OUT ^= BIT1;                                       // LE active
        __delay_cycles(30);
        P1OUT ^= BIT1;
        __delay_cycles(30);
        P1OUT ^= BIT0;                                      // OE active
        while ((duration--)>0)
        __delay_cycles(1);
}



void SetToNormalMode (void)
{
    P1OUT &= ~BIT5;                                     // CLK to Low

// 1 clk
    P1OUT &= ~BIT1;                                    // LE deactive
    __delay_cycles(30);

    P1OUT |= BIT0;                                      // OE active
    __delay_cycles(30);

    P1OUT ^= BIT5;                                     // CLK
    __delay_cycles(30);
    P1OUT ^= BIT5;
    __delay_cycles(30);

// 2 clk
    P1OUT &= ~BIT0;                                 // OE deactive
    __delay_cycles(30);

    P1OUT ^= BIT5;                                   // CLK
    __delay_cycles(30);
    P1OUT ^= BIT5;
    __delay_cycles(30);

// 3 clk
        P1OUT |= BIT0;                               // OE active
        __delay_cycles(30);

        P1OUT ^= BIT5;                              // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

// 4 clk
        P1OUT ^= BIT5;                              // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);


// 5 clk
       P1OUT ^= BIT5;                                 // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

    }



void SetToSpecialMode (void)
{
    P1OUT &= ~BIT5;

// 1 clk
    P1OUT &= ~BIT1;                                 // LE deactive
    __delay_cycles(30);

    P1OUT |= BIT0;                                   // OE active
    __delay_cycles(30);

    P1OUT ^= BIT5;                                  // CLK
    __delay_cycles(30);
    P1OUT ^= BIT5;
    __delay_cycles(30);

// 2 clk
    P1OUT &= ~BIT0;                              // OE deactive
    __delay_cycles(30);

    P1OUT ^= BIT5;                                 // CLK
    __delay_cycles(30);
    P1OUT ^= BIT5;
    __delay_cycles(30);

// 3 clk
        P1OUT |= BIT0;                              // OE active
        __delay_cycles(30);

        P1OUT ^= BIT5;                             // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

// 4 clk
        P1OUT |= BIT1;                              // LE active
        __delay_cycles(30);

        P1OUT ^= BIT5;                              // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

// 5 clk
        P1OUT &= ~BIT1;                            // LE deactive
        __delay_cycles(30);

      P1OUT ^= BIT5;                                 // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);
    }



void writeConfigurationCode (unsigned int code)   // writeConfigurationCode (code=CM|HC|CC0...CC5) function
{

    P1OUT &= ~BIT5;

    unsigned int i;
        i=8;
        P1OUT &= ~BIT1;                                  // LE deactive
        __delay_cycles(30);
        P1OUT |= BIT0;                                     // OE active
        __delay_cycles(30);

        while(i--)
        {
             if ((code & (0x01<<(7-i))) != 0 )
             {
                 P1OUT |= BIT7;
             }
             else
             {
                 P1OUT &= ~BIT7;
             }
             __delay_cycles(30);
             P1OUT ^= BIT5;                                 // CLK
             __delay_cycles(30);
            P1OUT ^= BIT5;
            __delay_cycles(30);
            if (i == 1) {P1OUT |= BIT1; }                // LE active
        }

        P1OUT &= ~BIT1;                                  // LE deactive
        __delay_cycles(30);
}



int readErrorStatusCode (void) {

    unsigned int i=8;
    unsigned int code=0x00;

    P1OUT &= ~BIT5;                          // CLK low
    P1OUT &= ~BIT1;                          // LE deactive
    __delay_cycles(30);
    P1OUT |= BIT0;                              // OE active
    __delay_cycles(30);

    // 1 clk
    P1OUT &= ~BIT0;                          // OE deactive
    __delay_cycles(30);
        P1OUT ^= BIT5;                         // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

    // 2 clk
        P1OUT ^= BIT5;                         // CLK
        __delay_cycles(30);
        P1OUT ^= BIT5;
        __delay_cycles(30);

    // 3 clk
            P1OUT ^= BIT5;                      // CLK
            __delay_cycles(30);
            P1OUT ^= BIT5;
            __delay_cycles(30);

     // 2 us from 1clk!!!
      // read Error Status Code
            while(i--)
             {
                P1OUT ^= BIT5;                     // CLK
                __delay_cycles(30);
                P1OUT |= BIT1;                      // LE active
                __delay_cycles(30);

                if ((P2OUT & 0x01) != 0 )
                  {
                      code |= (0x01<<i) ;
                  }
                  else
                  {
                      code &= !(0x01<<i) ;
                  }
                  __delay_cycles(30);
                  P1OUT ^= BIT5;                     // CLK
                  __delay_cycles(30);
             }
    return(code);
}



int main(void)
{
    unsigned int k = 0;

    WDTCTL = WDTPW | WDTHOLD;                // Stop watchdog timer

    P1DIR |= BIT7 | BIT5 | BIT1 | BIT0;                  // P1.0, 1.1, 1.5 & 1.7 as out
    P1OUT &= ~BIT7 | BIT5 | BIT1;                       // P1.1, P1.5 & 1.7 low
    P1OUT |= BIT0;                                                // P1.0 high

    P2DIR &= ~BIT0;                                             // P2.0 as input
    P2OUT |= BIT0;                                               // P2.0 as low

    P2OUT |= BIT1;                                              // Configure P2.1 as pulled-up
    P2REN |= BIT1;                                              // P2.1 pull-up register enable
    P2IES |= BIT1;                                                // P2.1 Hi/Low edge
    P2IE |= BIT1;

    PM5CTL0 &= ~LOCKLPM5;                          // Disable the GPIO power-on default high-impedance mode
                                                                           // to activate previously configured port settings
    P2IFG &= ~BIT1;                                           // P2.1 IFG cleared

    setLED (0, 10);

    SetToSpecialMode();
    writeConfigurationCode(0b00000000);
    SetToNormalMode();
    for (k=1; k<=0xff; k*=2){
        setLED (k, 1000);
        setLED (0, 10);
    }

 while (1)
     {
         __bis_SR_register(LPM3_bits | GIE);   // Enter LPM0,enable interrupts
         __no_operation();                                  // For debug,Remain in LPM0
         setLED (0xff, 1);
         }
}



#pragma vector=PORT2_VECTOR
__interrupt void Port_2(void)

{
    switch (m) {
        case 0:
        {
            SetToSpecialMode();
            writeConfigurationCode(0b00000000);
            SetToNormalMode();
            m=1;
            }
            break;
        case 1:
        {
            SetToSpecialMode();
            writeConfigurationCode(0b01000000);
            SetToNormalMode();
            m=2;
            }
            break;
        case 2:
        {
            SetToSpecialMode();
            writeConfigurationCode(0b01111111);
            SetToNormalMode();
            m=0;
            }
            break;
        default:
            break;
    }

    P2IFG &= ~BIT1;                                            // Clear P2.1 IFG
    __bic_SR_register_on_exit(LPM3_bits);   // Exit LPM3
}



// UNUSED_HWI_ISR()
#pragma vector=UNMI_VECTOR
#pragma vector=WDT_VECTOR
#pragma vector=TIMER1_B0_VECTOR
#pragma vector=TIMER1_B1_VECTOR
#pragma vector=TIMER0_B0_VECTOR
#pragma vector=TIMER0_B1_VECTOR
#pragma vector=PORT1_VECTOR
// #pragma vector=PORT2_VECTOR
#pragma vector=ECOMP0_VECTOR
#pragma vector=ADC_VECTOR
#pragma vector=EUSCI_B0_VECTOR
#pragma vector=EUSCI_A0_VECTOR
#pragma vector=RTC_VECTOR
#pragma vector=SYSNMI_VECTOR
#pragma vector=RESET_VECTOR

__interrupt void UNUSED_HWI_ISR (void)
{
__no_operation();
}
Aleksandar Dakić
Follow me