sábado, 12 de agosto de 2017

Introducción a la escritura de código en C para XMEGA (AVR1000)


1. Introducción

Cortos tiempo de desarrollo y requerimientos de productos electrónicos de alta calidad han hecho de los lenguajes de programación de alto nivel un requisito. La principal razón es que los lenfuajes de alto nivel hacen más facil el mantenimiento y la reutilización del código debido a la gran portabilidad  y legibilidad.
La simple elección del lenguaje de programación no asegura una alta legibilidad y reusabilidad; el buen estilo de codificación sí. Así pues, los perifericos XMEGA, los archivos cabecera y los controladores estan diseñados teniendo esto en cuenta.
El lenguaje de programacíón mas ampliamente usado para microcontroladores AVR es C, y esta nota de aplicación (AVR1000) se enfoca en la programación en C. Para proporcionar soporte a muchos de los compiladores de C para AVR disponibles los ejemplos son en la medida de lomposible escritos en ANSI C. Algunos de los ejemplo son especificados para IAR Embedded Workbench, pera la idea y metodos pueden ser uados para otros compiladores con cambios menores.

Ejemplo de codificación – AVR GCC
image


2. Módulos XMEGA

Un AVR XMEGA esta compuesto de varios bloques básicos. Un CPU AVR, SRAM, Flash, EEPROM, y una serie de perifericos. Esos bloques son llamados “tipos de módulo”. Un XMEGA puede tener una o más instancias de un tipo de módulo dado. Todas las instancias de un tipo de módulo tienen las mismas características y funciones.
Algunos de los módulos pueden ser un subconjunto de otros tipos de módulos. Estos módulos heredan un subconjunto de características del módulo superior, todas las características heredadas son completamente compatibles. Esto se aplica por ejemplo a los timer y puertos de entrada y salida. Un subconjunto de módulos para un timer significa que este tiene menos canales de captura y comparación que un timer completo. Similarmente, un puerto IO puede tener menos de ocho pines.
Un tipo de módulo puede ser un “USART”, mientras una instancia de este tipo puede ser “USARTC0”, donde el sufijo C0 indica que la instancia es el USART 0 en el puerto C. Cada módulo tiene un número de registros que contienen bits de control o de estado. Todos los módulos de un tipo dado contienen el mismo conjunto (o subconjunto) de registros, y todos estos registros contienen el mismo conjunto (o subconjunto) de bits de control y estado.

Tipo de módulo, instancias, registros y bits.image

Cada módulo tiene una dirección base fija en el mapa de memoria I/O y todos los registros contenidos en el módulo tienen direcciones de desplazamiento fijas relativas a dirección base del módulo. De esta manera, cada registro no sólo tendrá una dirección absoluta en el espacio de memoria, sino también una dirección relativa definida por un offset. Los desplazamientos de las direcciones de registros son iguales para todas las instancias de un tipo de módulo, simplificando la tarea de escribir controladores que puedan ser usados para todos los módulos de un tipo específico.

2.1 Convención de nomenclatura de registros


Los registros están divididos en registros de control, registros de estado y registros de datos y el nombre de dichos registros refleja esto.
Un registro de control de propósito general del módulo es llamado CTRL. Sí existen múltiples registrol de control en un módulo ellos tendrán un caracter como sufijo. En este caso el registro de control será llamado CTRLA, CTRLB, CTRLC etc. Esto también aplica a los registros de estado (STATUS).
Para registros que tienen una función específica el nombre de estos refleja esta funcionalidad. Por ejemplo, un registro de control que controla los niveles de interrupción de un módulo es llamado INTCTRL.
Ya que el bus de datos AVR es de 8bits, los registros mas grandes son implementados usando varios registros de 8 bits. Para un registro de 16 bits, el alto (High) y bajo (Low) son accesados agregando una “H” o “L” respectivamente al nombre del registro. Por ejemplo, el registro de cuenta de 16 bits del Timer/Counter son llamados CNT. Los dos bytes son llamados CNTL y CNTH.
Para registros más grandes que 16 bits, los bytes son numerados desde el byte menos significativo. Por ejemplo, el registro de calibración de 32 bits del ADC es llamado CAL. Los cuatro bytes son llamados CAL0, CAL1, CAL2 y CAL3 (desde el menos al más significativo). Muchos de los compiladores de C ofrecen un manejo automatico en el acceso a multiples bytes. En el caso del timer el nombre CNT, sin sufijos “H” o “L”, puede ser usado para realizar un acceso al registro de cuenta (CNT). Esto también se aplica en el caso de los registros de 32 bits.

