ATmega8 - VU5 LED-es kivezérlés mérő

2016-01-05

 

Fiatal korom óta szerettem volna építeni magamnak egy LED-es kivezérlés mérőt, hát most összejött. Csúcsérék tárolós, meg minden.

Volt néhány próbálkozásom, egy kicsit ezt jelzi a VU5 név, bár már nem vagyok biztos benne, hogy ez korrekt sorszám. Legutóbb egy ATTiny26-ot kínoztam a LED-ekkel, de azután nem fejeztem be. Az volt az alapvető probléma, hogy a LED-ek nem világítottak elég fényerővel.

Nemrég belefutottam a Banggood.com-on a AS30 Binaural 30 Segment LED DIY Kit Music Spectrum VU Meter nevű termékbe, amit a kínaiaktól 4kHUF környékén meg lehet rendelni a kitben. Nézegessétek meg a hozzá csatolt videókat, tök jól néz ki, nagyon olcsó, nagyon bosszantó. Beindult az agyrágó bogár, miből rakták ezek össze, látszólag elég nagy a fényereje? Az anyagokat böngészve állítólag 820 Ohm-os ellenállások vannak a LED-ekkel sorba kötve, ami szerintem valószínűtlen, vagy valami nagyon érzéken LED soraik vannak. Lehet. Próbáltam a procról is kisütni valamit. Az egyik vidób alapján sikerült kisilabizálnom a típust, találtam valami PDF-et is hozzá a neten, de a kínai írással egyenlőre sok gondom van. Annyi derült számomra ki, hogy az ősi MCS51 családra épülő kínai csoda. Úgy gondoltam, valószínűleg nem lehet jobb mint egy AVR, bár gyárthatják speciális port lábakkal is... Az AVR-ek port lábai max 40mA-rel terhelhetők le ésfel is. A táp lábak meg összességében max 300mA-t viselnek el. Ennyi LED-et nyilván mátrixba kell kötni, és multiplexelve, soronként villogtatva lehet vezérelni. Azon gondolkodtam, hogy meg kell próbálni a lehető legtöbb port lábat felhasználva, egyszerre a lehető legtöbb LED-et kapcsolgatni, a lehető legkevesebb sorra osztani őket, így egy-egy LED-re hosszabb időszelet juthat. Adódott, hogy ilyen LED-sorok már egy ideje arra vártak a fiókomban, hogy egyszer csak kivezérlés mérőt építek belőlük, és egy sorban 3 tokot kapcsolva azt hittem, hogy 3x8 LED-del kell operálnom, de nem ezek 3x10 LED-et tartalmaztak. Így adódott, hogy a LED-eket 4 sorra osztottam, soronként 15 LED-re. A végső tervem az volt, hogy az AVR táp lábait multiplexálva használva, az egyik LED sort a +Vcc felé kapcsolgatom, a másikat meg a GND felé, és így összességében a föld felé és táp felé menő max áramot kihasználva megduplázhatnám a kivehető meghajtó áramot. Azután a deszkamodell alapján bebizonyosodott, hogy erre nincs szükség.

A megépített kapcsolásba 100 Ohm-os ellenállások kerültek, ezekkel, max kivezérlésnél olyan 180mA körül alakul a felvett áram. Úgy gondolom, biztonsággal legfeljebb 82 Ohm-os ellenállások alkamazhatóak. A 180mA-t 60 LED-re elosztva olyan 3mA/LED jön ki. 15 port lábbal elosztva, olyan 12mA/láb adódik. Alább látható a kapcsolási rajz. Nem szoktam én ennyi alkatrészt egy kapcsolásba betenni:). Bal oldalon két egyenirányító kapcsolás van. Jobbra alúl van egy saját tápja az áramkörnek. Célszerű a mérendő jeltől galvanikusan függetlenül táplálni az áramkörünket. Lehet neki 12V egyen tápot adni, vagy egy kis trafóról 9V váltót. A legnagyobb területet a LED mátrix foglalja el. Enézést a rajzon a LED-eket vezérlő lábak funkcionálisan rendben vannak, de a sorrendjük az időközben változott. Előszőr a deszka panelen DIP tokos vezérlőt használtam, a panelre meg SMD került, kicsit eltérő a kiosztásuk. Azután, hogy a panel vonalvezetésén egyszerűsítsek, inkább a programban cserélgettem a lábakat.

