Diferencia entre revisiones de «Código Acelerómetro para Codewarrior 10.6»

De Wikitronica
Saltar a: navegación, buscar
Línea 52: Línea 52:
 
   •Auto-Encendido/Apagado
 
   •Auto-Encendido/Apagado
  
 +
=== Interfaz Serial===
 +
 +
-Direccionamiento Serial.
 +
 +
El MMA7660FC funciona como un esclavo que envía y recibe datos a través de una interfaz de 2 hilos I2C. La interfaz utiliza una serie
 +
de Línea de datos (SDA) y una línea de reloj en serie (SCL) para lograr una comunicación bidireccional entre el maestro (s) y el esclavo (s). UN
 +
maestro (típicamente un microcontrolador) inicia todas las transferencias de datos hacia y desde el dispositivo, y genera el reloj SCL que
 +
sincroniza la transferencia de datos.
 +
 +
Formato de mensaje de Escritura
 +
 +
[[Archivo:Escritura]]
 +
 +
Formato de mensaje de lectura
 +
 +
[[Archivo:lectura]]
  
 
== Descripción de funciones ==
 
== Descripción de funciones ==

Revisión del 12:22 24 jun 2016

Prueba de funcionamiento del acelerometro


A continuación se presenta el código para el funcionamiento del Acelérometro MMA7660SC, implementado en el módulo DEMOQE128.

Originalmente este código se diseñó para ser compilado en una versión de codewarrior más antigua que la versión 10.6. Por lo que se hizo el trabajo de mudar el programa y sus directivas a esta nueva versión.

A lo largo de esta sección se explicarán todas las funciones que son implementadas para ejecutar el acelerómetro, conjunto con esto se especifica cuáles fueron los módulos y registros utilizados para la implementación de este programa.

El hecho de entender el funcionamiento del acelerómetro en este módulo permite ampliar los conocimientos obtenidos para su uso en otras aplicaciones como:


Acelerometro MMA7660FC

El MMA7660FC es un output digital I²C, de muy bajo consumo de energía, de perfil capacitiva  micro-mecanizado con un filtro de paso bajo, la compensación de gravedad cero offset y la ganancia errores y la conversión a valores digitales de seis bits van en una salida de datos configurable por el usuario . El dispositivo puede ser utilizado para los cambios de datos del sensor, la orientación del producto y la detección a través de un gesto pin de interrupción (INT). El dispositivo es de 3 mm x 3 mm x 0,9 mm encapsulado en un DFN.

Aplicaciones Típicas

• Teléfono móvil / PMP / PDA: Orientación de detección (vertical / horizontal), Estabilidad de imagen, texto de desplazamiento, movimiento de marcar.

• PC portátil: antirrobo.

• Juegos: detección de movimiento, Auto-Enciende / Apaga para baja potencia Consumo.

• Cámara fotográfica digital: estabilidad de la imagen

Información técnica

Data Sheet aceleroo.png

Modos de Opercaion y tipos de Detecciones

•Modos de Operacion:

  •Modo sin Corriente
  •Modo Apagado
  •Modo en Espera
  •Modo Activo

• Tipos de detecciones:

  •Deteccion de orientacion
  •Deteccion de Golpe
  •Deteccion de agite
  •Auto-Encendido/Apagado

Interfaz Serial

-Direccionamiento Serial.

El MMA7660FC funciona como un esclavo que envía y recibe datos a través de una interfaz de 2 hilos I2C. La interfaz utiliza una serie de Línea de datos (SDA) y una línea de reloj en serie (SCL) para lograr una comunicación bidireccional entre el maestro (s) y el esclavo (s). UN maestro (típicamente un microcontrolador) inicia todas las transferencias de datos hacia y desde el dispositivo, y genera el reloj SCL que sincroniza la transferencia de datos.

Formato de mensaje de Escritura

Archivo:Escritura

Formato de mensaje de lectura

Archivo:Lectura

Descripción de funciones

Inicialización de periféricos

ICS

El módulo ICS (Internal Clock Source) maneja las diferentes opciones para la fuente de reloj. Tiene siete modos de operación: FEI, FEE, FBI, FBILP, FBE, FBELP, y stop. En este caso se trabajará con el modo por defecto FEI.

Para inicilizar el ICS deben modificarse tres de sus registros:

ICSC1: ICS Control Register 1

ICSC1.png

ICSC2: ICS Control Register 2

ICSC2.png

ICSSC: ICS Status and Control

ICSSC.png

Rutina de inicialización

#Define ICSC1_FEI 0x04
#Define ICSC2_FEI 0x06
#Define ICSSC_FEI 0x80