2.2 Convención de nomenclatura de bits


Los bits en los registros pueden tener una función individual o ser parte de un grupo de bits que tienen una función conjunta: Un bit individual puede ser un bit que habilita un módulo, ej. el USART ENABLE bit. Un grupo de bits puede consistir de dos o más bits que juntos seleccionan una configuración especifica del módulo al cual pertenecen. Un grupo de bits ofrecen hasta 2^n posibles opciones, donde n es el número de bits en el grupo. Los dos bits que controlan el nivel de interrupción de recepción completa del USART, RXINTLVL[1:0], es un ejemplo de un grupo de bits. Esos dos bits ofrecen las siguientes secciones:

Bits RXINTLVL y los correspondientes niveles de interrupciónimage

Solamente los bits que forman parte de un grupo tendrán como sufijo un número. El registro de control D de un Timer/Counter tiene dos grupos de bits. EVACT y EVSEL. Los bits de esos grupos tienen un número como sufijo, mientras que el bit EVDLY, que no forma parte de un grupo, no tiene sufijo.

image


3. Escribiendo código en C para XMEGA

La siguiente sección se enfoca en como escribir código en C para XMEGA. Los ejemplos muestras como hacer el código altamente legible y portable entre los diferentes dispositivos XMEGA. Los ejemplos también pueden ser usados como una guía para escribir código que sea fácil de escribir y mantener.
Los módulos XMEGA se encuentran en bloques dedicados y continuos en el espacio de memoria y se pueden ver como unidades encapsuladas. Esto se refleja en la forma en que se acceden a los módulos cuando se codifica en C: Los módulos son encapsulados usando estructuras en C, en las que están contenidos todos los registros del módulo. La siguiente figura muestra una ilustración de esto.

Modulos puestos en bloques dedicados en el espacio de memoria.image

Debemos notar que algunos registros no tienen una asociación directa con módulos. Aquellos registros no estan encapsulados en estructuras, ya que las estructuras son usadas para asociar registros con un módulo.
Para grandes proyectos, las estructuras de los modulos proporcionan una ventaja, no sólo la legibilidad, sino también porque los compiladores pueden reutilizar los driver y por lo tanto generar un código muy compacto.

3.1 Archivos cabecera XMEGA

Un archivos de cabecera dedicado esta disponible para cada dispositivo XMEGA. Si el dispositivo que se usará es especificado en la configuración del proyecto (asumiendo que usamos Atmel Studio), el compilador se encargará de incluir automáticamente el archivo cabecera correcto siempre que se incluya el archivo de dispositivo como se muestra en la soguiente figura.

image
La ventaja de esto es que si el dispositivo cambia, no será necesario cambiar los archivos de origen, sólo la configuración del proyecto.

3.2 Registros en los módulos

El mapa de entradas/salidas esta dispuesto de modo tal que todos los registros para un módulo periférico dado se coloquen en un bloque de memoria continua. Los registros pertenecientes a diferentes modulos no son mezclados. Esto hace posible organizar todos los módulos periféricos en estructuras. Donde la dirección de la estructura define l dirección base del módulo. Todos los registros pertenecientes a un módulo son elementos de la estructura del módulo.
Un ejemplo es módulo “USART”. El tipo estructura y la declaración para este módulo son mostradas en las siguientes figuras.

Declaración de tipo de móduloimage

Declaración de módulo(se instancia cada USART)image

Uso de la estructuraimage


3.2.1 Registros multibytes en módulos.

