diff options
Diffstat (limited to 'lib/msp430/wiring_analog.c')
-rw-r--r-- | lib/msp430/wiring_analog.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/lib/msp430/wiring_analog.c b/lib/msp430/wiring_analog.c new file mode 100644 index 0000000..1451ad7 --- /dev/null +++ b/lib/msp430/wiring_analog.c @@ -0,0 +1,297 @@ +/* + ************************************************************************ + * wiring_analog.c + * + * Arduino core files for MSP430 + * Copyright (c) 2012 Robert Wessels. All right reserved. + * + * + *********************************************************************** + Derived from: + wiring_analog.c - analog input and output + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2005-2006 David A. Mellis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#include "wiring_private.h" +#include "pins_energia.h" + +#if defined(__MSP430_HAS_ADC10__) && !defined(ADC10ENC) +#define ADC10ENC ENC +#endif +#if defined(__MSP430_HAS_ADC10__) && !defined(ADC10MEM0) +#define ADC10MEM0 ADC10MEM +#endif +#if defined(__MSP430_HAS_ADC10_B__) +#define REFV_MASK 0x70 +#define REF_MASK 0x31; +#endif + +#if defined(__MSP430_HAS_ADC10__) || defined(__MSP430_HAS_ADC10_B__) +uint16_t analog_reference = DEFAULT, analog_period = F_CPU/490, analog_div = 0, analog_res=255; // devide clock with 0, 2, 4, 8 +#endif + +void analogReference(uint16_t mode) +{ + // can't actually set the register here because the default setting + // will connect AVCC and the AREF pin, which would cause a short if + // there's something connected to AREF. + analog_reference = mode; +} + +//TODO: Can be a lot more efficiant. +// - lower clock rated / input devider to conserve Energia. +// - pin configuration logic. + +// Note set frequency before sending analog value +// Lowest fequency is defined by clock frequency F_CPU, and max counter value 2^16-1 +// fmin = F_CPU / 2^16 +void analogFrequency(uint32_t freq) +{ + if ( freq <= F_CPU/(4*65334L) ) { analog_div = ID_3; freq *=8; } + else if ( freq <= F_CPU/(2*65334L) ) { analog_div = ID_2; freq *=4; } + else if ( freq <= F_CPU/(4*65334L) ) { analog_div = ID_1; freq *=2; } + analog_period = F_CPU/freq; +} + +// Set the resulution (nr of counts for 100%), default = 255, large values may not work at all frequencies +void analogResolution(uint16_t res) +{ + analog_res = res; +} + +//Arduino specifies ~490 Hz for analog out PWM so we follow suit. +#define PWM_PERIOD analog_period // F_CPU/490 +#define PWM_DUTY(x) ( (unsigned long)x*PWM_PERIOD / (unsigned long)analog_res ) +void analogWrite(uint8_t pin, int val) +{ + pinMode(pin, OUTPUT); // pin as output + + if (val == 0) + { + digitalWrite(pin, LOW); // set pin to LOW when duty cycle is 0 + // digitalWrite will take care of invalid pins + } + else if (val == analog_res) + { + digitalWrite(pin, HIGH); // set pin HIGH when duty cycle is 255 + // digitalWrite will take care of invalid pins + } + else + { + + uint8_t bit = digitalPinToBitMask(pin); // get pin bit + uint8_t port = digitalPinToPort(pin); // get pin port + volatile uint8_t *sel; + + if (port == NOT_A_PORT) return; // pin on timer? + + sel = portSelRegister(port); // get the port function select register address + *sel |= bit; // set bit in pin function select register + + switch(digitalPinToTimer(pin)) { // which timer and CCR? + //case: T0A0 // CCR0 used as period register + case T0A1: // TimerA0 / CCR1 + TA0CCR0 = PWM_PERIOD; // PWM Period + TA0CCTL1 = OUTMOD_7; // reset/set + TA0CCR1 = PWM_DUTY(val); // PWM duty cycle + TA0CTL = TASSEL_2 + MC_1 + analog_div; // SMCLK, up mode + break; +#if defined(__MSP430_HAS_TA3__) || defined(__MSP430_HAS_T0A3__) + case T0A2: // TimerA0 / CCR2 + TA0CCR0 = PWM_PERIOD; // PWM Period + TA0CCTL2 = OUTMOD_7; // reset/set + TA0CCR2 = PWM_DUTY(val); // PWM duty cycle + TA0CTL = TASSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; +#endif +#if defined(__MSP430_HAS_T1A3__) + //case: T1A0 // CCR0 used as period register + case T1A1: // TimerA1 / CCR1 + TA1CCR0 = PWM_PERIOD; // PWM Period + TA1CCTL1 = OUTMOD_7; // reset/set + TA1CCR1 = PWM_DUTY(val); // PWM duty cycle + TA1CTL = TASSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; + case T1A2: // TimerA1 / CCR2 + TA1CCR0 = PWM_PERIOD; // PWM Period + TA1CCTL2 = OUTMOD_7; // reset/set + TA1CCR2 = PWM_DUTY(val); // PWM duty cycle + TA1CTL = TASSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; +#endif +#if defined(__MSP430_HAS_T2A3__) + //case: T2A0 // CCR0 used as period register + case T2A1: // TimerA2 / CCR1 + TA2CCR0 = PWM_PERIOD; // PWM Period + TA2CCTL1 = OUTMOD_7; // reset/set + TA2CCR1 = PWM_DUTY(val); // PWM duty cycle + TA2CTL = TASSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; + case T2A2: // TimerA2 / CCR2 + TA2CCR0 = PWM_PERIOD; // PWM Period + TA2CCTL2 = OUTMOD_7; // reset/set + TA2CCR2 = PWM_DUTY(val); // PWM duty cycle + TA2CTL = TASSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; +#endif +#if defined(__MSP430_HAS_T0B3__) + //case: T0B0 // CCR0 used as period register + case T0B1: // TimerB0 / CCR1 + TB0CCR0 = PWM_PERIOD; // PWM Period + TB0CCTL1 = OUTMOD_7; // reset/set + TB0CCR1 = PWM_DUTY(val); // PWM duty cycle + TB0CTL = TBSSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; + case T0B2: // TimerB0 / CCR1 + TB0CCR0 = PWM_PERIOD; // PWM Period + TB0CCTL2 = OUTMOD_7; // reset/set + TB0CCR2 = PWM_DUTY(val); // PWM duty cycle + TB0CTL = TBSSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; +#endif +#if defined(__MSP430_HAS_T1B3__) + //case: T1B0 // CCR0 used as period register + case T1B1: // TimerB0 / CCR1 + TB1CCR0 = PWM_PERIOD; // PWM Period + TB1CCTL1 = OUTMOD_7; // reset/set + TB1CCR1 = PWM_DUTY(val); // PWM duty cycle + TB1CTL = TBSSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; + case T1B2: // TimerB0 / CCR1 + TB1CCR0 = PWM_PERIOD; // PWM Period + TB1CCTL2 = OUTMOD_7; // reset/set + TB1CCR2 = PWM_DUTY(val); // PWM duty cycle + TB1CTL = TBSSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; +#endif +#if defined(__MSP430_HAS_T2B3__) + //case: T1B0 // CCR0 used as period register + case T2B1: // TimerB0 / CCR1 + TB2CCR0 = PWM_PERIOD; // PWM Period + TB2CCTL1 = OUTMOD_7; // reset/set + TB2CCR1 = PWM_DUTY(val); // PWM duty cycle + TB2CTL = TBSSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; + case T2B2: // TimerB0 / CCR1 + TB2CCR0 = PWM_PERIOD; // PWM Period + TB2CCTL2 = OUTMOD_7; // reset/set + TB2CCR2 = PWM_DUTY(val); // PWM duty cycle + TB2CTL = TBSSEL_2 + MC_1+ analog_div; // SMCLK, up mode + break; +#endif + + case NOT_ON_TIMER: // not on a timer output pin + default: // or TxA0 pin + if (val <= (analog_res >> 1)) { + digitalWrite(pin, LOW); // + } else { + digitalWrite(pin, HIGH); + } + } + } +} + +uint16_t analogRead(uint8_t pin) +{ +// make sure we have an ADC +#if defined(__MSP430_HAS_ADC10__) || defined(__MSP430_HAS_ADC10_B__) + // 0000 A0 + // 0001 A1 + // 0010 A2 + // 0011 A3 + // 0100 A4 + // 0101 A5 + // 0110 A6 + // 0111 A7 + // 1010 Internal temperature sensor + + //TODO: Only int. temp. sensor requires Tsample > 30us. + // The below ADC configuration applies this rule to all channels right now. + // ADC10CLK = 5MHz / 5 = 1Mhz + // Tsample = S&H / ADC10CLK = 64 / 1 MHz = 64 us + // Tconvert = 13 / ADC10CLK = 13 / 1 MHz = 13 us + // Total time per sample = Tconvert + Tsample = 64 + 13 = 67 us = ~15k samples / sec + + ADC10CTL0 &= ~ADC10ENC; // disable ADC + ADC10CTL1 = ADC10SSEL_0 | ADC10DIV_5; // ADC10OSC as ADC10CLK (~5MHz) / 5 +#if defined(__MSP430_HAS_ADC10__) + ADC10CTL0 = analog_reference | // set analog reference + ADC10ON | ADC10SHT_3 | ADC10IE; // turn ADC ON; sample + hold @ 64 × ADC10CLKs; Enable interrupts + ADC10CTL1 |= (pin << 12); // select channel + ADC10AE0 = (1 << pin); // Disable input/output buffer on pin +#endif +#if defined(__MSP430_HAS_ADC10_B__) + while(REFCTL0 & REFGENBUSY); // If ref generator busy, WAIT + REFCTL0 |= analog_reference & REF_MASK; // Set reference using masking off the SREF bits. See Energia.h. + ADC10MCTL0 = pin | (analog_reference & REFV_MASK); // set channel and reference + ADC10CTL0 = ADC10ON | ADC10SHT_4; // turn ADC ON; sample + hold @ 64 × ADC10CLKs + ADC10CTL1 |= ADC10SHP; // ADCCLK = MODOSC; sampling timer + ADC10CTL2 |= ADC10RES; // 10-bit resolution + ADC10IFG = 0; // Clear Flags + ADC10IE |= ADC10IE0; // Enable interrupts +#endif + __delay_cycles(128); // Delay to allow Ref to settle + ADC10CTL0 |= ADC10ENC | ADC10SC; // enable ADC and start conversion + while (ADC10CTL1 & ADC10BUSY) { // sleep and wait for completion + __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled + } + +#if defined(__MSP430_HAS_ADC10__) + /* POWER: Turn ADC and reference voltage off to conserve power */ + ADC10CTL0 &= ~(ADC10ON | REFON); +#endif + +#if defined(__MSP430_HAS_ADC10_B__) + /* POWER: Turn ADC and reference voltage off to conserve power */ + ADC10CTL0 &= ~(ADC10ON); + REFCTL0 &= ~REFON; +#endif + return ADC10MEM0; // return sampled value after returning to active mode in ADC10_ISR +#else + // no ADC + return 0; +#endif +} + +__attribute__((interrupt(ADC10_VECTOR))) +void ADC10_ISR(void) +{ +#if defined(__MSP430_HAS_ADC10) + __bic_SR_register_on_exit(CPUOFF); // return to active mode +#endif + +#if defined(__MSP430_HAS_ADC10_B__) + + switch(ADC10IV,12) { + case 0: break; // No interrupt + case 2: break; // conversion result overflow + case 4: break; // conversion time overflow + case 6: break; // ADC10HI + case 8: break; // ADC10LO + case 10: break; // ADC10IN + case 12: + __bic_SR_register_on_exit(CPUOFF); // return to active mode + break; // Clear CPUOFF bit from 0(SR) + default: break; + } + + ADC10IFG = 0; // Clear Flags +#endif +} |