void ICS_FEI(void) {
 if (NVICSTRM != 0xFF) 
  ICSTRM = NVICSTRM;
 else
  ICSTRM = 0xAD;
  ICSC1 = ICSC1_FEI;
  ICSC2 = ICSC2_FEI;
  ICSSC = ICSSC_FEI;
 while (ICSC1_CLKS != ICSSC_CLKST) {}
}

KBI

Es el módulo de interrupción por teclado.

El registro KBIxSC es un registro de estado y control.Está compuesto por 8 bits, de los cuales, los 4 bits menos significativos tienen un significado específico y permiten habilitar y deshabilitar distintas instancias de las interrupciones y manejar el control del registro.

           Kbi1.jpg

El registro KBIxPE es un registro que está relacionado con los pines de la tarjeta de desarrollo. Nos permite configurar cuáles pines se habilitarán para las interrupciones, los cuales posteriormente serán enlazados con algún dispositivo.

            Kbi2.jpg

Rutina de Inicialización

#Define KBI_SW KBI1PE_KBIPE2_MASK | KBI1PE_KBIPE3_MASK


void InitKBI(void) {
// Enable KBI1P[3:2] as interrupt
  KBI1PE = KBI_SW;
  KBI1SC = 0b00000110;
/*               ||||
                 |||+---- KBIMOD = KBI detection mode: 0=edge only
                 ||+----- KBIE   = KBI int enable: 1=enabled
                 |+------ KBACK  = KBI int acknowledge: 1=clr IRQF
                 +------- KBF    = KBI flag
*/
}

SCI

SCI (Serial Communications Interface), permite la transmisión y recepción de datos por el puerto serial. La configuración de este módulo es necesaria, ya que a través de su registro de datos (SCIxD) se lleva a cabo la transmisión de la data desde el acelerómetro al procesador. En este caso, para su inicialización solo hace falta modificar dos registros:

SCIxBDL

SCIxBDL.png

SCIxBDH

SCIxBDH.png

void InitSCI(word baud) {

  SCI1BD = baud;  // Se configura el valor de la tasa de baudios especificada
}

PeriphInit

Es quien se encarga de desabilitar interrupciones, habilito todos los pullups (Desde PTA hasta PTJ), iniciar el módulo SCI y además configurar ciertos parámetros del acelerómetro.

void PeriphInit(void)
{  
  // Desabilita las instrucciones de COP y habilita las instrucciones de STOP, RESET y BKGD
  SOPT1 = 0x23;
  
  // Selecciona el modo FEI
  // Seteamos el trimming del fBUS en 25 MHz
  ICS_FEI();
  
  // Enable all pullups
  PTAPE = 0xFF; 
  PTBPE = 0xFF; 
  PTCPE = 0xFF; 
  PTDPE = 0xFF; 
  PTEPE = 0xFF; 
  PTFPE = 0xFF; 
  PTGPE = 0xFF; 
  PTHPE = 0xFF; 
  PTJPE = 0xFF; 
  
  /* Configura PTG[2:1] como la sensitividad del acelerometro
      PTG2:PTG1
        0    0  = 1.5g
        0    1  = 2.0g
        1    0  = 4.0g
        1    1  = 6.0g
  */ //
  PTGD = 0x00; // selecciono como salida el puerto G
  PTGDD = 0x06; // selecciono sensibilidad de 1.5g
    
  // Timer2 overflow about every 1ms
  TPM2MOD = 25000; // registro que contiene el valor del contador
  // Detiene el Timer2 y selecciona 1 como un divisor prescalar
  TPM2SC = 0x00; // 
    
  // Initializes SCI Peripheral
  InitSCI(fei_baud); 
}

IIC

La función IIC_Configuration se encarga de setear los bits de dos registros para configurar dos cosas principalmente:

El Registro de Divisor de Frecuencia, para definir la taza de baudios y el tiempo de espera.

IICxFreg.jpg

IICxF.jpg

El Registro de Control del IIC, configurando La habilitación del modulo IIC, sus interrupciones y definiéndolo en modo esclavo, receptor y con una señal de acknowledge.

IICxC1.jpg

void IIC_configuration (void) {
 
  IIC2F = 0x90;         
  IIC2C1  = 0xC0;      
}

Funciones para la transmisión y recepción de datos

La función RecChar obtiene uno a unos los caracteres enviados a tráves del protocolo IIC desde el registro SCI1D. De igual forma, las funciones SendChar y SendMsg utilizan este registro, descomponen en caracteres el mensaje y lo envían uno a uno.

Los registros utilizados por estas funciones son:

SCIxC2: SCI Control Register 2