Algunos registros son usados conjuntamente con otros registros para representar valores de 16 y 32 bits. Como ejemplo de esto es la declaración de la estructura ADC de la siguiente figura.

Declaración del tipo de módulo ADCimage

En la figura anterior, los registros de resultado de cada canal CH0RES, CH1RES, CH2RES, CH3RES, y el registro de comparación CMP, son valores de 16 bits. Ellos son declarados usando la macro  _WORDREGISTER la cual es mostrada en la siguiente figura.

Macro _WORDREGISTER image

En la siguiente figura registro de cuenta, CNT, es un valor de 32 bits y es declarado usando la macro _DWORDREGISTER.

Declaración de tipo de modulo RTC32_structimage

Macro _DWORDREGISTER image

Como se ha visto, la macro WORDREGISTER usa los sufijos “H” y “L” para para los bytes altos y bajos respectivamente. La macro usa números como sufijos para indicar el orden de los bytes. Tanto los registros de 16 como los de 32 bits pueden ser accesados en modo 16/32 bits sin usar el sufijo como se muestra en la siguiente figura.

Uso de registros de 8, 16 y 32 bits.image


3.3 Direccionamiento de módulos.

Las definiciones de todos los módulos periféricos son encontradas en el archivo cabecera del dispositivo. La dirección para el módulo es especificada en ANSI C para tener compatibilidad con muchos de los compiladores disponibles. En la siguiente figura se muestra las definiciones de los ADC para los puertos A y B. Aquí se puede ver como las instancias de los módulos son desreferenciados a una dirección absoluta en la memoria, la cual coincide con la dirección base de la instancia del módulo. Los punteros al módulo son predefinidos en los archivos cabecera de cada XMEGA, por lo tanto ya no es necesario agregar estas definiciones a nuestro código.

Definición de módulos ADCimage

3.4 Máscaras de bits y máscaras de grupos de bits.

Los bits en los registros pueden ser manipulados usando máscaras predefinidas, o alternativamente la posición de los bits. No se recomiendo usar la posición de los bits. Las máscaras de bits predefinidas estan relacionadas a bits individuales, denominados máscara de bits o grupo de bits.
Una máscara de bit es usada tanto cuando se setea o limpia un bit. Una máscara de grupo es principalmente usada cuando limpiamos múltiples bits en un grupo. El seteo de multiples bits que forman parte de un grupo es cubierno en la sección 3.5.

3.4.1 Mascara de bit (Bit Mask).

Considere el registro de control del Timer D, CTRLD. Los grupos de bit, los nombres de bit, las posiciones de los bits y las máscaras de bit de este registro se pueden ver en la siguiente tabla.

Grupos de bit, nombres de bit, posiciones y máscaras de bit para los bits del registro CTRLD.image
Ya que el nombre de los bits necesita ser único para que el compilador pueda manejarlos, todos los bits estan prefijados con el nombre del módulo al cual pertenecen. En muchos casos, el nombre del tipo del módulo esta abreviado. Para todas las definiciones de bits relacionadas con los módulos Timer/Counter, los nombres de los bits tienen un prefijo “TC_”.
Para diferenciar entre las máscaras de bit y las posiciones de los bits se agrega un sufijo. Para una máscara de bit, el sufijo es “_bm”. El nombre de la máscara de bit para el bit EVDLY es TC_EVDLY_bm. El código mostrado en la siguiente figura usa una máscara de bit. El bit EVDLY en el registro CTRLD del Timer/Counter D0 es seteado sin alterar los otros bits del registro.

Uso de la máscara de bitimage

3.4.2 Máscara de grupo de bits.

