TODOPIC
Microcontroladores PIC => Todo en microcontroladores PIC => Mensaje iniciado por: Xavier25 en 28 de Abril de 2008, 16:36:59
-
Que tal compañeros, me encuentro en un problema tratando de diseñar SW que me permita controlar un pic para que me genera una señal PWM el cual me varie la frecuencia con un potenciometro en AN0 desde 1hz hasta 1Khz, con un ciclo de servicio constante que sea siempre el equivalente a un septimo del periodo, algo como esto:
(http://img181.imageshack.us/img181/531/ciclopwmtb7.th.jpg) (http://img181.imageshack.us/my.php?image=ciclopwmtb7.jpg)
Programando en asm me ha quedado esto, pero descubri que es muy dificil reslizar divisiones en este lenguaje (que no sean entre 2, 4 y 8), asi que con ayuda de una calculadora hice unos experimentos y descubri que restandole 1/8 a 1/4 de algun numero, se obtiene aproximadamente 1/7
p,ej, 74/4= 18.5 74/8=9.25 18.5-9.25=9.25
74/7= 10.57
Pero no es exacto como podran darse cuenta, y el lo que no se como resolver aparte de que la frecuencia no logro ajustarla, les dejo aqui el codigo.
###########################################################
List p=16F877A ;Tipo de procesador
include "P16F877A.INC" ;Definiciones de registros internos
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC
Periodo equ 0x20 ;Variable para el periodo
Duty_H equ 0x21
Duty_L equ 0x22 ;Variable para la anchura de pulso
resta equ 0x0c
org 0x00 ;Vector de Reset
goto Inicio
org 0x05 ;Salva el vector de interrupción
;Programa principal
Inicio clrf PORTA
clrf PORTB
clrf PORTC
bsf STATUS,RP0 ;Selecciona banco 1
clrf ADCON1 ;Puerta A analógica, justificación izda.
clrf TRISB ;Puerta B se configura como salida
movlw b'00000001'
movwf TRISA ;RA0 entrada
movlw b'11111011'
movwf TRISC ;RC2/CCP1 salida
movlw b'11001101'
movwf OPTION_REG ;Preescaler de 128 asociado al WDT
bcf STATUS,RP0 ;Selecciona banco 0
;Se activa el ADC y se selecciona el canal RA0/AN0. Frecuencia de trabajo Fosc/32
Loop clrwdt
movlw b'10000001'
movwf ADCON0 ;ADC en On, seleciona canal AN0
bcf PIR1,ADIF ;Restaura el flag del conversor AD
nop
bsf ADCON0,GO ;Inicia la conversión
ADC_0 btfss PIR1,ADIF ;Fin de conversión ?
goto ADC_0 ;Todavía no
movf ADRESH,w
movwf Periodo ;Registra valor actual para el periodo
movwf resta
movwf Duty_H ;Salva parte alta de la conversión
bsf STATUS,RP0 ;Selecciona página 1
bcf STATUS,C
rrf ADRESL,F
rrf ADRESL,F
bcf STATUS,C
rrf ADRESL,W
bcf STATUS,RP0 ;Selecciona página 0
andlw b'00110000'
movwf Duty_L ;Salva parte baja de la conversión
;El módulo CCP1 se configura en modo PWM con salida por RC2/CCP1. Los bits LSB se obtienen
;de la variable Duty_L
movlw b'00001100'
iorwf Duty_L,W
movwf CCP1CON ;Modo PWM para el módulo CCP1
;El periodo se determina según el valor de la variable "Periodo". Este se carga sobre el
;registro PR2.
movf Periodo,W
bsf STATUS,RP0 ;Selecciona página 1
movwf PR2
bcf STATUS,RP0 ;Selecciona página 0
;La anchura del pulso o "Duty" se determina según el valor con que se cargue el registro
;CCPR1L concatenado con los bits 4 y 5 de CCP1CON. Dichos valores se obtiene de las varia-
;bles Duty_H y Duty_L respectivamente.
clrw
bcf STATUS,C
rrf Duty_H,F
bcf STATUS,C
rrf Duty_H,F
bcf STATUS,C ; Ciclo de servicio dividido entre 4
rrf resta,F
bcf STATUS,C
rrf resta,F
bcf STATUS,C
rrf resta,F ; Ciclo de servicio dividido entre 8
bcf STATUS,C
movf resta,W
subwf Duty_H,F ; Resta del resultado entre 4 menos resultado de /8
bcf STATUS,C
rrf Duty_H,W
movwf CCPR1L
;Trabajando con un preescaler 1:1 y a una frecuancia de 4MHz, el TMR2 evoluciona cada 1 uS.
movlw b'00000100'
movwf T2CON ;TMR2 en On
goto Loop
end ;Fin del programa fuente
############################################################
Ese es todo el programa, se que se tienen que incrustar algunos retardos o cambiar el valor del timer2 pero no se muy bien cual es la manera, apenas hace dos meses comence de lleno con esto de la programacion en asm.
Me aconsejan que programe en C pero me es dificil comenzar otra vez a aprender un lenguaje, sin embargo, he hecho dos diferentes programas (con ayuda claro) que tampoco me van. estos son mucho mas cortos y sencillos.
///////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <16f877A.h>
#device ADC=8
#fuses NOPROTECT,NOCPD,NOLVP,NOWDT,XT
#use fast_io (A)
#use fast_io (B)
#byte resultado=0x20
#byte PORTB=0x06
#use delay(clock=4000000)
main()
{
set_tris_a(0x01);
set_tris_b(0x00);
setup_adc(adc_clock_div_32);
setup_adc_ports(AN0);
while(1)
{
set_adc_channel(0);
resultado = read_adc();
bit_set(PORTB,0);
delay_ms(resultado);
bit_clear(PORTB,0);
delay_ms(resultado);
delay_ms(resultado);
delay_ms(resultado);
delay_ms(resultado);
delay_ms(resultado);
delay_ms(resultado);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
Aqui solo muestrea la entrada AN0 y el resultado se envia a la salida con retardos equivalentes a la conversion. Aun asi es el que mas se ha acercado, lo malo fue la frecuencia a la que trabaja, ademas de que la salida no se puede leer bien,
////////////////////////////////////////////////////////////////////////////////////////////////
#include <16f877a.h>
#device ADC=8
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=4000000)
#use standard_io(b)
#use standard_io(c)
int duty;
void toma_adc_y_transmite(void){
// Lectura del canal 0
set_adc_channel(0);
duty=read_adc();
}
void main() {
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(RA0_ANALOG);
setup_ccp1(CCP_PWM);
setup_timer_2(T2_DIV_BY_1,255,1);
do {
toma_adc_y_transmite();
set_pwm1_duty(duty);
}while(true);
}
//////////////////////////////////////////////////////////////////////////////////////////////
Este ultimo es un ejemplo tipico sobre el modulo ccpx trabajando como PWM, en este no se como hacerle para que tambien varie el periodo, obtener un septimo del ciclo basta con dividir duty/7, y aun asi, no da exactamente un septimo.
He estado haciendo modificaciones a estos si ningun resultado, la verdad ya no se que hacer, he visto manuales, cursos y demas cosas que encontramos en san google y otros foros, pero no encuentro la manera, cualquier comentario o sugerencia sera bien recibido, gracias por su tiempo. :D
-
Hola!
A ver...
Cuando dices
y descubri que restandole 1/8 a 1/4 de algun numero, se obtiene aproximadamente 1/7
en realidad, lo que obtienes es....exactamente 1/8!!!!!! no?
1/8 + 1/8 = 1/4 , o sea...el "aproximadamente 1/7" es "exactamente 1/8" :)
Saludos.
-
...para que me genera una señal PWM el cual me varie la frecuencia con un potenciometro en AN0 desde 1hz hasta 1Khz, con un ciclo de servicio constante que sea siempre el equivalente a un septimo del periodo
Tengo entendido que PWM es modulación por ancho de pulso. Si se desea una frecuencia variable y ancho de pulso fijo... no es exactamente PWM, extrictamente hablando verdad? :? Si estoy errado por favor alguien que lo aclare.
#include <16f877a.h>
#device ADC=8
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=4000000)
#use standard_io(b)
#use standard_io(c)
int duty;
void toma_adc_y_transmite(void){
// Lectura del canal 0
set_adc_channel(0);
duty=read_adc();
}
void main() {
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(RA0_ANALOG);
setup_ccp1(CCP_PWM);
setup_timer_2(T2_DIV_BY_1,255,1);
do {
toma_adc_y_transmite();
set_pwm1_duty(duty);
}while(true);
}
Con este programa no variarás nunca la frecuencia como tú deseas, pues la instrucción "setup_timer_2" que es la que regula el período de la onda la tienes fija con los parámetros (T2_DIV_BY_1,255,1). Lo único que estás cambiando de acuerdo al valor que lees con el CAD es el ancho de pulso: set_pwm1_duty(duty)... y en ninguna parte insertas lo del septimo como tú quieres...
Es decir, con este programa si generas una PWM, frecuencia fija pero ancho de pulso variable...
Para hacer lo que tú quieres tendría que ser algo así, pero cambiando lo siguiente:
#include <16f877a.h>
#device ADC=8
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=4000000)
#use standard_io(b)
#use standard_io(c)
static int duty;
static int periodo;
void toma_adc_y_transmite(void)
{
// Lectura del canal 0
set_adc_channel(0);
periodo=read_adc();
periodo=255-periodo; //Para invertir la polaridad, mientras mayor sea el valor analógico
//menor será el período, así será mayor la frecuencia (F=1/P)
}
void main()
{
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(RA0_ANALOG);
setup_ccp1(CCP_PWM);
do {
toma_adc_y_transmite();
setup_timer_2(T2_DIV_BY_1,periodo,1);
duty=periodo/7;
set_pwm1_duty(duty);
}while(true);
}
Bueno no me dió chance de simularlo, en lo que pueda lo haré... pero lo compilé y no dió errores...
Trata de probar a ver y dices qué tal te fue...
Saludos!
:mrgreen:
-
Estas en lo cierto firepic, un PWM con un ciclo de trabajo constante y frecuencia variable simplemente no es un PWM, sino una modulación a frecuencia variable.
Saludos.
-
Gracias por confirmarme eso Gonzalo_BlackHawk.
Por cierto Xavier, en el programa que te recomendé, no está contemplado eso que mencionaste que la variación de frecuencia tiene que ser de 1Hz a 1Khz... para que sea así tendrían que hacerse algunos cálculos y la parte de especificar el setup_timer_2 cambiaría un poco.
En lo que tenga chance de revisar publicaré algo.
Nos vemos! :mrgreen:
-
Amigo Xavier, acabo de hacer la simulación del programa que te dí (aunque le modifiqué algunas cosas) en el proteus, y funcionó de lujo! En las pruebas que hice da un error de 3 - 4% No sé qué tanta presición necesitas, pero creo que está bastante bien.
Faltaría eso de definir que el rango de frecuencia sea de 1Hz a 1Khz; como lo tengo va de 3.9KHz a 38.5KHz... :shock: Bastante fuera de rango no? Bueno pero si varia la frecuencia y queda fijo el duty en 1/7 del período... eso es un adelanto :D Ok en lo que siga con lo otro te digo...
Te dejo los archivos de la simulación en proteus y el código, tanto en C como en .HEX
Nos leemos! :mrgreen:
-
Xavier leyendo la DS del pic me doy cuenta que usando la herramienta pwm del pic no se puede lograr una frecuencia tan baja...
Así que el programa que te dí no creo que te sirva de mucho.
Tendrás que utilizar timers para poder generar tu deseada señal de frecuencia modulada.
Voy a tratar de hacer algo para dártelo, pero no ahora porque ya tengo sueño y me voy a dormir :D
Estamos pendiente :-)
Saludos! :mrgreen:
-
:-/ Muchas gracias por contestar tan rapido
en realidad, lo que obtienes es....exactamente 1/8!!!!!! no?
1/8 + 1/8 = 1/4 , o sea...el "aproximadamente 1/7" es "exactamente 1/8" :)
:oops: Si viendolo bien tienes razon, me concentre tanto en obtener una division que no comprobe el resultado, exactamente es un octavo, gracias por la observacion. :)
Con este programa no variarás nunca la frecuencia como tú deseas
Si lo sabia lo que pasa es que publique el archivo original, no el que habia modificado, es muy parecido al que pusiste primero, mira:
#include <16f877a.h>
#device ADC=8
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=4000000)
#use fast_io(b)
#use fast_io(c)
int duty;
int temp;
void toma_adc_y_transmite(void){
// Lectura del canal 0
set_adc_channel(0);
duty=read_adc();
temp = 255-duty; // Esta linea la cambie de mil maneras, pasando por esta
}
void main() {
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(RA0_ANALOG);
setup_ccp1(CCP_PWM); // Configura CCP1 como PWM
setup_timer_2(T2_DIV_BY_4,temp,1); // frecuencia
do {
toma_adc_y_transmite();
set_pwm1_duty(duty/7); // Determina tiempo en alto
}while(true);
}
Y lo unico que me sale es una linea continua, en este momento no he comparado bien pero hay una lineas que no tengo, veo un if en el tuyo, y static int en las etiquetas, por lo demas es identico.
un PWM con un ciclo de trabajo constante y frecuencia variable simplemente no es un PWM, sino una modulación a frecuencia variable.
Bueno, yo me di a la idea de que esa una señal de ese tipo por que asi me la pidieron, pero ahora ya se que no es un pwm, muchas gracias hoy he aprendido bastante, hubiera escrito desde hace mucho, gracias a todos por sus opiniones, no se como agradecerles tanta ayuda, despues de estar vagando un buen rato y de repente encontrar la solucion, es algo muy satisfactorio. gracias :mrgreen: :mrgreen: :mrgreen: :mrgreen: :mrgreen: :mrgreen: :mrgreen:
Ahora volviento al tema, mi querido amigo, firepic, he recibido tu programa y va perfectamente bien, he modificado la frecuencia y he logrado obtener un rango nada despreciable de unos 233hz y 2.28 khz modificando asi estas lineas
setup_adc(ADC_CLOCK_DIV_64); Esta la copie de uno de los programas que mencione arriba, indicame por favor si hago mal en ponerla asi,
setup_timer_2(T2_DIV_BY_16,periodo,16); y esta es lo mas que pude modificarla, poniendo al maximo el postscale y el prescaler.
Despues de todo esto, se me ocurrio cambiar el cristal de 4Mhz por uno de 2Mhz y vualá :mrgreen:, el rango ahora es de 116 hz y 1.14Khz :mrgreen: :-/ :mrgreen: :-/ :mrgreen: :-/
Mañana mismo voy a ver que tal trabaja en la practica (bueno ya hoy dentro de unas cuantas horas mas :D), de todas maneras voy a veré como restarle unos 100 hz
Gracias a todos los que me ayudaron tanto y tan rapido, en especial a mi amigo firepic, no se como agracedertete de verdad, hiciste en menos de un dia lo que no habia podido hacer en mas de un mes, muchas gracias, te mantendre informado de los resultados, gracias de nuevo :mrgreen: :mrgreen: :mrgreen: :mrgreen: :mrgreen:
-
Excelentes ideas! :) Era lo que pensaba decirte para retardar un poco la cosa y lograr un período más largo y así una frecuencia más baja. Aunque lo del "setup_adc(ADC_CLOCK_DIV_64); " si no se me había ocurrido... en efecto es correcto, lo que estás haciendo es que la conversión A/D sea más lenta y así consigues un retardo en el programa que repercute en el período de tu señal... como decímos en venezuela: chévere!
Bueno como te dije antes también puedes usar timers para generar tu señal, pero si consigues que con el método que estás usando logres el rango deseado, maravilloso!
Avísame si resuelves lo de los 100Hz... si no entonces tendrás que poner manos a la obra con los timers y las interrupciones...
Ok pues, a divertirse en grande! :mrgreen:
-
Que tal ya he reslizado la practica y estas son unas imagenes de los resultados:
(http://img204.imageshack.us/img204/4606/tek0001vo1.th.jpg) (http://img204.imageshack.us/my.php?image=tek0001vo1.jpg) (http://img100.imageshack.us/img100/3342/tek0002vh3.th.jpg) (http://img100.imageshack.us/my.php?image=tek0002vh3.jpg) (http://img241.imageshack.us/img241/791/tek0003qb4.th.jpg) (http://img241.imageshack.us/my.php?image=tek0003qb4.jpg)
En cuanto logre modificar la frecuencia les informare, gracias, muchisimas gracias. Hasta luego :mrgreen: :mrgreen: :mrgreen: :-/
-
Excelente trabajo Xavier, lo del 1/7 se ve "al pelo" ! :)
Por cierto fue tu proyecto lo que me hizo profundizar más en lo del timer2 y por eso me surgió la pregunta que tú mismo me ayudaste a aclarar. Excelente! :mrgreen:
Nos leemos! :-)
-
Hola a todos !
Una consulta a Xavier.
No habría sido infinitamente más sencillo hacerlo con un CD4017 (http://www.datasheetarchive.com/pdf/1002368.pdf) ?
No quisiera que lo tomen a mal si es una sugerencia fuera de lugar, pero creo que todo se hubiera solucionado con un mínimo de hard.
Excepto que lograr esa forma de onda, sea parte de un proyecto mayor.
Ésa es la consulta.
Saludos a todos !
Mario
-
Saludos Marioguillote! :mrgreen:
Como buen ignorante que soy no conocía ese integrado. :?
Esta bien interesante... estuve leyendo un articulo (http://www.neoteo.com/cd4017.neo) muy bueno que publicó por allí el amigo Ariel... y entiendo que se pueda generar una señal fija de con un duty que sea 1/7 del período del reloj del cd4017... pero... y la variación de la frecuencia? Sería cuestión de adicionar hardware temporizador o se puede hacer con el mismo integrado?
Ok nos leemos! :-)
-
:D Que tal, si definitivamente es mas facil, me han dicho lo mismo cuando e ido a probar el circuito al tec, yo hubiera usado un tl494 (no conzco ese que mencionas) pero la aplicacion final que va a tener es para una matriz de solenoides (4x4), y para ahorrar el circuito de control para cada uno, un pic sera suficiente para controlar la etapa de potencia, bastara usar dos o 3 puertos para generar una salida que cierre el circuito en el solenoide deseado. El compañero firepic nos acaba de dar otra razon :mrgreen:
Por cierto fue tu proyecto lo que me hizo profundizar más en lo del timer2
Fue lo primero que se me vino a la mente, saludos :D
-
Hola firepic ! :)
La variación de frecuencia debería resolverse de manera externa, ya sea a través de un oscilador variable (fijo ó enclavado) ó de algún sistema basado en PIC, ó de algún sistema elemental como el que muestra Ariel a través de un 555 ó un oscilador con un UJT.
No conozco contadores decimales que posean oscilador incorporado, pero sí divisores binarios que los tengan cómo es el caso del CD4060 (http://www.datasheetarchive.com/pdf/1005068.pdf)
Hola Xavier !
Me has terminado de confundir del todo amigo ! :D
El TL494 es el IC que genera la señal SW en las fuentes de las PC y tu quieres áctivar solenoides .... a ésa velocidad ? :shock:
Lo importante es que tú tengas claro el asunto y te funcione de maravillas.
Éxitos ! Nos leemos ! Saludos a ambos ! :)
Mario
-
Epale! :mrgreen:
Ah ok entiendo Mario, era lo que me imaginaba, gracias por aclarar mi duda! :-/
Dale pues nos leemos! :-)