Arduino Due - manchester decoding with ADC comparator and interrupt

Hello community!

First of all: thanks a lot for you enourmos efforts of you to help everyone who asks for help. I've spent hours reading through your forum, benefiting of your knowledge.

Nevertheless, this time I was not able to find a solution for my specific problem - even though I thought it should be some kind of standard problem...

First of all, I try to give you all the relevant information needed:

HW: Arduino Due; so far no real sensor in use, only jumper cables for testing purposes of rising and falling edges
My skills: I'm familiar with programming in C, unfortunately not the highest level. Never worked with arduino before my actual project to be honest
My goal: Read (and later on interprete) a manchester protocol. In more detail: The protocol is current modulated with three levels at 7, 14 and 28mA, measured over a 50 Ohm resistor. Thus I have voltage levels at 0.35, 0.7 and 1.4 Volts. The first pulse of the cyclic sent protocol (9 bit long) starts with 28 or 14 mA, after that only data on 7 and 14 mA levels are transmitted.

My problem: As the code is manchester encoded and sent cyclic, I try to catch the beginning and all following edges with an ISR. This ISR should be triggered by a certain voltage (over the lowes level of 0.35V, e.g. 0.5V) and then detect every rising or falling edge. I did a lot of research but my coding experience seems to be too bad to understand everything I can find in regards of the ADC setup. During my research I found that I seem to have two possible ways to solve my problem:

  1. Use an external comparator which identifies the first rising edge which then sends a digital value to a digital pin to then use an ISR to get the ADC value
    OR
  2. Use the ADC configuration to program an comparator within the ADC which then saves the actual value of the ADC within an ISR.

I would love to go with the second solution.

During my research I found a - on the first look - very suitable solution for this:
https://forum.arduino.cc/t/analog-rising-and-falling-edge-detect-and-interrupt-on-arduino-due/640089
Trying to run this code on my Due after the mentioned modifications (placing the "ADC->ADC_ISR;" at the end and switching thresholds to "if (!Look_for_Rising){ADC->ADC_CWR = ADC_CWR_LOWTHRES(2048) | ADC_CWR_HIGHTHRES(4095);} // looking for falling"), I got some ghost edges detected from time to time. Also during startup I already get some rising edges detected.

Most confusing for me so far: the serial monitor detects edged independent of which analog pin I use. I tried DAC1, DAC2, A0, A1 and for all of them some edged are detected. Also I always get the pair of rising and falling edge right after each other - the Pin is still "connected" to the 3.3V board supply pin so no falling edge should be detected?!

What did I mess up here?

One addition which might cause some problems later on: I just need an ADC resolution of 8 Bit. As I understand, this could be easily done with modifying
"ADC_MR_LOWRES_BITS_12" to "ADC_MR_LOWRES_BITS_8"?

I hope some of you have an idea how to solve my problem :slight_smile:

Here is my complete code so far:

volatile boolean Look_for_Rising=true; // true is looking for rising/false is looking for falling

void setup() {
  Serial.begin(115200);
  adc_setup();
}

void loop() {
}

void adc_setup ()
{
  PMC->PMC_PCER1 |= PMC_PCER1_PID37;                    // ADC power ON
  ADC->ADC_WPMR = 0x41444300;                           // Disable write protect 
  ADC->ADC_CR = ADC_CR_SWRST;                           // Reset ADC
  ADC->ADC_MR |= ADC_MR_FREERUN_ON                      // ADC in free running mode
                 | ADC_MR_LOWRES_BITS_12                // 12 bits resolution
                 | ADC_MR_PRESCAL(1)
                 | ADC_MR_SETTLING_AST3
                 | ADC_MR_TRACKTIM(10)
                 | ADC_MR_TRANSFER(2);

  ADC->ADC_EMR = ADC_EMR_CMPMODE_OUT                    // Generates an event when the converted data is out of the comparison window.
                 | ADC_EMR_CMPSEL(0)                    // Compare channel 0 = A7
                 | ADC_EMR_CMPFILTER(0);                // Number of consecutive compare events necessary
                                                        // to raise the flag = CMPFILTER+1

  ADC->ADC_CWR = ADC_CWR_LOWTHRES(0) | ADC_CWR_HIGHTHRES(2047); // at beginning, looking for rising edge
  
  ADC->ADC_CHER = ADC_CHER_CH0;                         // Enable Channel 0 = A7
  ADC->ADC_IER = ADC_IER_COMPE;                         // Interrupt on Compare match enable
  NVIC_EnableIRQ(ADC_IRQn);                             // Enable ADC interrupt
}