Muchas funciones son controladas por un grupo de bits. Los bits EVACT[2:0] y EVSEL[3:0] son grupos de bits en el registro CTRLD del Timer/Counter. Los valores de esos bits seleccionan una configuración específica.
Cuando cambiamos bits en un grupo de bits, a menudo se requiere limpiar el grupo de bits antes de asignar un nuevo valor. Para decirlo de otro modo: No es suficiente con setear los bits que deben ser seteados, también se requiere limpiar los bits que deben ser limpiados. Para que esto sea fácil se definen las máscaras de grupos de bits. Las máscaras de grupo usan el mismo nombre como los bits en el grupo de bits y un sufijo “_gm”.
El código de la siguiente figura muestra como la máscara de grupo se relaciona con las máscaras de bit. En realidad, los valores de máscara de grupo se precalculados en los archivos cabecera, entonces el compilador no necesita calcular la misma constante una y otra vez.

Relación entre máscara de grupo y máscara de bit.image

La máscara de grupo de bits esta principalmente destinada para borrar una configuración antigua antes de escribir la nueva. En el código de la siguiente figura de muestra como hacerlo. El código limpia el grupo de bits EVACT en el registro CTRLD del Timer/Counter D0.

Uso de máscara de grupoimage

3.5 Máscara de configuración de grupo de bits


A menudo se requiere consultar la hoja de datos para investigar cual patrón de bits necesita ser usado cuando seteamos un grupo de bits para una configuración especifica. Esto también se aplica a las lecturas o depuración del código. Para incrementar la legibilidad y minimizar la probabilidad de setear bits incorrectamente, es que se encuentran disponibles las máscaras de configuración de grupo.
El nombre de una máscara de configuración de grupo es concatenado al módulo tipo, al nombre del grupo de bits, a una descripción de la configuración y un sufijo “_gc”, indicando que este es un grupo de configuración. Un ejemplo de esto es mostrado en la siguiente figura.

Composición del nombre del grupo de configuraciónimage

Inspeccionando la figura anterior podemos ver que esta es usada para seleccionar una configuración para los bits RXCINTLVL en el módulo USART. Este grupo selecciona un nivel de interrupción alto (HI) .
El grupo de bits para los niveles de interrupción por recepción completa está compuesto de dos bits, RXINTLVL[1:0]. La siguiente tabla muestra las configuraciones disponibles para este grupo. Los nombres de las configuraciones son “OFF”, “LO”, “MED” y “HI”. Estos nombres hacen más fácil la escritura y el mantenimiento del código.

Bits RXINTLVL y su correspondiente nivel de interrupciónimage

Para cambiar un grupo de bits a una configuración nueva, el grupo de bits de configuración (_gc) es típicamente usado junto con la máscara de grupo de bits (_gm), esto con el fin de asegurarse que la configuración vieja sea borrada primero. En la siguiente figura se aprecia un ejemplo de esto. Aquí se limpian los bits de nivel se interrupción para luego seleccionar un nuevo nivel (nivel medio).

Cambiando un grupo de configuraciónimage

El uso de la máscara de grupo para limpiar no es siempre requerida, el código mostrado en la siguiente figura muestra como todos los niveles de interrupción del USARTC0 pueden ser configurados a la vez. Recepción completa, transmisión completa y la interrupción por registro de datos vacío con seteadas a niveles medio, off y bajo respectivamente.

Seteando todas las interrupciones a la vezimage


3.5.1 Enumeración de máscara de configuración de grupo

A diferencia de las máscaras de bit (_bm) y máscaras de grupo (_gm), las máscaras de configuración de grupo son definidas usando enumeradores. Un enumerador es definido para cada grupo de bits. El enumerador para el grupo USART RXCINTLVL es mostrado en la siguiente figura.
Enumerador USART RXCINTLVLimage


3.6 Llamadas a funciones y módulos

Cuando escribimos drivers para un módulo tipo que tiene múltiples instancias, el hecho de que todas las instancias tenga el mismo mapa de memoria puede utilizarse para hacer que el driver sea reutilizable para todas las instancias del mismo módulo. Si el driver toma un argumento tipo puntero, apuntando a la instancia del módulo, el driver se puede utilizar para todos los módulos de este tipo.
La función para inicializar y acceder a los m´dulos Timer/Counter puede ser compartida por todas las instancias del módulo. Aunque hay una pequeña sobre carga en pasar el puntero del módulo a las funciones, el tamaño total del código a menudo se reducira ya que el código es reusado para todas las instancias de cada módulo. Incluso mas importantes, el tiempo de desarrollo, el costo de mantenimiento, y la portabilidad pueden ser mejoradas usando este enfoque.
El código de la siguiente figura muestra una función que usa un puntero a un módulo para seleccionar una fuente de clock para cualquier Timer/Counter.

