PicManía by RedRaven |
![]()
Búsqueda personalizada
|
Una colección de artículos sobre algunos conceptos técnicos de los distintos módulos que los PIC's implementan (como conversiones A/D, osciladores a cuarzo, alimentación... etc.) y que no tienen cabida en otras secciones de esta Web. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Algunas ideas sobre la Conversión A/D en los PIC's : Voltajes de Referencia |
Vamos a ver si soy capaz de explicarme razonablemente bien sin liarme demasiado ... |
|
|
|
Los Cristales y El Tiempo. Cálculo de tiempos según el Cristal oscilador que usamos |
En la
sección El Rincón del C dedicábamos un artículo al
uso de la Interrupción RTCC, en el que
mostrábamos una tabla de tiempos que tardaba el TIMER0 en dar una vuelta
de manivela completa, que dependía del Preescaler seleccionado. Está tabla
de tiempos adjuntaba como nota que el Cristal oscilador utilizado era de 4 Mhz. |
Sin embargo
soy capaz de imaginar a cualquiera de vosotros, amables Picmaníacos, con
los ojos cerrados metiendo la mano en vuestro saco de cristales. Sacando
uno al azar. La probabilidad de que dicho cristal sea de 4 Mhz es
calculable. Es directamente proporcional al numero de cristales de 4 Mhz que
haya en vuestro saco e inversamente proporcional al número de otros tipos
de cristales que tengáis en tan heterogénea mezcla. Una pequeña locura. |
¿Que
hacemos, entonces, si vais a utilizar un cristal cuyo valor este alejado, o
muy alejado, de nuestros 4 Mhz de referencia? Pues fácil y sencillo como
juego de chiquillo: Calculamos el Tiempo de RTCC en función del Cristal
que vamos a usar, o sea el tema de este artículo. |
La primera
fórmula que vamos a ver nos da el Tiempo que tarda RTCC en dispararse, sin
Preescaler, comenzando TIMER0 en 00h y terminando en FFh, o sea
desbordamiento completo: |
|
donde Time es el
tiempo en segundos (S) que tarda RTCC en saltar y FOSC
es la frecuencia de oscilación de nuestro cristal en Hercios
(Hz) |
Ejemplo: |
|
|
|
|
Sin embargo
es posible, yo diría mas que posible es muy probable, que no os sea útil
tener que esperar a toda una interrupción RTCC, de 00h a FFh, sino que
necesitéis periodos de tiempo mucho mas concretos. Veamos entonces qué se
puede hacer para obtener sucesivas RTCC's cada periodos definidos de
tiempo fijado por nosotros. |
Decíamos que
RTCC salta cada vez que TIMER0 pasa de FFh a 00h, a esto le llamamos
desbordamiento de TIMER0. Con la intención de calcular tiempos
concretos para este desbordamiento de TIMER0 la técnica que vamos a
utilizar es escribir en el contador TIMER0 el valor a partir del cuál
queremos que empiece a contar antes de llegar a FFh y pase de nuevo a 00h,
que es quien produce el desbordamiento. Cada vez que salte RTCC y dentro
de la rutina de tratamiento de esta interrupción volvemos a escribir el
valor inicial del contador para iniciar siempre la cuenta de TIMER0 con
nuestro valor deseado. |
Si en lugar
de empezar a contar desde 00h empezamos a contar siempre desde 80h (128 en
decimal), exactamente a la mitad del recorrido de TIMER0, entonces nuestra
RTCC saltará el doble de veces o lo que es lo mismo habremos dividido su
tiempo de desbordamiento por 2, o análogamente habremos multiplicado por 2
su frecuencia, o ... ¡Yá cállate!. |
Haciéndole una
pequeña transformación a nuestra primera fórmula
(1) podemos calcular qué
valor hay que escribir en TIMER0 para obtener un tiempo deseado de
desbordamiento de RTCC: |
|
donde MiTIMER0 es el
valor a escribir en el Contador TIMER0, Time es el tiempo en
segundos (S) que deseamos conseguir para RTCC y FOSC es la
frecuencia de oscilación de nuestro cristal en Hercios (Hz)
|
Ejemplo: |
|
|
|
Y con esto
casi hemos acabado con el tema de los tiempos y los cristales. Solo que
hay una pregunta que yo mismo me he hecho y que tras profundas
deliberaciones, estudios, búsquedas y tostado de pestañas yo mi me conmigo
me he respondido. Deseo compartir dicha respuesta con ustedes, amables
Picmaníacos: |
Pregunta: ¿Por qué 256?
¿Por que 4? ¿Qué relación tienen con FOSC? |
Respuesta: La respuesta
no está en el viento, sino en el interior de nuestro PIC, de cómo
funciona. |
Me explico:
Nuestro PIC, como Jesucristo, no es de este mundo. No participa de nuestro
continuo espacio-tiempo, sino que vive cuánticamente dando saltitos,
pasando de un estado a otro bajo la estricta batuta del Dios Cristal que
es quien le interpreta el son al que debe bailar. |
FOSC es la
velocidad de la batuta. Salta ... salta ... salta .... le dice al PIC, una
vez cada 0,00000025 segundos (0.25μs)
con un cristal de 4 Mhz, una vez cada 0,00000005 segundos (0.05μs)
con un cristal de 20 Mhz. Es lo que se conoce como el Ciclo de Reloj. |
Pero nuestro
PIC necesita 4 de estos ciclos para ejecutar un sola instrucción, como por
ejemplo incrementar en 1 el valor del contador de TIMER0. Es lo que se
conoce como Ciclo de Instrucción. |
Como nuestro
TIMER0 corre raudo y veloz desde 00h hasta FFh, o sea desde 0 hasta 255, o
sea que cuenta 256 veces, necesita por lo tanto 256 ciclos de
instrucción para dar una vuelta completa o lo que es lo mismo 256 *
4 ciclos de reloj FOSC. |
Con un cristal de 4
Mhz, que da un pulso cada 25μs,
TIMER0 necesita 256 * 4 * 0.25
μs para dar su vuelta lo que
significan 256 μs
por vuelta y como el mínimo Preescaler que podemos usar es 1:2 entonces
tenemos que la máxima velocidad que puede alcanzar RTCC con un Cristal de
4 Mhz es de 1 vuelta completa cada 512
μs. (Lo que es absolutamente
compatible con la tabla de tiempos del artículo dedicado al uso de la
Interrupción RTCC y que dio pie al presente
artículo) |
Bello. Sencillo a la par que elegante. Me gusta. |
¿Qué es el Watch Dog
Timer? I Y así evitamos que se nos "cuelgue" nuestro PIC.
|
El "Watch Dog Timer" o
"Temporizador Perro guardián" es un artificio que se utiliza de antiguo
para evitar que los microprocesadores se queden "colgados". No es extraño que en microelectrónica se den circunstancias de hardware o firmware no previstas por el diseñador en las que un microprocesador se quede en un estado indeterminado del que le sea imposible salir sin una ayuda externa. El WDT lo que hace fundamentalmente es resetear el micro tras un periodo de tiempo determinado. Su funcionamiento es similar a la Interrupción por Desbordamiento de un Timer, que se produce cuando un Timer que es incrementado continuamente pasa de su valor máximo al mínimo para comenzar de nuevo a contar. En el caso del WDT en lugar de saltar una interrupción se genera un reset automático en el momento de producirse dicho desbordamiento. Pero evidentemente en condiciones normales, nuestro micro funcionando correctamente, no debería producirse dicho reset automático. Para evitar que el reset se dispare es para lo que aplicamos el restart_wdt(); o sea que "restauramos" el timer del WDT, o lo que es lo mismo: lo volvemos a poner a 0 "a mano" y vuelve de nuevo a iniciar su cuenta para acercarse al abismo y amenazarnos con resetear el micro si antes no lo "restauramos" de nuevo. Un ejemplo tonto: Configuramos nuestro WDT para que salte cada 5 ms, por ejemplo. Entramos en una rutina que espera a que le lleguen una docena de caracteres vía rs232, y cada vez que le llega uno hace un restart_wdt(). Al recibir el doceavo carácter sale de la rutina y continua su ejecución normal. Por manos del demonio se nos escapa el hacha que con la que estábamos haciendo juegos malabares y corta accidentalmente el cable de la rs232, justo cuando el PIC había recibido el carácter número 11 de los 12 que esperaba. Por lo tanto nuestro programa se queda esperando un carácter que nunca le va a llegar, al menos durante el tiempo en que tardemos en sustituir el cable accidentado. ¿Y qué ocurre entonces con el resto de del programa que debía estar funcionando? pues que todo está detenido indefinidamente. Pero, para eso está el WDT. Como restaurábamos el contador cada vez que recibíamos un carácter y estos iban llegando, uno a uno en su cadencia natural, el WDT no se desbordaba y todo iba bien. Pero tras recibir nuestro 11 carácter y quedarse esperando el 12 nadie ha restaurado el WDT por lo que este camina, paso a paso, tick a tick, hasta el temible desbordamiento ... y éste se produce indefectiblemente 5 ms después de haber recibido el onceavo carácter. El PIC se resetea y todo vuelve a comenzar de nuevo. Si hemos sido lo suficientemente inteligentes como para escribir un 1 en la EEPROM al iniciar la recepción de los susodichos 12 bytes, y teníamos previsto escribir un 0 en la EEPROM en el mismo sitio para indicar que la última recepción de 12 bytes fue un completo éxito tendremos disponible un indicador veraz y seguro de que al reiniciarse nuestro PIC sabremos fehacientemente que la última recepción fue bien o por el contrario se convirtió en un completo, total y rotundo fracaso y, por lo menos, nos tomaremos con precaución el asunto de la RS232. Nuestro programa podrá seguir su curso evitando los terrenos pantanosos y habilitando los medios para solventar los problemas que nos hemos encontrado. |
Consiguiendo 4 Mhz para los 48 Mhz necesarios en los PIC's con USB 2.0 |
Voy a intentar explicar un
poco de dónde sale esto de los 4 Mhz para el
USB 2.0 de los PIC's. |
![]() |
Este sistema es complejo debido a la inmensa versatilidad y posibilidades que ofrece. Como podéis ver en OSC1 y OSC2 conectamos nuestro cristal: 4 Mhz, 8 Mhz, 12 Mhz, 16 Mhz, 20 Mhz, 24 Mhz, 40 Mhz ó 48 Mhz, que son los cristales validos compatibles con lo que sigue a continuación. Tened en cuenta que este cristal es el mismo para generar la frecuencia de 48 Mhz necesaria para el USB 2.0 y para el Clock del PIC, que pueden ser la misma o no, según la configuración que al final adoptemos, quiere esto decir que podemos tener el USB a 48 Mhz y nuestro programa en el PIC funcionando a 12 Mhz por ejemplo. Fijaos en que justo tras el Smicht Trigger del Primary Oscillator salen tres líneas en paralelo que van a módulos distintos con distintas posibilidades. La primera línea, la superior, va directamente al switch USBDIV que si está a cero indica que la frecuencia base original del cristal es directamente inyectada al USB, si pasa el switch FSEN que elige entre todo el sistema directo/PLL o el Primary Clock del CPU. Esta Opción de inyectar directamente la frecuencia del cristal es obviamente solo posible si usamos un Cristal de 48 Mhz que es lo que necesitamos para el USB. Cualquier otro cristal debe ser tratado para conseguir los 48 Mhz necesarios. El módulo USB Clock Source tiene a su entrada un PLL Prescaler, o sea un divisor de frecuencia. En cada una de sus salidas vamos a tener FOSC dividida por 1, 2, 3, 4, 5, 6, 10 ó 12. Y mediante PLLDIV que no es mas que un Multiplexor vamos a seleccionar la que deseamos usar. Así si nuestro cristal es de 12 Mhz y en PLLDIV colocamos un 010 estaremos dividiendo por 3 el valor de FOSC con lo que tendremos 4 Mhz a la salida del MUX. Si por el contrario el cristal es de 20 Mhz y en PLLDIV colocamos un 100 entonces dividiremos por 5 FOSC con lo que tendremos también 4 Mhz a la salida del MUX. Esta salida del MUX es lo que utilizamos para inyectársela al PLL de 96 Mhz. Si le metemos 4 Mhz él genera 96 Mhz. Es esta capacidad de pasar de 4 Mhz a 96 Mhz la que nos da la posibilidad de usar un montón de cristales distintos. Pero 96 Mhz es el doble de lo que nos hace falta para el USB que son 48 Mhz. Asi que inmediatamente después tenemos que tener, y tenemos, un divisor por 2 que es el segundo camino por el que llegamos a USBDIV y en este caso le pondremos un 1 para usar la señal proveniente del PLL. Fijaos que además de inyectar la señal oscilante en USBDIV también se conecta la señal del PLL a 96 Mhz en un Postscaler, otro divisor, en este caso por 2, 3, 4 ó 6 y cuyas señales van al CPUDIV. O sea que podemos generar una señal de reloj para nuestro PIC, no para el USB sino para la velocidad de ejecución de nuestro programa tomándola del PLL y que puede ser de 16 Mhz, 24 Mhz, 32 Mhz ó 48 Mhz. Pero además la señal original llegaba en paralelo al Oscilator Postcaler, otro divisor más, que de forma directa, sin pasar por el módulo PLL nos divide la frecuencia original del cristal por 1, 2, 3 ó 4 y que también va a parar al CPUDIV pero desde otro origen. Con este módulo podemos obtener otra gama de frecuencias distinta para hacer correr el programa. Cual de ambos CPUDIV vamos a utilizar lo seleccionamos con el switch FOSC3:FOSC0 que es de donde sacaremos la definitiva frecuencia de ejecución de programas. Por último también tenemos disponible una entrada proveniente del Primary Clock y que dividida por 4 llega también a FSEN y podemos utilizarla en lugar de la que le llega desde el canal directo/PLL Como podéis ver es toda una maravilla cómo esta montado este tema de los osciladores, sobre todo por lo que respecta a las inmensas capacidades que tiene para hacer correr nuestro PIC a decenas de velocidades distintas siendo capaz, al mismo tiempo de tener disponibles los 48 Mhz imprescindibles para el USB 2.0. Me descubro ante los ingenieros de Microchip y los aplaudo, se lo merecen. |
|
Dos Fuentes en Una: Un cuento de potencia. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
El PORTA de los PIC's y una de sus particularidades. |
Push-Pull y Open-Drain, dos en uno. |
|
|
|
RA0:RA3 & RA5, los
"normales" |
|
|
RA4,
el "raro" |
|
|
|
Un ejemplo con enjundia: Un Driver de Relé con uno u otro tipo de Pin. |
|
|
|
|
|
|
Ajustando TIMER0 para conseguir un segundo. |
Por Jóse Modulay del Foro Todopic |
Esas mates... Con un cristal de 4 MHz tienes un periodo de reloj de 1/4 MHz = 0.25 us Como cada ciclo máquina son 4 ciclos de reloj, tienes un ciclo máquina de 0.25 us * 4 = 1 us. Es decir, el timer se incrementa en una unidad cada microsegundo. La idea es buscar que el timer interrumpa cada cierto tiempo x,y que ese tiempo x sea divisor exacto de 1 segundo, por ejemplo 25 us, 100 us, 2 ms, 10 ms, etc... Además de esto, habrá que usar una variable contador que se irá incrementando cada vez que el timer se desborde, comprobando su valor cada vez que esto sucede y así llevar el control del tiempo transcurrido. La fórmula, medida en segundos, que determina el periodo de desbordamiento de un timer de 8 bits (timer 0) es: T = (256 - precarga) * Preescaler * 4 / Fosc Si fijamos el Preescaler a 16... T = (256 - precarga) * 16 * 4 / 4 MHz => precarga = 256 - T * 62500 T es la base de tiempos que andamos buscando y precarga es el valor que se le carga al timer tras cada interrupción para que vuelva a empezar a contar a partir de él. Se trata de buscar un valor para T que sea divisor de 1 segundo y que al multiplicarlo por 62500 el resultado sea un número entero. Por ejemplo 4 ms: precarga = 256 - 4 ms * 62500 = 256 - 250 = 6 O sea, para conseguir la base de tiempos de 4 ms tendrás que cargar al timer con el valor 6 (con Preescaler establecido a 16). Hecho esto, para temporizar un segundo tendrás que contar: 1 s / 4 ms = 250 interrupciones |
Referencias en esta misma
página: Los Cristales y El Tiempo Template para el uso de la Interrupción RTCC mediante el TIMER0 Una aplicación práctica de RTCC : Cronopic 1.0 |
Notas 18F4550: Power On Reset (POR) y Brown Out Reset (BOR) en sus
puertos. |
Es muy corriente entre los
primerizos, y aún entre los expertos, cometer cierto tipo de errores que
son de fácil solución pero que sólo después de haberlos vivido se tienen
en cuenta antes de enfrentar un nuevo diseño. Pero somos animales que
tropezamos múltiples veces en la misma piedra. |
Uno de de los errores mas
usuales consiste en dar por supuesto el estado, funcionamiento y/o
configuración, de un cierto pin de cualquier puerto tras un Power On
Reset, o sea justo después de haber dado la corriente a nuestro PIC. |
Este error se comete
fundamentalmente por dos causas: |
|
Por ejemplo: Supongamos que
hasta ahora he trabajado mucho con el 16F877 y empiezo ha hacerlo ahora
con el 18F4550. No es extraño encontrarnos con que la mitad del PORTB no
funciona. En el 16F877 todo el PORTB se inicia como Entradas Digitales
mientras que en el 18F4550 más de la mitad de dicho puerto, RB0:RB4, lo
hace como Entrada Analógica mientras el resto, RB5:RB7 está efectivamente
configurado como Entrada Digital. |
Estas situaciones son tan
usuales que hasta la propia
Microchip
toma medidas especiales en sus Datasheets enmarcando, destacando y
cambiando el color del fondo en sus comentarios al respecto. Si editáis
cualquier PDF de un PIC podréis encontrar en cada epígrafe de cada puerto
un rectángulo "Note" con estas circunstancias a las que nos estamos
refiriendo. Es mi intención pues también remarcar en este post estos comentarios o notas, no solo con el fin de tenerlas en cuenta con respecto al 18F4550 del que trata el hilo, sino en cualquier otro PIC con el que trabajéis. He seleccionado el 4550 porque es con el que trabajo más a menudo, sirva por tanto de ejemplo. PORTA ![]() En POR los pines RA0 a RA3 del PORTA se configuran como Entradas Analógicas, el pin RA4 como entrada digital. PORTB ![]() En POR los pines RB0 a RB4 del PORTB se configuran como Entradas Analógicas, los pines RB5 a RB7 como entradas digitales. Pero se puede utilizar el fuse PBADEN para que RB0 a RB4 se inicialicen en POR como entradas digitales también. Si no se especifica serán al POR analógicos. PORTC ![]() Todo el PORTC se configura en POR como entradas digitales. PORTD ![]() Todo el PORTD se configura en POR como entradas digitales. ![]() Cuando ponemos en marcha el módulo PWM se deshabilita automáticamente el SSP del PORTD. PORTE ![]() En POR los pines RE0 a RE2 del PORTE se configuran como Entradas Analógicas. ![]() En POR el pin RE3 del PORTE se configura como Entradas Digital solo si tenemos deshabilitado el Master Clear Reset. Ea, hasta la próxima. |
Escribiendo un Byte en
la EEPROM en el tiempo justo. Que ni nos sobre ni nos falte. |
El escribir la EEPROM requiere un tiempo que en relación a la velocidad de funcionamiento del PIC es un tiempo realmente largo, del orden de algunos milisegundos, tiene que direccionar, poner el dato, y darle a la EEPROM un pico de tensión para fijarlo y esperar a que se estabilice. Un error muy
común es no darle tiempo al PIC a que realice una buena escritura, un
valor muy normal que yo utilizaba antes era el de 5 ms colocando un
delay_ms(5) tras escribir el byte y siempre me quedaba la duda razonable
de si era demasiado corto, o de si estaba desperdiciando tiempo esperando
mas de la cuenta.
Con esto me aseguro de
que el PIC tiene el tiempo suficiente y necesario para escribir su byte,
ni más ni menos tiempo del que realmente necesite. |
#int_eeprom /** \brief Interrupción por : Fin escritura EEPROM interna. * */ void interrupt_service_rutine_eeprom(void) { flag_Writing_INTERNAL_EEPROM=0; } ... void writeByteINTEEPROM(int8 memAddress, int8 data){ flag_Writing_INTERNAL_EEPROM=1; enable_interrupts(int_eeprom); write_eeprom (memAddress, data); while(flag_Writing_INTERNAL_EEPROM==1){/*Wait for write finish*/} disable_interrupts(int_eeprom); } |
||
|
El Timer Power-Up. Para que todo se estabilice antes de empezar. |
Cuando la
alimentación del PIC comienza a crecer de 0 hasta VCC,
en el momento de darle corriente o cuando el MCLR después de haber
sido tirado a GND comienza a su vez a subir de GND a VCC,
está el PIC aún en Reset, hasta alcanzar un nivel
determinado de voltaje en el cuál se produce un POR (Power On
Reset) que es una señal que indica que el Reset puede ya efectivamente
desbloquearse y dejar al PIC correr alegre y jubiloso. En ese
momento arranca el oscilador de programa que tras estabilizarse hace que
el Reset Interno se desbloquee y comience a funcionar el PIC
con nuestro programa. |
|
|
Nota: En CCS C este Timer se activa con el fuse PUT |
¿Qué es el módulo CCP? |
Muchas veces he
contestado privadamente a esta pregunta, así que ha llegado el momento de
hacer un artículo y publicarlo en PicManía para que todos puedan acceder a
él. |
El nombre del módulo
CCP viene dado por las iniciales del acróstico de Input Capture,
Output Compare y Pulse Width Modulation. que desde la
negrura de los tiempos ha sido llamado módulo CCP. |
El por qué tres funciones
aparentemente tan dispares aparecen juntas en un único módulo, y no sólo
en los PIC de Microchip sino hasta donde alcanza mi conocimiento es un
bloque generalizado entre los distintos fabricantes como Motorola o
Atmel, estriba en su conjunción con un Timer. |
Intentaré describir lo
mas claro que pueda como funciona este sistema, aunque antes vamos a ver
qué ocurre con la mucho mas simple Interrupción Externa por PB0 de
un PIC16 (ó PB1 y PB2 en los PIC18) teniendo como objetivo el intento de
saber cuanto dura un pulso externo. |
La situación a que nos
enfrentamos es un pulso que estando en estado lógico bajo cambia a estado
lógico alto durante un tiempo y transcurrido éste vuelve a su estado
inicial bajo. Es un pulso bajo-alto-bajo y deseamos medir cuánto
dura ese estado alto intermedio. |
Esto mismo podemos
describirlo como el tiempo que tarda en aparecer un flanco de bajada tras
haber aparecido un inicial flanco de subida. En la
Serie Técnicas en C me dedicaba a
mostrar cómo realizarlo de tres formas distintas. |
Nuestro problema estriba
entonces en implementar un método para realizar lo siguiente: |
|
Si la señal la conectamos
al PIN_B0 de un PIC16 (ó PB1 y PB2 en los PIC18) con la
Interrupción Externa tenemos solucionados los puntos 1 y 3 de la tarea que
nos hemos propuesto, conocer cuando llegan ambos flancos. Las tareas 2 y
4, guardar los valores del Timer tenemos que escribirlo nosotros,
habilitar las variables y leer el Timer que deseamos. Es el método
descrito en Tiempo en Alto con INTEXT
donde en cada cambio de flanco realizamos su correspondiente t1=get_timer1();.
Fíjaos que el tema está en get_timer(). |
Si por el contrario en
vez de usar la Interrupción Externa usamos el módulo CCP
configurado para la función Input Capture tendremos la facilidad de
realizar dos tareas al mismo tiempo. Si le decimos al PIC que espere en
CCP un Input Capture por flanco de subida cuando éste llegue y
se dispare la Interrupción CCP tendremos ya en valor del Timer
guardado en su registro CCPRxH:CCPRxL, o sea que las tareas 1 y 2
por un lado y las 3 y 4 de nuestra lista por otro estarán completadas. Es
lo que describimos en Tiempo en Alto con
INTCCP en donde en lugar del get_timer1() usamos el
muchísimo mas directo t1=CCP_2 que es cómo se llama el
registro registro CCPRxH:CCPRxL en CCS C. |
Como podéis ver el CCP
en Input Capture lo que hace realmente es capturar el valor de un
Timer cuando se presenta un flanco determinado. Nosotros lo
utilizamos en nuestro ejemplo para conmutar el flanco a detectar y medir
el ancho del pulso. Otras aplicaciones sólo dependen de la imaginación del
artista. |
De forma simétrica o
complementaria a ésta de recoger el valor del Timer cuando hay un cambio
externo es el modo Output Compare del CCP. Este modo de
funcionamiento del CCP es justo lo contrario al anterior:
Producimos un cambio de estado en el pin del CCP cada vez que el Timer
alcanza un valor determinado. O sea que ponemos el registro CCPRx
a un valor que nos interese y cuando el Timer llega a ese valor el
pin se pone en Alto, en Bajo o conmuta, según programemos su función. Esto
lo podéis ver en Onda Simétrica Mediante
INTCCP en modo Compare donde utilizamos este método para generar un
pulso. |
Insisto de nuevo en la
idea: Input Capture determina el valor del Timer al cambiar el
estado del pin, Output compare cambia el estado del pin cuando el
Timer llega a un valor determinado. Son
maravillosamente simétricos y complementarios. |
Y por fin nos queda el
PWM que no es mas que especialización del anterior. Activando ésta
función del CCP lo que hacemos es cargar dos valores del
Output Compare para generar un ancho de pulso de salida determinado y
qué porcentaje del mismo va a estar en alto y cuál en bajo, o sea que
modulamos el ancho de pulso. Este PWM es como un Output
Compare sofisticado con el que con muy poco código podemos controlar
todos los parámetros del pulso que necesitamos generar. |
Bueno y eso es todo por ahora amigos. |
El concepto de Timeout
(y un ejemplo de uso en C) |
![]() Hay procesos que no sabemos cuánto duran (en tiempo) pero si sabemos cuánto es el máximo que pueden durar (o que queremos que duren). A este tiempo máximo vamos a llamarlo periodo para timeout (o salida de tiempo). Un definición estándar en Informática podría ser : Parámetro que indica a un programa el tiempo máximo de espera antes de abortar una tarea o función. O darla por concluida que es tipo de timeout que vamos a implementar en nuestro pequeño ejemplo. Uno de los usos mas frecuentes del control de procesos mediante timeout es para abandonar procesos que deberían acabar normalmente pero que por cualquier circunstancia no prevista o incontrolada no se acaba. Si durante un proceso P la única condición de salida del mismo es S y hay veces en que no se produce entonces el proceso P se quedará "colgado" al no abandonar nunca su funcionamiento dado que la condición S no se produce. El uso de timeout consiste es arrancar un timer al mismo tiempo que se inicia el proceso P. Y en éste se implementan dos salidas posibles, nuestra anterior salida S y en paralelo (OR) una nueva: la de haber cumplido el tiempo T que hemos fijado para el timer. A esta nueva condición de salida la conocemos como Salida por Timeout. ![]() Supongamos, imaginemos, planteemos un ejemplo donde lo que hemos visto nos sirva para algo (en caso contrario estaría un poco fuera de lugar ¿no?) Supongamos, decíamos, que tenemos una recepción vía USART serie de N caracteres, número que desconocemos y que unas veces es un solo carácter y otras todo un string de decenas de ellos. Nunca sabemos cuando va a llegar el primero ni cuantos van a seguirle, pero sabemos que van a venir todos mas o menos juntos, uno tras otro (o en pequeños paquetes) pero que nunca, nuca, nunca va a haber entre dos caracteres consecutivos mas de unos pocos milisegundos, digamos 5 ms por poner un ejemplo concreto. Podemos entonces decir que: Si recibimos un carácter inicial cualquier otro nos llegará antes de 5 ms, si pasan mas de esos 5 ms entonces el paquete a recibir está completo, ya ha acabado y podemos procesarlo. En este caso es razonable decir también que si establecemos un timeout de 10 ms tendremos la seguridad de que hemos recogido el paquete completo. Vamos entonces a acotar nuestro ejemplo describiendo lo que debemos implementar: Recibimos todos los caracteres que nos vayan llegando, cada vez que nos llega uno de ellos activamos un timer que cuente el paso de 10 ms y lo ponemos a cero, si el timer llega a completarse es que han pasado esos mismos 10 ms tras la recepción del último carácter. Ponemos entonces un flag en alto indicando esta circunstancia y paramos el timer. Si recibimos un nuevo carácter todo comenzará de nuevo. Notad que el truco está en "reiniciar el timer del timeout cada vez que se recibe un carácter. Sólo cuando se dejen de recibir caracteres el timer completará su recorrido y hará saltar el indicador de timeout" ![]() Bueno ahora solo nos queda escribir un poco de código que le enseñe a nuestro PIC que todo lo anterior no solo es posible hacerlo sino exactamente cómo queremos que lo haga. Primero vamos a montar un timer que desborde cada 10 ms mediante su particular interrupción. En principio no la habilitamos ya que esto se hará cuando se reciba el primer carácter. (con ánimo de no complicar innecesariamente este ejemplo voy a dar por valido el que el Timer0 se desborda exactamente cada 10 ms, esto no es necesariamente cierto pero el hacer eso es cuestión de configurar correctamente el timer, y no aporta ni quita nada al ejemplo que estamos estudiando)
|
/////////////////////////////////////// // RAM /////////////////////////////////////// int1 flagTimeOut = 0x00; // Flag quea indica timeout completo, inicializado a 0 /////////////////////////////////////// // INTERRUPCIONES : TIMER0 para timeout /////////////////////////////////////// #int_timer0 void timer0_isr() { // Interrupción por desbordamiento de timer0 flagTimeOut = 1; } |
||
|
Después una simple #int_rda que recibe caracteres sobre un buffer, pero tiene la particularidad de que habilita/inicializa el timer cada vez que recibe un nuevo carácter: |
/////////////////////////////////////// // RAM /////////////////////////////////////// int xbuff=0x00; // Índice: siguiente char en cbuff char cbuff[lenbuff]; // Buffer de recepción /////////////////////////////////////// // INTERRUPCIONES : RDA Recepción USART /////////////////////////////////////// #int_rda void serial_isr() { // Interrupción recepción serie USART if(kbhit()){ // Si hay algo pendiente de recibir ... cbuff[xbuff++]=getc(); set_timer0(0); enable_interrups(int_timer0); } } |
||
|
Y por fin en nuestro main() lo que hacemos es habilitar la recepción RDA pero no el Timer0 y esperar en el bucle infinito a que salte el Timeout para poder procesar la recepción: |
void main() { enable_interrupts(int_rda); // Habilitamos la interrupción por recepción serie enable_interrupts(global); // Habilitamos las interrupciones do{ // inicio del Bucle infinito if(flagTimeOut ==1){ // Si hemos completado una recepción ... flagTimeOut =0; // lo desmarcamos para poder reiniciar el tema disable_interrups(int_timer0); // detenemos el timer0 disable_interrupts(int_rda); // deshabilitamos int serie mientras procesamos execute_lo_que_sea(); // Aqui procesamos lo recibido sobre cbuff. xbuff=0x00; // reiniciamos el buffer de recepción enable_interrupts(int_rda); // Habilitamos int recepción serie } }while(TRUE); // final del Bucle infinito } |
||
|
Bueno, creo que eso es
todo. Espero que os sirva de algo. |
¿Qué es el Watch Dog
Timer? II
|
El mejor programa, el mas
elaborado, el mas perfecto y revisado del mundo, el mas libre de bugs y
limpio de inconsistencias y redundancias puede contener en su entrañas la
posibilidad remota de que, bajo ciertas circunstancias totalmente
impredecibles, extrañas y recónditas pero reales a fin de cuentas, se
cuele en ese limbo indefinido del que nunca sale. Podemos llegar a la conclusión de que nuestro mas preciado programa es fiable en un 99%. Es ese 1% restante es el que nos puede joder la vida. Siempre es bueno colocar esta salvaguarda última para que nuestro programa no entre en una situación de bucle infinito sin salida posible. El Wachtdog es la solución. Si no es refrescado en el lapsus de tiempo que definimos reseteará el PIC y nuestro programa comenzará a ejecutarse de nuevo. La situación imponderable, no medible, inimaginable ha sido salvada y nuestra aplicación vuelve por sus fueros y comienza de nuevo a caminar. Esto lleva a dos consideraciones: 1ª.- El Watchdog debe ser activado solo cuando hayamos alcanzado ese 99% de fiabilidad de nuestro programa. En caso contrario nos va a ser muy difícil detectar nuestras propias inconsistencias e incongruencias, nuestros fallos de diseño, nuestra falta de imaginación (revivir en nuestra cabeza lo que debe estar ocurriendo en el núcleo del PIC) serán ocultados, enmascarados, diluidos por el funcionamiento del Watchdog. Tenemos que poner todo nuestro empeño en que nuestro programa funcione sin Watchdog. Cuando estemos seguros de ello podremos activarlo para prevenir aquello que no hemos sido capaces de prever. Prevenir lo imprevisto (curiosa frase) 2ª.- Una vez decididos a activar el Watchdog hay que ser muy precavidos (ó listos ó inteligentes ó despiertos ó cautos) sobre dónde colocar los refrescos del Watchdog. Por un lado no podemos poner refrescos sin ton ni son, a tontas o a locas, en cualquier sitio porque nos parece bien, sin tener en cuenta su potencial riesgo de bucle sin salida. En este caso el Watchdog no nos serviría de nada ya que si acertamos a colocar el refresco justo dentro de un proceso realmente infinito y sin salida (es posible hacerlo: yo lo hice una vez y me dio dolores infinitos de cabeza) el bucle sería definitivamente infinito ya que al ser refrescado precisamente en él, nuestro Watchdog no nos sacaría nunca de él. Por otro lado hay que prever lo contrario, cualquier proceso normal y válido de nuestro programa cuya duración supere (o pueda superar) el del vencimiento del Watchdog. Es de cajón darse cuenta de que en estos procesos potencialmente largos debe tenerse prevista la posibilidad de que se nos desborde el Watchdog y nos lo dejemos a medio realizar. Debemos por tanto colocar en él un refresco. Un ejemplo típico y tópico de esto es cuando están involucrados procesos de comunicaciones en las que estamos esperando recibir algo que, por vaya usted a saber qué circunstancias, no nos llega. Mi consejo final: No debemos abusar de los refrescos y hay que colocarlo/s en aquel/llos puntos en que se den las dos siguientes circunstancias: a.- Si el programa pasa por allí es que todo va bien. b.- Si el programa pasa por allí el Watchdog puede desbordar. Yo usualmente empiezo por activarlo sin refresco alguno y compruebo que efectivamente se resetea el PIC. Después coloco un solo refresco, en el punto de inicio o vuelta al bucle infinito normal de mi programa (En C sería justo detrás de mi while(true)) y compruebo si los distintos procesos "caben" en el tiempo de desborde. Y por último coloco un refresco en cada uno de los que se que consumen mas tiempo. Con esta forma me aseguro de que he ajustado al mínimo indispensable los puntos de refresco con tal de que el Watchdog me sirva para algo en lo que realmente se me ha escapado y no he sido capaz de prever. Espero que os sirva. |
Nota: Esta página Web esta repleta de imágenes, textos, logotipos y demás material extraídos de los mas variados medios de los que no soy ni autor ni depositario de los correspondientes derechos de autor, uso y/o reproducción. Si Ud. es depositario de dichos derechos y desea que el material correspondiente sea eliminado de esta Web no dude en ponerse en contacto conmigo mediante e-mail y será inmediatamente retirado. Gracias. |
Esta página fue modificada el 07-08-2010 22:42:20