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.
Programiranje je urađeno u TI Code Composer Studio V10.0 u C-u, bez upotrebe DriverLib-a.
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);
}
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).
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);
}
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.
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.
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();
}