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

De Wikitronica
Saltar a: navegación, buscar
(Main del programa)
(Modificación del código: Identificar la posición de la Tarjeta de Desarrollo con LEDs)
 
(No se muestran 128 ediciones intermedias de 4 usuarios)
Línea 1: Línea 1:
[[Archivo:AcelerometroFuncionando.gif|300px|right|thumb|Prueba de funcionamiento del acelerometro]]
+
{{#ev:youtube|https://youtu.be/GekrpE3ryuA |500 | right}}
  
 +
Se presenta a continuación el código, el cuál es un ejemplo propiedad de PEmicro, del Acelerómetro MMA7660SC, implementado en el módulo DEMOQE128.
  
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 la versión de Codewarrior 6.3. Por lo que se hizo el trabajo de mudar el programa y sus directivas a la versión de Codewarrior 10.6.
 
+
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.
 
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.
Línea 18: Línea 17:
 
• Cámara fotográfica digital: estabilidad de la imagen
 
• Cámara fotográfica digital: estabilidad de la imagen
  
 +
=== Tarjeta de desarrollo DEMOQE128 ===
  
 +
Es un sistema de bajo costo diseñado por PEmicro para la demostración, evaluación y depuramiento del microcontrolador MC9S08QE128 fabricado por Freescale.
  
== Acelerómetro MMA7660FC ==
+
La tarjeta de desarrollo DEMOQE128 trae integrado, el microcolontrolador MC9S08QE128, y un acelerómetro digital MMA7660FC de 3 ejes salida digital (I²C).
  
El MMA7660FC es un output digital I²C, de muy bajo consumo de energía, de perfil capacitivo micro-mecanizado con un filtro de paso bajo, con compensación offset para gravedad cero, ganancia de errores y conversión a valores digitales de seis bits van en una salida de datos configurable por el usuario. El dispositivo puede ser utilizado para cambios de datos del sensor, la orientación del producto y la detección de gesticulaciones a través de un pin de interrupción (INT). El dispositivo es de 3 mm x 3 mm x 0,9 mm encapsulado en un DFN (plano, dual y libre de plomo).
+
[[Archivo:TarjetaDEMOQE128.jpg | center]]
  
 +
=== Conexiones ===
  
==== Información técnica ====
+
El MC9S08QE128 viene integrado con dos módulos IIC que permiten la comunicación entre el microcontrolador y el acelerómetro digital. El módulo que se utiliza en este caso es IIC2 cuyos pines SDA y SCL están conectados a PTH7 y PTH6 respectivamente.
[[Archivo:Data Sheet aceleroo.png]]
+
  
=== Modos de Opercaión y tipos de Detecciones ===
+
El acelerómetro posee 3 pines (INT, SDA y SCL) que van conectados al MC9S08QE128 como se muestra a continuación:
  
'''•Modos de Operación:'''
+
[[Archivo: Conexion.png | center]]
  
•Modo sin Corriente
+
=== Procedimiento para migrar el programa de Codewarrior 6.3 a la versión 10.6 ===
  
•Modo Apagado
+
El primer paso es acceder al ejemplo del Acelerómetro de la versión 6.3; Accediendo a la página de NXP<sup>1</sup>, seleccionando la opción para descargar Software Package Tool Kit, o descargándolo directamente: [[Medio:DEMOQE Toolkit.7z]] y dentro del .zip, se accederá a la ruta:
 +
/DEMOQE_Toolkit/DEMOQE128 Software Examples/DEMOQE_Accelerometer_Ex_S08/DEMOQE_Accelerometer_Ex.
  
•Modo en Espera
 
  
•Modo Activo
+
Una vez en el ejemplo, se deben ubicar los archivos que son de importancia para el funcionamiento del acelerómetro: "accelerometer.c"; "accelerometer.h" y "Start08.c".
  
 +
Cuando se tengan estos tres archivos, se creará un nuevo proyecto en la versión de Codewarrior 10.6, con las características habituales <sup>2</sup>. En la carpeta que se genera al crear este proyecto, se borrará el archivo main.c,que se genera automáticamente, se sustituirá el archivo "Start08.c" y se agregarán los .c y .h del acelerómetro que se ubicaron en el paso anterior.
  
'''• Tipos de detecciones:'''
+
[[Archivo:ProyectoAmbiente.jpg | center]]
 +
<center>''Ubicación de los .c y .h correspondientes en el proyecto''</center>
  
•Detección de Orientación
+
Se actualiza el proyecto para que estos nuevos archivos sean reconocidos y no queda más que compilarlo, debuggearlo y seleccionar el botón de "play" para iniciar el programa. Para apreciar el funcionamiento del acelerómetro se hace uso de la herramienta graficadora del Tookit.
  
•Detección de Golpe
+
____________________________________________________________________________________________________________________________________
  
•Detección de Agite
+
<sup>1</sup> www.nxp.com/products/sensors/touch-sensors/mcf51qe128-demonstration-board:DEMOQE128?fpsp=1&tab=Design_Tools_Tab
  
•Auto-Encendido/Apagado
+
<sup>2</sup> Los pasos para crear este proyecto serán: Help -> Welcome -> New Proyect Wizard -> Selecciono nombre del Proyecto -> Selecciono el MC9S08DEMOQE128 -> P&E USB Multilink Universal y P&E Full Chip Simulation -> Lenguaje C -> None -> None -> Finish
  
=== Interfaz Serial===
+
== Descripción de funciones ==
  
-Direccionamiento Serial.
+
=== Programa principal ===
  
El MMA7660FC funciona como un esclavo que envía y recibe datos a través de una interfaz de 2 hilos I²C. 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 (M) 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.
+
A continuación se presenta el código del main, el cuál ejecutará todas las rutinas necesarias para el funcionamiento del acelerómetro.
 +
En primer lugar llama a la inicialización de los periféricos, las interrupciones por keyboard y la configuración del módulo IIC. Posteriormente habilita las interrupciones y configura las muestras por segundo con las que trabajará el MMA7660.
  
Formato de mensaje de Escritura
+
Luego se configura el registro TPM haciendo 01 los bits correspondientes a CLKSA:B con el objetivo de seleccionar la fuente del reloj, la cual será igual a la tasa del reloj del bus.
 +
SW1 está relacionado a la entrada PTAD2 del módulo. Cuando ésta entrada sea 0, el acelerómetro se debe quedar en un ciclo infinito sin hacer nada.
  
[[Archivo:Escritura.png]]
+
Se tiene un segundo ciclo infinito, en el cual se ejecutará unas instrucciones siempre que SW4 sea 1. Esta definción está relacionada a PTDD3 e invocará a ShowAcceleration cuando se tengan los datos disponibles, en caso contrario esperará a que se actualice el MMA7660.  
  
Formato de mensaje de lectura
+
Para evitar problemas de inconsistencia de datos, se tienen dos instrucciones que se quedarán esperando si la entrada SDA está marcada como cero o si el IIC no ha detenido su ejecución. Adicionalmente, el programa sólo podrá verificar estas dos instrucciones si se cumple que el bus IIC está libre.
 
+
Una vez, se cumplan las condiciones para seguir la ejecución del programa, se guardarán los datos de lectura en el mma7660 y el maestro hará la lectura de una cantidad de bits específica.
[[Archivo:lectura.png]]
+
 
+
== Descripción de funciones ==
+
 
+
=== Main del programa ===
+
 
+
A continuación se presenta el código del main, en el cual se invocan las funciones que fueron declaradas para el funcionamiento del acelerómetro.
+
  
 +
Por último, si SW3 (Asociado a PTDD2) es 1, el programa se detendrá.
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
void main(void){
 
void main(void){
Línea 82: Línea 81:
 
   MMA7660_configuration();
 
   MMA7660_configuration();
 
    
 
    
// Selects fBUS as timer1 clock source and start timer
+
// Seleccionar fbus como fuente del timer1 e iniciar el temporizador
 
   TPM1SC = 0x08;
 
   TPM1SC = 0x08;
 
//  SendMsg("\fX, Y, Z\r\n");
 
//  SendMsg("\fX, Y, Z\r\n");
Línea 89: Línea 88:
 
     while(SW4){
 
     while(SW4){
 
       if(!(IIC_Rec_Data[0]&IIC_Rec_Data[1]&IIC_Rec_Data[2]&0x40)){
 
       if(!(IIC_Rec_Data[0]&IIC_Rec_Data[1]&IIC_Rec_Data[2]&0x40)){
         ShowAcceleration();        // Show acceleration data
+
         ShowAcceleration();        // Muestra los datos sobre la aceleración
       }else{                        // MMA7660 updating
+
       }else{                        // Actualización MMA7660  
 
       }
 
       }
 
        
 
        
       if (PTHD_PTHD7 == 1) {        //Wait for IIC bus to be free
+
       if (PTHD_PTHD7 == 1) {        //Espera a que el bus del IIC esté libre
 
        
 
        
         while (PTHD_PTHD7 == 0);    // Wait while pin is low
+
         while (PTHD_PTHD7 == 0);    // Esperará en un ciclo infinito mientras el pin del PTHD esté bajo
 
          
 
          
       while (IIC2C1_MST == 1);    // Wait untill IIC is stopped
+
       while (IIC2C1_MST == 1);    // Esperará hasta que el IIC se detenga
 
      
 
      
       //Read Xout, Yout, Zout
+
       //Se lee Xout, Yout, Zout
 
       mma7660[0] = 0x98;
 
       mma7660[0] = 0x98;
 
       mma7660[1] = 0x00;
 
       mma7660[1] = 0x00;
Línea 108: Línea 107:
 
       }
 
       }
 
              
 
              
     } //end while(SW4)
+
     }
 
     while(SW3){_Stop;}
 
     while(SW3){_Stop;}
   } //end for(;;)
+
   }  
 
      
 
      
 
}
 
}
Línea 138: Línea 137:
 
