Jump to content
PLC Forum


Sign in to follow this  
blu81

Pic 16F876 A 28 Pin E Ingresso Analogico

Recommended Posts

blu81

ciao a tutti sono un principiante , ho un problema dovrei leggere un segnale analogico e confrontarlo con 6 valori, in base al valore far accendere un display 7 segmenti, l'alimentazione è 12 V ho un 7805 per alimentare il pic e leggo la tensione di riferimento da RA0 piedino 2, utilizzo un quarzo da 20 Mhz e due condensatori da 22 pF non riesco a capire perchè non funziona questo è il codice

#define XTAL_FREQ 20MHZ

#define _LEGACY_HEADERS

#include <pic.h>

__CONFIG (HS & WDTDIS & PWRTEN & BORDIS & LVPDIS & DUNPROT & WRTEN & DEBUGDIS & UNPROTECT);

#include "delay.c"

unsigned int valore=0;

void main(void) {

TRISA=0b00000001;

TRISB=0b00000000;

TRISC=0b00000000;

ADCON0=0b10000001;

ADCON1=0b10001110;

while(1) {

DelayUs(20);

ADGO=1;

while(ADGO)

{continue;}

valore=ADRESL + (ADRESH<<8);

if ((valore<=680)&&(valore>=574)){

PORTB=1;

PORTB=2;

PORTB=4;

}

else if ((valore<=572)&&(valore>=470)){

PORTB=1;

PORTB=2;

PORTB=4;

PORTB=16;

PORTB=8;

}

else if ((valore<=468)&&(valore>=365)){

PORTB=1;

PORTB=2;

PORTB=4;

PORTB=64;

PORTB=8;

}

else if ((valore<=363)&&(valore>=272)){

PORTB=2;

PORTB=4;

PORTB=32;

PORTB=64;

}

else if ((valore<=270)&&(valore>=189)){

PORTB=1;

PORTB=4;

PORTB=8;

PORTB=32;

PORTB=64;

}

else if ((valore<=187)&&(valore>=105)){

PORTB=1;

PORTB=4;

PORTB=8;

PORTB=16;

PORTB=32;

PORTB=64;

}

else if (valore<=100){

PORTB=1;

PORTB=16;

PORTB=32;

PORTB=64;

}

}

}

Share this post


Link to post
Share on other sites

Livio Orsini

Questo è una parte di un programma in "C" (compilatore CCS) per leggere due valori analogici sulla porta RA

Sicura mente legge gli A/D a 10 bits, usa un 877 che è il fratello maggiore, per numero di porte, dell 876. per il resto non ci sono differenze.

Ti prego di osservare la parte di inizializzazione e lettura degli A/D_C.

/*******************************************************************************

*																			  *

*			 Livio S. Orsini - LSOEngineering								 *

*			 19- 09 - 2011 V0.01											  *

*																			  *

*  Questo programma legge due termoresistori PT100, ne fa la conversione in	*

*  centigradi e la visualizza su di un display 16 x 2						  *

*																			  *

*******************************************************************************/


#include <16F877A.H>

#DEVICE ADC=10

#fuses HS,NOPROTECT,PUT,BROWNOUT,NOWDT,DEBUG

#use Delay(oscillator=20M)

#use fast_io(

#use fast_io(c)

//


#define CLK 20000000		 // Frequenza dell'oscillatore

#zero_ram


//

#byte PORT_A = 5	  // Assign at port_a the phisical PORTA address

#byte PORT_B = 6	  // Assign at port_b the phisical PORTB address

#byte PORT_C = 7	  // Assign at port_c the phisical PORTC address

#byte PORT_D = 8	  // Assign at port_d the phisical PORTC address

#byte INTCON = 0x0B

/*******************************************************************************

*																			  *

*					 Dichiarazioni variabili globali						  *

*																			  *

*******************************************************************************/

short flg_ADC0 = 0;

/******************************************************************************/

int1 flg_ff = 0;

int1 flg_ID_req = 0;

int1 flg_tmut = 0;

//

int in;

int row =0;

int arr[6];

//

int16 time_data = 0;

int16 ltime_out;

int16 temperatura = 0;