SCIxC2.png

SCIxS1: SCI Status Register 1SCIxS1.png

SCIxD: SCI Data Register

SCIxD.png

Recibir caracter

char RecChar(void) {
  byte rec_char;

  if (SCI1S1_RDRF)  // Si el buffer de transmisión esta lleno
    rec_char = SCI1D; // Limpio el buffer
  SCI1C2_RE = 1;    //Habilito la transmisión
  while(!SCI1S1_RDRF){  };// Espero hasta que el buffer no esté vacío
  rec_char = SCI1D; // Obtengo el caracter enviado
  SendChar((char) rec_char);// Reenvío el caracter
  return (char) SCI1D;
}

Enviar caracter

void SendChar(char s_char) {

 SCI1C2 = 0x08;    // Habilito la transmisión de datos
 while(!SCI1S1_TDRE){ } //Mientras el buffer de transmisión de datos este lleno espero
 SCI1D = (byte) s_char;   //Escribe el caracter que será enviado
 }

Enviar mensaje

void SendMsg(char msg[]) {
  byte i=0;
  char nxt_char;

  SCI1C2 = 0x08;    //Habilito la transmisión
  nxt_char = msg[i++]; 
  while(nxt_char != 0x00) { //Mientras sea diferente de NULL
   while(!SCI1S1_TDRE){} //Mientras el buffer de transmsión de datos este lleno espero
   SCI1D = (byte) nxt_char; // Escribo el caracter en el registro de datos
   nxt_char = msg[i++]; //Leo el siguiente caracter del arreglo
  }
}

Conversión de datos

Hexadecimal a BCD (Binary-Coded Decimal)

word hex2bcd(word hex){
 byte dec[4],i;
 word bcd;

 for (i=0;i<4;i++){
   dec[i] = (byte) (hex%10);
   hex = (word) (hex/10);
 }

 if (hex>0){
   bcd=0xffff;
 }else{
   bcd=(word)((word)(dec[3]<<12) + (word)(dec[2]<<8) + (dec[1]<<4) + dec[0]);
 }
 return bcd;
}

De ASCII a un byte

byte asc2byte(char n_asc) {
 byte n;

 n = (byte)(n_asc - 0x30);     //Conviere de ASCII a int
 if(n > 0x09)           // if num is $a or larger...
   n -= 0x07;           // ...sub $7 to correct
 if(n > 0x0f)           // if lower case was used...
   n -= 0x20;           // ...sub $20 to correct
 if(n > 0x0f)           // if non-numeric character...
   n = 0x00;            // ...default to '0'
 return n;
}

De ASCII a una palabra

word asc2word(byte n_asc[2]) {
 word n,n2;
// assumes n_asc[0] is MSB, n_asc[1] is LSB
 n = (word)(n_asc[0] - 0x30);   //convert from ascii to int
 if(n > 0x09)           // if num is $a or larger...
    n -= 0x07;           // ...sub $7 to correct
 if(n > 0x0f)           // if lower case was used...
    n -= 0x20;           // ...sub $20 to correct
 if(n > 0x0f)           // if non-numeric character...
    n = 0x00;            // ...default to '0'
 n = (word)(n<<8);              // shift into high byte
 n2 = (word)(n_asc[1] - 0x30);  //convert from ascii to int
 if(n2 > 0x09)          // if num is $a or larger...
    n2 -= 0x07;          // ...sub $7 to correct
 if(n2 > 0x0f)          // if lower case was used...
    n2 -= 0x20;          // ...sub $20 to correct
 if(n2 > 0x0f)          // if non-numeric character...
    n2 = 0x00;           // ...default to '0'
 n += n2;               //
 return n;
}

De byte a ASCII

char * byte2asc(byte num, byte base) {
byte n;

if (base){
n=(byte)(hex2bcd(num));
}else{
n=num;
} //end if (base)
n_str[0] = (byte)((n>>0x04)+0x30);  // convert MSN to ascii
if(n_str[0]>0x39)           // if MSN is $a or larger...
n_str[0]+=0x07;           // ...add $7 to correct
n_str[1] = (byte)((n&0x0f)+0x30);   // convert LSN to ascii
if(n_str[1]>0x39)           // if LSN is $a or larger...
n_str[1]+=0x07;           // ...add $7 to correct
n_str[2] = 0x00;            // add line feed
return  (char *) n_str;
} //end byte2asc

De palabra a ASCII

