/* * adcport.c * * Created on: Mar 25, 2015 * Author: shinobi */ #include "adcport.h" #include #include #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 }