viernes, 15 de agosto de 2014

FAT + SD Card + ATmega128



En este post mostraré como usar la librería FatFs ( Generic FAT File System Module) la cual podemos encontrar en el sitio web FatFs.
Para este fin usaremos una tarjeta de prueba con ATmega128, un adaptador para microSD, un RTC DS3231 y una tarjeta microSD Samsung de 16GB clase 10.

                          SKU146934b                                images
 samsungcards-m


El ATmega128 corre con un cristal de 7.3728MHz.
El RTC es para obtener la fecha y hora al momento de crear y modificar los archivos creados.
El adaptador de microSD se comunica mediante SPI, tiene 6 señales (CS, MISO,MOSI,SCK,VCC y GND).
La tarjeta fue previamente formateada con FAT32.
En las pruebas se obtuvo velocidades de escritura de 148KB/s (más de 1Mb/s).




Los archivos de ejemplo, para varias marcas de microcontroladores, se pueden descargar en este link SAMPLES, y es en base a la carpeta avr_complex que se desarrolló este post.
Primero escribiremos las rutinas I2C para el RTC DS3231.
Aquí se presentan los archivos i2c.h y i2c.c.

 
/*
* i2c.h
*
* Created: 04/08/2014 11:50:27 p.m.
*  Author: Jonathan
*/

#ifndef I2C_H_
#define I2C_H_


#include <avr/io.h>
#include <util/twi.h>


#define        F_SCL     100000UL
#define        TWBRX0     (((F_CPU/F_SCL)-16)/2)
#define        TWBRX1     TWBRX0/4
#define        TWBRX2     TWBRX0/16
#define        TWBRX3     TWBRX0/64


#if        (TWBRX0<=0xFF)
    #define        TWBRX    TWBRX0
    #define        TWPSX    0
#elif    (TWBRX1<=0xFF)
#define        TWBRX    TWBRX1
    #define        TWPSX    1
#elif    (TWBRX2<=0xFF)
    #define        TWBRX    TWBRX2
    #define        TWPSX    2
#elif    (TWBRX3<=0xFF)
    #define        TWBRX    TWBRX3
    #define        TWPSX    3
#else
    #define        TWBRX    0
    #define        TWPSX    0                   
#endif

#define        NACK       0
#define        ACK        1


    void           I2C_Init(void);       
    uint8_t        I2C_Start(void);
    uint8_t        I2C_ReStart(void);
    void           I2C_Stop(void);
    uint8_t        I2C_Write(uint8_t );
    uint8_t        I2C_Read(uint8_t );   

#endif /* I2C_H_ */

/*
* i2c.c
*
* Created: 04/08/2014 11:52:16 p.m.
*  Author: Jonathan
*/


#include <stdio.h>
#include "i2c.h"


void    I2C_Init(void)
{
    TWBR = TWBRX;
    TWSR = TWPSX;
    TWAR = 0x00;
    TWCR = (1<<TWEN);
    DDRD &=~(1<<PIND0 | 1<<PIND1);
    PORTD |= 1<<PIND0 | 1<<PIND1;
}


uint8_t I2C_Start( void )
{
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
   
    while (!(TWCR & (1<<TWINT))){}
   
    if ((TWSR & 0xF8) != TW_START)
        return 0;
   
    return 1;
}


uint8_t I2C_ReStart( void )
{
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
   
    while (!(TWCR & (1<<TWINT)));
if ((TWSR & 0xF8) != TW_REP_START)
        return 0;
   
    return 1;
}


void    I2C_Stop( void )
{
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
   
    while(TWCR & (1<<TWSTO));
}


uint8_t I2C_Write( uint8_t data)
{
    TWDR = data;   
   
    TWCR = (1<<TWINT)|(1<<TWEN) ;
   
    while (!(TWCR & (1<<TWINT)));
   
    if ((TWSR & 0xF8) != TW_MT_SLA_ACK)
        return 0;
   
    return 1;
}


uint8_t I2C_Read( uint8_t ACK_NACK)
{
    while (!(TWCR & (1<<TWINT)));
   
    TWCR = (ACK_NACK)?((1<<TWINT)|(1<<TWEN)|
                      (1<<TWEA)):((1<<TWINT)|(1<<TWEN));
   
    while (!(TWCR & (1<<TWINT))){}
   
    return    TWDR;
}



Ahora los archivos DS3231.h y DS3231.c.

/*
* DS3231.h
* Created: 05/08/2014 12:03:19 a.m.
*  Author: Jonathan
*/


#ifndef DS3231_H_
#define DS3231_H_


#include "I2C.h"
#include "integer.h"


