Mostrando entradas con la etiqueta ATmega32. Mostrar todas las entradas
Mostrando entradas con la etiqueta ATmega32. Mostrar todas las entradas

lunes, 28 de marzo de 2016

I2C + RTC DS3231 usando C


En el siguiente post mostraré cómo leer y escribir, mediante I2C, un rtc DS3231 de Maxim integrated.

El DS3231 es un RTC serial con una exactitud muy alta gracias a su oscilador interno de 32KHz que es compensado ante los cambios de temperatura (TCXO u Osciladores de Cristal Compensados por Temperatura). La hoja de datos nos dice que podemos tener una exactitud de ±2ppm que podemos traducir en unos 126 seg por año.
La lectura y escritura de registros del DS3231 se realiza mediante la interfaz I2C a una velocidad de 400KHz.

El ejemplo presentado en este post es para un ATmega32, pero puede ser fácilmente adaptado a otros microcontroladores. 
En este post anterior (I2C con ATmega8) se explica el funcionamiento del bus I2C, aquí sólo mostraré y explicare las librerías I2C y RTC necesarias para la escritura y lectura del DS3231.


I2C ATmega

El ATmega32 cuenta con 4 registros los cuales son mostrados en la figura 1 y que debemos conocer . En ellos se configura la velocidad del bus, se controla el bus, se tiene acceso a los estados del bus y de puede leer/escribir los datos recibidos/enviados.

Figura 1.image


PARTE 1. Files I2C

file i2c.h.  Contiene las definiciones necesarias para configurar el bus I2C.
  • En la figura 2 se calculan los posibles valores del registro TWBR en base a la velocidad del bus I2C (400KHz).

Figura 2.image

  • En la figura 3 se selecciona el correcto valor del registro TWBR y de los bits de pre-escaler (TWPSX).

Figura 3.image

  • En la figura 4 se crean los prototipos de las funciones necesarias para el manejo del bus.

Figura 4.image



file i2c.c  Contiene la implementación de las funciones declaradas en el archivo de cabecera .h.

  • En I2C_Init se configura la velocidad del bus I2C.

Figura 5.image

  • I2C_Start envía una condición de start y espera que el envío se haya realizado. Retorna un 1 si el envío fue satisfactorio.
  • I2C_ReStart envía una nueva condición de start y espera que el envío se haya realizado. Retorna un 1 si el envío fue satisfactorio.

Figura 6.image


  • I2C_Stop envía una condición de stop y espera a que se haya ejecutado.

Figura 7.
image


  • I2C_Write envía el dato hacia el esclavo y retorna un 1 si el envío fue realizado y se ha recibido un ACK.

Figura 8.
image

  • I2C_Read espera que el bus se encuentre habilitado para iniciar una lectura y enviar un ACK o NACK de acuerdo al valor de la variable ACK_NACK.

Figura 9.image



PARTE 2. Files RTC

Antes de empezar con los files rtc.h y rtc.c explicaré brevemente lo que son las estructuras y las uniones.
  • Una estructura es un agrupamiento de variables bajo un nombre común. Estos agrupamientos pueden contener diferentes tipos de datos. Para definir una estructura se hace uso de la palabra reservada “struct”.
La estructura ejemplo contiene 3 miembros de diferente tipo (uint8_t, int16_t y un array int8_t).
La declaración de variable de tipo estructura es como sigue:
struct ejemplo var1: Aquí “var1” es una variable del tipo estructura ejemplo.
struct ejemplo
{
    uint8_t c;
    int16_t i;
    int8_t k[4];
};
Para acceder a los miembros de la estructura se hace uso del operador punto (.).
var1.c; var.i; var1.k[0], var1.k[1], var1.k[2] y var1.k[3].
Algo mas sencillo al momento de crear estructuras es la declaración de un alias usando la palabra cable “typedef”. Con esto evitaremos poner “struct ejemplo” cada ve que se quiera declarar una nueva variable.
Será suficiente colocar “ejemplo var2”.
typedef struct
{
    uint8_t c;
    int16_t i;
    int8_t k[4];
}ejemplo;
Las estructuras también pueden ser punteros y apuntadas por punteros tan igual como las tipos nativos (char, int, etc).  En estos casos se accede  a los miembros mediante el operador fecha (->).
variable: ejemplo var1.puntero: ejemplo *pt= &var1;
uso: pt->c; pt->i; pt->k[0], pt->k[1], pt->k[2] y pt->k[3].


  • Una union es similar a una estructura, pero estas difieren en que cada elemento en las estructuras tiene un espacio de memoria mientras en las uniones todos los elementos ocupan el mismo espacio de memoria. El tamaño de la unión depende del miembro mas grande.