void ADC_Handler() {
  // print what kind of interrupt is this 
  // 1=found Rising edge; 0=found Falling edge 
  //Serial.print("Looking for rising (1) or falling(0): ");
  
  if (Look_for_Rising == false){
    Serial.print("now looking for falling (0)! ");
  }
  if (Look_for_Rising == true){
    Serial.print("now looking for rising (1)! ");
  }

  Serial.println(Look_for_Rising); 
  
  Look_for_Rising=!Look_for_Rising; // invert what to looking for
    
  if (!Look_for_Rising){ADC->ADC_CWR = ADC_CWR_LOWTHRES(2048) | ADC_CWR_HIGHTHRES(4095);} // looking for falling
  if (Look_for_Rising) {ADC->ADC_CWR = ADC_CWR_LOWTHRES(0)    | ADC_CWR_HIGHTHRES(2047);} // looking for raising

  ADC->ADC_ISR; // Read and clear status register
}

Edit: So far I did not modify the thresholds in such a way to identify the three different levels - this needs to be done after I got a working code for only a digital rising/falling edge.

Nobody got an idea on how to configure the ADC properly?

To do a workaround, I started working with an interrupt generated by an digital pin by using a comparator (LM339). This is my ISR so far without testing it (I still need to set up the hardware):

// Solution with hardware comparator LM339

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <FiniteStateMachine.h> //Idee von https://www.teachmemicro.com/arduino-state-machine-tutorial/
#include <CircularBuffer.h> // https://github.com/rlogiacco/CircularBuffer

LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x27 (default) for a 20 chars and 4 line display

/* --- Variable naming ---
   first digit: type of variable (int = i, double = d, ...)
   second part: characteristic of variable (Button = Btt, variable = V, ...)
   third part: Name of variable
   ending: marking global variables with a '_g' to identify as global
*/

// UI-related variables and definitions
const int Btt_Up = 5;           // Button for 'UP'
int iBtt_Up_status_g = LOW;   // initial Digital value of UP
const int Btt_Down = 6;         // Button for 'DOWN'
int iBtt_Down_status_g = LOW; // initial Digital value of DOWN
const int Btt_Ok = 7;           // Button for 'OK'
int iBtt_Ok_status_g = LOW;   // initial Digital value of OK
const int Btt_Back = 8;         // Button for 'BACK'
int iBtt_Back_status_g = LOW; // initial Digital value of BACK
const int iBtt_Save = 9;      // Button for 'SAVE'
int iBtt_Save_status_g = LOW; // initial Digital value of SAVE
int iV_Cursor_g = 1;          // interne Variable für aktuelle Cursorposition