typedef struct {
    WORD    year;    /* 2000..2099 */
    BYTE    month;   /* 1..12 */
    BYTE    mday;    /* 1.. 31 */
    BYTE    wday;    /* 1..7 */
    BYTE    hour;    /* 0..23 */
    BYTE    min;     /* 0..59 */
    BYTE    sec;     /* 0..59 */
} RTC_t;


#define        DS3231_SECONDS         0x00       
#define        DS3231_MINUTES         0x01
#define        DS3231_HOURS           0x02
#define        DS3231_WEEKDAYS        0x03
#define        DS3231_DAYS            0x04
#define        DS3231_MONTHS           0x05
#define        DS3231_YEARS            0x06


#define        MASK_SEC            0B01111111
#define        MASK_MIN            0B01111111
#define        MASK_HORA           0B00111111
#define        MASK_DIA            0B00111111
#define        MASK_MES            0B00011111
#define        MASK_ANIO           0B11111111


#define        DS3231_READ         0xD1
#define        DS3231_WRITE        0xD0


#define   BCD2BIN(val) (((val)&0x0F)+((val)>>4)*10)
#define   BIN2BCD(val) ((((val)/10)<<4)+(val)%10)


void      DS3231_Init();
void      RTC_GetTime(RTC_t *);
void      RTC_SetTime(RTC_t *);   
void      DS3231_SetHora (uint8_t *);
void      DS3231_GetHora (uint8_t *);
void      DS3231_SetFecha(uint8_t *);
void      DS3231_GetFech(uint8_t );               
uint8_t   DS3231_GetReg(uint8_t );
void      DS3231_SetReg(uint8_t , uint8_t );


#endif /* DS3231_H_ */

/*
* DS3231.c
*
* Created: 05/08/2014 12:16:47 a.m.
*  Author: Jonathan
*/ 


#include <util/delay.h>
#include "DS3231.h"


void DS3231_Init()
{
    I2C_Init();   
}


void DS3231_SetHora (uint8_t *data)
{
    I2C_Start();
    I2C_Write(DS3231_WRITE);
    I2C_Write(DS3231_SECONDS);
    I2C_Write(data[2]);
    I2C_Write(data[1]);
    I2C_Write(data[0]);
    I2C_Stop();       
}
   
void DS3231_GetHora (uint8_t *data)
{
    I2C_Start();
    I2C_Write(DS3231_WRITE);
    I2C_Write(DS3231_SECONDS);
    I2C_ReStart();
    I2C_Write(DS3231_READ);
    data[2] =  (I2C_Read(ACK))& MASK_SEC;
    data[1] =  (I2C_Read(ACK))& MASK_MIN;
    data[0] =  (I2C_Read(NACK))& MASK_HORA;
    I2C_Stop();
}


void DS3231_SetFecha(uint8_t *data)
{
    I2C_Start();
    I2C_Write(DS3231_WRITE);
    I2C_Write(DS3231_DAYS);
    I2C_Write(data[0]);
    I2C_Write(data[1]);
    I2C_Write(data[2]);
    I2C_Stop();
}
   
void DS3231_GetFecha(uint8_t *data)
{
    I2C_Start();
    I2C_Write(DS3231_WRITE);
    I2C_Write(DS3231_DAYS);
    I2C_ReStart();
    I2C_Write(DS3231_READ);
    data[0] =  (I2C_Read(ACK)) & MASK_DIA;
    data[1] =  (I2C_Read(ACK)) & MASK_MES;
    data[2] =  (I2C_Read(NACK)) & MASK_ANIO;
    I2C_Stop();
}


uint8_t DS3231_GetReg(uint8_t address )
{
    uint8_t    ret;
    I2C_Start();
    I2C_Write(DS3231_WRITE);
    I2C_Write(address);
    I2C_ReStart();
    I2C_Write(DS3231_READ);
    ret = I2C_Read(NACK);
    I2C_Stop();   
    return ret;
}


void DS3231_SetReg( uint8_t address, uint8_t val )
{
    I2C_Start();
    I2C_Write(DS3231_WRITE);
    I2C_Write(address);
    I2C_Write(val);
    I2C_Stop();
    _delay_ms(1);
}


void RTC_SetTime( RTC_t *rtc )
{
    I2C_Start();
    I2C_Write(DS3231_WRITE);
    I2C_Write(DS3231_SECONDS);
    I2C_Write(BIN2BCD(rtc->sec));
    I2C_Write(BIN2BCD(rtc->min));
    I2C_Write(BIN2BCD(rtc->hour));
    I2C_Write(BIN2BCD(rtc->wday));
    I2C_Write(BIN2BCD(rtc->mday));
    I2C_Write(BIN2BCD(rtc->month));
    I2C_Write(BIN2BCD(rtc->year));
    I2C_Stop();
}


