adcport.c
266 lines
| 7.6 KiB
| text/x-c
|
CLexer
|
r216 | /* | ||
* adcport.c | ||||
* | ||||
* Created on: Mar 25, 2015 | ||||
* Author: shinobi | ||||
*/ | ||||
#include "adcport.h" | ||||
#include <avr/io.h> | ||||
#include <avr/interrupt.h> | ||||
#define SPI_SS_bm PIN4_bm /*!< \brief Bit mask para el pin SS. */ | ||||
#define SPI_MOSI_bm PIN5_bm /*!< \brief Bit mask para el pin MOSI. */ | ||||
#define SPI_MISO_bm PIN6_bm /*!< \brief Bit mask para el pin MISO. */ | ||||
#define SPI_SCK_bm PIN7_bm /*!< \brief Bit mask para el pin SCK. */ | ||||
#define SS_OFF PORTC.OUTSET=PIN4_bm;/*!< \brief Deselecciona el ADC (puerto SPI) */ | ||||
#define SS_ON PORTC.OUTCLR=PIN4_bm;/*!< \brief Selecciona el ADC (puerto SPI) */ | ||||
/* | ||||
* Al iniciar la adquisicion se debe activar la interrupcion del pin RDY a | ||||
* traves del PINC6 del XMEGA. | ||||
* Luego al leer los datos digitalizados, se debe desactivar. Al terminar | ||||
* se reactiva para esperar la siguiente interrupcion. | ||||
* Esto se debe a que el DOUT y el RDY del ADC comparten el pin. Si se dejara | ||||
* activa la interrupcion al leer, se generarian interrupciones e los flancos | ||||
* de bajada generados por la transferencia de las muestras | ||||
*/ | ||||
#define WAIT_DATA PORTC.INT0MASK=PIN6_bm/*!< \brief Activa interrupcion que indica dato nuevo */ | ||||
#define GET_DATA PORTC.INT0MASK=0/*!< \brief Desactiva iterrupcion que indica dato nuevo */ | ||||
volatile uint8_t buff_idx; | ||||
uint32_t * pfull_buff; | ||||
uint32_t * pread_buff; | ||||
volatile uint8_t buff_full_flg = 0; | ||||
/*! | ||||
* \fn adcport_ready_interrupt_config | ||||
* \brief configura el pin de MISO (conectado a DOUT del ADC), para que dispare | ||||
* una interrupcion por flanco de bajada. | ||||
* una vez disparada la interrupcion, se puede leer el pin; sin embargo, debe | ||||
* desactivarse antes de leer la interrupcion por flanco (con GET_DATA). De lo | ||||
* contrario se disparara la interrupcion varias veces al leer los datos, ya | ||||
* que ese el pin RDY y DOUT es el mismo en el ADC. | ||||
*/ | ||||
inline void adcport_ready_interrupt_config(){ | ||||
// pin6: MISO(xmega) --> DOUT/RDY(ADC) => pin6 entrada | ||||
PORTC.DIRCLR=PIN6_bm; | ||||
// El ADC llevara a "low" RDY cuando la conversion de un dato haya concluido | ||||
// Se debe leer el dato generado luego. Se espera una interrupcion de flanco | ||||
// de bajada para manejar esto | ||||
PORTC.PIN6CTRL=PORT_ISC_FALLING_gc; | ||||
// Se mapea la interrupcion externa INT0 de PORTC a PINC6 | ||||
PORTC.INT0MASK=PIN6_bm; | ||||
// Debido a que esta interrupcion va a manejar la adquisicion de datos, se | ||||
// le da maxima prioridad (nivel alto) | ||||
PORTC.INTCTRL=PORT_INT0LVL_HI_gc; | ||||
// Se habilita la atencion de interrupciones de nivel alto | ||||
PMIC.CTRL|= PMIC_HILVLEN_bm; | ||||
} | ||||
/*! | ||||
* \fn adcport_spi_config | ||||
* \brief configura el puerto SPI para que coincida con el requerimiento del ADC | ||||
* AD7178-2 | ||||
*/ | ||||
inline void adcport_spi_config(){ | ||||
PORTC.DIRSET = SPI_MOSI_bm | SPI_SCK_bm | SPI_SS_bm; | ||||
// Preescaler: clkper/2 (con clk2x). Maestro. CPOL=1,CPHA=1 | ||||
// MSB primero | ||||
SPIC.CTRL = SPI_CLK2X_bm|SPI_ENABLE_bm|SPI_MASTER_bm| | ||||
SPI_MODE1_bm|SPI_MODE0_bm; | ||||
} | ||||
/*! | ||||
* \fn adcport_config | ||||
* \brief Configura el microcontrolador para darle servicio a la interrupcion | ||||
* del pin "RDY" del ADC, que reacciona con un flanco de bajada cuando se ha | ||||
* terminado de digitalar una muestra nueva. | ||||
* Tambien configura el puerto SPI que servira para comunicarse con el ADC. | ||||
* \see adcport_ready_interrupt_config | ||||
* \see adcport_spi_config | ||||
*/ | ||||
inline void adcport_config(){ | ||||
adcport_ready_interrupt_config(); | ||||
adcport_spi_config(); | ||||
// TODO configurar ADC: datarate, ganancia, desactivar CRC, formato numerico | ||||
// de muestras debe ser "bipolar offset binary"(canales diferenciales). | ||||
} | ||||
/*! | ||||
* \fn adcport_open | ||||
* \brief Inicializa el buffer de entrada (para datos de 24bits del ADC) y | ||||
* activa la comunicacion a traves del pin "SS" del puerto SPI. | ||||
* \see adcport_close | ||||
*/ | ||||
inline void adcport_open(){ | ||||
buff_idx=0; | ||||
// TODO configurar interrupcion externa PPS (pin 6) | ||||
// TODO configurar interrupcion externa LOCK (pin 21) | ||||
pfull_buff = malloc(sizeof(uint32_t)*BUFF_SIZE); | ||||
pread_buff = malloc(sizeof(uint32_t)*BUFF_SIZE); | ||||
adcport_config(); | ||||
SS_ON; | ||||
} | ||||
/*! | ||||
* \fn adcport_close | ||||
* \brief Desactiva la comunicacion con el ADC a traves del pin "SS" del puerto | ||||
* SPI. | ||||
* \see adcport_open | ||||
*/ | ||||
inline void adcport_close(){ | ||||
SS_OFF; | ||||
free(pfull_buff); | ||||
free(pread_buff); | ||||
} | ||||
/*! | ||||
* \fn adcport_start | ||||
* \brief Inicia la digitalizacion de muestras del sensor. | ||||
* SPI. | ||||
* \see adcport_stop | ||||
* \see adcport_open | ||||
* \see adcport_close | ||||
*/ | ||||
inline void adcport_start(){ | ||||
// necesario para darle servicio con interrupciones al flanco de bajada | ||||
// del pin "RDY" | ||||
WAIT_DATA; | ||||
// TODO enviar comandos al ADC para que inicie la adquisicion. | ||||
} | ||||
/*! | ||||
* \fn adcport_stop | ||||
* \brief Pausa la digitalizacion de muestras del sensor. | ||||
* SPI. | ||||
* \see adcport_start | ||||
* \see adcport_open | ||||
* \see adcport_close | ||||
*/ | ||||
inline void adcport_stop(){ | ||||
// TODO enviar comandos al ADC para que deje de adquirir. | ||||
// necesario para cortar el servicio interrupcion del pin "RDY" | ||||
GET_DATA; | ||||
} | ||||
/*! | ||||
* \fn adcport_tranceiv | ||||
* \brief Realiza la transmision y recepcion simultanea de datos entre el ADC y | ||||
* el microcontrolador. | ||||
* Incluso en para leer un dato del ADC se debe transmitir, ya que solo la | ||||
* transmision genera clock en el pin "sclk" | ||||
* \param El dato a transmitir | ||||
* \return El dato leido del ADC | ||||
*/ | ||||
inline uint8_t adcport_tranceiv(uint8_t data){ | ||||
// | ||||
SPIC.DATA = data; | ||||
//Wait until transmission complete | ||||
while(!(SPIC.STATUS)&SPI_IF_bm); | ||||
// Return received data | ||||
return SPIC.DATA; | ||||
} | ||||
/*! | ||||
* \fn adcport_start | ||||
* \brief Inicia la digitalizacion de muestras del sensor. | ||||
* SPI. | ||||
* \see adcport_open | ||||
*/ | ||||
void adcport_read_sample(){ | ||||
uint32_t aux; | ||||
GET_DATA; // desactiva interrupciones de flaco de bajada | ||||
// Se le indica al adc que se va a leer el registro de data. | ||||
adcport_tranceiv(0x44); | ||||
// El byte mas significativo de la variable de 32bits es cero | ||||
// La codificacion de los numeros es "bipolar offset binary" y | ||||
// la transmision es MSB first | ||||
aux = adcport_tranceiv(0); | ||||
aux = (aux<<8)|adcport_tranceiv(0); | ||||
aux = (aux<<8)|adcport_tranceiv(0); | ||||
aux = (aux<<8)|adcport_tranceiv(0); | ||||
pread_buff[buff_idx]=aux; | ||||
WAIT_DATA; // reactiva interrupciones de flanco de bajada | ||||
} | ||||
/*! | ||||
* \fn adcport_getbuff | ||||
* \brief Devuelve la direccion del buffer lleno | ||||
* \return Direccion del buffer lleno. 0 si no esta lleno aun | ||||
*/ | ||||
inline uint32_t* adcport_getbuff(){ | ||||
if(buff_full_flg==1){ | ||||
buff_full_flg=0; | ||||
return pfull_buff; | ||||
} | ||||
return 0; | ||||
} | ||||
uint8_t adcport_get_param(uint8_t data){ | ||||
adcport_tranceiv(data); | ||||
return adcport_tranceiv(0); | ||||
} | ||||
/*! | ||||
* \brief interrupcion externa debe dispararse en flanco de bajada en PC6 (RDY del ADC). | ||||
* Cuando el ADC lleva este pin a "low", se debe leer el dato nuevo | ||||
*/ | ||||
ISR(PORTC_INT0_vect){ | ||||
adcport_read_sample(); | ||||
buff_idx++; | ||||
if(buff_idx>=100){ | ||||
uint32_t* paux = pread_buff; | ||||
pread_buff = pfull_buff; | ||||
pfull_buff = paux; | ||||
buff_full_flg=1; | ||||
// TODO dar aviso al programa principal que el buffer esta lleno. | ||||
// Puede ser a traves de una interrupcion "externa" en un pin que no se | ||||
// use, para lo cual debe estar configurado como salida y para recibir | ||||
// interrupciones de IO. | ||||
// para hacer que funcione como una interrupcion software, solo escribir | ||||
// en ese pin un valor segun se configure la interrupcion | ||||
} | ||||
} | ||||
/* TODO | ||||
* interrupcion del LOCK del GNSS (pin numero 21 del xmega) | ||||
* servira para indicar que el GNSS esta sincronizado con satelites y la hora y | ||||
* PPS son correctos, a partir de ese momento se pueden contar los PPS y | ||||
* identificarlos en el header. | ||||
*/ | ||||
ISR(PORTx_INTx_vect){ // FIXME | ||||
} | ||||
/* TODO | ||||
* interrupcion del PPS del GNSS (pin numero 6 del xmega) | ||||
* servira para sincronizar la hora. Debe agregar un numero de serie entre 0 y 255 | ||||
* a la cabecera del buffer que indentifique al PPS; y el numero de muestra que se adquirio | ||||
* en el momento de la llegada de esta interrupcion. | ||||
*/ | ||||
ISR(PORTx_INTx_vect){ // FIXME | ||||
} | ||||