// Manchester decoding related variables and definitions
//CircularBuffer<byte, 1000> Buffer; // max. 96kB of RAM available onboard
volatile unsigned long ulV_ActualTime_g = 0;   // unsigned long for micros function (actual runtime in uSec)
volatile unsigned long ulV_PreviousTime_g = 0; // unsigned long for micros function (actual runtime in uSec)
volatile unsigned long ulV_ActualTp_g = 0;     // saves the pulse width of the actual received protocol (t_p) --> speed(replacement)pulse defines this value for each protocol to compensate tolerances
volatile byte bV_ActualADCvalue_g = 0;               // actual value read out at the ADC
volatile byte bV_PreviousADCvalue_g = 0;               // previous value read out at the ADC
volatile byte bV_InformationEdgeFlag_g = false;   // indicates if seen edge contains data or if edge is only the needed change for upcoming data edge
volatile byte bV_actualDataProtocol_g[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // array for actual received data protocol; defined as volatile to ensure correct data change during ISR;
// first byte displays the speedpulse (1) or speed replacement pulse (0)
volatile byte bV_DataProtocolPosition_g = 0; // counter to display the bit-number of actual data protocol (0-9); defined as volatile to ensure correct data change during ISR

///////////////////   FSM    ///////////////////////
//define states and working functions of the FSM
State START = State(startScreen);
//[...] more states in complete code

//initiate state machine(s)
FSM Tester = FSM(START); // FSM initially starts up in state "START"
////////////////////////////////////////////////////

void setup()
{
  lcd.init();                      // initialize the lcd, driven by I2C interface
  lcd.backlight();

  Serial.begin(115200);
  byte Serialbuffer[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

  pinMode(Btt_Up, INPUT);
  pinMode(Btt_Down, INPUT);
  pinMode(Btt_Ok, INPUT);

  analogReadResolution(8); // ADC resolution set to 8 bit for optimized data consupmtion --> one value can be stored in a byte variable

  ///////////////////   ISR for data protocol    //////////////////
  // ISR using an external comparator, connected to digital pin 1 to trigger the ISR for catching the actual ADC value
  attachInterrupt(digitalPinToInterrupt(1), ISR_DataProtocol, CHANGE); // Pin 1 used for ISR

}


void loop()
{
  Tester.update();  
}

//////////////////////////  FSM States   ///////////////////////////////////
--> here comes the FSM which steers all the different possible tests performed later on
--> too long for a example to show the complete code here


//////////////////////////   ISR for manchester Protocol   ///////////////////////
void ISR_DataProtocol() {
  ulV_PreviousTime_g = ulV_ActualTime_g;
  ulV_ActualTime_g = micros();

  bV_PreviousADCvalue_g = bV_ActualADCvalue_g;
  bV_ActualADCvalue_g = analogRead(A0); // read ADC value

  if (ulV_ActualTime_g - ulV_PreviousTime_g > 100) { // checks if more than 100 uS passed by since the last edge --> if so, a new protocol seems to start
    bV_DataProtocolPosition_g = 0;
    //memset(bV_actualDataProtocol_g,0,sizeof(bV_actualDataProtocol_g)); // clears the data protocol of last protocol cycle
    for (int i = 0; i < 9; i++) {
      bV_actualDataProtocol_g[i] = 0; // clears the data protocol of last protocol cycle
    }
    //bV_actualDataProtocol_g[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // clears the data protocol of last protocol cycle
  }

  if (bV_DataProtocolPosition_g == 0) {
    if (bV_ActualADCvalue_g > 70) { // checks if speed pulse or speed replacement pulse is detected; 0.9V*(256/3.3V)=70
      bV_actualDataProtocol_g[0] = 1;
      return; // ends ISR at this point to save time --> other conditions don't need to be checked in this special case
    }
    if (bV_ActualADCvalue_g < bV_PreviousADCvalue_g) { // checks if actual measurement position is within t_p/2 after speed(replacement)pulse; 0,5V(256/3.3V)=38
      bV_DataProtocolPosition_g++; // increments protocol position by 1
      ulV_ActualTp_g = ulV_ActualTime_g - ulV_PreviousTime_g; // calculates the actual pulse width T_p for this cycle, depending of the speed(replacement)pulse width
      return; // ends ISR at this point to save time --> other conditions don't need to be checked in this special case
    }
  }

  if (bV_InformationEdgeFlag_g == true) { // sets information edge flag --> does the edge contain data or is it only a level change for the upcoming data?
    if ((ulV_ActualTime_g - ulV_PreviousTime_g) < (ulV_ActualTp_g * 0.8)) { // if time between two edges is smaller than minimal 80% of pulse width
      bV_InformationEdgeFlag_g == false;
      return; // ends ISR at this point to save time --> other conditions don't need to be checked in this special case

      // not needed because flag is already on TRUE:
      //}else if((ulV_ActualTime_g - ulV_PreviousTime_g)>(ulV_ActualTp_g * 0.8)){ // if no transition edge was needed due to bit change logic in manchester protocol (01 or 10), next edge is also containing information
      //  bV_InformationEdgeFlag_g == true;
      //}
    }
  } else { // Information flag is FALSE, then:
    if ((ulV_ActualTime_g - ulV_PreviousTime_g) < (ulV_ActualTp_g * 0.8)) { // if time between two edges is smaller than minimal 80% of pulse width
      bV_InformationEdgeFlag_g == true;
    }
  }


  if (bV_InformationEdgeFlag_g == true) {
    if (bV_ActualADCvalue_g > bV_PreviousADCvalue_g) { // calculates if bit states 0 or 1
      bV_actualDataProtocol_g[bV_DataProtocolPosition_g] = 1;
    } else {
      bV_actualDataProtocol_g[bV_DataProtocolPosition_g] = 0;
    }
  }


  bV_DataProtocolPosition_g++; // increments protocol position by 1

}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.