int16 temperatura1 = 0;

int16 temperatura2 = 0;

int16 dec_temper = 0;

//#include <F:\pr\prova_1820_19092011\myLCD.c>

			   //Libreria per gestione display 2*16

//#include <F:\pr\prova_1820_19092011\myMath.c>

		   //Libreria per funzioni aritmentiche speciali

void inizia()//inizializzazione

{

   boolean b;

   setup_adc(ADC_CLOCK_INTERNAL);

   setup_adc_ports(AN0_AN1_AN3);

   set_adc_channel(0);

   disable_interrupts(global);

   port_b_pullups(FALSE);			//Inizializzazioni Porte

   set_tris_a (0b11111111);	//tutti input

   set_tris_b(0xC0);		   //I primi 6 bit sono uscite!!

   set_tris_c(0x00);

   set_tris_d(0x00);

   set_tris_e(0x00);

   setup_timer_2(T2_DIV_BY_4, 255, 1);

/*-----------------26-05-2009 18.59-------------------------------------------*

  *						 inizializza diplay								 *

  * --------------------------------------------------------------------------*/

   delay_ms(20);

   lcd_init(); //Inizializza il display

// inizializzazione registro PORT_A A e timer1

   setup_counters(RTCC_INTERNAL,RTCC_DIV_4);

   setup_timer_1(T1_INTERNAL);

   set_timer1(0xB1DF);   //Caricato 65.535 - 20.000 = 45.535(0xB1DF)

						//==> 20.000 * 4 * 125nsec = 10msec

   ltime_out = 20;

// inizializzazione interrupts

   enable_interrupts(GLOBAL);

   enable_interrupts(int_timer1);

   enable_interrupts(INT_EXT);

   enable_interrupts (INT_RDA);

//inizializzazione A/D converter e periferia

   setup_adc(ADC_CLOCK_INTERNAL);

   setup_adc_ports(AN0_AN1_AN3);

   set_adc_channel(0);


}

//

/******************************************************************************/

//

void main()

{

   int i, idat;

   int a, b;

   int16 c;

   float ft;

   float fmt;

   int32 t1, t2, t3, t4;

//

   inizia();

//

	in = 0;

	while (TRUE)

	  {

		if (flg_ADC0== 1)

		  {

			  t4 = temperatura1;

			  // ------ tolta la parte visulazzazione

		 }

	  else

		{

			t4 = temperatura2;

			  // ------ tolta la parte visulazzazione


		  }

	  }


}

#int_timer1

void Timer10ms()

	{

			set_timer1(0x159F);   // Caricato 15535 ==>

				  // 60.000 * 4 * 125nsec = 30msec

				  // 65535 - 60000 = 5535(0x159F)

		   if (ltime_out >0)

			{

				ltime_out--;

		 }

		 else

		 {

				ltime_out = 33;

				if (flg_ADC0 == 1)

				   {

					 temperatura1 = read_adc();

					 set_adc_channel(1);

					 flg_ADC0 = 0;

				   }

				 else

				   {

					 temperatura2 = read_adc();

					 set_adc_channel(0);

					 flg_ADC0 = 1;

				   }

			}

   }[/code]

Edited by Livio Orsini

Share this post


Link to post
Share on other sites
blu81

grazie , ho una domanda l'ingresso analogico io l'ho messo direttamente su RA0/AN0 mi chiedevo se devo interporre qualcosa resistenza o un condensatore?

Share this post


Link to post
Share on other sites
Livio Orsini

Gio il Compilatore è CCS, almeno il mio, e le built in function hanno nomi differenti. :smile:

l'ingresso analogico io l'ho messo direttamente su RA0/AN0 mi chiedevo se devo interporre qualcosa resistenza o un condensatore?

No. Devi aver cura che il segnale non ecceda +5 V e assolutamente non vada in negativo.

Puoi mettre un gruppo RC in parallelo se credi di dover filtrare in qualche nodo il tuo segnale.

Edited by Livio Orsini

Share this post


Link to post
Share on other sites
Livio Orsini

Dovresti dire quale compilatore usi. In funzione del compilatore devi usare le sue "built in functions".