void RTC_GetTime( RTC_t *rtc )
{
    uint8_t buf[7];
   
    DS3231_GetHora(&buf[0]);
    DS3231_GetFecha(&buf[4]);
   
    rtc->sec = (buf[2]&0x0F) + ((buf[2]>>4)&7)*10;
    rtc->min = (buf[1]&0x0F) + (buf[1]>>4)*10;
    rtc->hour = (buf[0]&0x0F) + ((buf[0]>>4)&3)*10;
    rtc->wday = (buf[3]&0x07);
    rtc->mday = (buf[4]&0x0F) + ((buf[4]>>4)&3)*10;
    rtc->month = (buf[5]&0x0F) + ((buf[5]>>4)&1)*10;
    rtc->year = 2000+(buf[6]&0x0F) + (buf[6]>>4)*10;
}

Hasta ahora, y con las rutinas escritas, ya podemos comunicarnos con el RTC. Vale mencionar que estas rutinas puedes ser fácilmente modificadas para ser usadas con otros RTC como DS1307 ó  PCF8563. También debemos decir que el RTC no es necesario para el funcionamiento de FatFs, pero en ausencia de éste la hora y fecha de creación y modificación de los archivos generados será nula.

Ahora pasaremos a ver los archivos FatFs. Como dije antes, esto lo haremos usando los archivos de la carpeta “avrcomplex”. Como este ejemplo está hecho para un ATmega64, nosotros podemos usarlo sin mayores cambios para un ATmega128. Si quisiéramos usar este ejemplo con algún otro uC debemos realizar modificaciones en el archivo “mmc_avr.c”, específicamente en las rutinas “Power Control” y “Transmit/Receive data from/to MMC via SPI ”, ya que estas depende del uC usado.

De todos los archivos que se encuentran aquí sólo necesitaremos:

- integer.h
- ccsbcs_avr.c
- mmc_avr.c
- diskio.h
- ffconf.h
- ff.h y ff.c

Adicionalmente los archivos creados i2c.h, i2c.c, DS3231.h y DS3231.c.

En el archivos “ffconf.h” debemos realizar las siguientes modificaciones.

- Poner _FS_TINY a 0.
- _CODE_PAGE a 850.
- _USE_LFN a 2.

Si nuestro adaptador no tiene “power control” debemos eliminar los bloques indicados con “Remove this block if no socket power control” en “power_on” y “power_off” dentro de mmc_avr.
Si no tiene pin “card detect” o “Write Protect” debemos comentar el siguiente código en “disk_timerproc” dentro de mmc_avr.c.

   if (MMC_WP)                /* Write protected */
        s |= STA_PROTECT;
    else                    /* Write enabled */
        s &= ~STA_PROTECT;

    if (MMC_CD)                /* Card inserted */
        s &= ~STA_NODISK;
    else                    /* Socket empty */
        s |= (STA_NODISK | STA_NOINIT);


Con estos cambios estaremos listo para escribir la aplicación principal, para ello escribiremos un mensaje simple en un archivo de texto.

/*
* Prueba_1.c
*
* Created: 04/08/2014 11:49:54 p.m.
*  Author: Jonathan
*/


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "DS3231.h"
#include "ff.h"
#include "diskio.h"


FATFS fs;
FIL   file;
FRESULT resul;

DRESULT dst;
VUINT Timer;
UINT  temp;       
char setup_file[] = "0:ARCHIVO_0.txt";
char cadena[] = ".......DATOS A ESCRIBIR...";
RTC_t rtc_;


int main(void)
{           
    _delay_ms(1000);
    DS3231_Init();   
       
    OCR2 = (F_CPU/1024/100)-1;
    TCCR2 = 1<<WGM21 | 1<<CS22 | 1<<CS20;
    TIMSK |= 1<<OCIE2;
  

    // 15/08/2014 10:23:00
    //rtc_.mday  = 15;
    //rtc_.month = 8;
    //rtc_.year  = 14;
    //rtc_.hour  = 10;
    //rtc_.min   = 23;
    //rtc_.sec   = 0;
    //RTC_SetTime(&rtc_);   
   
    sei();       
   
    dst =  disk_initialize(0);
    resul = f_mount(&fs,(char const *)setup_file,1);
    resul = f_open(&file,(char const *)setup_file, FA_CREATE_ALWAYS | FA_WRITE);       
    resul = f_write(&file,cadena,sizeof(cadena),&temp);   
    f_close(&file);   
   
    while(1)
   {                              
        RTC_GetTime(&rtc);   
        _delay_ms(1000);
    }
}


