MCP3208 y PIC18f4550

Hola a todos. Escribo porque no he podido realizar la lectura de un ADC 3208 de MIcrochip el cual cuenta con 12 bits de resolución en un microcontrolador PIC18f4550. Programo en CCS , y estoy usando el driver que viene en CCS (MCP3208.C) pero no lo he logrado.
El cristal que uso es de 20MHZ y un pll5 ya que lo necesito para enviarlo por USB bulk-transfer. Lo de la comunicacion USB me funciona bien y por eso no la pongo en este esquematico.
El resultado de la lectura lo visualizo en 16 leds.
el codigo del pic18f es el siguiente
Código:
#include <test-3208.h>

long int lectura;


void main()
{
adc_init();
output_high(di5);
delay_ms(10);
for(;;){
   lectura = read_analog_mcp( 0 , 1 );
   leds(lectura);
   output_toggle(di5);
   delay_ms(500);
}//fin ciclo infinito

}//fin main
test-3208.h
Código:
#include <18F4550.h>
#include "MCP3208.C"
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN,MCLR,NOPBADEN
#use delay(clock=48000000)

void leds (long int conversion){
if (bit_test(conversion,15))  output_high(IN_D);
        else                 output_low (IN_D); 
if (bit_test(conversion,14))  output_high(PIN_C7);
        else                 output_low (PIN_C7); 
if (bit_test(conversion,13))  output_high(PIN_C6);
        else                 output_low (PIN_C6); 
if (bit_test(conversion,12))  output_high(PIN_C5);
        else                 output_low (PIN_C5);
if (bit_test(conversion,11))  output_high(PIN_C4);
        else                 output_low (PIN_C4); 
if (bit_test(conversion,10))  output_high(di4);
        else                 output_low (di4);
if (bit_test(conversion,9))  output_high(di3);
        else                 output_low (di3); 
if (bit_test(conversion,8))  output_high(PIN_C0);
        else                 output_low (PIN_C0);
if (bit_test(conversion,7))  output_high(PIN_D7);
        else                 output_low (PIN_D7); 
if (bit_test(conversion,6))  output_high(PIN_D6);
        else                 output_low (PIN_D6); 
if (bit_test(conversion,5))  output_high(PIN_D5);
        else                 output_low (PIN_D5); 
if (bit_test(conversion,4))  output_high(PIN_D4);
        else                 output_low (PIN_D4);
if (bit_test(conversion,3))  output_high(PIN_D3);
        else                 output_low (PIN_D3); 
if (bit_test(conversion,2))  output_high(PIN_D2);
        else                 output_low (PIN_D2);
if (bit_test(conversion,1))  output_high(PIN_D1);
        else                 output_low (PIN_D1); 
if (bit_test(conversion,0))  output_high(PIN_D0);
        else                 output_low (PIN_D0);}
configuracion de pines 18F4550.h, lo modifico para un proyecto que estoy haciendo
Código:
#device PIC18F4550
#nolist
#define di5     31744   // A0 entrada digital 5
#define di6     31745   // A1 entrada digital 6
#define di7     31746   // A2 entrada digital 7
#define di8     31747   // A3 entrada digital 8
#define PIN_A4  31748
#define CS      31749   // A5 CS
#define PIN_A6  31750

#define PIN_B0  31752
#define PIN_B1  31753
#define di1     31754   // B2 entrada digital 1
#define di2     31755   // B3 entrada digital 2
#define PIN_B4  31756
#define PIN_B5  31757
#define PIN_B6  31758
#define PIN_B7  31759

#define PIN_C0  31760
#define di3     31761   // C1 entrada digital 3
#define di4     31762   // C2 entrada digital 4
#define PIN_C4  31764
#define PIN_C5  31765
#define PIN_C6  31766
#define PIN_C7  31767

#define PIN_D0  31768
#define PIN_D1  31769
#define PIN_D2  31770
#define PIN_D3  31771
#define PIN_D4  31772
#define PIN_D5  31773
#define PIN_D6  31774
#define PIN_D7  31775

#define IN_D    31776   // E0 INDICADOR ENTRADAS DIGITALES HABILITADAS 
#define PWR     31777   // E1 INDICADOR ENCENDIDO MICROCONTROLADOR
#define USB     31778   // E2 INDICADOR USB CONECTADO
#define PIN_E3  31779
#define PIN_E7  31783
y la libreria MCP3208.c
Código:
#use delay(clock=48000000)

#ifndef MCP3208_CS

#define MCP3208_CLK  PIN_B0
#define MCP3208_DOUT PIN_B1
#define MCP3208_DIN  di1
#define MCP3208_CS   di2

#endif


void adc_init() {
   output_high(MCP3208_CS);
}