char * word2asc(word num, byte base) {
word n;

if (base){
n=hex2bcd(num);
}else{
n=num;
} //end if (base)

n_str[0] = (byte)((n>>12)+0x30);    // convert MSN to ascii
if(n_str[0]>0x39)           // if MSN is $a or larger...
n_str[0]+=0x07;           // ...add $7 to correct
n_str[1] = (byte)(((n>>8)&0x0f)+0x30);   // convert 2nd MSN to ascii
if(n_str[1]>0x39)           // if LSN is $a or larger...
n_str[1]+=0x07;           // ...add $7 to correct
n_str[2] = (byte)(((n>>4)&0x0f)+0x30);   // convert 2nd MSN to ascii
if(n_str[2]>0x39)           // if LSN is $a or larger...
n_str[2]+=0x07;           // ...add $7 to correct
n_str[3] = (byte)((n&0x0f)+0x30);   // convert 2nd MSN to ascii
if(n_str[3]>0x39)           // if LSN is $a or larger...
n_str[3]+=0x07;           // ...add $7 to correct
n_str[4] = 0x00;    // add line feed
return  (char *) n_str;

} //end word2asc

Aceleración

Read Acceleration

void ReadAcceleration(void){
  byte i;
  signed int temp;

  for(i=0;i<3;i++){
    temp = IIC_Rec_Data[i] & 0x3F;  
    if(IIC_Rec_Data[i] & 0x20){
       temp |= 0xFFC0;                                
       temp += 32;
       IIC_Converted_Data[i] = temp;
    }else{   
      IIC_Converted_Data[i] = temp + 32;
    }
  }
}

Show Acceleration

void ShowAcceleration (void)
{
  word SampleCNT;
  byte j,k;   
  
  ReadAcceleration();           
  ADCSC1 = 0x01;                 
  x.reading[samp]  = (dword)( IIC_Converted_Data[0] <<8);
  ADCSC1 = 0x08;                 
  y.reading[samp]  = (dword)( IIC_Converted_Data[1] <<8);
  ADCSC1 = 0x09;                 
  z.reading[samp]  = (dword)( IIC_Converted_Data[2] <<8);
  
  StartTPM(0);   //0 = TPM prescaler = /2

  if(samp>0){
    switch (mode){
      case filter: filter_data();   break;
      case avg   : avg_data();      break;
      default    : copy_data();
    }
  } else {
    copy_data();
  }
  
  SampleCNT = StopTPM();
  if (SampleCNT<0x0100) {
    for(j=0xff;j>0;j--){
      for(k=0x10;k>0;k--){}
    }
  }

  // Display Acceleration
  SendMsg("\r\n");
  SendMsg(word2asc((word)x.result[samp],dis_base));
  SendMsg(",");
  SendMsg(word2asc((word)y.result[samp],dis_base));
  SendMsg(",");
  SendMsg(word2asc((word)z.result[samp],dis_base));
  SendMsg(",");
  SendMsg(word2asc(SampleCNT,dis_base));
  
  // Shift array of results if we hit max
  if (samp >= max-1) {
    for (j=0;j<max-1;j++){
      x.result[j]  = x.result[j+1];
      x.reading[j] = x.reading[j+1];
      y.result[j]  = y.result[j+1];
      y.reading[j] = y.reading[j+1];
      z.result[j]  = z.result[j+1];
      z.reading[j] = z.reading[j+1];
    }
    samp = max-1;
  } else {
    samp++;
  } //end if (i => max) 
  
}

Funciones del Maestro

void Master_Read_and_Store(void) {
  IIC_Rec_Data[rec_count++] = IIC2D; 
}
void Master_Write_MMA7660_register(byte transbytes) {
  last_byte = 0;                    // Initialize variables to 0
 count = 0;
  bytes_to_trans = transbytes;       
  
  if (transbytes == 0) return; 
  
  IIC2C1_TX = 1;                    // Set TX bit for Address cycle
  IIC2C1_MST = 1;                   // Set Master Bit to generate a Start
  
  IIC2D = mma7660[count++];         // Send first byte (should be 7-bit address + R/W bit)    
}
void Master_Read_MMA7660_register(byte transbytes, byte recbytes) {

  rec_count = 0;                    // Initialize variables to 0
  last_byte = 0;                    
 count = 0;
 repeat_start_sent = 0;
 
  bytes_to_trans = transbytes;      
  num_to_rec = recbytes;
  
  
  if (transbytes == 0) return;  
    
  IIC2C1_TXAK = 0;
  IIC2C1_TX = 1;                    // Set TX bit for Address cycle
  IIC2C1_MST = 1;                   // Set Master Bit to generate a Start
  
  reading_mma7660_reg = 1;
  IIC2D = mma7660[count++];         // Send first byte (should be 7-bit address + R/W bit)    
}

