domingo, 13 de mayo de 2018

Principio de multitarea. Desterrando a los delays.


Este artículo tiene como base proponer una metodología para efectuar programas multitarea en microcontroladores usando el compilador CCS PicC.
Todo está en función de un temporizador y la directiva #use timer, no se hace uso de las interrupciones ya que estas estarán reservadas para tareas de mayor prioridad.

La directiva #use timer.

Esta directiva crea un timer tick usando uno de los temporizadores del Pic. El timer tick es inicializado a cero al iniciar el programa. Esta directiva crea una constante con nombre TICKS_PER_SECOND que especifica el número de ticks que ocurren en un segundo.
… mayor información en el help de CCS PicC.
La rutina principal main(), se convertirá en el lanzador de tareas y las tareas serán funciones.
Este modo es ideal para las tareas repetitivas, mientras que las secuenciales requieren algunos “trucos”.
Este método lo vi en uno de los ejemplos que CCS tiene disponible.
#use timer(timer=1,tick=10ms,bits=16,noisr)
Usa el temporizador 1, tick cada 10 milisegundos, tamaño de los ticks 16 bits, no se usa interrupción para actualizar los ticks.

Ejemplo.

Generar el parpadeo de tres leds, el primero con un periodo de 100[ms], el segundo con en periodo de 300[ms] y el tercero con un periodo de 1[s].
Es importante que el tiempo mínimo de repetición de la tarea sea mayor o igual a tick usado en el use timer.

En el archivo main.h

/*****************************************************************************/
#include <16F628A.h>
/*****************************************************************************/
#FUSES NOWDT                    //
#FUSES PUT                      //Power Up Timer
#FUSES MCLR                     //Master Clear pin enabled
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES NOPROTECT                //Code not protected from reading
/*****************************************************************************/
#use delay(internal=4MHz)
/*****************************************************************************/
#use timer(timer=1,tick=100ms,bits=16,noisr)
/*****************************************************************************/

En el archivo main.c.

*****************************************************************************/
#include <main.h>
/*****************************************************************************/
void tarea1()
{
   output_toggle(pin_a0);  //cambia de estado el bit 0 del puerto A
}
/*****************************************************************************/
void tarea2()
{
   output_toggle(pin_a1);  //cambia de estado el bit 1 del puerto A
}
/*****************************************************************************/
void tarea3()
{
   output_toggle(pin_a2);  //cambia de estado el bit 2 del puerto A
}
/*****************************************************************************/
void main()
{
   unsigned int16 tick,tick_tarea1=0,tick_tarea2=0,tick_tarea3=0;
   while(true)                                //bucle infinito
   {
      tick=get_ticks();     //lee el valor actual de los ticks
      if((tick-tick_tarea1)>=TICKS_PER_SECOND/10)//aprox 100ms
      {
         tick_tarea1=tick;//actualiza el los tick de la tarea1
         tarea1();                          //lanza la tarea 1
      }
      if((tick-tick_tarea2)>=TICKS_PER_SECOND/3.3)//aprox 300ms
      {
         tick_tarea2=tick; //actualiza el los tick de la tarea2
         tarea2();                           //lanza la tarea 2
      }
      if((tick-tick_tarea3)>=TICKS_PER_SECOND)       //aprox 1s
      {
         tick_tarea3=tick; //actualiza el los tick de la tarea3
         tarea3();                           //lanza la tarea 3
      }
   }
}
/*****************************************************************************/



sábado, 11 de marzo de 2017

WINAVR PUERTOS IO parte 1.


WinAvr nos permite acceder a los puertos de entrada y salida mediante los registros DDRx, PORTx y PINx, donde x corresponde al nombre del puerto a utilizar.

DDRx.


Es el registro que se ocupa de hacer que el puerto sea de entrada o salida según el valor que tenga.

Si los bits de DDRx son 0 entonces será de entrada y si los bits son 1 será de salida.

Ejemplo:

DDRA=0b00001111; //los bits 0 a 3 del puerto A son de salida el resto son de entrada.

PORTx.


Es el registro que escribe o lee el puerto.

Si los bits de PORTx son 0 entonces en el puerto se escribirá un nivel bajo y si los bits son 1 en puerto tendrá nivel alto.

Ejemplo:

PORTA=0b11000000; //los bits 0 a 5 tienen nivel bajo, el resto son de nivel alto (esto si el puerto es configurado como salida mediante el registro DDRA=0b11111111).

