11/29/2016

Sensores I2C con PIC12LF1840

Saludos,

después de varios días debatiéndome en que nuevo post publicar. Entre otras cosas tengo novedades que comentar, partidas que publicar y alguna que otra sorpresa.  Hoy voy a volver a trabajar con Pics.
Exactamente con el PIC12F1840, es un chip pequeño de 8 pines que tiene una particularidad, tiene I2C y es una buena oportunidad de emplear este chip para hacer sensores y actuadores “inteligentes” y poderlos colgar en el mismo bus.




En la imagen podemos ver la típica configuración de un bus i2c, el maestro es el que controla la comunicación, pregunta específicamente a cada esclavo y estos responden si es menester. Si has trabajado con Arduino los sensores i2c no te serán extraños, empleando la librería Wire(), tienes ejemplos para conectar sensores. Yo recomiendo emplear la librería I2C.h  es más eficiente y más sencilla de usar, o a mí me lo parece.


Utilizando el PIC12f1840 con el programa CCS compiler hay otros muchos editores como el MPLAB,  pero el CCS tiene una forma de iniciar los proyectos que te facilita mucho la vida a la hora de iniciar un proyecto con el PicWizard.


El primer ejemplo que voy a probar es el programa blink, controlar el encendido y apagado de un Led. Es algo totalmente innecesario para controlar un led no necesitamos utilizar i2c pero es un proyecto sencillo que nos permite probar el sistema.


El main.h tiene la siguiente configuración:

#include <12F1840.h>
#device adc=16

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES WDT_SW                   //No Watch Dog Timer, enabled in Software
#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O

#use delay(int=16000000)

#use i2c(Slave,Fast,sda=PIN_A2,scl=PIN_A1,address=0x50,force_hw, stream=I2CS)
#define DELAY 100

#use FIXED_IO( A_outputs=PIN_A4 )
int rcv_buf[0x10];
int cmd=0xFF;

la salida PIN_A4 es donde conectaremos el led
La dirección i2c de este proyecto será 0x50.
0X50 = 01010000b



7
6
5
4
3
2
1
0

A6
A5
A4
A3
A2
A1
A0
R/W
ACK
0
1
0
1
0
0
0
0
0
2
8



El bit de menor peso se emplea para indicar si la comunicación es de lectura o escritura. El ACK es el bit de reconocimiento que  envía el receptor 0 indica ACK, 1 indica NO ACK y 2 indica colisión.

Si habéis trabajado con sensores i2c, todos tienen registros donde podemos leer/escribir o solo leer o solo escribir. Cuando empecé a realizar este proyecto tenía en mente situar una variable en una dirección de memoria donde podría escribir y modificar el estado del led. Pues bien es más simple que esto.


#int_SSP
void  SSP_isr(void)
{
 int state, incoming;
 state = i2c_isr_state(I2CS);
    if(state < 0x80) //Maestro enviando.
    {
        incoming = i2c_read(I2CS);
        if (state == 1)  { cmd = incoming; }
        else if (state > 1) { rcv_buf[state-2]=incoming;}
    }
    else if (state>=0x80)   {  i2c_write(I2CS,input(PIN_A4)); } // Master leyendo.  
}



Ahí tenéis la interrupción según el valor de state, sabremos si hemos recibido una instrucción de lectura o de escritura. Si es menor de 128 (0x80) el máster quiere escribir en memoria, y lo guardaremos en el buffer de memoria que hemos definido, diferenciamos el primer byte como cmd, pues podemos emplearlo para diferenciar programas. Si no se quiere diferenciar, se podría hacer de la siguiente forma:


if(state < 0x80)    { rcv_buf[state-1]=i2c_read(I2CS); }



El programa principal seria.




void main()
{
   enable_interrupts(INT_SSP);  //Habilita la interrupcion de entrada i2c
   enable_interrupts(GLOBAL);
   setup_oscillator(OSC_16MHZ|OSC_TIMER1|OSC_PLL_OFF,0);

   //Example blinking LED program
   while(true)
   {
      if(rcv_buf[0]==0x01)
                       {
         output_high(PIN_A4);
      }else
     {
        output_low(PIN_A4);
      }
   }
}


Si el segundo byte que enviamos es un 1 el led se encenderá, si enviamos otra cosa se apagará. Con los pics siempre me ha funcionado una prueba antes de realizar el circuito y grabar el pic, simularlo en proteus para ello creamos un maestro con el mismo tipo de pic y le añadimos dos pulsadores.






Los pulsadores se emplean para enviar la secuencia de encendido y de apagado, no me he matado mucho a hacer programa del master. Las resistencias R2 y R3 son para poner a nivel alto el bus SDA y SCL.




                #include <main.h>
                #include "iic.c"
                int1 ctrl=false;
void main()
{
   setup_oscillator(OSC_16MHZ|OSC_TIMER1|OSC_PLL_OFF,0);
   while(TRUE)
   {
     if(input(PIN_A0))
     {
        if(!ctrl){  iic_write(0x50,0x00,0x1); }
        ctrl=true;
     }
     if(input(PIN_A3))
     {
        if(ctrl){ iic_write(0x50,0x00,0x00);}
        ctrl=false;
      }
    }
 }



Empleo una librería casera para enviar el i2c, porque con la que viene en le compilar CCS no envía el ACK y la comunicación no se completa. Al finalizar este articulo os dejare los enlaces de los programas.






Para hacer la prueba en realidad he empleado como Master a una arduino  el programa  envía la orden de encendido cada 500 ms y la orden de apagado cada 500ms.


Programa de  master arduino.
Programa  blink pic.
Programa master Pic.

El Pic12F1840 funciona tanto a 3,3V como a 5.0V, esto nos permite que se adapte a cualquier sistema i2c.

Como ya os dije el blink es un proyecto de prueba, ahora toca desarrollar nuevos sensores actuadores que nos permitan liberar carga al master y potencien nuestros proyectos. Por ejemplo, un Driver para motores I2C.





A la izquierda la placa con L272M para controlar dos motores, y a la derecha la misma placa con el PIC12F1840 para controlarlo por i2c.




Aquí os muestro la placa de motores y la led conectadas por i2c al arduino que hace de master.  El led con la dirección  0x50 alimentado a 5V y el driver con dirección  0x48 alimentado a 3,3V.

Esto tiene muchas posibilidades.


Anakleto