La union mostrada en la parte derecha ocupa sólo 4 bytes.
Después de la asignación los valores quedan distribuidos como en la siguiente figura en donde el byte mas significativo esta último ya que ATmel usa little endian.
                  image
El usuario verá las variables como en la siguiente figura.

             image

Creación:

         typedef union
         {
              uint8_t u8;
              uint16_t u16;
              uint32_t u32;
         }ejemplo;


Declaración:

            ejemplo var1;

Asignación:

           var1.u32 = 0x01234567

Ahora si pasaremos a los archivos rtc.

file rtc.h
  • En la figura 10 se crean las estructuras para hora y fecha. También hay una tercera estructura (RTC_t) que esta conformada por las estructuras Hora_t y Fecha_t.

Figura 10.image

  • En la figura 11 se definen las direcciones de los registros internos de DS3231. También una mascara para filtrar los datos leídos desde el DS3231. Finalmente, y ya que la dirección del DS3231 es 0b1101000, se define DS3231_READ y DS3231_WRITE para las lecturas y escrituras desde y hacia el RTC.

Figura 11.image

  • En la figura 12 se declaran los prototipos de las funciones para leer/escribir el RTC. Las 6 últimas funciones tienen un puntero a estructura(Hora_t, Fecha_t y RTC_t) .

Figura 12.image


file rtc.c
  • RTC_Init sólo inicializa el bus I2C.

Figura 13.image

  • En la figura 14 se muestran las funciones de lectura/escritura para cualquier registro del DS3231.

Figura 14.image

  • RTC_SetHora escribe una nueva hora en el DS3231. La nueva hora proviene de una estructura del tipo Hora_t. Del mismo modo RTC_SetFecha escribe una nueva fecha en el DS3231.

Figura 15.image

  • RTC_GetHora leer la hora del DS3231 y lo guarda en una estructura del tipo Hora_t que es apuntada por el puntero hora. RTC_GetFecha hace una tarea similar con la fecha.

Figura 16.image

  • RTC_GetTime hace uso de las funciones ya mencionadas para leer la hora y la fecha y guardarla en la estructura apuntada por el puntero rtc de tipo RTC_t. RTC_SetTime se encarga de escribir la hora y fecha de manera similar.

Figura 17.image



PARTE 3. main

En el main se configura el uart para enviar/recibir la hora y la fecha hacia/desde un programa hecho en Visual C#.
También se configura una interrupción cada 1 segundo para leer el DS3231.

Figura 18.image


Aquí presento el programa.

  • En la figura 19 se muestran los includes que necesita el main.

Figura 19.image

  • En la figura 20 se muestra la definición de la union TRAMA_t que une una estructura y un array de 10 elementos. Este nuevo tipo de dato (TRAMA_t) servirá para el envío y recepción de información hacia y desde el programa en la PC. En la linea 28 se crea la variable “tx_trama”.
  • En la linea 27 se declaran dos variables(rtc y rx_rtc) del tipo RTC_t. En la variable rtc se almacena la hora y fecha leidas del DS3231 cada 1 segundo. rx_rtc sirve para almacenar la hora y fecha recibidas desde el programa en la PC antes de ser enviadas al DS3231.
  • Las variables update_rtc y send_rtc son actualizadas en la interrupción del uart y sirven para actualizar el DS3231 o enviar la hora y fecha hacia la PC.

Figura 20.image

  • En las lineas 37-40 (Figura 21) se configura el uart.
  • En las lineas 42-44 se configura la interrupción del timer1 cada 1 segundo.
  • En las lineas 48 y 49 se inicializan las variables update_rtc y send_rtc.
  • También se inicializa la variable tx_trama y se activa el bit de interrupción global.

Figura 21.image

  • Sí el programa de la PC envía una nueva actualización para el RTC esta será guardada en rx_rtc (en la interrupción por recepción) y el RTC será actualizado en el while. También se enviará la hora y fecha actual hacia el PC si es requerido.

Figura 22.image

  • En la figura 23 se muestra la interrupción por recepción del uart. Aquí se reciben todos los datos, se hace un chequeo de integridad de los datos y de acuerdo al comando de lectura (‘R’) o escritura (‘W’) se actualizará las variables ‘update_rtc’ y ‘send rtc’.

Figura 23.image

  • Finalmente, en la interrupción del timer1 se lee la hora y fecha del rtc. También se muestran las funciones del lectura y escritura en el uart.

Figura 24.image


Enlaces de programas:

Programa RTC DS3231
HORA+FECHA.exe





jueves, 3 de marzo de 2016

Control, PID, de velocidad con ATmega



Este post será dedicado al control de velocidad de un pequeño motor DC usando el algoritmo PID.
El esquema general a desarrollar se muestra a continuación (Figura 1).