Configuración del TMP

void StartTPM(byte PS){
  TPM1SC = (byte)(0x08 | (0x07&PS));
  StartCount = TPM1CNT;
}
word StopTPM(void){
  StopCount = (word)(TPM1CNT - StartCount);
  TPM1SC = 0;
  return StopCount;
}

Rutinas de Interrupción

KBI_ISR

''interrupt VectorNumber_Vkeyboard ''

#Define KBI_VAL (PTAD&0x0C)>>2

void KBI_ISR(void){
  byte d,b;
  
  //capture which pin was pushed
  mode = (byte)(KBI_VAL);
  //debounce button
  for (d=0xff;d>0;d--){
    for (b=0x80;b>0;b--){}
  }
  //clear KBF
  KBI1SC_KBACK = 1;
}

IIC_ISR

''interrupt VectorNumber_Viicx'' 

void IIC_ISR(void) {

  IIC2S_IICIF = 1;              // Clear Interrupt Flag   
  if (IIC2C1_TX) {              // Transmit or Receive?                            
///////////////////// Transmit //////////////////////////// 
    if (repeat_start_sent) {
      IIC2C1_TX = 0;                // Switch to RX mode 
      if (num_to_rec == 1)
        IIC2C1_TXAK = 1;            // This sets up a NACK  
   IIC2D;                        // Dummy read from Data Register 
    } 
    else if ((last_byte) & (reading_mma7660_reg)) {
      IIC2C1_RSTA = 1;              //Repeat start
      IIC2D = (mma7660[0] | 0x01);  //Set Read bit
      repeat_start_sent = 1;
    }
 else if (last_byte) {           // Is the Last Byte? 
 IIC2C1_MST = 0;               // Generate a Stop 
 } 
 else if (last_byte != 1) {
   if (IIC2S_RXAK) {             // Check for ACK 
   IIC2C1_MST = 0;             // No ACk Generate a Stop 
   } 
   else if (!IIC2S_RXAK) {   
   IIC2D = mma7660[count++];     // Transmit Data    
   if (count == bytes_to_trans) 
     last_byte = 1;        
   }
 }
  } else {  
///////////////////// Receive ////////////////////////////  
    if ((num_to_rec - rec_count) == 2) {      
   IIC2C1_TXAK = 1;          // This sets up a NACK
   Master_Read_and_Store();    
    }
    else if ((num_to_rec - rec_count) == 1) {      
   IIC2C1_MST = 0;           // Send STOP 
   Master_Read_and_Store();
 } 
 else { 
   Master_Read_and_Store();
 }
  }
}

Modos de Operación

Por medio de interrupciones, se puede detectar el modo en el que el acelerómetro trabajará. Cada uno de estos modos se encarga de almacenar el valor que recibe cuando el sensor detecta un cambio en las coordenadas de X, Y, Z y puede Filtrar los datos de los valores recibidos (Función filter_data) Promedia el valor de las lecturas realizadas (Función Avg_data) o simplemente copiar los valores de la lectura de las variables (Función copy_data).

Filter_data

void filter_data(void) 
{ // guarda el valor de las coordenadas de X,Y,Z
  byte i;
  dword X, Y, Z;

  X = x.reading[samp];
  Y = y.reading[samp];
  Z = z.reading[samp];
  
  for (i=samp;i>0;i--){
    X = (X + ((x.reading[i] + x.result[i-1])>>1))>>1;
    Y = (Y + ((y.reading[i] + y.result[i-1])>>1))>>1;
    Z = (Z + ((z.reading[i] + z.result[i-1])>>1))>>1;
  }
  
  x.result[samp] = (word)X;
  y.result[samp] = (word)Y;
  z.result[samp] = (word)Z;
}

Avg_data

void avg_data(void) 
{// promedia el valor leido por x, y, z desde j hasta samp
  byte j;
  long x_avg=0, y_avg=0, z_avg=0;

  for (j=1;j<=samp;j++){
    x_avg += x.reading[j];
    y_avg += y.reading[j];
    z_avg += z.reading[j];
  }
  x.result[samp] = (word)(x_avg>>4);
  y.result[samp] = (word)(y_avg>>4);
  z.result[samp] = (word)(z_avg>>4);
}

Copy_data

void copy_data(void) { // copio el promedio leido en una variable resultado para x, y ,z
  
  x.result[samp] = x.reading[samp];
  y.result[samp] = y.reading[samp];
  z.result[samp] = z.reading[samp];
}

Material de Referencia

Código del proyecto:

Archivo:Accelerometer puntoc.pdf

Archivo:Accelerometer puntoh.pdf