Ejemplo de función usando un puntero a la instancia de un móduloimage

La función anterior toma dos argumentos: un puntero a módulo de tipo TC0_t, y un grupo de configuración de tipo TC_CLKSEL_t. El código en la función usa el puntero al módulo Timer/Counter para acceder al registro CTRLA y setear el nuevo clock.
El código de la siguiente figura muestra como la función puede ser usada para configurar diferentes módulos Timer/Counter con diferentes fuentes de clock.

Llamadas a la función que toma puntero a módulo como parámetroimage

4. Sumario

Para referencia, una vista general de los difentes sufijos usados se muestran en la siguiente tabla.

image



…..escribiendo…..

jueves, 10 de agosto de 2017

Empezando con XMEGA (AVR1005)


1. Introducción:


Desde que Atmel (ahora microchip) amplio su portafolio de productos con la familia XMEGA surgieron muchas preguntas, si esta es una nueva arquitectura, cómo transferir los conocimientos de megaAVR a AVR XMEGA, etc. Aquí trataremos brevemente las similitudes y diferencias entre las dos familias, y proporcionaremos una visión general de las herramientas disponibles.

Los usuario de AVR ocasionalmente mencionaron que ellos querían dispositivos con un menor consumo de potencia, periféricos más potentes, más timers, DMA, multiples niveles de interrupción, más memoria y un mejor rendimiento del CPU. Bien, con XMEGA ahora tienes todo esto, Adicionalmente tienes el sistema de eventos (Event System), motor de encriptación (Crypto Engine), ADC y DAC de 12-bit, calibración de los osciladores en tiempo real, chequeo CRC, y otras características.

Adicionalmente, los periféricos dentro de una familia son similares y están organizados de la misma manera para todos los miembros de la familia, haciendo que portar código de un XMEGA a otro sea una tarea sencilla.




2. Introducción a XMEGA(diferencias y similitudes con los megaAVR)


Esencialmente el XMEGA es un AVR de 8 bits. XMEGA usa la arquitectura y CPU AVR, pero con características adicionales y periféricos mejorados. XMEGA usa el mismo conjunto de instrucciones, las mismas herramientas de desarrollo y compiladores que megaAVR. El CPU puede funcionar más rápido que antes y con un voltaje más bajo.


2.1 Preguntas comunes acerca de XMEGA


  • XMEGA y megaAVR no son compatible en pines. Esto no fue posible de alcanzar debido al deseo de hacer el dispositivo más modular.

  • XMEGA opera con voltajes desde 1.6V hasta 3.6V. Esto se debe en parte al proceso de fabricación, el cual es seleccionado desde el punto de vista del bajo consumo de energía. La operación a 5V no es posible. El menor voltaje de funcionamiento no significa un menor rendimiento del CPU ya que XMEGA puede correr hasta 32MHz desde los 2.7V.

2.2 Estructuras y migración


Los XMEGA están estructurados para fácilmente portar entre dispositivos. Por ejemplo, PORTA y PORTB serán dedicados a las interfaces analógicas ADC y DAC o a dos Comparadores Analogicos. PORTC, D y E serán para interfaces seriales. También, todos los módulos están definidos en estructuras (typedef struct). Esto quiere decir que para acceder a los registros del ADC se debe hacer mediante la estructura ADC. Además, dado que el ADC es definido es una estructura, la idea es que esta estructura será la misma para todos los dispositivos de la misma familia. Entonces sí tu empezaste con un ATxmega64A4, debería ser una tarea sencilla migrar a un ATxmega128A4 o un ATxmega256A1; ya que tu estas usando la misma estructura y así tu puedes usar el exactamente el mismo código. Con megaAVR no podemos hacer una estructura de un módulo para definir todos ellos ya que los diferentes dispositivos podrían tener diferentes diseños.

