Soy nuevo en microcontroladores: estoy tratando de leer valores de ADC externos de un ADC AD7798 usando comunicación SPI.
Inicialmente, tengo que configurar algunos registros ADC; algunos registros no están configurados. Para configurar los registros, tengo que usar el registro de comunicación para seleccionar qué registro quiero configurar.
Por ejemplo, quiero configurar el registro de configuración AD7798 (16 bits). Tengo un código como este: #include #define ADC_CS PORTB.3 #define WG_CS PORTB.4 #define MOSI PORTB.5 #define MISO_PU PORTB.6 #define MISO_PIN PINB.6 #define SCK PORTB.7
//global functions.
unsigned int adcConfig;
unsigned int adcMode;
unsigned int adcId;
void init_io(void)
{
DDRB = 0xBF; // make SCK, MOSI, CS1, CS2 outputs
ADC_CS = 1; //disable ADC
WG_CS = 1; //disable WaveGenerator
MISO_PU = 1; //enable pull-up on MISO so we can test !RDY
}
unsigned char spi(unsigned char data)
{
//Start transmision
SPDR = data;
//Wait for transmision complete
while (!(SPSR & (1<<SPIF)));
return SPDR;
}
//Sets the waveform generator output to given phase
void SetWGPhase(unsigned int phase)
{
SPCR = 0x5A; // mode #2 F_CPU/64
WG_CS = 0; // enable
spi(0x20);
spi(0x00);
spi((phase >> 8) | 0xC0); //Load into phase register 0
spi(phase & 0x00FF);
WG_CS = 1;
}
void setupAd(){
SPCR = 0x5D;
ADC_CS = 0;
// while(spi(0x10) != 0x10);
spi(0x10); //set up communication register for configuration reg.
spi(0x07);
spi(0x10);
spi(0x08); //set up communication register for mode reg.
spi(0x00);
spi(0x0A);
ADC_CS = 1;
}
unsigned int ReadAd(void)
{
unsigned int data;
SPCR = 0x5D; // mode #3 F_CPU/16
CheckStatus();
ADC_CS = 0; // enable
while (MISO_PIN != 0) ; // wait for DOUT/!RDY line to go low
//Read data
spi(0x58); //Place readinstruction in communication register
data = spi(0xFF); // read hi-byte
data = (data << 8) | spi(0xFF); // and lo-byte.
ADC_CS = 1; // disable
return data;
}
unsigned char CheckStatus(void)
{
char adcStatus;
SPCR = 0x5D;
ADC_CS = 0; // enable
while(ADC_CS_PIN);
adcStatus = 0xFF;
while(!(adcStatus & 0x80)){
spi(0x40);
adcStatus = spi(0xFF);
}
ADC_CS = 1;
return adcStatus;
}
unsigned int ReadAdConfReg(void)
{
unsigned int retvalconfig;
SPCR = 0x5D;
ADC_CS = 0;
while (MISO_PIN != 0) ;
spi(0x50);
adcConfig = spi(0xFF);
adcConfig = (adcConfig << 8) | spi(0xFF);
retvalconfig= adcConfig;
ADC_CS = 1;
return retvalconfig;
}
unsigned int ReadAdModeReg(void)
{
unsigned retvalmode;
SPCR = 0x5D;
ADC_CS = 0;
while (MISO_PIN != 0) ;
spi(0x48);
adcMode = spi(0xFF);
adcMode = (adcMode << 8) | spi(0xFF);
retvalmode =adcMode;
ADC_CS = 1;
return retvalmode;
}
unsigned int ReadAdIdReg(void)
{
SPCR = 0x5D;
ADC_CS = 0;
while (MISO_PIN != 0) ;
spi(0x60);
adcId = spi(0xFF);
ADC_CS = 1;
return adcId;
}
cuando imprimo el registro de configuración, está dando el valor "16383". pero cuando apago/encendo el objetivo, obtengo "1808 (que es equivalente a 0x0710)" y luego da el mismo valor que "16383". También probé con diferentes configuraciones, pero no cambia, siempre imprime "16383", excepto apagar/encender. Creo que es el valor predeterminado.
Incluso con el registro de modo, siempre está imprimiendo "10 (que es equivalente a 0x000A)", pero ese es el valor que obtengo siempre, incluso si cambio la configuración a "0x0022".
Incluso he intentado leer el registro de identificación, pero está dando "0x48". pero en la hoja de datos menciona "0xX8" para AD7798. Gracias de antemano.
Alguien que me ayude por favor, no tengo idea de qué error estoy haciendo aquí.
Si bien esta hoja de datos me está haciendo sangrar los ojos con la complejidad de hacer una conversión/lectura simple, debe realizar algunos cambios simples.
No estoy seguro de por qué la gente está haciendo esto más difícil de lo que realmente es. Tira de la línea de selección de chip hacia abajo, y el IC de destino simplemente debería leer lo que le dé hasta que lo vuelva a subir. El hecho de que su método tome un valor de 8 bits no significa que no pueda llamarlo dos veces para pasar un valor de 16 bits a su IC. No hay necesidad de golpear nada. Eso es una tontería.
Llame a su método SPI para decirle al ADC que desea escribir en el registro de configuración:
spi(0x10);
Según entiendo de la hoja de datos, puede escribir inmediatamente su valor de 16 bits en el registro de configuración después de eso, por lo que haría:
spi(0x07);
spi(0x10);
Olvidé de qué manera terminará siendo ensamblado en el lado IC de destino, por lo que simplemente podría invertirlos si las cosas no funcionan bien. No hay necesidad de cambiar de bit ningún valor en absoluto. En el peor de los casos, debe volver a subir la línea CS antes de volver a bajarla para enviar los datos para escribir en el registro de configuración.
De lo contrario, esto es súper simple. Recuerde, si el dispositivo SPI de destino espera más de un byte, es decir, un valor de 16 bits, es muy probable que envíe los bytes en el orden en que aparecerían normalmente (por lo que si desea enviar 0x1234, tendría 0x12 y 0x34) funcionará bien y no necesitará cambiar nada.
spi()
función entonces. Entre llamadas, nunca deshabilita SS, por lo que será automático en la función. Tal vez podría agregar un argumento opcional hold_ss
.ChipSelectAd()
función, pero se equivoca al anularla entre un comando y los datos asociados.La línea de selección de chip en el AD7798 está activa en nivel bajo. Parece que tienes la polaridad al revés en tu código.
Debe configurar CS en bajo (0) al comienzo de la transferencia y en alto después de la transferencia. Así que prueba esto:
void setupADC()
{
ChipSelectAd(0); // was 1
spi(0x10);
spi(0x07);
spi(0x10);
ChipSelectAd(1); // was 0
}
También verifique que esté usando el modo SPI correcto (llamado CPOL y CPHA en muchos microcontroladores, por "polaridad de reloj" y "fase de reloj"). Esto determina qué borde del reloj activa las transiciones de datos y qué nivel tiene el reloj entre transacciones (durante la inactividad).
El AD7798 requiere CPOL=1 y CPHA=1 (modo SPI 3).
ChipSelectAd
está realizando la inversión (es por eso que ayuda a mostrar más código). ¿Qué procesador estás usando? (añadir esto a la pregunta)Como dice RocketMagnet
spi(0x07)<<8;
no tiene sentido. Aunque la función spi()
está definida char
, la está desplazando hacia la izquierda y luego la descarta.
spi(0x07 << 8);
desplaza el argumento 8 bits a la izquierda, por lo que será 0x0700
. Pero eso no hace lo que quieres. No anule la afirmación de SS (Slave Select) entre las llamadas de la spi()
función, por lo que será automático. Lo cambiaría para que tenga un argumento opcional hold_ss
, de modo que pueda cambiar el primer byte, luego el segundo, y solo luego anular la afirmación de SS.
De esa forma podrá transferir 16 bits de datos al ADC, sin tener que transferir datos antes de la spi()
llamada. Alternativamente, puede escribir una función spi16()
:
unsigned int spi16(unsigned int data16)
{
assert_SS(IOpin);
spi8(data >> 8); /* assuming LSB is shifted first */
spi8(data);
deassert_SS(IOpin);
}
(Estoy ignorando los datos entrantes por el momento).
La hoja de datos indica que el Registro de configuración realmente necesita 16 bits, pero aunque no brinda detalles, esperaría que los necesite como una sola transferencia. Verifique la spi()
función del compilador de su microcontrolador y la hoja de datos del microcontrolador para ver si las transferencias de 16 bits son realmente posibles en primer lugar. Si el controlador puede hacer transferencias de 16 bits, debería poder hacer esto:
spi(0x0710);
Si solo son posibles las transferencias de 8 bits, creo que tendrá que recurrir a un SPI de bits (que no es tan difícil).
editar
Rocketmagnet me señala la definición de la spi()
función:
char spi(char data)
Eso lo dice todo: el argumento es de tipo char
, por lo que solo es de 8 bits.
( Gracias por el puntero (sin juego de palabras), Rocketmagnet )
Que micro estas usando? No proporcionó información sobre lo que está spi()
haciendo allí. Podría estar enviando 8 bits. En cuyo caso, su cambio de 8 bits resulta en una tontería. Ahora, indicó que desea enviar "0x0710". Piense por un segundo lo que puede estar sucediendo en su código.
Parece estar confundido acerca de cómo se lleva a cabo esta transferencia SPI.
Primero, estás enviando 0x10. Entonces, suponiendo que su spi()
función envíe 16 bits, está enviando 0x0700. Acabas de transferir 0x100700.
Estoy bastante seguro de que su spi()
función, de hecho, no envía sus 16 bits. Básicamente, estás enviando dos bytes: 0x10 y 0x00. Bien hecho.
Debe invertir el orden de los bytes al enviar varios bytes a través de SPI. Primero, envíe 0x07, y solo luego envíe 0x10.
stevenvh
Rocketmagnet
Rocketmagnet
Verendra
Trygve Laugstöl