Figura 1.
pid1

El algoritmo será implementado en un ATmega32. Este microcontrolador recibirá, por el puerto serie, órdenes desde una interfaz gráfica hecha en visual c# (Figura 2).

El programa en la PC permitirá enviar hacia el controlador(ATmega32) el SetPoint(SP), Kp(Ganancia proporcional), Ti(Tiempo integrativo en seg) y OUT cuando se seleccione modo manual. También permitirá graficar PV, SP y OUT. PV, SP y OUT son escalados de 0 a 100.

Figura 2.
PID


DEL SOFTWARE (en el controlador mega32).

Del uC usaremos:

  • Los pines PD0 y PD1 (INT0 e INT1) para leer las salidas(A y B) del encoder tal como en un post anterior.
  • El UART para recibir, mediante interrupción, SP,Kp,Ti y OUT(en modo manual) y para enviar PV y OUT(en modo automático).
  • El Timer1 para generar una interrupción cada 100ms que permitirá ejecutar el algoritmo de control y hacer el envío de datos a la PC.
  • El Timer2 para generar una señal PWM equivalente a la salida (OUT) del controlador. La salida será por el pin PD7/OC2.

El programa funcionará a una frecuencia de 7.3728MHz y el UART a 115200bps tal como se muestra en la figura 3.

Figura 3.
image


Se usaran las uniones de la figura 4. TRAMA_t es para la recepción y envío de datos hacia/desde la PC y States_t para el manejo de los estados del encoder.

Figura 4.
image
Las variables a usar son:

Figura 5.
image


martes, 1 de marzo de 2016

App Inventor + Led ATmega


Aquí les dejo un vídeo de cómo hacer una aplicación en el app inventor.




Los programas los pueden descargar aquí:



Se usó un modulo HC-06, ATmega32 y un programado USBasp.

image


El programa envía 3 comandos: CMD1, CMD2, y CMD3.

El CMD1 y 2 son enviados con una orden, 0 ó 1, para apagar o encender un led.
Se envía CMD10 y CMD11 para apagar y encender un led rojo. Para el led verde se envía CMD20 y CMD21.
También se envía el CMD3 acompañado de un número, entre 0 y 255, para cambiar la intensidad de luz de un tercer led amarillo usando PWM.
Los tres comando llevan al final un salto de linea (\n) como fin de trama.

El programa final se ve como en la siguiente figura:


Screenshot_2016-03-02-01-57-30


Breve explicación:

Cada vez que se presione un botón BTNGreen, y si hay conexión con el modulo bluetooth, el programa enviara CMD20 ó CMD21 de acuerdo a la variable green_status. También se cambia el color y texto del botón.

image


miércoles, 24 de febrero de 2016

Encoder + ATmega32


En este post desarrollaré un programa para leer las entradas de un tipo encoder incremental en cuadratura.

La señal en cuadratura se caracteriza por tener dos señales cuadradas que se encuentras desfasadas 90° una con respecto a la otra. Esto es mostrado en la figura “Señales del encoder”.
El movimiento rotacional puede ser medido contando los flancos que produce cada una de las señales. La relación de fase entre las dos señales determina la dirección de rotación. Cuando en giro es horario la señal QDPH0 (Comúnmente encontrada como A) adelanta a QDPH90 (B) en 90°. En el sentido antihorario ocurre exactamente lo contrario.

Fig. 1 Señales del encoder

Sin título

Para nuestro ejemplo conectaremos las salidas CLK(A) y DT(B) de un encoder, como el mostrado en Fig.2, a las entradas PD2(INT0) y PD3(INT1) de un ATmega32. El encoder y el uC son alimentado con 5Volt.

Fig. 2 Encoder
encoder-module-ky-040-1

El ejemplo es muy sencillo y puede funcionar con una lectura constante (en while) de las señales A y B, usando una interrupción para la señal A ó usando dos interrupciones para las señales A y B.

Primero, y luego de crear el proyecto, debemos declarar la velocidad del CPU (F_CPU es necesaria para las rutinas _delay…) y la del UART si fuera necesario. Aquí muestro una imagen de mi configuración.

image


Bueno, aquí el programa.

Includes:
image

Variables a usar:

image

14.- Se declara una variable global del tipo int32_t (cuenta) para que almacene las cuentas del encoder. Esta tiene que ser del tipo volatile para poder usarla sin problemas en las interrupciones.
15.- cambio las usaremos como un flag que nos indica que en valor de cuenta ha cambiado.
16.- str[30] almacenara un string que será enviado por el puesto serie.
18-26.- Se crea una nuevo tipo de dato(States_t) para almacenar el estado actual del encoder (S0) y el estado anterior (S1).
27.- Se crea la variable enc que es del tipo States_t.