ATmega8 - ADC újragondolva

2015-12-31

 

Volt egy kis időm, megint nekifogtam a kedvenc kivezérlés mérő témámnak. Na de hogyan is kell egy ATmega8-cal feszültséget mérni? Időközben ugye kigondoltam, hogy tréfás angol betüszavakkal elkeresztelt nevű regiszterek, meg bitek helyett sokkal célszerűbb inkább olyan makrókat készíteni, amivel lényegesen egyszerűbben vezérelhetjük a mikrovezérlőnket. Jó alaposan belegabalyodtam, és egy fél napot vadásztam a biteket, a szó szoros értelmében :(. A nagyjából elkészült áramkörömet is vissza kellett bontanom. Ez a méreg... Viszont ha már ezzel megdolgoztam, úgy gondolom érdemes közzé tennem mire jutottam.

Nem biztos, hogy minden újdonság. A tkiraaly_atmega8.h-t kiegészítettem egy kis note-tal, segít ha hirtelen kell a tok bekötése.


********************************************************************************
*
*                              +---------+
*               NRESET - PC6  ++ 1    28 |  PC5 - ADC5 - SCL
*                  RXD - PD0  |  2    27 |  PC4 - ADC4 - SDA
*                  TXD - PD1  |  3    26 |  PC3 - ADC3
*                 INT0 - PD2  |  4    25 |  PC2 - ADC2
*                 INT1 - PD3  |  5    24 |  PC1 - ADC1
*             XCK - T0 - PD4  |  6    23 |  PC0 - ADC0
*                        VCC  |  7    22 |  GND
*                        GND  |  8    21 |  AREF
*         XTAL1 - OSC1 - PB6  |  9    20 |  AVCC
*         XTAL2 - OSC2 - PB7  | 10    19 |  PB5 - SCK
*                   T1 - PD5  | 11    18 |  PB4 - MISO
*                 AIN0 - PD6  | 12    17 |  PB3 - MOSI - OC2
*                 AIN1 - PD7  | 13    16 |  PB2 - NSS - OC1B
*                 ICP1 - PB0  | 14    15 |  PB1 - OC1A
*                             +----------+
*
********************************************************************************

Szintén a tkiraaly_atmega8.h-t kiegészítettem a port lábak állítgatását segítő makrókkal, ide csak néhány mintát tetem, a header file-ban az összes megvan.


// Port labak allitasa

#define PB0_OUT              BS( DDRB, 0)          
#define PB1_OUT              BS( DDRB, 1)
#define PB2_OUT              BS( DDRB, 2)
#define PB3_OUT              BS( DDRB, 3)

#define PB0_1                BS( PORTB, 0)          
#define PB1_1                BS( PORTB, 1)          
#define PB2_1                BS( PORTB, 2)          
#define PB3_1                BS( PORTB, 3)          

#define PB0_0                BC( PORTB, 0)          
#define PB1_0                BC( PORTB, 1)          
#define PB2_0                BC( PORTB, 2)          
#define PB3_0                BC( PORTB, 3)          

Ezek az érdekesebb részek. A header file feldolgozása során meghatározásra kerül a ADC_PRESC, az AD órajel előosztójának értéke. Persze ha mást akarunk, egyszerűen előbb kell létrehoznunk, a továbbiakban azt az értéket fogja a többi makró használni. Azután meghatározhatjuk, hány bites felbontást szeretnénk. A nagy számok bűvöletében ne feledkezzünk meg, hogy precízebb méréshez precízebb analóg körítés is szükséges, sok mindenre jó az a 8 bit. Azután meg kell határozzuk az AD referencia feszültség forrását. Talán legegyszerűbb a belsőt használni, ekkor 10mV-os lépéseket kapunk. Persze azt is ki kell jelöljük, melyik bemeneten szeretnénk mérni. Azután meghatározhatjuk, hogy az AD folyamatoan ciklikusan mérjen, vagy az általunk meghatározott időpontokban. Gondolom takarékosságból le is lehet az AD-t állítani, illetve újra lehet engedélyezni. Ne rettenjünk meg a számos lehetőségtől, mindjárt két példával megvilágítom a hogyan lehet a makróimat használni.


// ADC-hez

// ADC orajel elooszto valaszasa, lehetoleg nagy orajelhez
#ifndef ADC_PRESC
   #if F_CPU > 12 MHZ                      // div 128
      #define                ADC_PRESC  0B00000111
   #else
      #if F_CPU > 6 MHZ                    // div 64
         #define             ADC_PRESC  0B00000110
      #else
         #if F_CPU > 3 MHZ                 // div 32
            #define          ADC_PRESC  0B00000101
         #else
            #if F_CPU > 1,5 MHZ             // div 16
               #define       ADC_PRESC  0B00000100 
            #else
               #if F_CPU > 0,75 MHZ         // div 8
                  #define    ADC_PRESC  0B00000011 
               #else
                  #if F_CPU > 0,37 MHZ      // div 4
                     #define ADC_PRESC  0B00000010 
                  #else                     // div 2
                     #define ADC_PRESC  0B00000001 
                  #endif
               #endif
            #endif
         #endif
      #endif
   #endif
#endif

#define ADC_8BIT             BS( ADMUX, ADLAR)                       // 8 bit felbontas, ADCH
#define ADC_10BIT            BC( ADMUX, ADLAR)                       // 10 bit felbontas, ADCH+ ADCL

#define ADC_VREF_AREF        ADMUX&= 0B00111111                      // Uref - AREF lab
#define ADC_VREF_AVCC        ADMUX&= 0B00111111; ADMUX|= 0B01000000  // Uref - AVCC lab (analog tap))
#define ADC_VREF_INT                             ADMUX|= 0B11000000  // Uref - belso 2V56

#define ADC_IN0              ADMUX&= 0B11110000                      // bemenet valasztas
#define ADC_IN1              ADMUX&= 0B11110000; ADMUX|= 0B00000001
#define ADC_IN2              ADMUX&= 0B11110000; ADMUX|= 0B00000010
#define ADC_IN3              ADMUX&= 0B11110000; ADMUX|= 0B00000011
#define ADC_IN4              ADMUX&= 0B11110000; ADMUX|= 0B00000100
#define ADC_IN5              ADMUX&= 0B11110000; ADMUX|= 0B00000101
#define ADC_IN6              ADMUX&= 0B11110000; ADMUX|= 0B00000110
#define ADC_IN7              ADMUX&= 0B11110000; ADMUX|= 0B00000111
#define ADC_INT_1V3          ADMUX&= 0B11110000; ADMUX|= 0B00001110  // belso ref.
#define ADC_INT_0V                               ADMUX|= 0B00001111  // belso ref.

#define ADC_FREE_RUN         ADCSRA= 0B11100000+ ADC_PRESC           // ADC folyamatos
#define ADC_START            ADCSRA= 0B11001000+ ADC_PRESC           // ADC inditas IT-hez

#define ADC_DISABLE          BC( ADCSRA, ADEN)                       // ADC tiltasa
#define ADC_ENABLE           BS( ADCSRA, ADEN)                       // ADC tiltas feloldasa

Az első példaprogram arról szól, hogyan kell az AVR AD-ját folyamatos mérésre használni, mint mondjuk egy szimpla multimétert. A program végét lehagytam, már korábban ismertettem a soros port használatát, meg a kpri()-t. A kód tulajdonképpen a main()-ban lévő néhány sor. Egyetlen makró beállítja a soros portot. Azután szép sorban beállítjuk az AD-t, és az ADC_FREE_RUN-ra elkezd folyamtosan mérni. Nekünk csak annyi a dolgunk, hogy időnként, ebben az esetben 500msec-ként, kiolvassuk az ADHC regisztert, és decimális formában kiküldjük a soros portra.


/*******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2015.12.31.
*   Chip         -  Atmel ATmega8
*   Compiler     -  avr-gcc (WinAVR)
*   Program      -  ADC demo, szabadon futo meres
*   
********************************************************************************
*   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


#include "tkiraaly_atmega8.h"
#include <avr/pgmspace.h>
#include <stdio.h>
#include <stdarg.h>
#include <util/delay.h>


char * kpri( const char *, ...);                 // sajat printf
char * kpri_u2s( U16, U16, U8, U8);              // szam atalakitasa string-re
char * kpri_pad( char *, char *, U8, U8, U8);    // string levagasa, feltoltese, athelyezese
void usartra( char *);                           // string kiiras USART-ra
void usart_putc( char);                          // egy betu kiirasa USART-ra


int main()
{
   USART_SET_9600_8N1;
   
   ADC_8BIT;
   ADC_VREF_INT;                       // belso 2V56 ref.fesz, nincs szuro kondi
   ADC_IN5;                            // 5. bemenet
   ADC_FREE_RUN;
  
   usartra( kpri( PSTR( "Meres teszt\r")));
   for(;;)                             // meres 0,5 sec-enkent
   {
      usartra( kpri( PSTR( "M - %d\r"), ADCH));
      _delay_ms( 500);
   }
   return 0;
}

...

A második példaprogramban az AD meghatározott időpontban kerül indításra az ADC_START makróval, majd a megszakítás rutinban kerül az ADCH kiolvasásra. Ez részben azért jó, mert nem fut folyamatosan az AD, energia takarékossabb, másrészt meghatározott időpontban indíthatjuk a mérést. Jó lehet egy mini szkóp projethez is, de méréseknél gyakori feladat, hogy adott időpontban mérjünk. Az AVR konfigurálása kiegyészül a megszakítások engedélyezésével, de látható, hogy viszonylag áttekinthetően és egyszerűen vezérelhetjük az AVR-t.


/*******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2015.12.31.
*   Chip         -  Atmel ATmega8
*   Compiler     -  avr-gcc (WinAVR)
*   Program      -  ADC demo, utemezett meres, megszakitassal
*   
********************************************************************************
*   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


#include "tkiraaly_atmega8.h"
#include <avr/pgmspace.h>
#include <stdio.h>
#include <stdarg.h>
#include <util/delay.h>


volatile U8 v= 0;


char * kpri( const char *, ...);                 // sajat printf
char * kpri_u2s( U16, U16, U8, U8);              // szam atalakitasa string-re
char * kpri_pad( char *, char *, U8, U8, U8);    // string levagasa, feltoltese, athelyezese
void usartra( char *);                           // string kiiras USART-ra
void usart_putc( char);                          // egy betu kiirasa USART-ra


ISR( ADC_vect)			                             // IT, ha AD meres lefutott
{
   v= ADCH;
}


int main()
{
   USART_SET_9600_8N1;
   
   ADC_8BIT;
   ADC_VREF_INT;                       // belso 2V56 ref.fesz, nincs szuro kondi
   ADC_IN5;                            // 5. bemenet
   IT_ADC_ENABLE;
   IT_ENABLE;
  
   usartra( kpri( PSTR( "Meres teszt\r")));
   for(;;)                             // meres 0,5 sec-enkent
   {
      ADC_START;
      usartra( kpri( PSTR( "M - %d\r"), v));
      _delay_ms( 500);
   }
   return 0;
}

...

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