'''Rutina de inicialización'''
 
'''Rutina de inicialización'''
  
Se habilitará el bit correspondiente a ICSC1IREFS, con el cual se hace referencia al reloj interno. Además, con este rutina de inicialización los bits correspondientes a ICSC2EREFS e ICSC2ERCLKEN se habilitan, donde el primero permite seleccionar la fuente del reloj externa, solicitando al oscilador. El segundo, nos permite seleccionar la referencia al reloj externo que será usado como ICSERCLK
+
Si el valor del NVICSTRM es distinto de 0, ajustaré el registro ICS Trim para modificar la frecuencia del reloj de referencia interno proporcionalmente de acuerdo al valor que tenga cargado NVICSTRM.
Por último, se habilitan los bits correspondientes a ICSSCLKST con los cuales seleccionamos la referencia externa del reloj con el modo FLL Bypassed.
+
 
 +
Se habilitará el bit correspondiente a ICSC1IREFS, para utilizar el reloj interno de referencia. Además, con este rutina de inicialización los bits correspondientes a ICSC2EREFS e ICSC2ERCLKEN se habilitan, donde el primero permite seleccionar la fuente del reloj externa, solicitando al oscilador. El segundo, nos permite seleccionar la referencia externa del reloj que será usado como ICSERCLK
 +
Por último, se habilitan los bits correspondientes a ICSSCLKST con los cuales seleccionamos el reloj externo de referencia con el modo FLL desviado.
 +
 
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
#Define ICSC1_FEI 0x04
 
#Define ICSC1_FEI 0x04
Línea 148: Línea 150:
 
void ICS_FEI(void) {
 
void ICS_FEI(void) {
 
  if (NVICSTRM != 0xFF)  
 
  if (NVICSTRM != 0xFF)  
   ICSTRM = NVICSTRM;
+
   ICSTRM = NVICSTRM;             // Ajusta el valor del TRIM si la ubicación de NV no está en vacío.
 
  else
 
  else
   ICSTRM = 0xAD;
+
   ICSTRM = 0xAD;                 //Se ajusta para controlar la frecuencia del reloj de referencia interna y controla el periodo de referencia del reloj.
   ICSC1 = ICSC1_FEI;
+
   ICSC1 = ICSC1_FEI;             // Se coloca el bit 2 del ICS1 en 1 (el de IREFS), para utilizar el Reloj Interno de referencia.
   ICSC2 = ICSC2_FEI;
+
   ICSC2 = ICSC2_FEI;             // Se colocan los bits 1 y 2 del ICS2 en 1 (ERCLKEN y EREFS, respectivamente), ERCKLEN habilitará la fuente del reloj externa para ser usada como ICSERCLK y el segundo para seleccionar el Oscilador como reloj de referencia externa
   ICSSC = ICSSC_FEI;
+
   ICSSC = ICSSC_FEI;             // Se coloca el campo CLKSET en 10, para seleccionar el reloj externo de referencia con FLL desviado.
  while (ICSC1_CLKS != ICSSC_CLKST) {}
+
  while (ICSC1_CLKS != ICSSC_CLKST) {} // Espera si que la frecuencia del Bus (ICSC1_CLKS) sea distinta al modo del reloj (ICSSC_CLKSET) 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Línea 179: Línea 181:
  
 
void InitKBI(void) {
 
void InitKBI(void) {
// Enable KBI1P[3:2] as interrupt
+
// Habilitado KBI1P[3:2] como interrupciones
 
   KBI1PE = KBI_SW;
 
   KBI1PE = KBI_SW;
 
   KBI1SC = 0b00000110;
 
   KBI1SC = 0b00000110;
 
/*              ||||
 
/*              ||||
                 |||+---- KBIMOD = KBI detection mode: 0=edge only
+
                 |||+---- KBIMOD = Detección de modo KBI: 0= solo borde
                 ||+----- KBIE  = KBI int enable: 1=enabled
+
                 ||+----- KBIE  = Habilitar KBI: 1=enabled
                 |+------ KBACK  = KBI int acknowledge: 1=clr IRQF
+
                 |+------ KBACK  = Se Habilita para limpiar la bandera
                 +------- KBF    = KBI flag
+
                 +------- KBF    = Está relacionado a la bandera del KBI. En 0 No se detectó interrupción por KBI.
 
*/
 
*/
 
}
 
}
Línea 225: Línea 227:
 
    
 
    
 
   // Selecciona el modo FEI
 
   // Selecciona el modo FEI
   // Seteamos el trimming del fBUS en 25 MHz
+
   // El trimming del fBUS es configurado en 25 MHz
 
   ICS_FEI();
 
   ICS_FEI();
 
    
 
    
Línea 246: Línea 248:
 
         1    1  = 6.0g
 
         1    1  = 6.0g
 
   */ //
 
   */ //
   PTGD = 0x00; // selecciono como salida el puerto G
+
   PTGD = 0x00; // Se selecciona la sensibilidad de 1.5g
   PTGDD = 0x06; // selecciono sensibilidad de 1.5g
+
   PTGDD = 0x06; // Se selecciona como salida el puerto G
 
      
 
      
 
   // Timer2 overflow about every 1ms
 
   // Timer2 overflow about every 1ms
   TPM2MOD = 25000; // registro que contiene el valor del contador
+
   TPM2MOD = 25000; // Es un registro que contiene el valor del contador
 
   // Detiene el Timer2 y selecciona 1 como un divisor prescalar
 
   // Detiene el Timer2 y selecciona 1 como un divisor prescalar
 
   TPM2SC = 0x00; //  
 
   TPM2SC = 0x00; //  
Línea 276: Línea 278:
 
'''Rutina de inicialización'''
 
'''Rutina de inicialización'''
  
Se inicializa el bit correspondiente a IIC2CI-IICEN para habilitar el modulo de IIC y además se inicializa el bit correspondiente a IIC2CI-IICIE con el objetivo de habilitar las solicitudes de interrrupción
+
Se inicializa el bit correspondiente a IIC2CI-IICEN para habilitar el modulo de IIC y además se inicializa el bit correspondiente a IIC2CI-IICIE con el objetivo de habilitar las solicitudes de interrupción. Además, se configura el IIC2F-MULT con los bits 10 con el objetivo de establecer la tasa de baudio del IIC, colocando el MULT en un valor de 04.
 
+
  
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
void IIC_configuration (void) {
 
void IIC_configuration (void) {
 
   
 
   
   IIC2F = 0x90;         
+
   IIC2F = 0x90;        //Se asigna un valor de 04 que corresponde a un factor multiplicador para establecer la tasa de baudio del IIC.
   IIC2C1  = 0xC0;    
+
   IIC2C1  = 0xC0;       //Los primeros 4 bits (1100) sirven para habilitar el modulo IIC y para habilitar las interrupciones
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Línea 294: Línea 295:
  
 
Se utiliza para habiliar la transmisión o la recepción.
 
Se utiliza para habiliar la transmisión o la recepción.
 +
 
'''SCIxC2: SCI Control Register 2'''
 
'''SCIxC2: SCI Control Register 2'''
  
Línea 299: Línea 301:
  
 
Se utiliza para saber si se han enviado o leído datos desde el módulo.
 
Se utiliza para saber si se han enviado o leído datos desde el módulo.
 +
 
'''SCIxS1: SCI Status Register 1'''
 
'''SCIxS1: SCI Status Register 1'''
  
Línea 305: Línea 308:
  
 
Se utiliza para la recepción y transmisión de datos.
 
Se utiliza para la recepción y transmisión de datos.
 +
 
'''SCIxD: SCI Data Register'''
 
'''SCIxD: SCI Data Register'''
  
Línea 316: Línea 320:
  
 
   if (SCI1S1_RDRF)  // Si el buffer de transmisión esta lleno
 
   if (SCI1S1_RDRF)  // Si el buffer de transmisión esta lleno
     rec_char = SCI1D; // Limpio el buffer
+
     rec_char = SCI1D; // Se limpia el buffer
   SCI1C2_RE = 1;    //Habilito la transmisión
+
   SCI1C2_RE = 1;    //Se habilita la transmisión
   while(!SCI1S1_RDRF){  };// Espero hasta que el buffer no esté vacío
+
   while(!SCI1S1_RDRF){  };// Se espera hasta que el buffer no esté vacío
   rec_char = SCI1D; // Obtengo el caracter enviado
+
   rec_char = SCI1D; // Se obtiene el caracter enviado
   SendChar((char) rec_char);// Reenvío el caracter
+
   SendChar((char) rec_char);// Se reenvía el caracter
 
   return (char) SCI1D;
 
   return (char) SCI1D;
 
}  
 
}  
Línea 330: Línea 334:
 
void SendChar(char s_char) {
 
void SendChar(char s_char) {
  
  SCI1C2 = 0x08;    // Habilito la transmisión de datos
+
  SCI1C2 = 0x08;    // Se habilita la transmisión de datos
 
  while(!SCI1S1_TDRE){ } //Mientras el buffer de transmisión de datos este lleno espero
 
  while(!SCI1S1_TDRE){ } //Mientras el buffer de transmisión de datos este lleno espero
  SCI1D = (byte) s_char;  //Escribe el caracter que será enviado
+
  SCI1D = (byte) s_char;  //Se escribe el caracter que será enviado
 
  }
 
  }
 
</syntaxhighlight>
 
</syntaxhighlight>
Línea 343: Línea 347:
 
   char nxt_char;
 
   char nxt_char;
  
   SCI1C2 = 0x08;    //Habilito la transmisión
+
   SCI1C2 = 0x08;    //Se habilita la transmisión
 
   nxt_char = msg[i++];  
 
   nxt_char = msg[i++];  
 
   while(nxt_char != 0x00) { //Mientras sea diferente de NULL
 
   while(nxt_char != 0x00) { //Mientras sea diferente de NULL
 
   while(!SCI1S1_TDRE){} //Mientras el buffer de transmsión de datos este lleno espero
 
   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
+
   SCI1D = (byte) nxt_char; // Se escribe el caracter en el registro de datos
   nxt_char = msg[i++]; //Leo el siguiente caracter del arreglo
+
   nxt_char = msg[i++]; //Se lee el siguiente caracter del arreglo
 
   }
 
   }
 
}
 
}
Línea 356: Línea 360:
 