PINx.


Este registro se utiliza para efectuar la lectura de un puerto configurado como entrada.

Ejemplo:

var=PINA; //Guarda en la variable var el valor de los niveles que existe en el puerto A (esto si se configura al puerto A como entrada mediante el registro DDRA=0b00000000).

En WinAVR para tener acceso a los puertos de entrada y salida se debe incluir la librería io.h del siguiente modo.

#include <avr/io.h>

Para poder hacer uso de retardos en los programas se tiene la librería delay.h y se la llama del siguiente modo.

#include <util/delay.h>

Ejercicio 1.


Hacer un contador ascendente por el puerto D con un tiempo de retardo de 200m[s].

Solución.

Se debe configurar el puerto como salida (0b11111111=255) y luego hacer que el valor del puerto se incremente en 1.

#include <avr/io.h>     //incluye la librería para el acceso a los puertos

#include <util/delay.h>           //incluye la librería para los retardos

int main(void)                        

{

            PORTD=0;                  //borra el puerto D

            DDRD=255;               //configura todo el puerto D como salida

            while(1)

            {

                        PORTD++;                 //el puerto D se incrementa en 1

                        _delay_ms(200);         //retardo de 200ms

            }

            return 0;

}

Ejercicio 2.


Reflejar por el puerto D los valores lógicos que existen el puerto C.

Solución.

Pare este caso el puerto D debe ser configurado como salida y el puerto C como entrada y para no utilizar resistores externos, en el puerto C activaremos los pull-up internos (estos existen en todos los puertos del ATMEGA 32). Hay que tener cuidado de no manipular bit 2 (PUD) del registro SFIOR.

#include <avr/io.h>

#include <avrlib/avrlibdefs.h>

#include <util/delay.h>

int main(void)

{

            PORTD=0;      //borra el puerto D

            PORTC=255;  //habilita el pull up en el puerto C

            DDRD=255;   //puerto D como salida

            DDRC=0;        //puerto C como entrada

            while(1)

            {

                        PORTD=PINC;           //escribe en el puerto D los valores leídos en el puerto C

                        _delay_ms(10);                       //retardo de 10ms

            }

}

El acceso a los bits individuales de los puertos es algo engorroso teniendo que hacer bastante uso de los enmascaramientos pero podemos lidiar con esos problemas con la ayuda de las librerías proporcionadas por Procyon AVRlib (la manera de obtenerlos y situarlos en el lugar adecuado se mencionan en la anterior entrega titulada WINAVR 0).

La nueva librería a utilizar es avrlibdefs.h y la incluiremos del siguiente modo.

#include <avrlib/avrlibdefs.h>

Las funciones disponibles para la manipulación de bits de los puertos son:

bit_is_set(sfr, bit);       //Retorna verdadero si el bit esta a 1

bit_is_clear(sfr, bit);   //Retorna verdadero si el bit es 0

cbi(reg,bit);                 //Pone a 0 el bit

sbi(reg,bit);                 //Pone a 1 el bit

Ejercicio 3.


Hacer que el bit 1 del puerto D muestre el estado invertido del bit 5 del puerto C.





Solución.

Los puertos después del reset siempre inician como entradas así que solo hacen falta definir los puertos de salida y para no usar resistores externos utilizaremos los pull up internos del puerto C.

#include <avr/io.h>

#include <avrlib/avrlibdefs.h>

#include <util/delay.h>

int main(void)

{

            sbi(PORTC,5); //pull up activado en el bit 5 del puerto C

            sbi(DDRD,1);  //bit 1 del puerto D como salida

            while(1)

            {

                        if(bit_is_set(PINC,5))

                        {

                                   cbi(PORTD,1);

                        }

                        else

                        {

                                   sbi(PORTD,1);

                        }

                        _delay_ms(100);

            }

}

Ejercicio 4.


Efectuar un contador ascendente descendente visualizado en un display conectado al puerto B. Cuando el pin 0 del puerto A esta a nivel bajo el contador es ascendente y de otro modo el contador es descendente.