ADON = 1 come osserva Gio indica che il processore usa le porte A come ingressi analogici ed attiva il convertitore AD; in pratica dice che lo switch interno al micro è nello stato di vero per quanto riguarda la conversione.

nel compilatore che uso io le funzioni sono differenti. Leggi l'inizializzaizone e poi osserva come eseguo la commutazione tra i due canali (nell'interrupt a tempo).

Share this post


Link to post
Share on other sites
blu81

il compilatore è hi-tech ansi c , il segnale è al massimo 4 volt quindi non metto nulla all'ingresso,come frequenza di clock ho fosc/32 impostata nel registro ADCON1 ora riprovo

Share this post


Link to post
Share on other sites
giacomo56

Non spieghi perchè non funziona. Il bit ADON è settato correttamente con ADCON0=0b10000001;

Ciao.

Edited by giacomo56

Share this post


Link to post
Share on other sites
blu81

praticamente si accende solo un segmento alla volta non quelli che setto invece dell'if potrei mettere lo

Switch ?????

Share this post


Link to post
Share on other sites
Livio Orsini
praticamente si accende solo un segmento alla volta...

Forse cominciamo a capire che cosa non funziona: non è un problema di acquisizione, ma di pilotaggio.

Prendiamo come esempio il primo livello. tu hai scritto:

if ((valore<=680)&&(valore>=574)){

PORTB=1;

PORTB=2;

PORTB=4;

}
Cosa farà il processore? Prima istruzione: bit 0 =1 e tutti gli altri = 0. Secondo istruzione: bit 1 = 1 e tutti gli altri = 0 e così via. Se vuoi accendere contemporaneamente tutti i primi 4 bits devi scrivere:

{

    .......

    PORTB = 7; // oppure PORTB = 0b00000111

....

}

Usare l'istruzione "if" oppure l'istruzione "while...switch...case" è indifferente; è solo una questione di ottimizzazione dipendente da come il compilatore risolve il comando.

Share this post


Link to post
Share on other sites
blu81

grazie di tutto così funziona, ho capito in pratica alzavo solo un bit alla volta, ora ho un'altra domanda se dovessi pilotare un display a segmenti ma non alimentato a 5v ma a 9,5 v ad anodo comune posso mettere gli anodi comuni su un partitore dalla 12v e le uscite del pic come negativi???

Share this post


Link to post
Share on other sites
giacomo56

Secondo me non si può fare sia perchè non basta un partitore, sia perchè le uscite del pic, a parte il pin RA4 che è a drain aperto, non sono adatte a pilotare carichi con tensioni superiori ai 5V. Poi perchè proprio 9.5V.

Ciao.

Share this post


Link to post
Share on other sites
Livio Orsini

Come minimo devi usare un traslatore di livello tipo questi

Share this post


Link to post
Share on other sites
blu81

9,5 volt perchè il 7 segmenti che vorrei montare ha 5 led in serie e deve essere alimentato a 9,5 volt , grazie Livio sei gentilissimo

Share this post


Link to post
Share on other sites
giacomo56

I led vanno pilotati in corrente per cui puoi usare benissimo la tensione di 12V che hai a disposizione. Basta mettere in serie ad ogni segmento una resistenza che limiti la corrente. Per esempio, se vuoi una corrente di 10 mA devi usare una resistenza di (12 - 9.5) / 10 = 250 Ohm dove 9.5V è la caduta di tensione sui 5 diodi. Naturalmente i resistori vanno messi in serie alle uscite dell'integrato suggerito da Livio.

Ciao.

Share this post


Link to post
Share on other sites
blu81

ora ho un altro problema come posso fare per visualizzare il valore della variale "valore" su un display 7 segmenti premendo un pulsante? questa variabile è il contenuto della conversione analogico digitale. Dovrei dividere questo valore per 10 100 e 1000 e poi visualizzarli sul display con un ritardo di 2 secondi per cifra

Share this post


Link to post
Share on other sites
Livio Orsini