=== Conversión de datos ===
 
=== Conversión de datos ===
  
Las funciones definidas a continuación tienen el objetivo de hacer la conversión de los datos para el funcionamiento correcto del acelerómetro.
+
Las funciones definidas a continuación tienen el objetivo de hacer la conversión de los datos para el funcionamiento correcto del programa.
 
==== Hexadecimal a BCD (Binary-Coded Decimal) ====
 
==== Hexadecimal a BCD (Binary-Coded Decimal) ====
  
Línea 479: Línea 483:
  
 
==== Read Acceleration ====
 
==== Read Acceleration ====
 +
Está función transforma los valores enviados desde le acelerómetro en un entero con signo.
  
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
Línea 485: Línea 490:
 
   signed int temp;
 
   signed int temp;
  
   for(i=0;i<3;i++){
+
   for(i=0;i<3;i++){ //Se realiza una extensión de signo para cada valor valor almacenado en la estructura
     temp = IIC_Rec_Data[i] & 0x3F;   
+
     temp = IIC_Rec_Data[i] & 0x3F;  //Se hace una máscara para guardar los 6bits menos significativos
     if(IIC_Rec_Data[i] & 0x20){
+
     if(IIC_Rec_Data[i] & 0x20){ //Si el valor es negativo
 
       temp |= 0xFFC0;                                 
 
       temp |= 0xFFC0;                                 
 
       temp += 32;
 
       temp += 32;
Línea 500: Línea 505:
 
==== Show Acceleration ====
 
==== Show Acceleration ====
  
Imprime la aceleración en la terminal
+
Esta función se encarga de definir el modo en el que el acelerómetro trabajará (Para filtrar data, promediar data o copiar la data) que envía el acelerómetro a través del protocolo IIC . (Es asignado de acuerdo a la variable IIC_Converted_Data).
 +
 
 +
Posteriomente, el maestro envía por el puerto serial el mensaje con las coordenadas de la posición leída mediante el uso de la función SendMsg() (carácter por carácter leído) y, ya que se está trabajando con buffers de espacio limitado por un 'max', en caso de que se llenen porque han leído muchos datos, estos son re-escritos.
 +
 
 +
'''Nota importante:''' Las asignaciones que se le hacen al registro ADCSC1 (Módulo Conversor Analógico Digital) no son necesarias para el funcionamiento de este programa. Se infiere que los responsables de realizar la migración del Acelerómetro Analógico al Digital, no tomaron en cuenta estas declaraciones en esta función y olvidaron borrarlas. Por lo que se pueden comentar o eliminar y el código funcionará a la perfección.
  
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
Línea 508: Línea 517:
 
   byte j,k;   
 
   byte j,k;   
 
    
 
    
   ReadAcceleration();          
+
   ReadAcceleration();   // Se leen los datos de la aceleración detectados por el acelerómetro       
   ADCSC1 = 0x01;                
+
   //ADCSC1 = 0x01;    
 
   x.reading[samp]  = (dword)( IIC_Converted_Data[0] <<8);
 
   x.reading[samp]  = (dword)( IIC_Converted_Data[0] <<8);
   ADCSC1 = 0x08;                 
+
   //ADCSC1 = 0x08;                 
 
   y.reading[samp]  = (dword)( IIC_Converted_Data[1] <<8);
 
   y.reading[samp]  = (dword)( IIC_Converted_Data[1] <<8);
   ADCSC1 = 0x09;                 
+
   //ADCSC1 = 0x09;                 
 
   z.reading[samp]  = (dword)( IIC_Converted_Data[2] <<8);
 
   z.reading[samp]  = (dword)( IIC_Converted_Data[2] <<8);
 
    
 
    
 
   StartTPM(0);  //0 = TPM prescaler = /2
 
   StartTPM(0);  //0 = TPM prescaler = /2
  
   if(samp>0){
+
   if(samp>0){ // Se escoge el modo de funcionamiento del programa
 
     switch (mode){
 
     switch (mode){
 
       case filter: filter_data();  break;
 
       case filter: filter_data();  break;
Línea 535: Línea 544:
 
   }
 
   }
  
   // Display Acceleration
+
   // Utilizado para enviar los caracteres donde se mostrará la acelración
 
   SendMsg("\r\n");
 
   SendMsg("\r\n");
 
   SendMsg(word2asc((word)x.result[samp],dis_base));
 
   SendMsg(word2asc((word)x.result[samp],dis_base));
Línea 545: Línea 554:
 
   SendMsg(word2asc(SampleCNT,dis_base));
 
   SendMsg(word2asc(SampleCNT,dis_base));
 
    
 
    
   // Shift array of results if we hit max
+
   //Se desplaza el arreglo de los resultados si llega al máximo
 
   if (samp >= max-1) {
 
   if (samp >= max-1) {
 
     for (j=0;j<max-1;j++){
 
     for (j=0;j<max-1;j++){
Línea 558: Línea 567:
 
   } else {
 
   } else {
 
     samp++;
 
     samp++;
   } //end if (i => max)
+
   }  
 
    
 
    
 
}
 
}
Línea 564: Línea 573:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Configuracion del MMA7660fC ===
+
=== Configuración del MMA7660FC ===
  
El MMA7660 usa la configuracion por defecto. 120 samples/second, se desabilitan las interrupciones. Entra en modo activo.
+
Para poder usar el acelerómetro se debe cambiar a modo activo, para lograr esto se debe modificar el registro MODO cuya dirección es 0x07 en el mapa de memoria del MMA7660FC. Escribir en este registro resetea el sleep timing y limpia los registros XOUT, YOUT, ZOUT y TILT.
 +
 
 +
 
 +
[[Archivo:Registro_Modo.png | center]]
 +
 
 +
 +
Se utiliza la configuracion por defecto (120 muestras/segundo) y al cambiar solo el bit 0 (MODE) se entra en modo activo.
  
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
void MMA7660_configuration(void){
 
void MMA7660_configuration(void){
 
    
 
    
   mma7660[0] = 0x98;
+
   mma7660[0] = 0x98; // Dirección de escritura maestra
   mma7660[1] = 0x07;
+
   mma7660[1] = 0x07; // Se registra MODE del acelerómetro
   mma7660[2] = 0x01;
+
   mma7660[2] = 0x01; // Se envía este valor para cambiar a 1 el bit MODE del registro para cambiar el acelerómetro a modo activo
 
   Master_Write_MMA7660_register(3);
 
   Master_Write_MMA7660_register(3);
 
    
 
    
Línea 580: Línea 595:
  
 
=== Funciones del Maestro ===
 
=== Funciones del Maestro ===
 +
 +
====Master_Read_and_Store====
 +
 +
Se define esta función, realizada por el maestro, que tiene el objetivo de tomar el contenido del registro de datos del IIC (IIC2D) y almacenarlo en la variable IIC_Rec_Data[] para su posterior uso.
  
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
  
 
void Master_Read_and_Store(void) {
 
void Master_Read_and_Store(void) {
   IIC_Rec_Data[rec_count++] = IIC2D;  
+
   IIC_Rec_Data[rec_count++] = IIC2D;   /*Se guarda el valor enviado por el acelerómetro*/
 
}
 
}
  
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
====Master_Write_MMA7660_register====
 +
 +
En esta función el Maestro inicializa al esclavo (acelerómetro) y le transfiere ciertos bytes. Para esto, habilita el bit de la transferencia correspondiente al módulo de IIC (IIC2CI_TX) y además habilita el bit para generar el estado de Start en el protocolo de IIC (IIC2C1_MST) para comunicarse con un esclavo indicando el envío de datos. Posterior a esto envía el primer byte.
 +
Si el byte a transferir que se recibe como dato es cero entonces no se ejecuta la explicación anterior. 
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
  
 
void Master_Write_MMA7660_register(byte transbytes) {
 
void Master_Write_MMA7660_register(byte transbytes) {
   last_byte = 0;                    // Initialize variables to 0
+
   last_byte = 0;                    // Inicialización de variables en 0.
 
  count = 0;
 
  count = 0;
 
   bytes_to_trans = transbytes;       
 
   bytes_to_trans = transbytes;       
 
    
 
    
   if (transbytes == 0) return;  
+
   if (transbytes == 0) return;   /*Si no hay ningún byte para transferir */
 
    
 
    
   IIC2C1_TX = 1;                    // Set TX bit for Address cycle
+
   IIC2C1_TX = 1;                    // Se setea el bit para hacer la transmisión
   IIC2C1_MST = 1;                  // Set Master Bit to generate a Start
+
   IIC2C1_MST = 1;                  // Se genera la señal de inicio (START)
 
    
 
    
   IIC2D = mma7660[count++];        // Send first byte (should be 7-bit address + R/W bit)    
+
   IIC2D = mma7660[count++];        // Se envia el primer byte. Puede ser una dirección de 7 bits mas 1 bit de W/R.    
 
}
 
}
  
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
====Master_Read_MMA7660_register====
 +
 +
Esta función permite que se activen el bit de transferencia y el bit para generar la señal de inicio(START) y así poder comunicarse con el esclavo.
 +
Adicionalmente el bit correspondiente a IIC2CI_TXAK se le asigna el valor de cero, con el objetivo de que se envíe una señal de recibido en el bus luego de haber recibido un byte de data.   
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
void Master_Read_MMA7660_register(byte transbytes, byte recbytes) {
 
void Master_Read_MMA7660_register(byte transbytes, byte recbytes) {
  
   rec_count = 0;                    // Initialize variables to 0
+
   rec_count = 0;                    // Inicialización de variables en 0
 
   last_byte = 0;                     
 
   last_byte = 0;                     
 
  count = 0;
 
  count = 0;
Línea 618: Línea 645:
 
    
 
    
 
    
 
    
   if (transbytes == 0) return;
+
   if (transbytes == 0) return;   //Si no hay byte para transferir
 
      
 
      
   IIC2C1_TXAK = 0;
+
   IIC2C1_TXAK = 0;                 //Se coloca en 0 el bit de Acknowledge
   IIC2C1_TX = 1;                    // Set TX bit for Address cycle
+
   IIC2C1_TX = 1;                    // Se coloca el bit en 1 para seleccionar que se va a transferir data
   IIC2C1_MST = 1;                  // Set Master Bit to generate a Start
+
   IIC2C1_MST = 1;                  // Se genera la señal de START.
 
    
 
    
 
   reading_mma7660_reg = 1;
 
   reading_mma7660_reg = 1;
   IIC2D = mma7660[count++];        // Send first byte (should be 7-bit address + R/W bit)    
+
   IIC2D = mma7660[count++];        // Se envía el primer byte, puede ser una direccion de 7 bits mas 1 bit de w/r    
 
}
 
}
  
Línea 632: Línea 659:
 
=== Configuración del TMP ===
 
=== Configuración del TMP ===
  
Modulo Periférico Timer/PWM
+
La configuración detallada se puede encontrar en:
  
 
+
Artículo : [[Código_Acelerometro_Rutinas_de_Interrupción]]
-- Cuando el MCU entra en el modo de parada, el reloj de los módulos TPM1 y TPM2 se para . los módulos de media
+
operación  . Si la MCU está configurado para entrar en el modo de stop2 o stop1 , los módulos TPM se restablecerán
+
al despertar de la parada y deben ser reinstaladas .
+
 
+
[[Archivo:TPM.png]]
+
 
+
<syntaxhighlight lang="c">
+
void StartTPM(byte PS){
+
  TPM1SC = (byte)(0x08 | (0x07&PS));
+
  StartCount = TPM1CNT;
+
}
+
 
+
</syntaxhighlight>
+
<syntaxhighlight lang="c">
+
word StopTPM(void){
+
  StopCount = (word)(TPM1CNT - StartCount);
+
  TPM1SC = 0;
+
  return StopCount;
+
}
+
 
+
</syntaxhighlight>
+
  
 
=== Rutinas de Interrupción ===
 
=== Rutinas de Interrupción ===
  
'''KBI_ISR'''
+
Para información más detallada acceder: Artículo: [[Código_Acelerometro_Rutinas_de_Interrupción]]
 
+
  El modulo KBI en se puede considerar como el modulo IRQ extendido. La diferencia entre estos módulos es que el modulo KBI es sensible a varios pines, y  el modulo IRQ es sensible a un solo pin. La interrupción del modulo IRQ tiene mayor prioridad que la del modulo KBI. El modulo KBI no necesita ser habilitado desde CONFIG .
+
 
+
En el modulo KBI los modos también son por nivel y/o flanco. Se puede configurar la polaridad en cada pin KBI para que ingrese ya sea por nivel alto y/o flanco de subida o por nivel bajo y/o flanco de bajada. Ademas se puede configurar si un pin queda como pin de propósito general o como pin KBI.
+
 
+
[[Archivo:KBIIC.png]]
+
  
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
Línea 675: Línea 675:
 
   byte d,b;
 
   byte d,b;
 
    
 
    
   //capture which pin was pushed
+
   //Identifica cual PIN fue presionado
 
   mode = (byte)(KBI_VAL);
 
   mode = (byte)(KBI_VAL);
   //debounce button
+
   //Cuando se suelta el botón:
 
   for (d=0xff;d>0;d--){
 
   for (d=0xff;d>0;d--){
 
     for (b=0x80;b>0;b--){}
 
     for (b=0x80;b>0;b--){}
 
   }
 
   }
   //clear KBF
+
   //Limpia el Registro KBF
 
   KBI1SC_KBACK = 1;
 
   KBI1SC_KBACK = 1;
 
}
 
}
Línea 692: Línea 692:
  
  
 +
interrupt VectorNumber_Viicx void IIC_ISR(void) {
  
 +
    IIC2S_IICIF = 1;              // Limpiar la bandera de interrupcion
  
el IIC se puede usar con operaciones donde funge de  esclavo, Se enviara una señal que sera la encargada de iniciar la comunicación .
+
    if (IIC2C1_TX) {              // Si está habilitado el bit para la transmisión
  
Si se implementa como  como maestro inicializaremos la  comunicación desde  el registro IICD.
+
///////////////////// Transmit ////////////////////////////
  
void IIC_ISR(void) {
+
      if (repeat_start_sent) {
 +
 
 +
        IIC2C1_TX = 0;                //Se coloca para recibir
 +
 
 +
        if (num_to_rec == 1)
 +
 
 +
        IIC2C1_TXAK = 1;            // Se habilita el bit para recibir el acknowledge
 +
 
 +
        IIC2D;                     
 +
 
 +
      }
 +
 
 +
      else if ((last_byte) & (reading_mma7660_reg)) {
 +
 
 +
        IIC2C1_RSTA = 1;              //Se habilita para repetir el START
 +
 
 +
        IIC2D = (mma7660[0] | 0x01);  //Setea el bit de read
 +
 
 +
        repeat_start_sent = 1;
 +
 
 +
      }
 +
 
 +
      else if (last_byte) {          // Si es el ultimo byte
 +
 
 +
        IIC2C1_MST = 0;              // Genera un STOP y el modo de operacion cambia de maestro a esclavo
 +
 
 +
      }
 +
 
 +
      else if (last_byte != 1) {            //Si no es el ultimo byte
 +
 
 +
          if (IIC2S_RXAK) { //Para verificar si se recibio o no un acknowledge. Entra en el if si no se recibió el acknowledge
 +
 
 +
              IIC2C1_MST = 0;            // Genera un STOP y el modo de operacion cambia de maestro a esclavo
 +
 
 +
              }
 +
 
 +
          else if (!IIC2S_RXAK) {      //Para verificar si se recibio o no un acknowledge.Entra en el if si se recibió el acknowledge
 +
 
 +
              IIC2D = mma7660[count++];    //Se transfiere la data
 +
 
 +
              if (count == bytes_to_trans)
 +
 
 +
            last_byte = 1;
 +
 
 +
            }
 +
 
 +
      }
 +
 
 +
    } else {
 +
 
 +
///////////////////// Receive ////////////////////////////
 +
 
 +
        if ((num_to_rec - rec_count) == 2) {
 +
 
 +
          IIC2C1_TXAK = 1;          // Se habilita para recibir acknowledge
 +
 
 +
          Master_Read_and_Store();
 +
 
 +
        }
 +
 
 +
        else if ((num_to_rec - rec_count) == 1) {
 +
 
 +
          IIC2C1_MST = 0;          // Se envia una señal de STOP y el modo de operacion ahora es del esclavo
 +
 
 +
          Master_Read_and_Store();
 +
 
 +
        }
 +
 
 +
        else {
 +
 
 +
          Master_Read_and_Store();
 +
 
 +
      }
  
  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();
+
}
+
  }
+
}
+
  
 
</syntaxhighlight>
 
</syntaxhighlight>
Línea 795: Línea 829:
  
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
void copy_data(void) { // copio el promedio leido en una variable resultado para x, y ,z
+
void copy_data(void) { // Se copia el promedio leido en una variable resultado para x, y ,z
 
    
 
    
 
   x.result[samp] = x.reading[samp];
 
   x.result[samp] = x.reading[samp];
Línea 802: Línea 836:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
=== Modificación del código: Identificar la posición de la Tarjeta de Desarrollo con LEDs ===
 +
 +
Se realizaron unas pequeñas modificaciones al código presentado anteriormente para que, además de mostrar la aceleración de la tarjeta mediante la herramienta graficadora del Toolkit, se mostrara la inclinación en el eje "Y" haciendo uso de sus LEDs. Estos se encenderán en forma de "cascada" si la tarjeta se inclina en una u otra dirección.
 +
 +
*Prueba de funcionamiento:
 +
 +
{{#ev:youtube|https://youtu.be/R5EN2hpUVKQ |500 | center}}
 +
 +
Agregarle esta nueva característica al proyecto sólo necesitó modificar dos funciones del archivo acelerómetro.c, que se explicarán a continuación:   
 +
 +
==== PeriphInit ====
 +
 +
En esta función únicamente se agregó la habilitación de los periféricos relacionados a los LEDs (PTCDD y PTEDD) y posteriormente su inicialización como apagados por defecto.
 +
 +
 +
<syntaxhighlight lang="c">
 +
void PeriphInit(void)
 +
 +
  // Desabilita las instrucciones de COP y habilita las instrucciones de STOP, RESET y BKGD
 +
  SOPT1 = 0x23;
 +
 
 +
  // Selecciona el modo FEI
 +
  // El trimming del fBUS es configurado 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; // Se selecciona la sensibilidad de 1.5g
 +
  PTGDD = 0x06; // Se selecciona como salida el puerto G
 +
 +
  // Habilitación de los LEDs
 +
  PTCDD = 0x3F;
 +
  PTEDD = 0xC0;
 +
 
 +
  // LEDs apagados por defecto
 +
  PTCD = 0x3F;
 +
  PTED = 0xC0;
 +
   
 +
  // Timer2 overflow about every 1ms
 +
  TPM2MOD = 25000; // Es un 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);
 +
}
 +
</syntaxhighlight>
 +
 +
==== ShowAcceleration ====
 +
 +
Utilizando como ayuda la herramienta graficadora del Toolkit se visualizaron los rangos de valores que alcanzaba la posición "Y" cuando se movía la Tarjeta de Desarrollo progresivamente en ese eje específico. Luego se estableció un criterio para definir entre que rangos de valores encendería (y apagaría) cada LED (PTC0, PTC1, PTC2, PTC3, PTC4, PTC5, PTE6 y PTE7) y posteriormente estos valores se convirtieron hexadecimal para poder ser manipulados en el código.
 +
 +
En la función ShowAcceleration, justo después de la sección "Muestra la Aceleración", se agregaron varios ''if'', con ''else if'', cuyas condiciones serán los rangos de valores en los que se encontrará la variable "y.result[samp]" (En hexadecimal son: 0x07D0 para límite inferior y 0x32C8 para límite superior). Cada uno de esos ''if'' se encargará de encender un LED específico y apagar los otros, para que al realizar el movimiento, se pueda observar de forma progresiva el movimiento en "Cascada" de los LEDs. 
 +
 +
<syntaxhighlight lang="c">
 +
 +
void ShowAcceleration (void)
 +
{
 +
  word SampleCNT;
 +
  byte j,k; 
 +
 
 +
  ReadAcceleration();            // Lee los datos de aceleración
 +
  //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--){}
 +
    }
 +
  }
 +
 +
  // Muestra la Aceleración
 +
  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));
 +
 
 +
  if ( (y.result[samp]>=0x32C8) && (y.result[samp]<=0x36B0) ){
 +
  PTCD=0x3E;
 +
  PTED=0xC0;
 +
  }
 +
  else if ( (y.result[samp]>=0x2CEC) && (y.result[samp]<=0x32C8) ){
 +
  PTCD=0x3D;
 +
  PTED=0xC0;
 +
}
 +
  else if ( (y.result[samp]>=0x2328) && (y.result[samp]<=0x2CEC) ){
 +
  PTCD=0x3B;
 +
  PTED=0xC0;
 +
}
 +
  else if ( (y.result[samp]>=0x1F40) && (y.result[samp]<=0x2328) ){
 +
  PTCD=0x27;
 +
  PTED=0xC0;
 +
  }
 +
  else if ( (y.result[samp]>=0x1388) && (y.result[samp]<=0x1F40) ){
 +
    PTCD=0x1F; 
 +
    PTED=0xC0;
 +
  }
 +
  else if ( (y.result[samp]>=0x0CE4) && (y.result[samp]<=0x1388) ){
 +
    PTED=0x80;
 +
    PTCD=0x3F;
 +
  }
 +
  else if ( (y.result[samp]>=0x07D0) && (y.result[samp]<=0x0CE4) ){
 +
    PTED=0x40;
 +
    PTCD=0x3F;
 +
  }
 +
  else{
 +
  PTCD=0x3F;
 +
    PTED=0xC0;
 +
  }
 +
  // 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++;
 +
  }
 +
 
 +
}
 +
 +
 +
</syntaxhighlight>
  
 
== Material de Referencia ==
 
== Material de Referencia ==
  
Código del proyecto:
+
'''Manuales: '''
 +
 
 +
[[Archivo: Manual_MC9S08QE128.pdf ]]
 +
 
 +
[[Archivo: Manual_HCS08.pdf ]]
 +
 
 +
[[Archivo: Manual_MMA7660FC.pdf]]
 +
 
 +
'''Código del proyecto:'''
  
 
[[Archivo:Accelerometer puntoc.pdf]]
 
[[Archivo:Accelerometer puntoc.pdf]]
  
 
[[Archivo:Accelerometer puntoh.pdf]]
 
[[Archivo:Accelerometer puntoh.pdf]]

Revisión actual del 11:25 2 jul 2016

Se presenta a continuación el código, el cuál es un ejemplo propiedad de PEmicro, del Acelerómetro MMA7660SC, implementado en el módulo DEMOQE128.

Originalmente este código se diseñó para ser compilado en la versión de Codewarrior 6.3. Por lo que se hizo el trabajo de mudar el programa y sus directivas a la versión de Codewarrior 10.6.

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:

•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

Tarjeta de desarrollo DEMOQE128

Es un sistema de bajo costo diseñado por PEmicro para la demostración, evaluación y depuramiento del microcontrolador MC9S08QE128 fabricado por Freescale.

La tarjeta de desarrollo DEMOQE128 trae integrado, el microcolontrolador MC9S08QE128, y un acelerómetro digital MMA7660FC de 3 ejes salida digital (I²C).

TarjetaDEMOQE128.jpg

Conexiones

El MC9S08QE128 viene integrado con dos módulos IIC que permiten la comunicación entre el microcontrolador y el acelerómetro digital. El módulo que se utiliza en este caso es IIC2 cuyos pines SDA y SCL están conectados a PTH7 y PTH6 respectivamente.

El acelerómetro posee 3 pines (INT, SDA y SCL) que van conectados al MC9S08QE128 como se muestra a continuación:

Conexion.png

Procedimiento para migrar el programa de Codewarrior 6.3 a la versión 10.6

El primer paso es acceder al ejemplo del Acelerómetro de la versión 6.3; Accediendo a la página de NXP1, seleccionando la opción para descargar Software Package Tool Kit, o descargándolo directamente: Medio:DEMOQE Toolkit.7z y dentro del .zip, se accederá a la ruta: /DEMOQE_Toolkit/DEMOQE128 Software Examples/DEMOQE_Accelerometer_Ex_S08/DEMOQE_Accelerometer_Ex.


Una vez en el ejemplo, se deben ubicar los archivos que son de importancia para el funcionamiento del acelerómetro: "accelerometer.c"; "accelerometer.h" y "Start08.c".

Cuando se tengan estos tres archivos, se creará un nuevo proyecto en la versión de Codewarrior 10.6, con las características habituales 2. En la carpeta que se genera al crear este proyecto, se borrará el archivo main.c,que se genera automáticamente, se sustituirá el archivo "Start08.c" y se agregarán los .c y .h del acelerómetro que se ubicaron en el paso anterior.

ProyectoAmbiente.jpg
Ubicación de los .c y .h correspondientes en el proyecto

Se actualiza el proyecto para que estos nuevos archivos sean reconocidos y no queda más que compilarlo, debuggearlo y seleccionar el botón de "play" para iniciar el programa. Para apreciar el funcionamiento del acelerómetro se hace uso de la herramienta graficadora del Tookit.

____________________________________________________________________________________________________________________________________

1 www.nxp.com/products/sensors/touch-sensors/mcf51qe128-demonstration-board:DEMOQE128?fpsp=1&tab=Design_Tools_Tab

2 Los pasos para crear este proyecto serán: Help -> Welcome -> New Proyect Wizard -> Selecciono nombre del Proyecto -> Selecciono el MC9S08DEMOQE128 -> P&E USB Multilink Universal y P&E Full Chip Simulation -> Lenguaje C -> None -> None -> Finish

Descripción de funciones

Programa principal

A continuación se presenta el código del main, el cuál ejecutará todas las rutinas necesarias para el funcionamiento del acelerómetro. En primer lugar llama a la inicialización de los periféricos, las interrupciones por keyboard y la configuración del módulo IIC. Posteriormente habilita las interrupciones y configura las muestras por segundo con las que trabajará el MMA7660.

Luego se configura el registro TPM haciendo 01 los bits correspondientes a CLKSA:B con el objetivo de seleccionar la fuente del reloj, la cual será igual a la tasa del reloj del bus. SW1 está relacionado a la entrada PTAD2 del módulo. Cuando ésta entrada sea 0, el acelerómetro se debe quedar en un ciclo infinito sin hacer nada.

Se tiene un segundo ciclo infinito, en el cual se ejecutará unas instrucciones siempre que SW4 sea 1. Esta definción está relacionada a PTDD3 e invocará a ShowAcceleration cuando se tengan los datos disponibles, en caso contrario esperará a que se actualice el MMA7660.

Para evitar problemas de inconsistencia de datos, se tienen dos instrucciones que se quedarán esperando si la entrada SDA está marcada como cero o si el IIC no ha detenido su ejecución. Adicionalmente, el programa sólo podrá verificar estas dos instrucciones si se cumple que el bus IIC está libre. Una vez, se cumplan las condiciones para seguir la ejecución del programa, se guardarán los datos de lectura en el mma7660 y el maestro hará la lectura de una cantidad de bits específica.

Por último, si SW3 (Asociado a PTDD2) es 1, el programa se detendrá.

void main(void){

  PeriphInit();
  InitKBI();
  IIC_configuration();
  
  EnableInterrupts;
  
  MMA7660_configuration();
   
// Seleccionar fbus como fuente del timer1 e iniciar el temporizador
  TPM1SC = 0x08;
//  SendMsg("\fX, Y, Z\r\n");
  while (!SW1){}
  for(;;){
    while(SW4){
      if(!(IIC_Rec_Data[0]&IIC_Rec_Data[1]&IIC_Rec_Data[2]&0x40)){
        ShowAcceleration();         // Muestra los datos sobre la aceleración
      }else{                        // Actualización MMA7660 
      }
      
      if (PTHD_PTHD7 == 1) {        //Espera a que el bus del IIC esté libre
       
        while (PTHD_PTHD7 == 0);    // Esperará en un ciclo infinito mientras el pin del PTHD esté bajo
        
       	while (IIC2C1_MST == 1);    // Esperará hasta que el IIC se detenga 
       	
       	//Se lee Xout, Yout, Zout
       	mma7660[0] = 0x98;
       	mma7660[1] = 0x00;
       	Master_Read_MMA7660_register(2,3); 
      
      }else {
      
      }
            
    }
    while(SW3){_Stop;}
  } 
    
}

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

Si el valor del NVICSTRM es distinto de 0, ajustaré el registro ICS Trim para modificar la frecuencia del reloj de referencia interno proporcionalmente de acuerdo al valor que tenga cargado NVICSTRM.

Se habilitará el bit correspondiente a ICSC1IREFS, para utilizar el reloj interno de referencia. Además, con este rutina de inicialización los bits correspondientes a ICSC2EREFS e ICSC2ERCLKEN se habilitan, donde el primero permite seleccionar la fuente del reloj externa, solicitando al oscilador. El segundo, nos permite seleccionar la referencia externa del reloj que será usado como ICSERCLK Por último, se habilitan los bits correspondientes a ICSSCLKST con los cuales seleccionamos el reloj externo de referencia con el modo FLL desviado.

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


void ICS_FEI(void) {
 if (NVICSTRM != 0xFF) 
  ICSTRM = NVICSTRM;              // Ajusta el valor del TRIM si la ubicación de NV no está en vacío.
 else
  ICSTRM = 0xAD;                  //Se ajusta para controlar la frecuencia del reloj de referencia interna y controla el periodo de referencia del reloj.
  ICSC1 = ICSC1_FEI;             // Se coloca el bit 2 del ICS1 en 1 (el de IREFS), para utilizar el Reloj Interno de referencia.
  ICSC2 = ICSC2_FEI;             // Se colocan los bits 1 y 2 del ICS2 en 1 (ERCLKEN y EREFS, respectivamente), ERCKLEN habilitará la fuente del reloj externa para ser usada como ICSERCLK y el segundo para seleccionar el Oscilador como reloj de referencia externa
  ICSSC = ICSSC_FEI;             // Se coloca el campo CLKSET en 10, para seleccionar el reloj externo de referencia con FLL desviado.
 while (ICSC1_CLKS != ICSSC_CLKST) {} // Espera si que la frecuencia del Bus (ICSC1_CLKS) sea distinta al modo del reloj (ICSSC_CLKSET)  
}

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

Se deben configurar los bits correspondientes al registro KBI1PE para habilitar las interrupciones de los pines que son necesarios para el programa.En este caso se habilitan los bits correspondientes a KBI1P2 y KBI1P3 con el objetivo de habilitar las interrupciones de los pines PTA2 y PTA3 de la tarjeta de desarrollo. Por otro lado, se habilita el bit 1 correspondiente al registro KBI1SC con el objetivo de habilitar las solicitudes de interrupciones. Además se procede a habilitar el bit 2 como mecanismo de limpieza de la bandera asociada al registro.

#Define KBI_SW KBI1PE_KBIPE2_MASK | KBI1PE_KBIPE3_MASK


void InitKBI(void) {
// Habilitado KBI1P[3:2] como interrupciones
  KBI1PE = KBI_SW;
  KBI1SC = 0b00000110;
/*               ||||
                 |||+---- KBIMOD = Detección de modo KBI: 0= solo borde 
                 ||+----- KBIE   = Habilitar KBI: 1=enabled
                 |+------ KBACK  = Se Habilita para limpiar la bandera
                 +------- KBF    = Está relacionado a la bandera del KBI. En 0 No se detectó interrupción por KBI.
*/
}

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
  // El trimming del fBUS es configurado 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; // Se selecciona la sensibilidad de 1.5g
  PTGDD = 0x06; // Se selecciona como salida el puerto G 
    
  // Timer2 overflow about every 1ms
  TPM2MOD = 25000; // Es un 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


Rutina de inicialización

Se inicializa el bit correspondiente a IIC2CI-IICEN para habilitar el modulo de IIC y además se inicializa el bit correspondiente a IIC2CI-IICIE con el objetivo de habilitar las solicitudes de interrupción. Además, se configura el IIC2F-MULT con los bits 10 con el objetivo de establecer la tasa de baudio del IIC, colocando el MULT en un valor de 04.

void IIC_configuration (void) {
 
  IIC2F = 0x90;         //Se asigna un valor de 04 que corresponde a un factor multiplicador para establecer la tasa de baudio del IIC.
  IIC2C1  = 0xC0;       //Los primeros 4 bits (1100) sirven para habilitar el modulo IIC y para habilitar las interrupciones
}

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:

Se utiliza para habiliar la transmisión o la recepción.

SCIxC2: SCI Control Register 2

SCIxC2.png

Se utiliza para saber si se han enviado o leído datos desde el módulo.

SCIxS1: SCI Status Register 1

SCIxS1.png


Se utiliza para la recepción y transmisión de datos.

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; // Se limpia el buffer
  SCI1C2_RE = 1;    //Se habilita la transmisión
  while(!SCI1S1_RDRF){  };// Se espera hasta que el buffer no esté vacío
  rec_char = SCI1D; // Se obtiene el caracter enviado
  SendChar((char) rec_char);// Se reenvía el caracter
  return (char) SCI1D;
}

Enviar caracter

void SendChar(char s_char) {

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

Enviar mensaje

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

  SCI1C2 = 0x08;    //Se habilita 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; // Se escribe el caracter en el registro de datos
   nxt_char = msg[i++]; //Se lee el siguiente caracter del arreglo
  }
}

Conversión de datos

Las funciones definidas a continuación tienen el objetivo de hacer la conversión de los datos para el funcionamiento correcto del programa.

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

Está función transforma los valores enviados desde le acelerómetro en un entero con signo.

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

  for(i=0;i<3;i++){ //Se realiza una extensión de signo para cada valor valor almacenado en la estructura
    temp = IIC_Rec_Data[i] & 0x3F;  //Se hace una máscara para guardar los 6bits menos significativos
    if(IIC_Rec_Data[i] & 0x20){ //Si el valor es negativo
       temp |= 0xFFC0;                                
       temp += 32;
       IIC_Converted_Data[i] = temp;
    }else{   
      IIC_Converted_Data[i] = temp + 32;
    }
  }
}

Show Acceleration

Esta función se encarga de definir el modo en el que el acelerómetro trabajará (Para filtrar data, promediar data o copiar la data) que envía el acelerómetro a través del protocolo IIC . (Es asignado de acuerdo a la variable IIC_Converted_Data).

Posteriomente, el maestro envía por el puerto serial el mensaje con las coordenadas de la posición leída mediante el uso de la función SendMsg() (carácter por carácter leído) y, ya que se está trabajando con buffers de espacio limitado por un 'max', en caso de que se llenen porque han leído muchos datos, estos son re-escritos.

Nota importante: Las asignaciones que se le hacen al registro ADCSC1 (Módulo Conversor Analógico Digital) no son necesarias para el funcionamiento de este programa. Se infiere que los responsables de realizar la migración del Acelerómetro Analógico al Digital, no tomaron en cuenta estas declaraciones en esta función y olvidaron borrarlas. Por lo que se pueden comentar o eliminar y el código funcionará a la perfección.

void ShowAcceleration (void)
{
  word SampleCNT;
  byte j,k;   
  
  ReadAcceleration();   // Se leen los datos de la aceleración detectados por el acelerómetro        
  //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){ // Se escoge el modo de funcionamiento del programa
    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--){}
    }
  }

  // Utilizado para enviar los caracteres donde se mostrará la acelración
  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));
  
  //Se desplaza el arreglo de los resultados si llega al máximo
  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++;
  } 
  
}