//***************************************************************************

ISR(TIMER2_COMP_vect)
{
    Timer++;           
    disk_timerproc();   
}


DWORD get_fattime (void)
{
    RTC_t rtc;

    RTC_GetTime(&rtc);
   
    return      ((DWORD)(rtc.year - 1980) << 25)
                | ((DWORD)rtc.month << 21)
                | ((DWORD)rtc.mday << 16)
                | ((DWORD)rtc.hour << 11)
                | ((DWORD)rtc.min << 5)
                | ((DWORD)rtc.sec >> 1);

}

El código Anterior debería generar un archivo de nombre “ARCHIVO_0.txt” con el texto “.......DATOS A ESCRIBIR...” dentro del mismo.
Si el RTC fue configurado anteriormente a la hora y fecha correctos, podríamos verificar esto en el archivo generado.
Si aún no fue configurado, se puede usar el código comentado para poner la hora y fecha correcta. Luego debemos volver a comentar esa parte del código.
Tener presente que el Timer2 es configurado para generar interrupciones cada 10ms, las cuales son necesarias para la libreria Fat.

Por último escribiremos 1MByte de datos para verificar la velocidad de escritura que podemos alcanzar. Para ello usaremos el Timer0, éste generará interrupciones cada 100us, y en cada interrupción se incrementará la variable “time”. Al iniciar la escritura guardaremos time en t0, y al terminar la escritura obtendremos el tiempo transcurrido en t1 = time – t0, luego calcularemos el tiempo tomado para escribir los 2048*512 bytes y lo escribiremos al final del archivo.

/*
* Prueba_2.c
*
* Created: 04/08/2014 11:49:54 p.m.
*  Author: Jonathan
*/


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "DS3231.h"
#include "ff.h"
#include "diskio.h"


FATFS fs;
FIL   file;
FRESULT resul;

DRESULT dst;

VUINT Timer;
UINT temp;       
char setup_file[] = "0:ARCHIVO_0.txt";
RTC_t rtc_;
char    datos[512];   
volatile uint32_t time,t0,t1;   


int main(void)
{           
    _delay_ms(1000);
    DS3231_Init();   
       
    OCR2 = (F_CPU/1024/100)-1;
    TCCR2 = 1<<WGM21 | 1<<CS22 | 1<<CS20;
    TIMSK |= 1<<OCIE2;
   
    OCR0 = (F_CPU/8/10000)-1;
    TCCR0 = 1<<WGM21 | 0<<CS02 | 1<<CS01 | 0<<CS00;
    TIMSK |= 1<<OCIE0;
   
    sei();       
   
    dst =  disk_initialize(0);
    resul = f_mount(&fs,(char const *)setup_file,1);
    resul = f_open(&file,(char const *)setup_file, FA_CREATE_ALWAYS | FA_WRITE);       
   
    for (uint16_t i=0; i<512; i++)
    {   
        datos[i]=i;
    }

    t0 = time;
       
    for (uint16_t i=0; i<2048; i++)                // 2048*512 = 1MB   
    {
        f_write(&file,datos,sizeof(datos),&temp);   
    }
    t1=time - t0;
    float rate = 1024.0*10000.0/t1;
    char rate_s[15];
    sprintf(&rate_s,"\r\n%f KBs",rate);    
    f_write(&file,rate_s,15,&temp);
    f_close(&file);   
   
    while(1)
   {                              
        RTC_GetTime(&rtc);   
        _delay_ms(1000);
    }
}

//******************************************************************************
//******************************************************************************


ISR(TIMER2_COMP_vect)
{
    Timer++;           
    disk_timerproc();   
}


ISR(TIMER0_COMP_vect)
{   
    time++;
}


DWORD get_fattime (void)
{
    RTC_t rtc;

    RTC_GetTime(&rtc);
   
    return      ((DWORD)(rtc.year - 1980) << 25)
    | ((DWORD)rtc.month << 21)
    | ((DWORD)rtc.mday << 16)
    | ((DWORD)rtc.hour << 11)
    | ((DWORD)rtc.min << 5)
    | ((DWORD)rtc.sec >> 1);

}


El resultado final se vera así:

archivo


Aquí vemos que la velocidad de escritura fue de 148KB/s.

Espero que esto sea de su ayuda. Hasta la próxima.

Saludos.

viernes, 27 de junio de 2014

MASTER SPI CON DMA – ATXMEGA

 

En este post mostraré como usar el DMA para quitarle carga al CPU y acelerar las transferencias del SPI en modo master.
Debemos tener en cuenta que según el manual de los XMEGA esto no es posible. “No es posible” ya que no hay un evento que pueda disparar la transferencia de datos, solo existe el evento de recepción de datos ( DMA_CH_TRIGSRC_SPIC_gc  para el SPI C).

