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.