Configuración del MMA7660FC

Para poder usar el acelerómetro se debe cambiar a modo activo, para lograr esto se debe modificar el registro MODO cuya dirección es 0x07 en el mapa de memoria del MMA7660FC. Escribir en este registro resetea el sleep timing y limpia los registros XOUT, YOUT, ZOUT y TILT.


Registro Modo.png


Se utiliza la configuracion por defecto (120 muestras/segundo) y al cambiar solo el bit 0 (MODE) se entra en modo activo.

void MMA7660_configuration(void){
  
  mma7660[0] = 0x98; // Dirección de escritura maestra
  mma7660[1] = 0x07; // Se registra MODE del acelerómetro
  mma7660[2] = 0x01; // Se envía este valor para cambiar a 1 el bit MODE del registro para cambiar el acelerómetro a modo activo
  Master_Write_MMA7660_register(3);
  
}

Funciones del Maestro

Master_Read_and_Store

Se define esta función, realizada por el maestro, que tiene el objetivo de tomar el contenido del registro de datos del IIC (IIC2D) y almacenarlo en la variable IIC_Rec_Data[] para su posterior uso.

void Master_Read_and_Store(void) {
  IIC_Rec_Data[rec_count++] = IIC2D;   /*Se guarda el valor enviado por el acelerómetro*/
}

