/* * Copyright (C) 2004 by Ole Reinhardt , * Kernelconcepts http://www.kernelconcepts.de * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ * */ /*! * \file arch/avr/dev/adc.c * \brief AVR adc driver * * \verbatim * $Id: adc.c 4937 2013-01-22 11:38:42Z haraldkipp $ * \endverbatim */ /*! * \addtogroup xgAvrAdc */ /*@{*/ /* Not ported. */ #ifdef __GNUC__ #include #include #include #include #include #include #include #ifndef ADC_INITIAL_CHANNEL #define ADC_INITIAL_CHANNEL ADC0 #endif #ifndef ADC_INITIAL_REF #define ADC_INITIAL_REF AVCC #endif #ifndef ADC_INITIAL_MODE #define ADC_INITIAL_MODE SINGLE_CONVERSION #endif #ifndef ADC_INITIAL_PRESCALE #define ADC_INITIAL_PRESCALE ADC_PRESCALE_DIV64 #endif #define ADC_BUF_SIZE 16 // this may only be a power of two #if defined(__GNUC__) && defined(__AVR_ENHANCED__) uint8_t adc_sleep_mode = SLEEP_MODE_ADC; /* AT90CAN128 uses a different register to enter sleep mode */ #if defined(SMCR) #define AVR_SLEEP_CTRL_REG SMCR #else #define AVR_SLEEP_CTRL_REG MCUCR #endif #endif /********** DRIVER GLOBALS **********/ adc_mode_t current_mode = ADC_OFF; // The buffers are FIFO buffers implemented as char // arrays with head and tail pointers. // All read and write functions return a 0 for // success and a 1 for fail, if a successfull read, // the character is placed into *read. // buffer: [data1 data2 ... datalast head tail] #define _adc_buf_head ADC_BUF_SIZE #define _adc_buf_tail (ADC_BUF_SIZE+1) uint16_t *ADC_buffer = NULL; inline int ADCBufRead(uint16_t * buf, uint16_t * read) { uint8_t tail, head; tail = buf[_adc_buf_tail]; head = buf[_adc_buf_head]; if (head != tail) { *read = buf[tail]; buf[_adc_buf_tail] = (tail + 1) & (ADC_BUF_SIZE-1); return 0; } return 1; } inline int ADCBufWrite(uint16_t * buf, uint16_t * write) { uint8_t tail, head; tail = buf[_adc_buf_tail]; head = buf[_adc_buf_head]; if ((head + 1) % ADC_BUF_SIZE != tail) { buf[head] = *write; buf[_adc_buf_head] = (head + 1) & (ADC_BUF_SIZE-1); return 0; } return 1; } void ADCBufInit(uint16_t * buf) { buf[_adc_buf_head] = 0; buf[_adc_buf_tail] = 0; } static void ADCInterrupt(void *arg) { uint16_t ADC_value; #ifdef ADC ADC_value = inw(ADC); #else ADC_value = inw(ADCW); #endif if (ADCBufWrite(ADC_buffer, &ADC_value) != 0) { // Send error message } } void ADCInit() { if (ADC_buffer) return; ADCSetChannel(ADC_INITIAL_CHANNEL); ADCSetRef(ADC_INITIAL_REF); ADCSetMode(ADC_INITIAL_MODE); ADCSetPrescale(ADC_INITIAL_PRESCALE); ADC_buffer = NutHeapAlloc(sizeof(uint16_t) * (ADC_BUF_SIZE + 2)); ADCBufInit(ADC_buffer); if (NutRegisterIrqHandler(&sig_ADC, ADCInterrupt, NULL)) { // MR 2006-01-11: we do not free buffer as this would cost ROM and is not likely return; } // Enable ADC sbi(ADCSR, ADEN); // Enable ADC interrupt sbi(ADCSR, ADIE); } void ADCSetRef(adc_ref_t reference) { ADCStopConversion(); #ifdef __AVR_ENHANCED__ switch (reference) { case AVCC: cbi(ADMUX, REFS1); sbi(ADMUX, REFS0); break; case AREF: cbi(ADMUX, REFS1); cbi(ADMUX, REFS0); break; case INTERNAL_256: sbi(ADMUX, REFS1); sbi(ADMUX, REFS0); break; } #endif /* __AVR_ENHANCED__ */ } void ADCSetMode(adc_mode_t mode) { ADCStopConversion(); switch (mode) { case FREE_RUNNING: sbi(ADCSR, ADFR); break; case SINGLE_CONVERSION: cbi(ADCSR, ADFR); break; case ADC_OFF: break; } current_mode = mode; } uint8_t ADCSetPrescale(uint8_t prescalar) { ADCStopConversion(); if (prescalar > 128) { prescalar = 128; } switch (prescalar) { case ADC_PRESCALE_DIV2: cbi(ADCSR, ADPS2); cbi(ADCSR, ADPS1); cbi(ADCSR, ADPS0); break; case ADC_PRESCALE_DIV4: cbi(ADCSR, ADPS2); sbi(ADCSR, ADPS1); cbi(ADCSR, ADPS0); break; case ADC_PRESCALE_DIV8: cbi(ADCSR, ADPS2); sbi(ADCSR, ADPS1); sbi(ADCSR, ADPS0); break; case ADC_PRESCALE_DIV16: sbi(ADCSR, ADPS2); cbi(ADCSR, ADPS1); cbi(ADCSR, ADPS0); break; case ADC_PRESCALE_DIV32: sbi(ADCSR, ADPS2); cbi(ADCSR, ADPS1); sbi(ADCSR, ADPS0); break; case ADC_PRESCALE_DIV64: sbi(ADCSR, ADPS2); sbi(ADCSR, ADPS1); cbi(ADCSR, ADPS0); break; case ADC_PRESCALE_DIV128: sbi(ADCSR, ADPS2); sbi(ADCSR, ADPS1); sbi(ADCSR, ADPS0); break; default: return 1; break; } return 0; } void ADCSetChannel(adc_channel_t adc_channel) { uint8_t current_admux; current_admux = inb(ADMUX) & 0xF8; outb(ADMUX, (current_admux | adc_channel)); } void ADCBufferFlush(void) { ADCBufInit(ADC_buffer); } void ADCStartConversion() { sbi(ADCSR, ADSC); } void ADCStartLowNoiseConversion() { ADCSetMode(SINGLE_CONVERSION); #if defined(__GNUC__) && defined(__AVR_ENHANCED__) { uint8_t sleep_mode = AVR_SLEEP_CTRL_REG & _SLEEP_MODE_MASK; set_sleep_mode(adc_sleep_mode); /* Note: avr-libc has a sleep_mode() function, but it's broken for AT90CAN128 with avr-libc version earlier than 1.2 */ AVR_SLEEP_CTRL_REG |= _BV(SE); __asm__ __volatile__ ("sleep" "\n\t" :: ); AVR_SLEEP_CTRL_REG &= ~_BV(SE); set_sleep_mode(sleep_mode); } #else sbi(ADCSR, ADSC); #endif } void ADCStopConversion() { if (current_mode != FREE_RUNNING) { // Send warning message return; } // Terminate and restart the ADC // When restarted, start_conversion needs to be // called again cbi(ADCSR, ADEN); cbi(ADCSR, ADSC); sbi(ADCSR, ADEN); } uint8_t ADCRead(uint16_t * value) { return ADCBufRead(ADC_buffer, value); } inline adc_mode_t ADCGetMode(void) { return (current_mode); } #endif /*@}*/