Devi fare una classica conversione binario BCD. Se non sai come fare ti emtto il codice, però è veramente banale per 3 cifre. Dividi la viarabile per 1000, il risultato è il valore della cifra più significativa. Sottrai alla variabile il valore ottenuto moltiplicato per 1000. Il risultato è il nuovo valore che dividerai per 100, ripetendo l'operazione precedente per ricerca del resto, resto che saranno le decine, cheovrai dividere per 10 per visualizzarne il valore, ripento poi l'operazione per ottenere il resto che sono le unità. Prima di effetture le operazioni si esegue il test per verificare che il valore sia >= a 1000, 100 e 10.

Share this post


Link to post
Share on other sites
blu81

lo so che è semplice, ma se mi potresti aiutare con il codice in c mi farebbe molto piacere!!!!!!!

Share this post


Link to post
Share on other sites
Livio Orsini

Questa è una routine completa che fa tutte le conversioni.

#device PIC16F877

/*-----------------------------------------------------------------------------*

 *																			 *

 *				 Conversione binario-->decimale-->ASCII					  *

 *				 Ingresso: numero compreso tra 0 e 9.999.999			   *

 *				 Uscita: array di 5 byte contenenti i caratteri ASCII	    *

 *				 Se flg_ascii == 1 allora converti inASCII, altrimenti	   *

 *				 in BCD													  *

 *																			 *

 *----------------------------------------------------------------------------*/