Master_Write_MMA7660_register

En esta función el Maestro inicializa al esclavo (acelerómetro) y le transfiere ciertos bytes. Para esto, habilita el bit de la transferencia correspondiente al módulo de IIC (IIC2CI_TX) y además habilita el bit para generar el estado de Start en el protocolo de IIC (IIC2C1_MST) para comunicarse con un esclavo indicando el envío de datos. Posterior a esto envía el primer byte. Si el byte a transferir que se recibe como dato es cero entonces no se ejecuta la explicación anterior.

void Master_Write_MMA7660_register(byte transbytes) {
  last_byte = 0;                    // Inicialización de variables en 0.
 count = 0;
  bytes_to_trans = transbytes;       
  
  if (transbytes == 0) return;    /*Si no hay ningún byte para transferir */
  
  IIC2C1_TX = 1;                    // Se setea el bit para hacer la transmisión 
  IIC2C1_MST = 1;                   // Se genera la señal de inicio (START)
  
  IIC2D = mma7660[count++];         // Se envia el primer byte. Puede ser una dirección de 7 bits mas 1 bit de W/R.    
}

Master_Read_MMA7660_register

Esta función permite que se activen el bit de transferencia y el bit para generar la señal de inicio(START) y así poder comunicarse con el esclavo. Adicionalmente el bit correspondiente a IIC2CI_TXAK se le asigna el valor de cero, con el objetivo de que se envíe una señal de recibido en el bus luego de haber recibido un byte de data.