Nézzük a mester hogyan forraszt SMD tokozású ATmega8-at. Előveszi a jó öreg 3mm-es hegyű Weller pákáját, egy csipesszel a panelhez szoratja az IC-t, majd átellenes sarkokban odapöttyinti a lábakat, majd végigpöttyinti mindet és ... lehet hogy innen érdemesebb lenne újrakezdeni?

Számítógépes játékokat lehet újra kezdeni, de ez a VALÓSÁG :). A mester fog egy darab gyantát, mert tubusos folyasztószerre sajnálja a pénzt, és jól nyakon csöpögteti az IC-t. Még nem látszik jól a változás, csak az, hogy a panel feliratában elgépeltem a web lapom címét :(.

Egészen elfogadható az eredmény, egy kis denaturált szesz és egy erre rendszeresített fogkefével való lesikálás után. Az az igazság, hogy nem én tudok SMD-t forrasztani, hanem a folyósító szer, a gyanta gondoskodik arról, hogy az ón csak a megfelelő helyen legyen. Volt idő, amikor nem voltam ám ilyen bátor.

Itt látható a vezérlő panel felülről. Igazából büszke vagyok rá, hogy ilyen digitális kapcsolást sikerül mindössze három átkötáéssel egy oldalas panelre megterveznem.

A mikrovezérlő programása folyik a képen az APH2 fejjel. Az előlap csupán a LED sorokat tartalmazza, és erre van merőlegesen a sorok lábára forrasztva a vezérlő panel. Eleve benne volt a koncepciómban, az alsó/felső LED-ek ilyen összehuzalozása, mert tudtam, hogy egyébként kétoldalas panelre, vagy sok átkötésre lenne szükség. A széleken látszik a sorok bekötése kis vezeték darabokkal.

Még egy kép a két panel összeforrasztásáról. Az előlap egyik felére és a vezérlő panel egyik sarkára is tettem egy-egy csillagot, ezzek tartoznak össze.

Itt látható az áramkört vezérlő program. A main() az AD inicializálásával kezdődik, majd megadjuk melyik port lábak lesznek a kimenetek. Egyetlen végtelen ciklusból áll a program. Minden ciklus végén 1 msec várakozás van. A cikluban első lépésben kikapcsolásra kerül az összes LED. Azután kijelzés szakaszban minden ciklusban másik sorra kerül a vezérlés. Az adott sornak megfelelő oszlop és pont kijelző rutinok kerülnek meghívásra. Ezek beállítják az adott sorhoz a LED-eket, majd bekapcsolásra kerül az adott sor. Ezután következik a mérés szakasz. A mérés szakasz megadott periódusonként aktiválódik csak. Váltakozva méri meg a bal és a jobb oldal szintjét, elhelyezi a megfelelő változóba. Gondoskodik a csúcs érték figyeléséről is. A mérések gyakoriságán állíthatunk a MEASURE_PERIOD értékkel, a csúcsérték tartását pedig a PEAK_PERIOD-dal szabályozhatjuk. A programban számos olyan definícióra, makró, hivatkozás van, ami a csatolt tkiraaly_atmega8.h-ban található meg.


/*******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2016.01.03.
*   Chip         -  Atmel AMega8
*   Compiler     -  avr-gcc ( WinAVR)
*
*   LED VU mero
*
********************************************************************************
*   PonyProg Configuration and Security Bits (bepipalva):
*
*   CKSEL3, CKSEL2, CKSEL1, CKSEL0
*   P       P       -       -       0011  Calibrated Internal RC Oscillator 4MHz
*
*   Calibrated Internal RC Oscillator
*   SUT1  SUT0
*   -     P         Slowly rising power
*   
*******************************************************************************/
#define F_CPU                _4MHZ     // orajel 4MHz


#include "tkiraaly_atmega8.h"
#include <util/delay.h>

#define ROW_LEFT_LOW_ON      PB7_1
#define ROW_RIGHT_LOW_ON     PB6_1
#define ROW_RIGHT_HIGH_ON    PC5_1
#define ROW_LEFT_HIGH_ON     PC4_1

#define LED0_ON              PC3_1
#define LED1_ON              PC2_1
#define LED2_ON              PC1_1
#define LED3_ON              PC0_1
#define LED4_ON              PB2_1
#define LED5_ON              PB1_1
#define LED6_ON              PB0_1
#define LED7_ON              PD7_1
#define LED8_ON              PD6_1
#define LED9_ON              PD5_1
#define LEDA_ON              PD4_1
#define LEDB_ON              PD3_1
#define LEDC_ON              PD2_1
#define LEDD_ON              PD1_1
#define LEDE_ON              PD0_1

#define LEFT_IN              ADC_IN7
#define RIGHT_IN             ADC_IN6

#define MEASURE_PERIOD       25
#define PEAK_PERIOD          25



void led_bar_low( U8);                 // LED oszlop kijelzes also reszen
void led_bar_high( U8);                // LED oszlop kijelzes felso reszen
void led_point_low( U8);               // LED pont kijelzes also reszen
void led_point_high( U8);              // LED pont kijelzes felso reszen




int main( void)
{
   U8 row_count= 3;                    // kijelzo sor valtas
   U8 measure_count= 0;                // meres szamlalo
   U8 left_nright= 0;                  // bal / nem jobb - oldal valtas
   U8 left_u= 0;                       // bal feszultseg
   U8 left_u_peak= 0;                  // bal csucs feszultseg
   U8 left_count= 0;                   // bal szamlalo
   U8 right_u= 0;                      // jobb feszultseg
   U8 right_u_peak= 0;                 // jobb csucs feszultseg
   U8 right_count= 0;                  // jobb szamlalo

   ADC_8BIT;                           // 8 bit felbonas
   ADC_VREF_INT;                       // belso 2V56 ref.fesz, nincs szuro kondi
   ADC_IN5;                            // 5. bemenet
   ADC_FREE_RUN;                       // folyamatos meres

   PB7_OUT;                            // ROWs 0..3
   PB6_OUT;
   PC5_OUT;
   PC4_OUT;

   PD0_OUT;                            // LEDs 0..14
   PD1_OUT;
   PD2_OUT;
   PD3_OUT;
   PD4_OUT;
   PD5_OUT;
   PD6_OUT;
   PD7_OUT;

   PC0_OUT;
   PC1_OUT;
   PC2_OUT;
   PC3_OUT;

   PB0_OUT;
   PB1_OUT;
   PB2_OUT;

   for(;;)
   {
      PORTB= 0;                             // torles
      PORTC= 0;
      PORTD= 0;
      if( row_count) row_count--;
      else row_count= 3;
      switch( row_count)                    // kijelzes
      {
         case 0:
                 led_bar_high( left_u);
                 led_point_high( left_u_peak);
                 ROW_LEFT_HIGH_ON;
                 break;
         case 1:
                 led_bar_low( left_u);
                 led_point_low( left_u_peak);
                 ROW_LEFT_LOW_ON;
                 break;
         case 2:
                 led_bar_high( right_u);
                 led_point_high( right_u_peak);
                 ROW_RIGHT_HIGH_ON;
                 break;
         case 3:
                 led_bar_low( right_u);
                 led_point_low( right_u_peak);
                 ROW_RIGHT_LOW_ON;
                 break;
      }
      if ( measure_count) measure_count--;  // meres
      else
      {
         measure_count= MEASURE_PERIOD;      
         if( left_count) left_count--;      // csucs eretekek idozitese
         else left_u_peak= 0;
         if( right_count) right_count--;
         else right_u_peak= 0;
         if ( left_nright)
         {                                  // bal meres
            left_u= ( ADCH >> 2) + 1;
            if ( left_u> 30) left_u= 30;
            if ( left_u >= left_u_peak)
            {
               left_u_peak= left_u;
               left_count= PEAK_PERIOD;
            }                 
            left_nright= 0;
            RIGHT_IN;
         }
         else
         {                                  // jobb meres
            right_u= ( ADCH >> 2) + 1;
            if ( right_u> 30) right_u= 30;
            if ( right_u >= right_u_peak)
            {
               right_u_peak= right_u;
               right_count= PEAK_PERIOD;
            }
            left_nright= 1;
            LEFT_IN;
         }
      }
      _delay_ms( 1);
   }
}




void led_bar_low( U8 n)                // LED oszlop also reszen 
{
   if( n) {LED0_ON; n--;}   
   if( n) {LED1_ON; n--;}   
   if( n) {LED2_ON; n--;}   
   if( n) {LED3_ON; n--;}   
   if( n) {LED4_ON; n--;}   
   if( n) {LED5_ON; n--;}   
   if( n) {LED6_ON; n--;}   
   if( n) {LED7_ON; n--;}   
   if( n) {LED8_ON; n--;}   
   if( n) {LED9_ON; n--;}   
   if( n) {LEDA_ON; n--;}   
   if( n) {LEDB_ON; n--;}   
   if( n) {LEDC_ON; n--;}   
   if( n) {LEDD_ON; n--;}   
   if( n) {LEDE_ON; n--;}   
}




void led_bar_high( U8 n)               // LED oszlop felso reszen
{
   if( n > 15) 
   {
      n-= 15;
      if( n) {LEDE_ON; n--;}   
      if( n) {LEDD_ON; n--;}   
      if( n) {LEDC_ON; n--;}   
      if( n) {LEDB_ON; n--;}   
      if( n) {LEDA_ON; n--;}   
      if( n) {LED9_ON; n--;}   
      if( n) {LED8_ON; n--;}   
      if( n) {LED7_ON; n--;}   
      if( n) {LED6_ON; n--;}   
      if( n) {LED5_ON; n--;}   
      if( n) {LED4_ON; n--;}   
      if( n) {LED3_ON; n--;}   
      if( n) {LED2_ON; n--;}   
      if( n) {LED1_ON; n--;}   
      if( n) {LED0_ON; n--;}   
   }
}




void led_point_low( U8 n)              // LED pont also reszen
{
   switch( n)
   {
      case  1: LED0_ON; break;
      case  2: LED1_ON; break;
      case  3: LED2_ON; break;
      case  4: LED3_ON; break;
      case  5: LED4_ON; break;
      case  6: LED5_ON; break;
      case  7: LED6_ON; break;
      case  8: LED7_ON; break;
      case  9: LED8_ON; break;
      case 10: LED9_ON; break;
      case 11: LEDA_ON; break;
      case 12: LEDB_ON; break;
      case 13: LEDC_ON; break;
      case 14: LEDD_ON; break;
      case 15: LEDE_ON;
      default: break;
   }
}




void led_point_high( U8 n)             // LED pont felso reszen
{
   switch( n- 15)
   {
      case  1: LEDE_ON; break;
      case  2: LEDD_ON; break;
      case  3: LEDC_ON; break;
      case  4: LEDB_ON; break;
      case  5: LEDA_ON; break;
      case  6: LED9_ON; break;
      case  7: LED8_ON; break;
      case  8: LED7_ON; break;
      case  9: LED6_ON; break;
      case 10: LED5_ON; break;
      case 11: LED4_ON; break;
      case 12: LED3_ON; break;
      case 13: LED2_ON; break;
      case 14: LED1_ON; break;
      case 15: LED0_ON;
      default: break;
   }
}

Itt a vége, fuss el véle, legytek az én vendégeim, innen letölthetitek a programot, panel tervet, miegymást összecsomagolva.