mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 08:06:13 +01:00
Interrupt time ACK manager
This commit is contained in:
parent
5a23fc717f
commit
b0debd1fab
65
DCC.cpp
65
DCC.cpp
|
@ -402,10 +402,6 @@ byte DCC::ackManagerStash;
|
||||||
int DCC::ackManagerCv;
|
int DCC::ackManagerCv;
|
||||||
byte DCC::ackManagerBitNum;
|
byte DCC::ackManagerBitNum;
|
||||||
bool DCC::ackReceived;
|
bool DCC::ackReceived;
|
||||||
int DCC::ackTriggerMilliamps;
|
|
||||||
unsigned long DCC::ackPulseStart;
|
|
||||||
int DCC::ackMaxCurrent;
|
|
||||||
int DCC::ackPollCount;
|
|
||||||
bool DCC::debugMode=false;
|
bool DCC::debugMode=false;
|
||||||
|
|
||||||
ACK_CALLBACK DCC::ackManagerCallback;
|
ACK_CALLBACK DCC::ackManagerCallback;
|
||||||
|
@ -416,8 +412,6 @@ void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[]
|
||||||
ackManagerByte = byteValueOrBitnum;
|
ackManagerByte = byteValueOrBitnum;
|
||||||
ackManagerBitNum=byteValueOrBitnum;
|
ackManagerBitNum=byteValueOrBitnum;
|
||||||
ackManagerCallback = callback;
|
ackManagerCallback = callback;
|
||||||
ackMaxCurrent=0;
|
|
||||||
ackPollCount=0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const byte RESET_MIN=8; // tuning of reset counter before sending message
|
const byte RESET_MIN=8; // tuning of reset counter before sending message
|
||||||
|
@ -434,10 +428,8 @@ void DCC::ackManagerLoop() {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case BASELINE:
|
case BASELINE:
|
||||||
if (resets<RESET_MIN) return; // try later
|
if (resets<RESET_MIN) return; // try later
|
||||||
ackTriggerMilliamps=Hardware::getCurrentMilliamps(false) + ACK_MIN_PULSE;
|
DCCWaveform::progTrack.setAckBaseline(debugMode);
|
||||||
if (debugMode) DIAG(F("\nACK_BASELINE trigger mA=%d\n"),ackTriggerMilliamps);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case W0: // write 0 bit
|
case W0: // write 0 bit
|
||||||
case W1: // write 1 bit
|
case W1: // write 1 bit
|
||||||
{
|
{
|
||||||
|
@ -445,10 +437,8 @@ void DCC::ackManagerLoop() {
|
||||||
byte instruction = WRITE_BIT | (opcode==W1 ? BIT_ON : BIT_OFF) | ackManagerBitNum;
|
byte instruction = WRITE_BIT | (opcode==W1 ? BIT_ON : BIT_OFF) | ackManagerBitNum;
|
||||||
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
|
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
|
||||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 6);
|
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 6);
|
||||||
ackPulseStart=0;
|
DCCWaveform::progTrack.setAckPending(debugMode);
|
||||||
ackMaxCurrent=0;
|
}
|
||||||
ackPollCount=0;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WB: // write byte
|
case WB: // write byte
|
||||||
|
@ -456,9 +446,7 @@ void DCC::ackManagerLoop() {
|
||||||
if (resets<RESET_MIN) return; // try later
|
if (resets<RESET_MIN) return; // try later
|
||||||
byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
|
byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
|
||||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 6);
|
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 6);
|
||||||
ackPulseStart=0;
|
DCCWaveform::progTrack.setAckPending(debugMode);
|
||||||
ackMaxCurrent=0;
|
|
||||||
ackPollCount=0;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -468,9 +456,7 @@ void DCC::ackManagerLoop() {
|
||||||
if (debugMode) DIAG(F("\nVB %d %d"),ackManagerCv,ackManagerByte);
|
if (debugMode) DIAG(F("\nVB %d %d"),ackManagerCv,ackManagerByte);
|
||||||
byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
|
byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
|
||||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5);
|
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5);
|
||||||
ackPulseStart=0;
|
DCCWaveform::progTrack.setAckPending(debugMode);
|
||||||
ackMaxCurrent=0;
|
|
||||||
ackPollCount=0;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -482,47 +468,16 @@ void DCC::ackManagerLoop() {
|
||||||
byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum;
|
byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum;
|
||||||
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
|
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
|
||||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5);
|
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5);
|
||||||
ackPulseStart=0;
|
DCCWaveform::progTrack.setAckPending(debugMode);
|
||||||
ackMaxCurrent=0;
|
|
||||||
ackPollCount=0;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WACK: // wait for ack (or absence of ack)
|
case WACK: // wait for ack (or absence of ack)
|
||||||
{
|
{
|
||||||
if (resets > 6) { //ACK timeout
|
byte ackState=DCCWaveform::progTrack.getAck(debugMode);
|
||||||
if (debugMode) DIAG(F("\nWACK fail polls=%d, resets=%d, max=%dmA"), ackPollCount, resets, ackMaxCurrent);
|
if (ackState==2) return; // keep polling
|
||||||
ackReceived = false;
|
ackReceived=ackState==1;
|
||||||
break; // move on to next prog step
|
break; // we have a genuine ACK result
|
||||||
}
|
|
||||||
|
|
||||||
int current=Hardware::getCurrentMilliamps(false);
|
|
||||||
if (current > ackMaxCurrent) ackMaxCurrent=current;
|
|
||||||
|
|
||||||
ackPollCount++;
|
|
||||||
|
|
||||||
// An ACK is a pulse lasting between 4.5 and 8.5 mSecs (refer @haba)
|
|
||||||
|
|
||||||
if (current>ackTriggerMilliamps) {
|
|
||||||
if (ackPulseStart==0) ackPulseStart=micros(); // leading edge of pulse detected
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// not in pulse
|
|
||||||
if (ackPulseStart==0) return; // keep waiting for leading edge
|
|
||||||
|
|
||||||
// detected trailing edge of pulse
|
|
||||||
long pulseDuration=micros()-ackPulseStart;
|
|
||||||
|
|
||||||
if (pulseDuration>1000 && pulseDuration<9000) {
|
|
||||||
if (debugMode) DIAG(F("\nWACK-OK polls=%d, max=%dmA, pulse=%duS"),ackPollCount, ackMaxCurrent, pulseDuration);
|
|
||||||
ackReceived=true;
|
|
||||||
DCCWaveform::progTrack.killRemainingRepeats(); // probably no need after 8.5ms!!
|
|
||||||
break; // we have a genuine ACK result
|
|
||||||
}
|
|
||||||
if (debugMode) DIAG(F("\nWACK-bad pulse polls=%d, max=%dmA, now=%dmA, pulse=%duS"), ackPollCount, ackMaxCurrent,current, pulseDuration);
|
|
||||||
ackPulseStart=0; // We have detected a too-short or too-long pulse so ignore and wait for next leading edge
|
|
||||||
return; // keep waiting
|
|
||||||
}
|
}
|
||||||
case ITC0:
|
case ITC0:
|
||||||
case ITC1: // If True Callback(0 or 1) (if prevous WACK got an ACK)
|
case ITC1: // If True Callback(0 or 1) (if prevous WACK got an ACK)
|
||||||
|
|
4
DCC.h
4
DCC.h
|
@ -86,10 +86,6 @@ private:
|
||||||
static int ackManagerCv;
|
static int ackManagerCv;
|
||||||
static byte ackManagerStash;
|
static byte ackManagerStash;
|
||||||
static bool ackReceived;
|
static bool ackReceived;
|
||||||
static int ackTriggerMilliamps;
|
|
||||||
static int ackMaxCurrent;
|
|
||||||
static int ackPollCount;
|
|
||||||
static unsigned long ackPulseStart;
|
|
||||||
static ACK_CALLBACK ackManagerCallback;
|
static ACK_CALLBACK ackManagerCallback;
|
||||||
static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback);
|
static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback);
|
||||||
static void ackManagerLoop();
|
static void ackManagerLoop();
|
||||||
|
|
|
@ -59,6 +59,7 @@ DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
|
||||||
bits_sent = 0;
|
bits_sent = 0;
|
||||||
sampleDelay = 0;
|
sampleDelay = 0;
|
||||||
lastSampleTaken = millis();
|
lastSampleTaken = millis();
|
||||||
|
ackPending=false;
|
||||||
}
|
}
|
||||||
void DCCWaveform::beginTrack() {
|
void DCCWaveform::beginTrack() {
|
||||||
setPowerMode(POWERMODE::ON);
|
setPowerMode(POWERMODE::ON);
|
||||||
|
@ -138,9 +139,6 @@ bool DCCWaveform::interrupt1() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCCWaveform::killRemainingRepeats() {
|
|
||||||
transmitRepeats=0; // will go idle at end of current packet
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCCWaveform::interrupt2() {
|
void DCCWaveform::interrupt2() {
|
||||||
// set currentBit to be the next bit to be sent.
|
// set currentBit to be the next bit to be sent.
|
||||||
|
@ -187,6 +185,11 @@ void DCCWaveform::interrupt2() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ACK check is prog track only and will only be checked if bits_sent=4 ...
|
||||||
|
// This means only once per 9-bit-byte AND never at the same cycle as the
|
||||||
|
// relatively expensive packet change code just above.
|
||||||
|
if (ackPending && bits_sent==4) checkAck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -211,3 +214,64 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea
|
||||||
int DCCWaveform::getLastCurrent() {
|
int DCCWaveform::getLastCurrent() {
|
||||||
return lastCurrent;
|
return lastCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Operations applicable to PROG track ONLY.
|
||||||
|
// (yes I know I could have subclassed the main track but...)
|
||||||
|
|
||||||
|
void DCCWaveform::setAckBaseline(bool debug) {
|
||||||
|
if (isMainTrack) return;
|
||||||
|
ackThreshold=Hardware::getCurrentMilliamps(false) + ACK_MIN_PULSE;
|
||||||
|
if (debug) DIAG(F("\nACK-BASELINE mA=%d\n"),ackThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCWaveform::setAckPending(bool debug) {
|
||||||
|
if (isMainTrack) return;
|
||||||
|
(void)debug;
|
||||||
|
ackMaxCurrent=0;
|
||||||
|
ackPulseStart=0;
|
||||||
|
ackPulseDuration=0;
|
||||||
|
ackDetected=false;
|
||||||
|
ackCheckStart=millis();
|
||||||
|
ackPending=true; // interrupt routines will now take note
|
||||||
|
}
|
||||||
|
|
||||||
|
byte DCCWaveform::getAck(bool debug) {
|
||||||
|
if (ackPending) return (2); // still waiting
|
||||||
|
if (debug) DIAG(F("\nACK-%S after %dmS max=%dmA pulse=%duS"),ackDetected?F("OK"):F("FAIL"), ackCheckDuration, ackMaxCurrent, ackPulseDuration);
|
||||||
|
if (ackDetected) return (1); // Yes we had an ack
|
||||||
|
return(0); // pending set off but not detected means no ACK.
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCWaveform::checkAck() {
|
||||||
|
// This function operates in interrupt() time so must be fast and can't DIAG
|
||||||
|
|
||||||
|
if (sentResetsSincePacket > 6) { //ACK timeout
|
||||||
|
ackCheckDuration=millis()-ackCheckStart;
|
||||||
|
ackPending = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCurrent=Hardware::getCurrentMilliamps(false);
|
||||||
|
if (lastCurrent > ackMaxCurrent) ackMaxCurrent=lastCurrent;
|
||||||
|
// An ACK is a pulse lasting between 4.5 and 8.5 mSecs (refer @haba)
|
||||||
|
|
||||||
|
if (lastCurrent>ackThreshold) {
|
||||||
|
if (ackPulseStart==0) ackPulseStart=micros(); // leading edge of pulse detected
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not in pulse
|
||||||
|
if (ackPulseStart==0) return; // keep waiting for leading edge
|
||||||
|
|
||||||
|
// detected trailing edge of pulse
|
||||||
|
ackPulseDuration=micros()-ackPulseStart;
|
||||||
|
|
||||||
|
if (ackPulseDuration>1000 && ackPulseDuration<9000) {
|
||||||
|
ackCheckDuration=millis()-ackCheckStart;
|
||||||
|
ackDetected=true;
|
||||||
|
ackPending=false;
|
||||||
|
transmitRepeats=0; // shortcut remaining repeat packets
|
||||||
|
return; // we have a genuine ACK result
|
||||||
|
}
|
||||||
|
ackPulseStart=0; // We have detected a too-short or too-long pulse so ignore and wait for next leading edge
|
||||||
|
}
|
||||||
|
|
|
@ -44,13 +44,16 @@ class DCCWaveform {
|
||||||
void schedulePacket(const byte buffer[], byte byteCount, byte repeats);
|
void schedulePacket(const byte buffer[], byte byteCount, byte repeats);
|
||||||
volatile bool packetPending;
|
volatile bool packetPending;
|
||||||
volatile byte sentResetsSincePacket;
|
volatile byte sentResetsSincePacket;
|
||||||
void killRemainingRepeats();
|
void setAckBaseline(bool debug); //prog track only
|
||||||
|
void setAckPending(bool debug); //prog track only
|
||||||
|
byte getAck(bool debug); //prog track only 0=NACK, 1=ACK 2=keep waiting
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static void interruptHandler();
|
static void interruptHandler();
|
||||||
bool interrupt1();
|
bool interrupt1();
|
||||||
void interrupt2();
|
void interrupt2();
|
||||||
|
void checkAck();
|
||||||
|
|
||||||
bool isMainTrack;
|
bool isMainTrack;
|
||||||
|
|
||||||
|
@ -75,5 +78,17 @@ class DCCWaveform {
|
||||||
POWERMODE powerMode;
|
POWERMODE powerMode;
|
||||||
unsigned long lastSampleTaken;
|
unsigned long lastSampleTaken;
|
||||||
unsigned int sampleDelay;
|
unsigned int sampleDelay;
|
||||||
|
|
||||||
|
// ACK management (Prog track only)
|
||||||
|
bool ackPending;
|
||||||
|
bool ackDetected;
|
||||||
|
int ackThreshold;
|
||||||
|
int ackMaxCurrent;
|
||||||
|
unsigned long ackCheckStart; // millis
|
||||||
|
unsigned int ackCheckDuration; // millis
|
||||||
|
|
||||||
|
unsigned int ackPulseDuration; // micros
|
||||||
|
unsigned long ackPulseStart; // micros
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <TimerOne.h> // use IDE menu Tools..Manage Libraries to locate and install TimerOne
|
#include <TimerOne.h> // use IDE menu Tools..Manage Libraries to locate and install TimerOne
|
||||||
|
#include "avdweb_AnalogReadFast.h"
|
||||||
#include "Hardware.h"
|
#include "Hardware.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
|
@ -41,7 +42,12 @@ void Hardware::setSignal(bool isMainTrack, bool high) {
|
||||||
int Hardware::getCurrentMilliamps(bool isMainTrack) {
|
int Hardware::getCurrentMilliamps(bool isMainTrack) {
|
||||||
int pin = isMainTrack ? MAIN_SENSE_PIN : PROG_SENSE_PIN;
|
int pin = isMainTrack ? MAIN_SENSE_PIN : PROG_SENSE_PIN;
|
||||||
float factor = isMainTrack ? MAIN_SENSE_FACTOR : PROG_SENSE_FACTOR;
|
float factor = isMainTrack ? MAIN_SENSE_FACTOR : PROG_SENSE_FACTOR;
|
||||||
int rawCurrent = analogRead(pin);
|
|
||||||
|
// IMPORTANT: This function can be called in Interrupt() time within the 56uS timer
|
||||||
|
// The default analogRead takes ~100uS which is catastrphic
|
||||||
|
// so analogReadFast is used here. (-2uS)
|
||||||
|
int rawCurrent = analogReadFast(pin);
|
||||||
|
|
||||||
return (int)(rawCurrent * factor);
|
return (int)(rawCurrent * factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,6 @@ void WifiInterface::loop(Stream & wifiStream) {
|
||||||
switch (loopstate) {
|
switch (loopstate) {
|
||||||
case 0: // looking for +
|
case 0: // looking for +
|
||||||
connectionId=0;
|
connectionId=0;
|
||||||
streamer.flush();
|
|
||||||
if (ch=='+') loopstate=1;
|
if (ch=='+') loopstate=1;
|
||||||
break;
|
break;
|
||||||
case 1: // Looking for I
|
case 1: // Looking for I
|
||||||
|
@ -129,6 +128,7 @@ void WifiInterface::loop(Stream & wifiStream) {
|
||||||
case 6: // reading for length
|
case 6: // reading for length
|
||||||
if (ch==':') loopstate=(datalength==0)?99:7; // 99 is getout without reading next char
|
if (ch==':') loopstate=(datalength==0)?99:7; // 99 is getout without reading next char
|
||||||
else datalength=datalength*10 + (ch-'0');
|
else datalength=datalength*10 + (ch-'0');
|
||||||
|
streamer.flush();
|
||||||
break;
|
break;
|
||||||
case 7: // reading data
|
case 7: // reading data
|
||||||
streamer.write(ch);
|
streamer.write(ch);
|
||||||
|
|
55
avdweb_AnalogReadFast.h
Normal file
55
avdweb_AnalogReadFast.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2016 Albert van Dalen http://www.avdweb.nl
|
||||||
|
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 3 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 at http://www.gnu.org/licenses .
|
||||||
|
|
||||||
|
AUTHOR: Albert van Dalen
|
||||||
|
WEBSITE: http://www.avdweb.nl/arduino/libraries/fast-10-bit-adc.html
|
||||||
|
|
||||||
|
HISTORY:
|
||||||
|
1.0.0 7-3-2018 analogReadFast for SAMD21 (10/12bit) and AVR (10bit)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef analogReadFast_H
|
||||||
|
#define analogReadFast_H
|
||||||
|
|
||||||
|
int inline analogReadFast(byte ADCpin);
|
||||||
|
|
||||||
|
#if defined(__arm__)
|
||||||
|
int inline analogReadFast(byte ADCpin) // inline library functions must be in header
|
||||||
|
{ ADC->CTRLA.bit.ENABLE = 0; // disable ADC
|
||||||
|
while( ADC->STATUS.bit.SYNCBUSY == 1 ); // wait for synchronization
|
||||||
|
|
||||||
|
int CTRLBoriginal = ADC->CTRLB.reg;
|
||||||
|
int AVGCTRLoriginal = ADC->AVGCTRL.reg;
|
||||||
|
int SAMPCTRLoriginal = ADC->SAMPCTRL.reg;
|
||||||
|
|
||||||
|
ADC->CTRLB.reg &= 0b1111100011111111; // mask PRESCALER bits
|
||||||
|
ADC->CTRLB.reg |= ADC_CTRLB_PRESCALER_DIV64; // divide Clock by 64
|
||||||
|
ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1 | // take 1 sample
|
||||||
|
ADC_AVGCTRL_ADJRES(0x00ul); // adjusting result by 0
|
||||||
|
ADC->SAMPCTRL.reg = 0x00; // sampling Time Length = 0
|
||||||
|
|
||||||
|
ADC->CTRLA.bit.ENABLE = 1; // enable ADC
|
||||||
|
while(ADC->STATUS.bit.SYNCBUSY == 1); // wait for synchronization
|
||||||
|
|
||||||
|
int adc = analogRead(ADCpin);
|
||||||
|
|
||||||
|
ADC->CTRLB.reg = CTRLBoriginal;
|
||||||
|
ADC->AVGCTRL.reg = AVGCTRLoriginal;
|
||||||
|
ADC->SAMPCTRL.reg = SAMPCTRLoriginal;
|
||||||
|
|
||||||
|
return adc;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int inline analogReadFast(byte ADCpin)
|
||||||
|
{ byte ADCSRAoriginal = ADCSRA;
|
||||||
|
ADCSRA = (ADCSRA & B11111000) | 4;
|
||||||
|
int adc = analogRead(ADCpin);
|
||||||
|
ADCSRA = ADCSRAoriginal;
|
||||||
|
return adc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user