/* * AD7794.c - routines to work with ADC AD7794 by SPI * * Copyright 2013 Edward V. Emelianoff * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #include "main.h" #include "AD7794.h" #include "spi.h" #include "cdcacm.h" volatile uint8_t data_error = 0; uint8_t SPI_buffer[4]; // IO buffer: contains data to transmit & received data #define check_err() if(data_error){DBG("SPI error!\n"); data_error = 0; return 0;} #define ERR_SPI() do{data_error = 1; return NULL;}while(0) void SPI_buffer_clear(){ uint8_t i; for(i = 0; i < 4; i++) SPI_buffer[i] = 0; } /** * Send len bytes from SPI buffer to register reg * @param reg - register to write + key READ_FROM_REG in case of read request * @param len - length of data to transmit/receive (without reg!): max 3!!! * @return received data */ uint8_t *send_SPI_data(uint8_t reg, uint8_t len){ data_error = 0; if(len > 3) return NULL; // error: too big data array SPI_buffer[0] = reg; len++; if(!write_SPI(SPI_buffer, len)) ERR_SPI(); if(!read_SPI(SPI_buffer, len)) ERR_SPI(); return &SPI_buffer[1]; } #undef ERR_SPI #define ERR_SPI() do{data_error = 1; return 0;}while(0) /** * Write data to 8bit register over SPI * @param reg - register to write * @param data - data to write * @return readed data */ uint8_t sendByte(uint8_t reg, uint8_t data){ SPI_buffer_clear(); SPI_buffer[1] = data; if(!send_SPI_data(reg, 1)) ERR_SPI(); return SPI_buffer[1]; // return readed data } /** * Write data to 16bit register over SPI * @param reg - register to write * @param data - data to write * @return readed data */ uint16_t sendWord(uint8_t reg, uint16_t data){ uint16_t ret; SPI_buffer_clear(); SPI_buffer[1] = (data >> 8 )& 0xff; // HSB first SPI_buffer[2] = data & 0xff; if(!send_SPI_data(reg, 2)) ERR_SPI(); ret = ((uint16_t)SPI_buffer[1]) << 8 | SPI_buffer[2]; return ret; // return readed data } /** * reads 24 bit ADC data register * @return value of ADC || 1 (0 indicates error) */ uint32_t read_ADC_data(){ uint32_t ret; SPI_buffer_clear(); if(!send_SPI_data(DATA_REGISTER | READ_FROM_REG, 3)) return 0; ret = SPI_buffer[1] << 16 | SPI_buffer[2] << 8 | SPI_buffer[3]; if(!ret) ret = 1; // there could be a very little case that we get 0 return ret; } /** * Checks ending of ADC and/or errors * @return 1 in case of data ready, set data_error to 1 in case of error */ uint8_t check_data_ready(){ uint8_t ret = 0; //DBG("check_data_ready\n"); uint8_t x = sendByte(STAT_REGISTER | READ_FROM_REG, 0); if(data_error){ DBG("some data error\n"); ret = 1; // data not ready, but error flag is set }else if((x & 0x18) != 0x08 || x & DATA_ERROR || x & STATUS_NOREF){ // test for AD7794 status register #ifdef EBUG if((x & 0x18) != 0x08) DBG("There's no AD7794 signature found\n"); if(x & DATA_ERROR) DBG("DATA_ERROR\n"); if(x & STATUS_NOREF) DBG("STATUS_NOREF\n"); #endif if(x) data_error = x; else data_error = 1; // 0 -> no signature ret = 1; }else if(!(x & DATA_NOTRDY)) ret = 1; return ret; } #define read_AD7794_conf() sendWord(CONF_REGISTER | READ_FROM_REG, 0) static uint8_t ADC_gain = 1; /** * Changes ADC gain coefficient * @param gain - log2(gain) * gain voltage range (Vref+-) * 0 2.5 V * 1 1.25 V * 2 625 mV * 3 312.5 mV * 4 156.2 mV * 5 78.125 mV * 6 39.06 mV * 7 19.53 mV * @return 0 in case of wrong gain value */ uint8_t change_AD7794_gain(uint8_t gain){ if(gain > 7) return 0; ADC_gain = gain; uint16_t Conf = read_AD7794_conf(); check_err(); Conf &= ~GAIN_MASK; // clear gain bits Conf |= ADC_gain << 8; // update gain sendWord(CONF_REGISTER, Conf); check_err(); return 1; } uint8_t AD7794_set_channel(uint8_t channel){ uint16_t Conf = read_AD7794_conf(); check_err(); Conf &= ~CHANNEL_MASK; // clear channel number Conf |= (CHANNEL_MASK & channel); // set new channel sendWord(CONF_REGISTER, Conf); check_err(); return 1; } /** * make internal full-scale calibration for given channel * !! first run setup_AD7794() and after - this function !! * @param channel - channel to activate * @return 0 on error */ uint8_t AD7794_calibration(uint8_t channel){ if(ADC_gain == 7){ DBG("gain = 7, can't calibrate!\n"); return 0; } if(!AD7794_set_channel(channel)) return 0; sendWord(MODE_REGISTER, INT_FS_CAL | U16(0x0f)); // make a full-scale calibration on lowest speed check_err(); return 1; } /** * Setup ADC: write keys to registers * @param config - keys to be written to configuration register * @param io - keys to be written to io register * @return 0 on error */ uint8_t setup_AD7794(uint16_t config, uint8_t io){ sendWord(MODE_REGISTER, POWERDOWN_MODE); // put ADC into powerdown mode sendWord(CONF_REGISTER, ADC_gain << 8 // gain | config); check_err(); sendByte(IO_REGISTER, io); check_err(); return 1; } // we modify this macro to zero steps counter in case of error #define check_errR() if(data_error){DBG("SPI error!\n"); data_error = 0; N = 0; return 0;} /** * Start ADC transform & read data value * !! first run setup_AD7794(), AD7794_calibration() and after - this function !! * @param channel - channel number to transform * @return: * AD7794_NOTRDY when measurement in process * ADC data value if ready * 0 in case of error */ uint32_t read_AD7794(uint8_t channel){ static uint8_t N = 0; // operation number uint8_t dr; switch (N){ case 0: // start: set channel //P("A0 ", uart1_send); if(!AD7794_set_channel(channel)){ return 0; // 0 in return means error } break; case 1: // put ADC to a single conversion mode //P("A1 ", uart1_send); sendWord(MODE_REGISTER, SINGLE_MODE | U16(0x0f)); // the lowest speed; check_errR(); break; case 2: // wait for data reading & check errors //P("A2", uart1_send); dr = check_data_ready(); check_errR(); if(!dr) return AD7794_NOTRDY; break; default: // last step -> return readed data //P("\n", uart1_send); N = 0; return read_ADC_data(); } N++; return AD7794_NOTRDY; } /** * This function tries to reset ADC's SPI controller & check returned status * If * @param * @return !!! ADC_NO_ERROR in case of succsess!!! * Other error code in case of error (ADC_ERR_NO_DEVICE is critical error!) */ uint8_t reset_AD7794(){ uint8_t i, ret = ADC_NO_ERROR; for(i = 0; i < 3; i++){ sendByte(0xFF, 0xFF); // >= 32 bits of 1 if(data_error) break; } if(data_error){ DBG("SPI error: can't send reset sequence!\n"); data_error = 0; return ADC_ERR_NO_DEVICE; } Delay(1); // give some time to "reboot" check_data_ready(); if(data_error){ DBG("SPI error: device can't do reset!\n"); if((data_error & 0x18) != 0x08) // no signature ret = ADC_ERR_NO_DEVICE; else ret = ADC_ERR_OTHER; data_error = 0; } return ret; }