void Master_Read_MMA7660_register(byte transbytes, byte recbytes) {

  rec_count = 0;                    // Inicialización de variables en 0
  last_byte = 0;                    
 count = 0;
 repeat_start_sent = 0;
 
  bytes_to_trans = transbytes;      
  num_to_rec = recbytes;
  
  
  if (transbytes == 0) return;   //Si no hay byte para transferir 
    
  IIC2C1_TXAK = 0;                  //Se coloca en 0 el bit de Acknowledge
  IIC2C1_TX = 1;                    // Se coloca el bit  en 1 para seleccionar que se va a transferir data
  IIC2C1_MST = 1;                   // Se genera la señal de START.
  
  reading_mma7660_reg = 1;
  IIC2D = mma7660[count++];         // Se envía el primer byte, puede ser una direccion de 7 bits mas 1 bit de w/r    
}

Configuración del TMP

La configuración detallada se puede encontrar en:

Artículo : Código_Acelerometro_Rutinas_de_Interrupción

Rutinas de Interrupción

Para información más detallada acceder: Artículo: Código_Acelerometro_Rutinas_de_Interrupción

''interrupt VectorNumber_Vkeyboard ''

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

void KBI_ISR(void){
  byte d,b;
  
  //Identifica cual PIN fue presionado
  mode = (byte)(KBI_VAL);
  //Cuando se suelta el botón:
  for (d=0xff;d>0;d--){
    for (b=0x80;b>0;b--){}
  }
  //Limpia el Registro KBF
  KBI1SC_KBACK = 1;
}