void write_adc_byte(BYTE data_byte, BYTE number_of_bits) {
   BYTE i;

   delay_us(2);
   for(i=0; i<number_of_bits; ++i) {
      output_low(MCP3208_CLK);
      if((data_byte & 1)==0)
         output_low(MCP3208_DIN);
      else
         output_high(MCP3208_DIN);
      data_byte=data_byte>>1;
      delay_us(50);
      output_high(MCP3208_CLK);
      delay_us(50);
   }
}


BYTE read_adc_byte(BYTE number_of_bits) {
   BYTE i,data;

   data=0;
   for(i=0;i<number_of_bits;++i) {
      output_low(MCP3208_CLK);
      delay_us(50);
      shift_left(&data,1,input(MCP3208_DOUT));
      output_high(MCP3208_CLK);
      delay_us(50);
   }
   return(data);
}


long int read_analog_mcp(BYTE channel, BYTE mode) {
   int l;
   long int h;
   BYTE ctrl_bits;

   delay_us(200);

   if(mode!=0)
      mode=1;

   output_low(MCP3208_CLK);
   output_high(MCP3208_DIN);
   output_low(MCP3208_CS);

   if(channel==1)               // Change so MSB of channel #
      ctrl_bits=4;            //      is in LSB place
   else if(channel==3)
      ctrl_bits=6;
   else if(channel==4)
      ctrl_bits=1;
   else if(channel==6)
      ctrl_bits=3;
   else
      ctrl_bits=channel;

   ctrl_bits=ctrl_bits<<1;

   if(mode==1)                  // In single mode
      ctrl_bits |= 1;
   else                        // In differential mode
      ctrl_bits &= 0xfe;

   ctrl_bits=ctrl_bits<<1;      // Shift so LSB is start bit
   ctrl_bits |= 1;

   write_adc_byte( ctrl_bits, 7);   // Send the control bits

   h=read_adc_byte(8);
   l=read_adc_byte(4)<<4;

   output_high(MCP3208_CS);

   return((h<<8)|l);
}


long int read_analog( BYTE channel )   // Auto specifies single mode
{
   return read_analog_mcp( channel, 1);
}


void convert_to_volts( long int data, char volts[6]) {
   BYTE i, d, div_h, div_l;
   long int temp,div;

   div=0x3330;

   for(i=0;i<=4;i++) {
     temp=data/div;
     volts[i]=(BYTE)temp+'0';
     if(i==0) {
       volts[1]='.';
       i++;
     }
     temp=div*(BYTE)temp;
     data=data-temp;
     div=div/10;
   }
   volts[i]='\0';
}
y el circuito esquematico es el que anexo. Muchas gracias de antemano a las personas que me puedan colaborar.:)
 

Adjuntos

  • esquematico1.png
    esquematico1.png
    27 KB · Visitas: 18
  • esquematico2.png
    esquematico2.png
    22.2 KB · Visitas: 13
  • test-3208.rar
    60.1 KB · Visitas: 10
Buenas tardes a todos, quisiera comentar que ya logre solucionar el problema, ya puedo leer el MCP3208 pero ahora tengo un error de escala, ya que al ponerle el voltaje de entrada igual al voltaje de referencia la salida no me da todos en 1 (4095 ya que es de 12 bits), me da como resultado 3839 (0b 1110 1111 1111 o en hexa 0x0EFF), es como si estuviera perdiendo un bit de resolución. Por favor cualquier ayuda se las agradecería.
 
Hola hace ya tiempo use el mcp3208 con un ATmega644 y me funciono bien, la funcion es la siguiente:
solo cambia las funciones spi_slave_rx y spi_master_tx por las que manejan el spi de tu micro

unsigned int mcp3208_read(char canal)
{
volatile unsigned char low, high;

PORTB &= ~(1<<4); //cero(reg ,bit); SS pin

spi_master_tx(((canal>>2)|0x06)&0x07);
high = spi_slave_rx();
spi_master_tx((canal<<6)&0xC0);
high = spi_slave_rx();
spi_master_tx(0x00);
low = spi_slave_rx();

PORTB |= (1<<4); //uno(reg ,bit); SS pin
high &= 0x0F;
return ((unsigned int)(high << 8)) | ((unsigned int)(low));
}

por cualquier cosa, una simulacion en proteus y codigo en avr studio 4
 

Adjuntos

  • spi_lcd.rar
    146.7 KB · Visitas: 17
Última edición:
Gracias hamster por tu respuesta, pero quisiera preguntarte por esta parte del codigo
Código:
unsigned int mcp3208_read(char canal)
{
volatile unsigned char low, high;

PORTB &= ~(1<<4);	//cero(reg ,bit); SS pin

spi_master_tx(((canal>>2)|0x06)&0x07); 
high = spi_slave_rx();
spi_master_tx((canal<<6)&0xC0);
high = spi_slave_rx();
spi_master_tx(0x00);
low = spi_slave_rx();

PORTB |= (1<<4);	//uno(reg ,bit); SS pin
high &= 0x0F;
return ((unsigned int)(high << 8)) | ((unsigned int)(low));
}
no entiendo como configuras para la lectura, porque hay 2 lecturas del byte alto...
por mi parte tengo el siguiente codigo....podrias decirme donde esta mi error porfavor
Código:
output_low(CS);
   delay_us(1);
   spi_xfer    (AdcSpi, 0x06 , 8);
   H = spi_xfer(AdcSpi, 0xC0, 8);
   L = spi_xfer(AdcSpi,  0x00,       8);
   delay_us(1);
   output_high(CS);
   H1 =(H&0x0F);
           
   
   lectura= make16(H1,L);