Pero gracias a que el DMA tiene cuatro canales y que se puede establecer prioridades de los cuatro canales es que se puede implementar la transferencia por SPI usando el DMA.

Aquí el ejemplo:


image

Rx y Tx son los vectores a transmitir y recibir.

Habilitamos el DMA y establecemos la prioridad de los canales. La prioridad debe ser establecida ya que el CH0(Rx) y CH1(Tx) serán usados con el mismo evento, y debemos sacar el datos recibido antes de enviar el nuevo dato.


image


Configuramos el CH0 para recibir los datos por el SPIC. Los datos recibimos se guardan en Rx.


image

Configuramos el CH1 para transmitir los datos por el SPIC. Los datos trasmitidos se sacaran de Tx. Rx y Tx deben ser de la misma longitud. Pero TRFCNT se configura con un dato menos(desde Tx[1] hasta Tx[12]) ya que Tx[0] se usara para disparar la transferencia.


image

Se configura los pines MISO, MOSI, SCK y SS. Tambien Configuramos el SPI.


image

Habilitamos los canales CH0 y CH1.


image

Ponemos el SS a cero.
Ponemos el primer dato a transmitir (Tx[0]) en el registro de datos. Esto dispara la transmisión de todos los datos. Cuando termine de salir el primer dato también debería haber llegado el primer dato y esto activara la transferencia del DMA. Gracias a la prioridad establecida al inicio, primero en CH0 guardara el dato recibido y luego el CH1 pondrá en el registro de salida el nuevo dato a enviar.
Esto continuara hasta que se reciban todos los bytes.
Se espera a que termine la transmisión y ponemos SS a uno.


image

Arriba se muestra la imagen de SCK y MOSI.

Lo mostrado arriba funciona, pero personalmente no me gusta el tiempo de retardo que existe entre byte y byte. Al parecer esto se debe a que el SPI solo tiene un buffer.


image

Esto se puede apreciar con mejor claridad en la imagen anterior, y esto puede reducir la velocidad efectiva del SPI.



Para evitar este problema la solución puede ser usar el USART en modo MASTER SPI. Tener en cuenta que en este modo si se hay dos eventos para disparar la transmisión y recepción.

Aquí el código:

- La primera parte es la misma.

image

Se configura el CH0 para recibir los datos, para ellos se usa el evento RXC.


image

Mediante CH1 se transmitirá los datos de Tx. Se usará la evento DRE.


image

Se configura los pines Tx, Rx y SCK, también el USART en modo MASTER SPI y se habilita la Transmisión y Recepción.


image

Ponemos el SS a cero y habilitamos CH0 y CH1 del DMA.
Se espera a que termine la transmisión y ponemos SS a uno

Ahora, y gracias a los tres niveles de buffer del USART, ya no veremos retardos entre la salida de bytes.

image


Bueno, espero esto les ayude.

Hasta pronto !.

lunes, 18 de noviembre de 2013

USBasp y ATMEL STUDIO 6.X


En este post mostrare como configurar el ATMEl STUDIO para que nos permita usar nuestro USBasp.

Lo primero es ir a la pestaña Tools->External Tools....



Ya dentro debemos llenar algunos parámetros:
Title: Debe contener el nombre de nuestra elección, en este caso pondremos USBasp.
Command: Debe contener la ruta del avrdude.exe.
Arguments: Debe contener los argumentos soportados por el avrdude para realizar la programación.



La opción "Arguments" debe tener el siguiente texto:


-c usbasp : Le indica al avrdude que se usara el programador USBasp(esto no se debe cambiar).
-p ATMEGA328P: Indica el uC que se usara, esto se puede cambiar para cada dispositivo.
-U flash:w...:Indica la ruta del archivo ".hex" a programar (esto no se debe cambiar).

Al final nos aparecerá un nuevo botón


Si damos click en el botón "USBasp" obtendremos.



Ahora, si queremos tener un botón de acceso rápido en la barra de herramientas debemos ir a "Tools->Customize...".


Aquí debemos agregar el USBasp.




En la misma ventana, en "Commands" debemos seleccionar "Toolbar->USBasp" y dar click en el botón "Add Command..." 





Luego nos aparecera otra ventana donde debemos seleccionar "Tools" en "Categories:" y "External Command1" en "Commands:".




El resultado final será:


Con este boton podremos programar usando el USBasp sin problemas.

Espero esto les ayude.

Saludos.

jueves, 8 de agosto de 2013

Funcionamiento del Acelerómetro ADLX345