Las familias XMEGA son distinguibles por una letra: La primera familia es ATxmega[###]A[#](Familia A). Antes de la letra de la familia se encuentra un número el cual representa el tamaño de la memoria flash, y después de la letra de la familia encontraras otro número que será el ID de la familia, el cual identifica dispositivos individuales. Dispositivos con el mismo ID tiene exactamente las mismas características y el mismo número de pines pero pueden estar disponibles en diferentes tamaños de memoria.


2.3 Manual y Hoja de datos


La estructura de los XMEGA hace posible hacer un gran manual para describir todos los módulos y periféricos dentro de una familia. Los detalles que harán un dispositivo diferente otro, como características y periféricos disponibles serán descritos en una pequeña hoja de datos.


Hoja de datos
image

Manual
image


Por ejemplo, se podrían recopilar todas las funciones descritas para todos los dispositivos A1, A3 y A4 en la familia XMEGA-A. Así pues el manual XMEGA A es un documento profundo para todos los módulos que están disponibles en la familia A. Por otro lado la hoja de datos para la sub familia A3 que no son comunes con A1 y A4.


2.4 Sistema de eventos (Event System)


El sistema de eventos es un características totalmente nueva de los XMEGA. No encontraras Event System in megaAVR. El sistema de eventos es comunicación entre los periféricos del XMEGA. Un canal de eventos no es un bus, sino un canal dedicado en el que sólo se puede señalar un evento a la vez. Ya que los eventos están en un canal dedicado, estos serán muy predecibles y rápidos. Realmente, habrá un máximo de 2 ciclos de reloj. Con los eventos, el CPU no está involucrado y no hay interrupciones (ISRs). Finalmente, el sistema de eventos es extremadamente poderoso ya que este no ocupa el CPU y puede ahorrar energía e incrementar el rendimiento en el sistema debido a que se reduce el número de interrupciones.


XMEGA Event System trata sobre la comunicación entre perifericos
image


Veamos un ejemplo. Tu puede por ejemplo usar el sistema de eventos para enlazar dos timer de 16 bits y obtener uno de 32 bits. O puedes usar un evento para sincronizar dos módulos. Por ejemplo, puedes usar el evento de cambio para realizar un conversión con el ADC y una captura de la entrada con el timer para obtener el tiempo exacto de cada conversión. Aún más, otro ejemplo podría ser permitir que un evento dispare conversiones ADC para muestrear música desde un micrófono y cuando la conversión se haya completado el DMA copiará los datos a la RAM. Cuando el usuario presione un switch se generará un evento que copiará los bytes al DAC para reproducir lo previamente grabado. Todo esto se puede hacer estando el XMEGA en modo sleep.



2.5 Segunda Generación PicoPower


Los XMEGA llevan la tecnología PicoPower al siguiente nivel. Todos los XMEGA son probados para operar a 1.6V. Eso significa que todos los periféricos y módulos que pueden correr a 3.6V también pueden correr a 1.6V. Un menor voltaje implica un menor consumo de corriente. Hay nuevos diseños para el watchdog para el circuito Brown Out (BOD). El watchdog corre típicamente con 1uA. La corriente de consumo para el BOD es también de 1uA. De todos las características de bajo consumo, quizá la mejor sería reducir el tiempo en modo activo y remplazar este trabajando más a menudo en IDLE modo usando el DMA y el Sistema de eventos.


2.6 Sistema de reloj


La fuente de reloj y frecuencia pueden ser cambiadas mientras el XMEGA está funcionando. Se puede seleccionar un oscilador interno(RC) de 2MHz y uno de 32MHz. El RC de 2MHz es la fuente de reloj por defecto cuando el XMEGA inicia. Este inicia rápidamente en contraste a cristales externos y resonadores cerámicos los cuales necesitan bastante tiempo para empezar a oscilar. El dispositivo puede comenzar ejecutando el código de inicialización mientras el oscilador externo se estabiliza. Una vez que el oscilador externo está listo para ser usado el cambio de fuente de reloj debe ser realizado por el programa. La frecuencia de reloj puede ser cambiada en cualquier momento mediante el uso del PLL y prescalers.

Si usamos el PLL interno, la señal de reloj puede ser multiplicada (x1, x2, x3,…, x31). Tú puedes incrementar la señal de reloj principal (max 32MHz) o incrementa la velocidad de los periféricos, por ejemplo se puede alcanzar 128MHz como señal de entrada de un Timer/Counter para obtener un señal PWM con alta resolución.

Adicionalmente, el “Monitor de fallas de la fuente de reloj externa” puede detectar si la señal de reloj falla(ej: debido a daño físico del cristal) y automáticamente cambiar al RC interno de 2MHz.


2.7 Controlador de Interrupciones Multinivel.


Ya que los XMEGA tiene más fuentes de interrupción que un megaAVR, se ha adicionó un controlador de interrupciones multinivel. Esto facilita el manejo de muchas fuentes de interrupciones de manera oportuna de acuerdo con las prioridades establecidas. Si por ejemplo una fuente de interrupción con una alta frecuencia es atendida con tanta frecuencia que las otras interrupciones en el mismo nivel no son atendidas, entonces puede ser buena idea bajar la prioridad de la primera fuente de interrupción, ósea la de alta frecuencia.

2.8 Principales Funciones Analogicas.


Con XMEGA se introducen algunas características importantes para un MCU de 8 bits. Tanto el ADC como el DAC y tienen una resolución de 12 bits. El ADC puede alcanzar hasta 2 millones de muestras por segundo. También hay una característica llamada “event or interrupt on conversion complete”, la cual sólo generará una interrupción de conversión completa cuando el resultado se encuentre por debajo o por encima de un cierto límite.

El DAC puede dar dos salidas incluso cuando sólo se usa un módulo. Esto es gracias a que se cuenta con un circuito separado de muestreo y retención que permitirá, por ejemplo, una salida estéreo con sólo un DAC.

El Comparador Analógico (AC) tiene una nueva característica llamada “windows mode”(modo ventana) cuando se usan dos módulos AC a la vez. Entonces, tu puedes ingresar dos limites (nivel bajo y alto) y la señal de entrada a ser comparada. El resultado estaría debajo, dentro o sobre la ventana. El resultado la comparación también se puede reflejar en un pin de salida.


2.9 Interfaz de Bus Externo (EBI).


El EBI disponible en XMEGA soporta memorias hasta de 16MB. Son soportadas las memorias SRAM y SDRAM. Las memorias SDRAM son de bajo costo y gran capacidad, pero no es posible combinarlas con otras unidades mapeadas que requieran una interfaz de bus tipo SRAM. Debido al soporte para grandes memorias, el tiempo de ejecución de cada instrucción que accede a memorias ha cambiado ligeramente. Además, cuando se compila para memorias de gran capacidad el tipo de dato usado para direccionar cambia de 16bits a 24bits.


2.10 Motor de Encriptación (Crypto Engine).


Para la comunicación con protocolos, ya sea alámbrico o inalámbrico, la encriptación de la información es necesaria para una comunicación segura.

XMEGA implementa tanto AES como DES, y puede realizar tanto la encriptación como la desencriptación. AES es un hardware que libera al CPU de toda la carga. Este módulo puede proveer 1.25Mbps de comunicación encriptada con una llave de 128bits.



3 Que herramientas usar?.



Para la escritura de código se puede usar el ya muy conocido Atmel Studio. Se recomienda leer la nota de aplicación AVR1000 (Getting Started Writing C-code for XMEGA) que es una introducción al estilo de codificación que se ha elegido y también describe como acceder a los periféricos.
Los programadores/depuradores recomendados son el ATMEL-Ice ó el JTAGICE3 (ya no está en producción).






lunes, 17 de abril de 2017

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