Este es para leer el canal 3, pero el problema es con la escala, ya que no me entrega la escala maxima....
De antemano muchas gracias por tu respuesta.
 
Si te fijas en la hoja de datos del mcp3208 en las figuras 6.1 y 6.2 ... veras que tienes que transmitirle al mcp3208 tres mensajes de 8 bits y recibes 3 mensajes de 8 bits. en el código que he puesto arriba ...
la primera vez que recibo dato lo guardo en high dado que la funcion spi de lectura necesita alguna variable en donde guardar la lectura. la primera vez que leo el mcp lo guardo en high(este primer dato es ??????????? es decir no importa) el segundo byte a leer este si es mi parte alta (los tres bits superiores no importan)... por lo tanto hay que leerlo y guardarlo en high y el ultimo se guarda en low .....
revisa las figuras 6.1 y 6.2 y lo entenderas...

y con tu código, realmente no se que hace la función spi_xfer, tendria que conocerla para poderte ayudar.
 
Última edición:
claro, yo te entiendo lo de los 3 datos a enviar para configurar el adc, lo que no entiendo es algunas operaciones en especifico:

PORTB &= ~(1<<4); //cero(reg ,bit); SS pin habilitar el spi Chip select

spi_master_tx(((canal>>2)|0x06)&0x07); esta operacion no la entiendo, se que envias un dato por el spi pero no se cual
high = spi_slave_rx(); leer el adc
spi_master_tx((canal<<6)&0xC0);esta operacion no la entiendo, se que envias un dato por el spi pero no se cual
high = spi_slave_rx();leer el adc byte alto
spi_master_tx(0x00);
low = spi_slave_rx();leer el adc byte bajo

PORTB |= (1<<4); //uno(reg ,bit); SS pin Deshabilitar el spi Chip select
high &= 0x0F; eliminar el nibble alto del byte alto
return ((unsigned int)(high << 8)) | ((unsigned int)(low));retorna la lectura de 12 bits

en mi código solo le envió en el primer dato el x,x,x,x,x,Start, Single/diff, d2 y leo el byte alto
el segundo dato envio d1,d0,x,x,x,x,x,x y leo el byte bajo, y despues si elimino el nibble alto del byte alto.... Pero asi de todas formas me ocurre del error bastante alto de conversion. No se que mas pueda hacer.
Muchas gracias(y)
 
mirando la siguiente imagen:
Screenshotcopy.png


//para decirle al mcp3208 que canal hay que leer debes enviarle un start bit, sgl,d2,d1,d0 ver tabla

unsigned int mcp3208_read(char canal)
{
volatile unsigned char low, high;

PORTB &= ~(1<<4); //chip select cero enable

spi_master_tx(((canal>>2)|0x06)&0x07); //esta es la parte A en el gráfico, si te fijas tienes que enviar un 1 de inicio y luego el sgl,d2 ..... mi función mcp3208_read recibe un numero char que es canal a leer.. el mcp tiene 8 canales el cual se representa mediante un número de 3 bits d2,d1,d0 ..... en esta parte solo transmito el bit de inicio , sgl y d2 --> en las primeras tres posiciones el resto son ceros
( ( (canal>>2) | 0x06) & 0x07 ) -- compruébalo ***

high = spi_slave_rx(); // con esto lees el primer byte ver A en la gráfica ..esto correspondería a ?????????? (desconocido y no importa, puedes guardarlo donde sea)

spi_master_tx((canal<<6)&0xC0); // .... en esta parte solo transmito los bits d1, d0 en llas posiciones 7 y 6 del byte a transmitir--> el resto de bit no importan yo los pongo a cero
(canal<<6) & 0xC0 -- compruébalo ***

high = spi_slave_rx(); //leo el dato parte B

spi_master_tx(0x00); //parte C transmito algo que no importa
low = spi_slave_rx();

PORTB |= (1<<4); //chip select uno disable
high &= 0x0F;
return ((unsigned int)(high << 8)) | ((unsigned int)(low)); //uno los dos byte
}

en las partes con los *** hace la prueba introduciendo un canal de entre 0 y 7 a ver que te queda (lo que se transmite) y veras que es simplemente para ubicar los bits necesarios para seleccionar el canal.
 
Última edición:
Atrás
Arriba