En este post mostrare un ejemplo de como poner en funcionamiento el acelerómetro digital de 3 ejes ADLX345.
 
Este acelerómetro cuenta con una resolución de 13 bits, y puede medir hasta ±16 g. La salida digital es de 16 bits en complemento a dos y puede ser obtenida mediante los protocolos SPI ó I2C.
 
ADXL345
 
Al ADLX345 se puede encontrar en aplicaciones móviles. Este puede medir la aceleración estática de la gravedad para medir indirectamente inclinaciones, también se puede medir la aceleración dinámica producto de un movimiento o choque. Su alta resolución (3.9mg/LSB) permite medir cambios en inclinación menores a 1.0°.
 
ADXL345_image

El ejemplo es desarrollado usando C++ en el ATMEL STUDIO 6.1. Se usa el protocolo I2C con dirección de esclavo 0x53 (pin ALT ADDRESS a GND).
Para configurar el ADLX345 hay que realizar tres operaciones básicas en tres de sus registros.
1.- En el registro 0x31—DATA_FORMAT— se debe configurar la resolución de bits y el rango de medición.
2.- En el registro 0x2C—BW_RATE— se debe configurar el ancho de banda y la velocidad de salida de los datos.
3.- En los registro 0x1E, 0x1F, 0x20—OFSX, OFSY, OFSZ— se debe setear offset par cada eje.
Antes de realizar estas operaciones ponemos en ADLX345 en standby, y al finalizar volver al modo de medición.
Luego se leerán los registros de datos y se enviaran hacia el computador.

Aquí se muestra un ejemplo del programa.




Los datos pueden ser leídos con cualquier programa que tenga acceso al puerto serie (tal como hyperterminal).

Para recibir los datos y hacer los cálculos de gravedades y ángulos de inclinación desarrolle un programa en visual c#, el que se muestra abajo. Aquí se pueden ver los datos recibidos, la cantidad de gravedad en cada eje, y la inclinación de los tres ejes con respecto a la gravedad.
 
 
ADXL345


Los cálculos se realizan como sigue:

image

El Factor depende de la resolución y rango con que se configuro el dispositivo. Por ejemplo para 10 bits de resolución y ±2g de rango el factor es 0.00390625.

image
 
Las componentes, en gravedades, de cada eje están dadas por las ecuaciones anteriores. Xout, Yout y Zout son los datos, en complemento a dos, obtenidos del ADLX345. Por ejemplo si Xout=44, Yout=200, Zout=161 entonces Gx=0.171875, Gy=0.78125, Gz=0.62890625.
 
image
 
La resultante de éstas componentes, usando la ecuación anterior, debe ser 1g. Usando los datos anteriores G=1.017553758.
 
image
 
Los respectivos ángulos de inclinación, con respecto a la vertical o a la gravedad, de cada eje son calculados usando las ecuaciones anteriores. Usando los datos anteriores los ángulos de inclinación de cada eje son 9.89°,  51.37° y 38.96°.
 
 
COMO CALIBRAR EL ADLX345
 
El procedimiento de calibración del ADLX345 es muy sencillo, solo debemos colocar el acelerómetro en posición horizontal ( es decir, que toda la gravedad se encuentre sobre el eje Z). En esta posición se debe tomar una cierta cantidad de datos, pueden ser 100 o 200 datos, luego se debe promediar los datos correspondientes a cada eje.
 
image
 
 
El valor que se debe aplicar a los registros de calibración se calculan como sigue:
 
 
image
 
 
Espero este post los ayude en algo.
 
Hasta pronto !!!.
 

martes, 12 de junio de 2012

Formas de bloquear y desbloquear un ATmega8

Bloquear un ATmega es algo muy común cuando se inicia con estos micros. En mi primera semana con los ATmega8 llegue a bloquear hasta 4, pero felizmente logre desbloquearlos.

Aquí presento algunos consejos que son obtenidos en base a la experiencia, espero esto ayude a algunos.
Un problema no tan común, pero que les podría ocurrir (por desconocimiento) es deshabilitar el reset. Esto puede ocurrir programando el fuse "Reset Disable(Enable PC6 as i/o pin);"RSTDISBL=0". Este fuse se encuentre el High Fuse.

Hacer esto cuando se programa mediante SPI bloqueara el uC. Luego de esto no se podrá realizar ninguna operación con un programador SPI. La literatura indica usar un PPHV (Programador Paralelo de Alto Voltaje) para habilitar nuevamente el Reset. Personalmente nunca cometí este error.

Ahora el problema mas común, bloquearlo por no escoger el oscilador correcto. Todos estos fuses los encontramos en Low Fuse.

