TODOPIC
Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: danipic19 en 14 de Enero de 2010, 12:17:16
-
Buenas,
Me presento, soy nuevo en el foro, y aunque llevo un par de meses que os visito nunca había escrito ningún post. Tengo un problema: a principios de la semana que viene tengo que entregar el proyecto final de carrera y no consigo programar el PIC16F877-20/p (el de 40 pines).
A continuación descrivo los problemas y adjunto el código que he hecho. Por cierto, los comentarios no estan muy bien hechos...
1. ADCs. Necesito leer 3 señales analógicas, y diría que lo consigo pero creo que tarda mucho, ya que para realizar el programa después de leer estas señales me tarda unos 40 segundos... Creo que el ADC está mal configurado.
#include <16f877A.h>
#device ADC=8 // ADC devuelven 8 bits
#fuses NOPROTECT,NOCPD,NOLVP,NOWDT,XT,PUT //Configuracion de timers, reloj, proteccion de datos...
#use fast_io (A) //Input y output sin reprogramar los pines, como lo haria el #use standard_io
#use fast_io (B)
#use delay(clock=20000000) //Reloj de 4MHz
#use standard_io(A)
#use standard_io(B)
#use standard_io(C)
#use standard_io(D)
#use standard_io(E)
#int_rtcc //rutina que até a interrupció
int8 sensor1, sensor2, consigna, sortida; //Variables para el resultado de la conversión AD
float sens1d, sens2d, consd;
float K1, K2, K3, vh1pt, vh2pt, conspt, vpt;
float incvh1, incvh2, incvcons, incK1, incK2, incK3, invconsfinal, out1, inputact=0, integrador;
float inputant=0, integant=0, out2, outfinal;
void main()
{
//Se activa el ADC y se selecciona el canal RA0/AN0. Frecuencia de trabajo Fosc/32
set_tris_b(0x00); //portb como salida(RB0,las demas desactivadas)
set_tris_e(0x07);
set_tris_d(0x00);
disable_interrupts(GLOBAL); //todas las interrupciones desactivadas
output_b(0x00);
output_d(0x00);
setup_adc(ADC_CLOCK_DIV_16); //Ajusta frecuencia de muestreo del ADC
setup_adc_ports(AN0);
K1=-0.443;
K2=-2.129;
K3=-22.76;
vh1pt=3.06;
vh2pt=3.95;
conspt=vh2pt;
vpt=7.8;
//PROGRAMA PRINCIPAL
while(1)
{
set_adc_channel(0); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
sensor1=read_adc(); //Inicia la conversión y lee el resultado
sens1d=sensor1;
set_adc_channel(1); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
sensor2=read_adc(); //Inicia la conversión y lee el resultado
sens2d=sensor2;
set_adc_channel(2); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
consigna=read_adc(); //Inicia la conversión y lee el resultado
consd=consigna;
incvh1=sens1d-vh1pt;
incvh2=sens2d-vh2pt;
incvcons=consd-conspt;
invconsfinal=incvh2-incvcons;
incK2=K2*incvh1;
incK3=K3*incvh2;
out1=incK1+incK2;
inputact=invconsfinal;
integrador=integant+((inputact+inputant)*(1/2));//1=3.138 si interrupció
inputant=inputact;
integant=integrador;
out2=invconsfinal*K1*integrador;
outfinal=out2+out1+vpt;
sortida=outfinal*256/10;
output_b(sortida);
}
}
De esta manera cre que hacen algo. Si los configuro como he visto en numerosos ejemplos me dice que no reconoce AN1 y AN2:
setup_adc_ports(AN0); //RA0 entrada analógica
set_adc_channel(0); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
sensor1=read_adc(); //Inicia la conversión y lee el resultado
sens1d=sensor1;
setup_adc_ports(AN1); //RA1 entrada analógica
set_adc_channel(1); //Selección del canal 1 (RA1)
delay_us(10); //Temporiza 10uS
sensor2=read_adc(); //Inicia la conversión y lee el resultado
sens2d=sensor2;
setup_adc_ports(AN2); //RA2 entrada analógica
set_adc_channel(2); //Selección del canal 2 (RA2)
delay_us(10); //Temporiza 10uS
consigna=read_adc(); //Inicia la conversión y lee el resultado
consd=consigna;
2. Interrupción: Todo mi porograma debería estar dentro de una interrupción, ya que lo quiero "ejecutar" cada 5 segundos:
Con el código que pongo a continuación obtengo una interrupción cada 3,138 segundos aproximadamente y lo que hago es cambiar de estado el puerto_b, cosa que veo imposible con un TIMER0 de 8 bits y el valor de preescaler que le doy:
#include <16f877.h>
#fuses NOPROTECT,NOCPD,NOLVP,NOWDT,XT,PUT //Configuracion de timers, reloj, proteccion de datos...
#use fast_io (A) //Input y output sin reprogramar los pines, como lo haria el #use standard_io
#use fast_io (B)
#use delay(clock=20000000) //Reloj de 4MHz
#use standard_io(A)
#use standard_io(B)
#use standard_io(C)
#use standard_io(D)
#use standard_io(E)
byte puerto_b;
#int_rtcc //rutina que até a interrupció
void interrupcion()
{
puerto_b=~puerto_b;
output_b(puerto_b);
set_RTCC(0);
}
void main()
{
set_tris_b(0x00);
puerto_b=0x00;
output_b(puerto_b);
setup_counters (RTCC_INTERNAL, RTCC_DIV_4); //programació timer0
set_RTCC(0);
enable_interrupts(INT_RTCC); // Habilita interrupcion timer0.
enable_interrupts(GLOBAL); // Habilita todas las interrupciones.
while(1)
{
}
}
Sé que hago algo mal, que no configuro bien el timer o lo que sea, lo he probado con el TIMER1 que es de 16 bits y tampoco consigo que funcione...
3. Integrador: Quien tenga conocimientos de regulación automàtica lo conocerà mejor. La fórmula bàsica del integrador es:
integrador=integant+((inputact+inputant)*(T/2));
donde:
integant=integrador anterior
inputact=entrada actual
inputant=entrada anterior
T=periodo que pasa entre que hace el cálculo hasta que lo vuelve a realizar.
El problema que tengo es que no sé como encontrar el valor de T... Si lo hiciera dentrro de una interrupción de 5 segundos pues T sería 5 segundos aproximadamente.
Alguien me puede ayudar?
Gracias de antemano.
Saludos!
-
Por cierto, para la configuración de los ADCs si lo hago de la segunda forma, como los ejemplos que he visto, y en vez de incluir la libreria del 16F877 añado la del 16F877A no me da errores al compilar, pero entonces no creo que pueda programar el PIC...
-
cuando usas cristal de 20Mhz el fuse debe ser HS.
Saludos
-
Hola jhozate,
Te refieres a cambiar esto:
#fuses NOPROTECT,NOCPD,NOLVP,NOWDT,XT,PUT //Configuracion de timers, reloj, proteccion de datos...
Por esto?
#HS NOPROTECT,NOCPD,NOLVP,NOWDT,XT,PUT //Configuracion de timers, reloj, proteccion de datos...
-
O esto?
#fuses NOPROTECT,HS,NOCPD,NOLVP,NOWDT,XT,PUT //Configuracion de timers, reloj, proteccion de datos...
-
si, pero elimina el XT.
Adicionalmente te falta configurar tus canales analogos, necesitas tres y solo has puesto AN0.
Si quieres lograr 5 segundos puedes hacer una base de tiempo y sobre esa contar multiples interrrupciones hasta lograr 5 segundos.
-
Los canales analògicos en teoria se confuguran como muestro a continuación, pero al compilar me dice que no reconoce AN1 y AN2, en cambio para AN0 no dice nada:
setup_adc_ports(AN0); //RA0 entrada analógica
set_adc_channel(0); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
sensor1=read_adc(); //Inicia la conversión y lee el resultado
sens1d=sensor1;
setup_adc_ports(AN1); //RA1 entrada analógica
set_adc_channel(1); //Selección del canal 1 (RA1)
delay_us(10); //Temporiza 10uS
sensor2=read_adc(); //Inicia la conversión y lee el resultado
sens2d=sensor2;
setup_adc_ports(AN2); //RA2 entrada analógica
set_adc_channel(2); //Selección del canal 2 (RA2)
delay_us(10); //Temporiza 10uS
consigna=read_adc(); //Inicia la conversión y lee el resultado
consd=consigna;
Lo de crear la base de tiempo de 5 segundos no lo entiendo (soy muy novato)...
Gracias por la ayuda!
-
Si no me equivoco no puedes escoger arbitrariamente el canal analogo, verifica la hoja de datos del pic y te daras cuenta q hay restricciones con ello. Asi que debes definirlo, si quieres 3 canales
entonces podria ser asi:
setup_adc(adc_clock_div_16);
setup_adc_ports(AN0_AN1_AN3) //obligado si solo quieres 3 canales, hoja de datos
y cuando necesites leerlos solo haces
set_adc_channel(0);
valor=read_adc();
delay_us(10);
set_adc_channel(1);
valor1=read_adc();
delay_us(10);
set_adc_channel(3);
valor3=read_adc();
la base de tiempo puedes generarla con el timer0, por ejemplo para lograr 100mS, puedes hacer una de base de tiempo de 50mS, y contarla dos veces:
#int_timer0
void timer0_isr()
{
i++;
if(i==2) // verificas si ya pasaron dos interrupciones de 50mS
{
....aqui lo q quieres hacer
}
set_timer(cargar_del_timer);
}
-
revisa el archivo 16f877A.h ahí te indican cuales son las posibilidades que tienes para configurar el puerto analógico y todo lo demás.
-
Gracias a todos por la ayuda.
Acabo de ir a comprar un cristal de 4MHz por si acaso (el que usáis la mayoría de vosotros).
Ahora voy a probar lo que me habéis enseñado. SI no funciona le cambio el cristal al PIC y vuelvo a probar.
Agradezco muchísimo vuestra ayuda.
Si acaba funcionando os tendré que invitar a unas birras!
-
Os comento,
El ADC funciona pero programando con el siguiente código me da la misma salida en el puerto_b tanto si entro la entrada analógica por AN0, como por AN1, como por AN3, y sólo le doy orden de que de salida al puerto_b con AN3. :S
Por qué pasa esto?
while(1)
{
setup_adc_ports(AN0_AN1_AN3); //RA0 entrada analógica
set_adc_channel(0); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
resultado1=read_adc(); //Inicia la conversión y lee el resultado
//output_b(resultado1);
set_adc_channel(1); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
resultado2=read_adc(); //Inicia la conversión y lee el resultado
//output_b(resultado2);
set_adc_channel(2); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
resultado3=read_adc(); //Inicia la conversión y lee el resultado
output_b(resultado3);
-
Perdón, tampoco hace lo de antes... Le digo que ponga en el port_b el resultado3, y para una entrada analógica en AN0 y en AN1 pone el resultado en el port_b (cosa que no tendría que hacer), y para entrada analógica en AN3 no hace nada... Me había equivocado en el código ya que en la última lectura había puesto set_adc_channel(2) y es set_adc_channel(3), pero aun así hace el tonto...
while(1)
{
setup_adc_ports(AN0_AN1_AN3); //RA0 entrada analógica
set_adc_channel(0); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
resultado1=read_adc(); //Inicia la conversión y lee el resultado
//output_b(resultado1);
set_adc_channel(1); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
resultado2=read_adc(); //Inicia la conversión y lee el resultado
//output_b(resultado2);
set_adc_channel(3); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
resultado3=read_adc(); //Inicia la conversión y lee el resultado
output_b(resultado3);
-
Ahora me pone en el port_b el resultado tanto si entro la entrada analògica en AN0 ó AN1 ó AN3. Pongo el código completo para ver si encontráis el error (yo no lo encuentro...):
#include <16f877.h>
#device ADC=8 // ADC devuelven 8 bits
#fuses NOPROTECT,HS,NOCPD,NOLVP,NOWDT //Configuracion de timers, reloj, proteccion de datos...
#use fast_io (A) //Input y output sin reprogramar los pines, como lo haria el #use standard_io
#use fast_io (B)
#use delay(clock=20000000) //Reloj de 20MHz
int8 resultado0, resultado1, resultado3; //Variable para el resultado de la conversión AD
void main()
{
//Se activa el ADC y se selecciona el canal RA0/AN0. Frecuencia de trabajo Fosc/32
set_tris_b(0x00); //portb como salida(RB0,las demas desactivadas)
set_tris_d(0x00);
disable_interrupts(GLOBAL); //todas las interrupciones desactivadas
output_b(0x00);
output_d(0x00);
setup_adc(adc_clock_div_8); //Ajusta frecuencia de muestreo del ADC
while(1)
{
setup_adc_ports(AN0_AN1_AN3); //RA0 entrada analógica
set_adc_channel(0); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
resultado0=read_adc(); //Inicia la conversión y lee el resultado
//delay_us(10); //Temporiza 10uS
//output_b(resultado0);
set_adc_channel(1); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
resultado1=read_adc(); //Inicia la conversión y lee el resultado
//delay_us(10); //Temporiza 10uS
//output_b(resultado1);
set_adc_channel(3); //Selección del canal 0 (RA0)
delay_us(10); //Temporiza 10uS
resultado3=read_adc(); //Inicia la conversión y lee el resultado
//delay_us(10); //Temporiza 10uS
output_b(resultado3);
}
}
-
Si entro 3 entradas analógicas a los pines AN0, AN1 y AN3 al mismo tiempo, manda la primera entrada que entro, es decir, que en el port_b obtengo la primera entrada que he conectado... :S
-
Punto 1 resuelto!
El problema es que no se pueden dejar las entradas analógicas al aire! Hay que conectarlas como mínimo a masa!
Ahora a por el punto 2!
-
Punto 2 resuelto!!!
Ahora falta el punto 3 que en teoria es el más fàcil, pero la pregunta es:
Si el programa se ejecutará, por ejemplo, cada 2 segundos, cuánto tarda en ejecutarse el código? Es decir, que el programa no se ejecuta hasta que salta la interrupción, pero cuánto tarda en ejecutarse el código después de la instrucción? Supongo que será del orden de microsegundos o algún milisegundo, no?
-
En el foro hay un articulo sobre el modulo analógico (Usa el buscador) para que lo leas detalladamente, así te darás cuenta que 10us no es correcto, por ejemplo y entenderás que estas haciendo. También es necesario que le des una leída en general al datasheet del PIC para tener una idea global, como para identificar un fuse XT o HS, ect.
Saludos!
-
He aqui (http://www.todopic.com.ar/foros/index.php?topic=27929.msg230402#msg230402) el modulo conversor A/D de los 16F87X/PIC18F2455/2550/4455/4550, no tiene pierde.
-
Suky,
He buscado muchas veces en el buscador y no lo había encontrado, está muy bien explicado.
Por qué pones ""setup_adc(ADC_OFF);"" al finalizar la lectura? En mi programa debo usar 3 ADCs y leer contínuamente, por lo que no tengo que poner "setup_adc(ADC_OFF);", no? Y la lectura le tengo que poner dentro del "while(1){", no?
Ahora mismo tengo el ultimo código que he colgado y funcionan correctamente.
Mi problema ahora es la interrupción. Lo de que la he programado a 10ms es correcto, ya que:
T(timer0)=4*(1/Fosc)*PREESCALER*(256-PRECARGA)
Donde 256=valor máximo que se puede representar con los 8 bits del timer0.
Entonces:
0,010=4*(1/20e6)*256*(256-61)
Así que
PREESCALER=256
PRECARGA=61
Y la interrupción la obtengo cada 10ms (COMPROBADO CON OSCILOSCOPIO).
El problema que tengo es que cuando pongo el contador como dice jhozate el tiempo que quiero no es correcto:
#int_timer0
void timer0_isr()
{
i++;
if(i==2) // verificas si ya pasaron dos interrupciones de 50mS
{
....aqui lo q quieres hacer
}
set_timer(cargar_del_timer);
}
Si pongo un contador que llegue hasta 200, en teoría 200*0.010s = 2 segundos, y calculado en plan chapucero este tiempo con un cronómetro (calculando la media) obtengo un tiempo de 2,468 aproximadamente. Me sobran 0.468 segundos que no sé si es el tiempo que tarda el PIC en hacer la comparación del contador, pero creo que esto es una bestialidad!
A continuación pongo el código del programa para la interrupción cada 2 segundos, y lo único que debe hacer es cambiar el estado del puerto B cuando el contador llega a 200. Qué hago mal?
#include <16f877.h>
#fuses NOPROTECT,HS,NOCPD,NOLVP,NOWDT,PUT //Configuracion de timers, reloj, proteccion de datos...
#use fast_io (A) //Input y output sin reprogramar los pines, como lo haria el #use standard_io
#use fast_io (B)
#use delay(clock=20000000) //Reloj de 4MHz
#use standard_io(A)
#use standard_io(B)
#use standard_io(C)
#use standard_io(D)
#use standard_io(E)
byte puerto_b;
int comptador;
#int_rtcc //rutina que até a interrupció
void timer0_interrupcion()
{
comptador++;
if(comptador==200)
{
puerto_b=~puerto_b;
output_b(puerto_b);
set_RTCC(61);
comptador=0;
}
}
void main()
{
set_tris_b(0x00);
puerto_b=0x00;
output_b(puerto_b);
setup_counters (RTCC_INTERNAL, RTCC_DIV_256); //programació timer0
set_RTCC(61);
enable_interrupts(INT_RTCC); // Habilita interrupcion timer0.
enable_interrupts(GLOBAL); // Habilita todas las interrupciones.
comptador=0;
while(1)
{
}
}
-
En el ejemplo dice que se toma un valor y se apaga el conversor, como tu necesitas tomar continuamente no debes apagarlo. Y si, las conversiones deben ir dentro del while para que se cumpla lo que necesitas, una captura continua...
Respecto a que haces mal con la temporización, pues la actualización del timer, no debe ser solo cuando es 200 sino siempre y para una mayor precisión debe ser la primer tarea al ingresar a la interrupción.
Saludos!
-
Ya me funciona la interrupción perfectamente gracias al último comentario de Suky, bueno, gracias a todos los comentarios!
Gracias Suky y jhozate por la ayuda que me habéis ofrecido. Si os pudiera ofrecer algo a cambio lo haría sin duda.
Ahora sólo me queda crear el código de todo el conjunto, y probarlo en el sistema de dpósitos de agua real.
Adjunto el código para la generación de una interrupción cada 2 segundos por si alguien lo necesita. El programa lo único que hace es cambiar el estado del port_b cada 2 segundos. Si quieres más o menos segundos solo hay que cambiar la variable contador, teniendo en cuenta que como máximo se puedo poner "contador=255" ya que contador es una variable int. Si se necesita más tiempo supongo que con cambiar int por byte o añadir otro contador es suficiente.
PD: no os fijéis mucho en los comentarios del código.
Gracias a todos por la ayuda!!!
#include <16f877.h>
#fuses NOPROTECT,HS,NOCPD,NOLVP,NOWDT,PUT //Configuracion de timers, reloj, proteccion de datos...
#use fast_io (A) //Input y output sin reprogramar los pines, como lo haria el #use standard_io
#use fast_io (B)
#use delay(clock=20000000) //Reloj de 4MHz
#use standard_io(A)
#use standard_io(B)
#use standard_io(C)
#use standard_io(D)
#use standard_io(E)
int8 puerto_b;
int comptador;
#int_rtcc //rutina que até a interrupció
void timer0_interrupcion()
{
set_RTCC(61);
comptador++;
if(comptador==200)
{
puerto_b=~puerto_b;
output_b(puerto_b);
comptador=0x00;
}
}
void main()
{
set_tris_b(0x00);
puerto_b=0x00;
output_b(puerto_b);
setup_counters (RTCC_INTERNAL, RTCC_DIV_256); //programació timer0
set_RTCC(61);
enable_interrupts(INT_RTCC); // Habilita interrupcion timer0.
enable_interrupts(GLOBAL); // Habilita todas las interrupciones.
comptador=0x00;
while(1)
{
}
}
-
:-/ q bueno q lo hayas resuelto, q pasó con lo del control?..ojala q puedas presentar el proyecto en el foro.
Saludos
-
jhozate, de momento no me funciona todo el conjunto del programa.
Déjame que esta semana realice pruebas con el sistema completo y a ver si consigo que funcione.
En cuanto tenga algo colgará todo el programa.
Gracias por todo a todos los que me habéis ayudado.
Un saludo!
-
El compilador me acmite
SETUP_ADC_PORTS(ra0_analog);//valido
setup_adc_ports(AN0_AN1_AN3);
pero porque no me acmite solo esto?
setup_adc_ports(aN3);
-
Solo necesito una entrada analogica y me da mucho trabajo cambiar a AN0
ra0 y ra1 ya estan muy ocupadas como salidas digitales
-
pero porque no me aDmite solo esto?
setup_adc_ports(aN3);
La respuesta a esa pregunta esta en el datasheet del micro, Y es lo PRIMERO que deberias ver. No es simplemente "probar" en el compilador.
Lamentablemente la unica opcion es esa para los canales del ADC, no hay otra forma de configurarlo, se comienza desde A0 en adelante. es decir AN0,AN1 o AN0,AN1,AN2 ... etc en ese micro.
(Pagina 112)
Veo 3 soluciones:
- Tratar de conseguir otra fuente de un valor aleatorio para darle a la semilla, Supongamos amplificar el ruido y leer repetidamente una señal digital ¿?
- Cambiar los pines que tenes como digitales y dejar solo A0 para ese canal analogico
- Como imagino que tenes 89578914908190 microcontroladores conectados entre uno y otros (sarcasmo). Podrias usar el ADC de otro u otroS PICs para que te envien los datos a ese y asi obtener la semilla.
Solo necesito una entrada analogica y me da mucho trabajo cambiar a AN0
ra0 y ra1 ya estan muy ocupadas como salidas digitales
A no ser que tengas realizadas las plaquetas lo cual por el estado del proyecto no deberias tener las plaquetas en su forma final, es simplemente cambiar 2 cablecitos o cortar 2 caminos y soldar dos cables.
En el programa simplemente usar el Reemplazar (usualmente en el menu Edit, y casi siempre el segundo al lado de archivo) y reemplaza todos los PIN_A0 por PIN_A3, y con eso finalizaste cambiando todos tus pines.
-
Pues valla lata empezar con A0 es empezar de 0 muchas cosas
enviar los datos desde el micro nº 100 (sin exagerar tanto) Eso si que No, otra comunicacion serie no estas son las que mas realentizan todo el proceso.
-
Pues valla lata empezar con A0 es empezar de 0 muchas cosas
enviar los datos desde el micro nº 100 (sin exagerar tanto) Eso si que No, otra comunicacion serie no estas son las que mas realentizan todo el proceso.
Es una counicacion que lo haces 1 sola ves cuando encendes la maquina, y nunca mas. No se que ves como para que te lo ralentize. Tendras que esperar 1ms mas cuando comienza. Seguro que tenes delays que demoran mucho mas de lo que va a durar esa comunicacion, encima 1 sola ves.
-
Si seria valido pasarselo como las opciones iniciales
1 sola ves Pero los 3 Master tienen las A0 - A3 Ocupadas. De todas tengo un sub-proyecto aparte y veo que funciona, concluido.