Skip to content

Commit

Permalink
Add support for ATmega2560, ATmega1280. Closes #1.
Browse files Browse the repository at this point in the history
  • Loading branch information
JChristensen committed Feb 16, 2020
1 parent 4dc4fda commit cda538b
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 21 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Since each call to `read()` measures only a single cycle, it may be desirable, d

Because measuring AC causes the current transformer to output positive and negative currents, a DC bias must be applied to ensure that below-ground voltages are not applied to the microcontroller's ADC input. It is also necessary to ensure that peak voltages do not exceed the microcontroller's supply voltage (Vcc). Do the math to ensure that your circuit operates within safe limits; also see the example below.

This library is specific to the AVR microcontroller architecture and will not work on others. Timer/Counter1 is used to trigger the ADC conversions and so is not available for other purposes.
This library is specific to the AVR microcontroller architecture and will not work on others. Only ATmega328/P, ATmega1280 and ATmega2560 are supported. Timer/Counter1 is used to trigger the ADC conversions and so is not available for other purposes.

## Example Design Calculations

Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=CurrentTransformer
version=2.2.2
version=2.3.0
author=Jack Christensen <[email protected]>
maintainer=Jack Christensen <[email protected]>
sentence=Arduino Library for measuring current in 50/60Hz circuits using current transformers.
Expand Down
59 changes: 40 additions & 19 deletions src/CurrentTransformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@
CT_Sensor::CT_Sensor(uint8_t channel, float ratio, float burden)
: m_ratio(ratio), m_burden(burden)
{
if (channel >= 14) channel -= 14; // if user passed A0-A5, adjust accordingly
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if (channel >= 54) channel -= 54; // if user passed pin number, convert to channel number
m_channel = channel & 0x0f; // ruthlessly coerce to an acceptable value
#else
if (channel >= 14) channel -= 14; // if user passed pin number, convert to channel number
m_channel = channel & 0x07; // ruthlessly coerce to an acceptable value
#endif
}

volatile bool CT_Control::adcBusy;
volatile int CT_Control::adcVal;
const uint16_t CT_Control::sampleSize(65); // number of samples to cover one cycle
volatile bool CT_Control::adcBusy;
volatile int CT_Control::adcVal;
const uint16_t CT_Control::sampleSize(65); // number of samples to cover one cycle
const uint16_t CT_Control::ADC_MAX(1023);
const uint16_t CT_Control::OCR50(F_CPU / 50 / CT_Control::sampleSize / 2 - 1);
const uint16_t CT_Control::OCR60(F_CPU / 60 / CT_Control::sampleSize / 2 - 1);
Expand All @@ -32,13 +37,17 @@ float CT_Control::begin()
ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // default adc configuration
ADCSRB = 0;
// set AVcc as reference, 1.1V bandgap reference voltage as input
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(10); // Vref settling time
ADCSRA |= _BV(ADSC); // start conversion
loop_until_bit_is_clear(ADCSRA, ADSC); // wait for it to complete
int mv = 1125300L / ADC; // calculate AVcc in mV (1.1 * 1000 * 1023)
m_vcc = static_cast<float>(mv) / 1000.0;

// set up the timer
TCCR1B = 0; // stop the timer
TCCR1A = 0;
Expand All @@ -54,7 +63,7 @@ float CT_Control::begin()
// set up the adc
ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE); // enable ADC, auto trigger, interrupt when conversion complete
ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // ADC clock prescaler: divide by 128 (for 125kHz)
ADCSRB = _BV(ADTS2) | _BV(ADTS0); // trigger ADC on Timer/Counter1 Compare Match B
ADCSRB = _BV(ADTS2) | _BV(ADTS0); // trigger ADC on Timer/Counter1 Compare Match B

return m_vcc;
}
Expand All @@ -73,32 +82,44 @@ void CT_Control::end()

void CT_Control::read(CT_Sensor *ct0, CT_Sensor *ct1)
{
uint8_t n(0); // sample count
int32_t sumsq0(0), sumsq1(0); // sum of squares
ADMUX = _BV(REFS0) | ct0->m_channel; // set channel, and AVcc as reference
while (!adcBusy); // wait for one conversion
uint8_t n(0); // sample count
int32_t sumsq0(0), sumsq1(0); // sum of squares
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if (ct0->m_channel > 7) {ADCSRB |= _BV(MUX5);}
else {ADCSRB &= !_BV(MUX5);}
#endif
ADMUX = _BV(REFS0) | (ct0->m_channel & 0x07); // set channel, and AVcc as reference
while (!adcBusy); // wait for one conversion
while (adcBusy);

do
{
// read ct0
ADMUX = _BV(REFS0) | ct0->m_channel; // set channel, and AVcc as reference
while (!adcBusy); // wait for next conversion to start
while (adcBusy); // wait for conversion to complete
int32_t v0 = adcVal; // get the reading, promote to 32 bit
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if (ct0->m_channel > 7) {ADCSRB |= _BV(MUX5);}
else {ADCSRB &= !_BV(MUX5);}
#endif
ADMUX = _BV(REFS0) | (ct0->m_channel & 0x07); // set channel, and AVcc as reference
while (!adcBusy); // wait for next conversion to start
while (adcBusy); // wait for conversion to complete
int32_t v0 = adcVal; // get the reading, promote to 32 bit

// read ct1
ADMUX = _BV(REFS0) | ct1->m_channel; // set channel, and AVcc as reference
while (!adcBusy); // wait for next conversion to start
while (adcBusy); // wait for conversion to complete
int32_t v1 = adcVal; // get the reading, promote to 32 bit
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if (ct1->m_channel > 7) {ADCSRB |= _BV(MUX5);}
else {ADCSRB &= !_BV(MUX5);}
#endif
ADMUX = _BV(REFS0) | (ct1->m_channel & 0x07); // set channel, and AVcc as reference
while (!adcBusy); // wait for next conversion to start
while (adcBusy); // wait for conversion to complete
int32_t v1 = adcVal; // get the reading, promote to 32 bit

// accumulate the sum of squares,
// subtract 512 (half the adc range) to remove dc component
sumsq0 += (v0 - ADC_MAX/2) * (v0 - ADC_MAX/2);
sumsq1 += (v1 - ADC_MAX/2) * (v1 - ADC_MAX/2);
} while (++n < sampleSize);

// calculate rms voltage and current
float Vrms0 = m_vcc * sqrt(static_cast<float>(sumsq0) / static_cast<float>(sampleSize - 1)) / ADC_MAX;
float Vrms1 = m_vcc * sqrt(static_cast<float>(sumsq1) / static_cast<float>(sampleSize - 1)) / ADC_MAX;
Expand Down

0 comments on commit cda538b

Please sign in to comment.