Tener presente que Low Fuse esta compuesto por.

LF

Donde…
BODLEVEL : Permite seleccionar el nivel del BOD.
BODEN: Permite habilitar el BOD.


SUT: Permite escoger un retardo (en ms) para el inicio del programa, esto después de haber energizado.
CKSEL: Permite escoger entre los diferentes tipos de fuentes de reloj.


- Si programamos Ext. Clock [CKSEL=0000]:Aplicando una señal de [4MHz-6MHz] al pin XTAL1 deberíamos poder trabajar sin problemas.



- Si programamos Int. RC Osc [CKSEL=0001 hasta CKSEL=0100] : Con este oscilador no deberían tener problemas, solamente tener en cuenta que la frecuencia de programación ISP debe ser menor a 1/4 la frecuencia RC. Es decir que si escogemos RC=2MHz nuestra frecuencia ISP debe ser menor a 500KHz.



- Si programamos Ext. RC Osc[CKSEL=0101 hasta CKSEL=1000]: Aplicando una señal de [4MHz-6MHz] al pin XTAL1 deberíamos poder trabajar sin problemas, o al menos poder cambiar a otro tipo de oscilador.



- Si programamos Ext. Low-Freq. Crystal [CKSEL=1001] : En teoría deberíamos poner un cristal de 32768Hz, pero generando una señal de similar frecuencia y aplicándola a XTAL1 es suficiente. Recordar que la frecuencia ISP debe ser menor a 1/4. Si aplicamos 32.768KHz la frecuencia ISP debe ser menos a 8KHz.



- Si programamos Ext. Crystal/Resonator[CKSEL=1010 hasta CKSEL=1111]: Aplicando una señal de [4MHz-6MHz] al pin XTAL1 deberíamos poder trabajar sin problemas.


Consideraciones:


-  Todas las pruebas fueron hechas usando un ATmega8, un programador USBasp y el programa SinaProg 2.
-  Las frecuencia de 32.768KHz y 6MHz fueron generadas por otro uC y aplicadas al pin XTAL1 del uC que se quería programar. Las frecuencia fueron generadas usando el Timer1 en modo CTC.
Configuración del Timer1:


0x0000 Para generar 6MHz con F_CPU=12MHz y N=1.
Formula para Calcular OCR1A :


Bueno, espero esto les sea util
Saludos…!!!

jueves, 15 de diciembre de 2011

SET CLOCK(SCK) de programación del USBasp con SinaProg 2.0

El USBasp tiene 12 posibles frecuencia de programación, estas son:

1.5 MHz, 750 KHz, 375 KHz, 187.5 KHz, 93.75 KHz, 32 KHz, 16 KHz, 8 KHz, 4 KHz, 2 KHz, 1 KHz y 500 Hz.

Para poder acceder a esas velocidades usando el SinaProg, que a su vez usa el avrdude, debemos modificar el archivo Speed.txt.

El Sinaprog 2.0 lo pueden descargar de este enlace SinaProg 2.0 .

Y en este otro enlace se encuentran los pasos para realizar la modificación. PASOS.
No pongo imagenes ya que en el pdf se encuentra todo lo necesario.

Un saludo y Felices Fiestas…!!!

domingo, 27 de noviembre de 2011

EEPROM INTERNA DE LOS ATMEGA

En este post mostraré un pequeño ejemplo sobre la lectura y escritura de la eeprom interna de los atmega.
En el ejemplo trabajo con un mega8, pero el código es fácilmente aplicable a otros ATmega.

El mega8 tiene 512 Bytes en su eeprom (este tamaño varia con cada dispositivo). Su eeprom solamente permite, además de leer, borrar y escribir en una sola operación (la cual demora 8.5 milisegundo aprox), esto en comparación con otros ATmega que permiten solamente borrar, solamente escribir y borrar, y escribir en una misma operación, ósea permiten realizar tareas separadas lo cual puede ayudar a reducir tiempo de escritura.

Bueno, vamos con el programa y explicaré algunas cositas.

Primero un par de macros nuevas que usaré.
Esta primera macro espera hasta que el bit indicado sea 0.

macro1

Esta espera a que el bit sea 1.

macro2

Ahora la primera parte del programa.

ima1

Aquí usaremos el uart para enviar algunos datos, la velocidad será de 9600.
Si queremos iniciar la eeprom con algunos datos tenemos que declararlos. La declaración de los datos y la separación de espacios de la eeprom inicia con “eseg” (segmento de eeprom).