Solución.
Para generar los dígitos en el display se hará uso de un decodificador implementado en forma de vector.
#include <avr/io.h>
#include <avrlib/avrlibdefs.h>
#include <util/delay.h>
int main(void)
{
            //decodificador para el display
unsigned char tabla[]={63,6,91,79,102,109,125,7,127,103};
            unsigned char contador=0;
            PORTD=0;                  //borra el puerto D
            DDRD=255;                //puerto D como salida
            sbi(PORTA,0);            //pullup en el bit 0 del puerto D
            while(1)
            {
                        PORTD=tabla[contador];
                        _delay_ms(500);
                        if(bit_is_clear(PINA,0))
                        {
                                   if(++contador==10)    //primero incrementa contador y luego compara
                                   {
                                               contador=0;
                                   }
                        }
                        else
                        {
                                   if(--contador==255)    //primero decrementa contador y luego compara
                                   {
                                               contador=9;
                                   }
                        }
            }
            return 0;
}



Hasta la siguiente entrega.

lunes, 6 de febrero de 2017

Grabando AVRs


   GRABANDO AVRs


Cuando trabajamos con AVRs podemos encontrar muchas herramientas de software libre (WINAVR por ejemplo). En cuanto al Hardware también podemos encontrar libremente el grabador para AVRs conocido como USBasp del cual se pueden encontrar los esquemas fácilmente. En mi caso compre el grabador ya hecho a un compañero de mi facultad.

Pero con el grabador no es suficiente adicionalmente se necesita un programa que haga funcionar al grabador (USBasp), por fortuna de estos hay varios y el que me parece bastante practico es el conocido Khazama, este programa es libre y se lo puede descargar de la página del autor.


Instalando Kazama.
Instalamos y aceptamos todo lo que os pida. Una vez instalado tenemos.
Al ejecutar el programa es mejor ejecutarlo como administrador.
Luego seleccionamos el microcontrolador ATMEGA32.

Instalando controladores para USBasp.

Una vez conectado el grabador (USBasp) puede que la PC no lo reconozca por falta de los controladores. Buscando en el administrador de dispositivos tenemos.
Ahora procedemos a instalar los controladores.
Seleccionamos “buscar software del controlador en el equipo”.
Seleccionar la ruta donde se instaló khazama para mi caso fue (win10 32 bits)
C:\Program Files\khazama.com\Khazama AVR Programmer\win-driver\libusb_1.2.4.0

Luego aceptar la advertencia de software desconocido. Ahora tenemos el USBasp listo para trabajar con khazama y grabar AVRs.

Conectando USBasp al AVR.

ATMEGA32 vienen de fábrica configurado para trabajar con reloj interno de 1M[Hz] por eso el grabador USBasp tiene un pin de selección de reloj lento (Slow SCK=GND), esto puede ser molesto algún momento ya que el AVR se graba lento. Para cambiar esta situación se puede elegir un oscilador interno más rápido (16M[Hz]) o usa un cristal externo.
Importante. Si se hace uso de un cristal externo la siguiente vez que se grabe el AVR debe hacerse con el mismo cristal conectado.
Pines de conexión de USBasp y AVR.
Trabajando con khazama.

Si se instaló la versión v1.7.0 configuramos la velocidad del reloj ISP a automático.

Si se desea se puede tikear todas las opciones excepto Write Fuses and Lock esto para no tener accidentes.
Cuidado si se deshabilita el bit SPIEN al momento de grabar los fuses tendremos un caos… bueno no, solo perderemos la capacidad de grabar al AVR, para saber el real efecto de esto se puede buscar en internet.
Ahora se configurara el AVR para que trabaje con un cristal externo de 16M[Hz] para obtener la mayor velocidad de trabajo de la cpu.
Primero leemos la configuración de los fuses presionando el icono de candado cerrado presionando Read All.

Desplazándonos a la parte interior tenemos los fuses que configuran la fuente de reloj.



Una vez seleccionado se procede a grabar los fuses con el boton Write All y listo, el AVR ahora trabajara con cristal externo (16M[Hz]) la próxima vez que se desee grabar el AVR el cristal debe estar presente.
A partir de este momento ya podemos cambiar la velocidad del reloj ISP del grabador USBasp cambiando la posición del clock select, para mi caso el grabador queda asi.
Después de esto ya no tendremos la necesidad de manejar los fuses y solo nos dedicaremos a grabar nuestros programas en el AVR.

Código básico de prueba.
 Una vez creado el proyecto en WINAVR editamos el Makefile y cambiamos el valor de la F_CPU a 16M[Hz].

Una vez compilado solo queda grabar el chip.