bin_bcd_ascii (int32 bin, short flg_ascii)


	 {

	    int i;

	    int16 arr[7];

	    int32 b;


	    i = 0;					 //0

	    if ( bin > 999999)

			    {

				   arr[i] = bin / 1000000;

				   b = arr[i];

				   b = b * 1000000; //milioni

				   bin = bin - b;

			    }

		    else

				   arr[i] = 0;

	    i++;					 //1

	    if ( bin > 99999)

			    {

				   arr[i] = bin / 100000;

				   b = arr[i];

				   b = b * 100000;

				   bin = bin - b;  //centinaia di migliaia

			    }

		    else

				   arr[i] = 0;

	    i++;					 //2

	    if ( bin > 9999)

			    {

				   arr[i] = bin / 10000;

				   b = arr[i];

				   b = b * 10000;

				   bin = bin - b;  //decine di migliaia

			    }

		    else

				   arr[i] = 0;


	    i++;					 //3

	    if ( bin > 999)

			    {

				   arr[i] = bin / 1000;

				   b = arr[i];

				   b = b * 1000;

				   bin = bin - b;  // migliaia

			    }

		    else

				   arr[i] = 0;


	    i++;					 //4

	    if ( bin > 99)

			    {

				   arr[i] = bin / 100;

				   b = arr[i];

				   b = b * 100;

				   bin = bin - b;  //centinaia

			    }

		    else

				   arr[i] = 0;


	    i++;					 //5

	    if ( bin > 9)

			    {

				   arr[i] = bin / 10;

				   b = arr[i];

				   b = b * 10;

				   bin = bin - b;  //decine

			    }

		    else

				   arr[i] = 0;


	    i++;					 //6

	    if (( bin < 10) && (bin >= 0))

				   arr[i] = bin ;

		    else

				   arr[i] = 0;

  if (flg_ascii == 1)

	 {

		 for ( i = 0; i < 7; i++)

		    {

			  arr[i] = arr[i] + 0x30;

		    }

	 }

	 return (arr[i]);

Share this post


Link to post
Share on other sites
blu81

scusatemi ma questo problema mi sta facendo uscire pazzo e non capisco perchè non funziona , leggo un valore analogico dalla porta AN0 (da 0,4 a 4,4 volt) faccio la media del valore con il ciclo for lo memorizzo nell'eprom e faccio il confronto con il precedente

per il primo valore tutto bene ma il secondo mi da sempre errore?????

if (!BTN1) // se pulsante1 premuto (quando è premuto, porta il pin allo stato logico basso)

{

DelayMs(200); // ritardo per antirimbalzo

if (!BTN1) // se dopo 100ms il pulsante è ancora premuto, non si tratta di un rimbalzo

{

PORTB=0b01000000;

DelayS(1);

PORTB=0b00000000;

DelayS(1);

PORTB=0b01000000;

DelayS(1);

PORTB=0b00000000; // accende e spegne il segmento centrale per inizio programmazione

DelayS(1);

PORTB=0b01110001; // F°

media=0;

vm=0;

pss=1;

while(pss){

PORTB=0b01110001; // F°

if(!BTN1) {

DelayMs(200);

if(!BTN1) {

valore=0;

media=0;

for(i=1;i<=20;i++) {

DelayMs(400);

ADGO=1;

while(ADGO)

{continue;}

valore=ADRESL + (ADRESH<<8);

media + = valore;

}

vm=media/20;

eei_write_int(vm,0); //memorizzo il valore della F a 16 bit nella posizione 0 ma occupa anche la 1

vlettof=eei_read_int(0);

if(vlettof > 800) {

pss=0;

}

else {

DelayS(1);

PORTB=0b01111001;

DelayS(1);

}

}

}

}

DelayS(1);

PORTB=0b00000110; // 1°

media=0;

vm=0;

pss=1;

while(pss){

PORTB=0b00000110; // 1°

if(!BTN1) {

DelayMs(200);

if(!BTN1) {

valore=0;

media=0;

for(i=1;i<=20;i++) {

DelayMs(400);

ADGO=1;

while(ADGO)

{continue;}

valore=ADRESL + (ADRESH<<8);

media + = valore;

}

vm=media/20;

eei_write_int(vm,14); //memorizzo il valore della 1 a 16 bit nella posizione 2 ma occupa anche la 3

vletto1=eei_read_int(14);

if(vlettof > vletto1) {

pss=0;

}

else {

DelayS(1);

PORTB=0b01111001;

DelayS(1);

}

}

}

}

sempre lo stesso codice poi

if((vlettof > vletto1 )&&(vletto1 > vletto2)&&(vletto2 > vletto3)&&(vletto3>vletto4)&&(vletto4>vletto5)&&(vletto5 > vletto6)){

DelayS(1);

PORTB=0b01110011;

DelayS(1);

PORTB=0b01111001;

DelayS(1);

PORTB=0b01110011;

DelayS(1);

PORTB=0b01110011;

DelayS(1);

PORTB=0b01111001;

DelayS(1);

}

else {

DelayS(1);

PORTB=0b01110110;

DelayS(1);

PORTB=0b00111111;

DelayS(1);

PORTB=0b01110110;

DelayS(1);

PORTB=0b00111111;

DelayS(1);

}

dimenticavo le funzioni di scrittura e lettura sono queste

unsigned int eei_read_int(unsigned char address)

{

unsigned int BH; // byte alto (8 bit più a sinistra del valore INT)

unsigned int BL; // byte basso (8 bit più a destra del valore INT)

unsigned int EEVALUE; // valore di ritorno

BL=eei_read_byte(address);

BH=eei_read_byte(address+1);

EEVALUE=BL | (BH<<8); // sommo (con OR) il byte basso e il byte alto spostato di 8 bit verso sinistra

return EEVALUE;

}

scrittura

void eei_write_int(unsigned int data, unsigned char address)

{

// il byte basso è scritto nell'indirizzo address

// il byte alto è scritto nell'indirizzo address+1

unsigned int BH; // byte alto (8 bit più a sinistra del valore INT)

unsigned int BL; // byte basso (8 bit più a destra del valore INT)

BL = data & 0xFF; // metto a zero gli 8 bit più a sinistra

BH = data >> 8; // sposto il valore di 8 bit a destra

eei_write_byte(BL,address);

eei_write_byte(BH,address+1);

}

Share this post


Link to post
Share on other sites
Livio Orsini
per il primo valore tutto bene ma il secondo mi da sempre errore

Errore di scrittura o di comparazione? se usi MPLAB con pickit o ICD puoi vedere direttamente il dato scritto nella EEPROM.

Io non uso HiTech ma CCS e le built in function son differrenti quindi non ti saprei dire se le procedure di scrittura e lettura della EEPROM sono corrette.

Share this post


Link to post
Share on other sites
blu81

si infatti lo visualizzo e ho FF FF o altri valori grandissimi questo è il problema, ma la cosa strana è che che per il valore precedente lo legge scrive e compara perfettamente ed anche per i successivi per questo non ne vuole proprio sapere

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...