.org    0x000    .db    0x00,0x01,0x02,0x03,0x04 –> Esta parte de código asignará los valores en hexadecimal a las posiciones de memoria iniciadas en 0x000.
.org    0x010    .db    "hola mundo" –> Esta otra parte también asignará los valores a las posiciones iniciando en 0x010.
.org    0x020    .db    0x30,0x31,0x32,0x33,0x34 –> Aquí la asignación inicia en 0x20 y termina en 0x24,
.db "ING. ELECTRONICA"—> Para esta parte no se da un inicio u origen, pero por encontrarse debajo de la asignación anterior que terminó en 0x24, ésta tendrá su inicio en 0x25.
.org    0x50
    data_eeprom_10:    .byte    10     // separa 10 Bytes iniciando en 0x50.
    data_eeprom_5:     .byte     5      // separa 5  Bytes iniciando en 0x5A.
    data_eeprom_16:   .byte     16    // separa 16 Bytes iniciando en 0x5F.

La eeprom cuenta con 3 registros:
EEAR(EEARH:EEARL): Es el registro donde se debe escribir la dirección del byte que se quiere leer o escribir.
EEDR : Es el registro del cual se leerá el dato en una operación de lectura, y en él se debe poner el dato en una operación de escritura.
EECR: Es el resistor donde se encuentran los bits de configuración. Aquí se indicara que operación se desea realizar.

Las rutinas de lectura y escritura son mostradas abajo, en ellas lo primero que hay que hacer es esperar a que termine la operación anterior de escritura (si es que la hubiera). Antes de llamar a la rutina la dirección para leer debe estar en X y el dato retorna en R16, y para escribir la dirección la dirección está en Y y el dato en R16. R16,X y Y pueden ser cambiado a cualquier otro registro.

ima4

En la parte principal de programa se inicia la pila y se configura el uart.


En el programa encontramos tres bucles.
En el primer bucle se leerán los datos que inician en la dirección X=0x10 (“hola mundo”), estos datos se enviarán por el uart y también se irán guardando en la posición Y=data_eeprom_10.
Luego hay un saldo de línea.

El funcionamientos de los otros dos bucles es similar al primero.
Aquí están las rutinas de salto de línea y de envió de datos por el uart.

ima3

Ahora… luego de escribir todo este programa y compilarlo obtenemos como salida un archivo .eep.

panel

En este archivo se encuentran, en formato intel hex, todos los datos de para iniciar nuestra eeprom, este archivo se debe grabar en nuestro microcontrolador después de grabar la flash.

eefile

Al abrir el archivo EEPROM.eep veremos lo mostrado en la imagen superior. El formato es intel y allí podemos ver las posiciones de memoria y cada uno de los bytes.

Ahora simularemos esto en proteus.

p1

Para cargar nuestros datos a la eeprom  interna debemos hacer doble click sobre el ATMEGA8.
En la ventana que nos aparece debemos ir hasta “Advanced Properties” y seleccionar “Initial Contents of Data EEPROM” y luego en la parte derecha (usando el icono de carpeta) debemos seleccionar nuestro archivo con los datos iniciales.

p2

Luego cuando iniciemos nuestra simulación veremos en la eeprom del ATmega lo siguiente.

p4

Si no les aparece lo anterior, y cada vez que quieran resetear la data guardada en la eeprom, deben hacer click en “Reset Persistent Data”…Esto borra todo el contenido de las eeprom’s que se encuentren en la simulación.
p3

Cuando ejecutemos nuestro programa veremos lo siguiente.

p5
p6

Ahora… y seguro se preguntan como transformo mi archivo EEPROM.eep a EEPROM.bin (es el tipo de archivo en el cual debe estar la data incial para la eeprom)--- proteus no permite cargar los datos iniciales desde el .eep.

Para esto pueden usar el “avr-objcopy.exe”, este ejecutable los pueden encontrar dentro de su instalación del avr gcc, se encuentra dentro de una carpeta de nombre bin.
Hay que usar la ventana de comando para hacer la conversión. Un ejemplo seria así.

>avr-objcopy.exe –I ihex ruta_del_archivo_hex.hex –O binary nombre_del_archivo_de_salida.bin

O mas fácil aun, pueden usar este programita que me tome la molestia de hacer en visual C#. Si no lo pueden ejecutar deben instalar en netframework 3.5.

programa

Este programa le pasa los parámetros correctos al “avr-objcopy.exe” para crear el archivo binario. El ejecutable de esta aplicación debe estar en la misma carpeta que el “avr-objcopy.exe”.

Si abren el archivo binario verán los siguiente.

eebin

En esta oportunidad además de este programita muy útil, también compartiré los archivos EEPROM.asm, macros.inc, EEPROM.eep, EEPROM.bin.

El link: EEPROM

Bueno, espero esto les ayude… Hasta pronto.