IIC_ISR

''interrupt VectorNumber_Viicx'' 


interrupt VectorNumber_Viicx void IIC_ISR(void) {

     IIC2S_IICIF = 1;              // Limpiar la bandera de interrupcion

     if (IIC2C1_TX) {              // Si está habilitado el bit para la transmisión

///////////////////// Transmit ////////////////////////////

       if (repeat_start_sent) {

         IIC2C1_TX = 0;                //Se coloca para recibir

         if (num_to_rec == 1)

         IIC2C1_TXAK = 1;            // Se habilita el bit para recibir el acknowledge

         IIC2D;                       

       }

       else if ((last_byte) & (reading_mma7660_reg)) {

         IIC2C1_RSTA = 1;              //Se habilita para repetir el START

         IIC2D = (mma7660[0] | 0x01);  //Setea el bit de read

         repeat_start_sent = 1;

       }

       else if (last_byte) {           // Si es el ultimo byte

         IIC2C1_MST = 0;               // Genera un STOP y el modo de operacion cambia de maestro a esclavo

       }

       else if (last_byte != 1) {            //Si no es el ultimo byte

          if (IIC2S_RXAK) { //Para verificar si se recibio o no un acknowledge. Entra en el if si no se recibió el acknowledge

               IIC2C1_MST = 0;             // Genera un STOP y el modo de operacion cambia de maestro a esclavo

               }

           else if (!IIC2S_RXAK) {      //Para verificar si se recibio o no un acknowledge.Entra en el if si se recibió el acknowledge

              IIC2D = mma7660[count++];     //Se transfiere la data

              if (count == bytes_to_trans)

             last_byte = 1;

            }

       }

     } else {

///////////////////// Receive ////////////////////////////

         if ((num_to_rec - rec_count) == 2) {

           IIC2C1_TXAK = 1;          // Se habilita para recibir acknowledge

           Master_Read_and_Store();

         }

         else if ((num_to_rec - rec_count) == 1) {

          IIC2C1_MST = 0;           // Se envia una señal de STOP y el modo de operacion ahora es del esclavo

          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) { // Se copia 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];
}

Modificación del código: Identificar la posición de la Tarjeta de Desarrollo con LEDs

Se realizaron unas pequeñas modificaciones al código presentado anteriormente para que, además de mostrar la aceleración de la tarjeta mediante la herramienta graficadora del Toolkit, se mostrara la inclinación en el eje "Y" haciendo uso de sus LEDs. Estos se encenderán en forma de "cascada" si la tarjeta se inclina en una u otra dirección.

  • Prueba de funcionamiento:

Agregarle esta nueva característica al proyecto sólo necesitó modificar dos funciones del archivo acelerómetro.c, que se explicarán a continuación:

PeriphInit

En esta función únicamente se agregó la habilitación de los periféricos relacionados a los LEDs (PTCDD y PTEDD) y posteriormente su inicialización como apagados por defecto.


void PeriphInit(void)
{  
  // Desabilita las instrucciones de COP y habilita las instrucciones de STOP, RESET y BKGD
  SOPT1 = 0x23;
  
  // Selecciona el modo FEI
  // El trimming del fBUS es configurado 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; // Se selecciona la sensibilidad de 1.5g
  PTGDD = 0x06; // Se selecciona como salida el puerto G 

  // Habilitación de los LEDs
  PTCDD = 0x3F; 
  PTEDD = 0xC0;
  
  // LEDs apagados por defecto
  PTCD = 0x3F; 
  PTED = 0xC0;
    
  // Timer2 overflow about every 1ms
  TPM2MOD = 25000; // Es un 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); 
}

ShowAcceleration

Utilizando como ayuda la herramienta graficadora del Toolkit se visualizaron los rangos de valores que alcanzaba la posición "Y" cuando se movía la Tarjeta de Desarrollo progresivamente en ese eje específico. Luego se estableció un criterio para definir entre que rangos de valores encendería (y apagaría) cada LED (PTC0, PTC1, PTC2, PTC3, PTC4, PTC5, PTE6 y PTE7) y posteriormente estos valores se convirtieron hexadecimal para poder ser manipulados en el código.

En la función ShowAcceleration, justo después de la sección "Muestra la Aceleración", se agregaron varios if, con else if, cuyas condiciones serán los rangos de valores en los que se encontrará la variable "y.result[samp]" (En hexadecimal son: 0x07D0 para límite inferior y 0x32C8 para límite superior). Cada uno de esos if se encargará de encender un LED específico y apagar los otros, para que al realizar el movimiento, se pueda observar de forma progresiva el movimiento en "Cascada" de los LEDs.

void ShowAcceleration (void)
{
  word SampleCNT;
  byte j,k;   
  
  ReadAcceleration();            // Lee los datos de aceleración
  //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--){}
    }
  }

  // Muestra la Aceleración
  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));
  
  if ( (y.result[samp]>=0x32C8) && (y.result[samp]<=0x36B0) ){
	  PTCD=0x3E;
	  PTED=0xC0;
  }
  else if ( (y.result[samp]>=0x2CEC) && (y.result[samp]<=0x32C8) ){
	  PTCD=0x3D;
	  PTED=0xC0;
 }
  else if ( (y.result[samp]>=0x2328) && (y.result[samp]<=0x2CEC) ){
	  PTCD=0x3B;
	  PTED=0xC0;
 }
  else if ( (y.result[samp]>=0x1F40) && (y.result[samp]<=0x2328) ){
	  PTCD=0x27;
	  PTED=0xC0;
  }
  else if ( (y.result[samp]>=0x1388) && (y.result[samp]<=0x1F40) ){
  	  PTCD=0x1F;  
  	  PTED=0xC0;
  }
  else if ( (y.result[samp]>=0x0CE4) && (y.result[samp]<=0x1388) ){
  	  PTED=0x80;
  	  PTCD=0x3F;
  }
  else if ( (y.result[samp]>=0x07D0) && (y.result[samp]<=0x0CE4) ){
  	  PTED=0x40;
  	  PTCD=0x3F;
  }
  else{
	  PTCD=0x3F;
  	  PTED=0xC0;
  }
  // 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++;
  } 
  
}

Material de Referencia

Manuales:

Archivo:Manual MC9S08QE128.pdf

Archivo:Manual HCS08.pdf

Archivo:Manual MMA7660FC.pdf

Código del proyecto:

Archivo:Accelerometer puntoc.pdf

Archivo:Accelerometer puntoh.pdf