Pages: [1]   Go Down
Author Topic: Arduino to Stand-alone ATMEGA328 Communication Problem  (Read 803 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


My project is a cable distance measurement system for adverse environments.  The cable reel has an optical encoder which provides 100 pulses per revolution.  I am using an Arduino Uno to count the number of pulses in both a clockwise and counterclockwise direction to determine the number of cable reel rotations.  The number of rotations is then converted to the length of cable deployed.  The amount of cable deployed is then displayed on a display using the Nootronics Video Experimenter and the TVOut library.  This system works great when using non-optical encoders with fewer pulses per revolution.  I have also constructed a stand alone circuit card with the ATMEGA328P chip and the LM1881 sync separator chip and it also works great.  The problem I am having is for high resolution optical encoders that I have to interface to for some projects. Pulses are missed when the Arduino is off servicing the video stuff.  One solution is to have 2 processors: one for the video overlay processing and the other for the pulse counting/direction processing.  I am testing this approach now using the Arduino Uno for one processor (video overlay) and a stand alone ATMEGA 328P (pulse counting/direction) for the other processor.
I know that I am making this too difficult, but I am having problems trying to pass the number of pulses counted (unsigned integer) from the pulse counting/direction processor to the other processor using the serial interface.  I can't use Interrupts on the video overlay processor since they cause interference with the TVOut library, so I am left with using the pollserial library.  Pollserial does not have the capability of the standard interrupt driven serial interface (e.g. no parseInt() or readBytesUntil() functions).  Using the test code implementation, I am able to send a single ASCII byte over the interface, but I haven't found a way to send multiple bytes of integer data.
Please provide help in possible approaches to sending two bytes of integer data to the overlay processor within the constraints of having to use the pollserial library for serial interface approaches.  The test code for both processors compiles and executes satisfactorily and is included below.


ATMEGA328P Code:
Code:
#include <pollserial.h>
#include <digitalWriteFast.h>
#define encoderAPin  2  // the pin that Channel A is attached to
#define encoderBPin  4  // the pin that Channel B is attached to
pollserial pserial;
const char startOfNumberDelimiter = '<';
const char endOfNumberDelimiter = '>';
// Variable definition
volatile long encoderOutputCounter = 0.0;   // counter for the number of encoder pulses
unsigned long time = 0;

void setup()  {
  pinMode(encoderAPin, INPUT);
  pinMode(encoderBPin, INPUT);
  attachInterrupt(0, readEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
  pserial.begin(9600);
}

void loop() {
  if((millis()-time)>1000)  {
    pserial.print(startOfNumberDelimiter);
    pserial.print(encoderOutputCounter);
    pserial.print(endOfNumberDelimiter);
    pserial.println();
    time = millis(); 
  }   
}


void readEncoder() {
  /* Read the encoder outputs on the rising and falling edges of the encoder channel A.
   If the encoder channel A and encoder channel B outputs are both high or both low, it is spinning
   forward. If they are different, it's going backward.
   */
  if (digitalReadFast(encoderAPin) == digitalReadFast(encoderBPin)) {
    encoderOutputCounter++;
  }
  else {
    encoderOutputCounter--;
  }
}



Arduino Uno Code:
Code:
#include <pollserial.h>
#include <TVout.h>
#include <fontALL.h>
#include <digitalWriteFast.h>

#define W 136
#define H 96
pollserial pserial;
TVout tv;
unsigned char originx = 4;
unsigned char originy = 78;
const int displayBlankPin = 10;   // the pin that the display blanking wwitch is attached to
const float minDrumDia = 22.0;  // the initial drum diameter in inches
const float maxDrumDia = 25.0;
const float PulseCountPerRev = 200.0;
const char startOfNumberDelimiter = '<';
const char endOfNumberDelimiter = '>';

// Variable definition
volatile long encoderOutputCounter = 0.0;   // counter for the number of encoder pulses
float drumDia=0.0;
float cableDist = 0.0;  //amount of cable deployed
unsigned long time;

void setup()  {
  tv.begin(NTSC, W, H);
  tv.select_font(font4x6);
  initOverlay();
  tv.fill(0);
  pinMode (displayBlankPin, INPUT);
  digitalWrite(displayBlankPin, HIGH);   // tie other side of display blanking switch to ground
  tv.set_hbi_hook(pserial.begin(9600));
}


// Initialize ATMega registers for video overlay capability.
// Must be called after tv.begin().

void initOverlay() {
  TCCR1A = 0;
  // Enable timer1.  ICES1 (Bit 6) is set to 0 for falling edge detection on input capture pin.
  TCCR1B = _BV(CS10);

  // Enable input capture interrupt
  TIMSK1 |= _BV(ICIE1);

  // Enable external interrupt INT0 on pin 2 with falling edge.
  EIMSK = _BV(INT0);
  EICRA = _BV(ISC01);
}

// Required to reset the scan line when the vertical sync occurs
ISR(INT0_vect) {
  display.scanLine = 0;
}

void processInput()
{
  static long receivedNumber = 0;
  static boolean negative = false;
  byte c = pserial.read();
  switch(c)
  {
  case endOfNumberDelimiter:
    if(negative)
      encoderOutputCounter = -receivedNumber;
    else
      encoderOutputCounter = receivedNumber;
  case startOfNumberDelimiter:
    receivedNumber = 0;
    negative = false;
    break;

  case '0'...'9':
    receivedNumber*=10;
    receivedNumber+=c-'0';
    break;

  case'-':
    negative=true;
    break;

  }   //end of switch
}   //end of processInput


void loop() {
  if(pserial.available())
    processInput();

  // Drum diameter compensation loop; provides estimate of drum diameter as a function of cable deployed
  if((encoderOutputCounter)>=0 && (encoderOutputCounter)<2400)  {  //1 row of cable deployed
    drumDia=minDrumDia;
    goto exitDiaComp;
  }

  if((encoderOutputCounter)>=2400 && (encoderOutputCounter)<4800)  {  //2 rows of cable deployed
    drumDia=(minDrumDia + (minDrumDia+1.0))/2.0;
    goto exitDiaComp;
  }

  if((encoderOutputCounter)>=4800 && (encoderOutputCounter)<7200)  {   //3 rows of cable deployed
    drumDia=(minDrumDia + (minDrumDia+2.0))/2.0;
    goto exitDiaComp; 
  }

  if((encoderOutputCounter)>=7200 && (encoderOutputCounter)<9600)  {   //4 rows of cable deployed
    drumDia=(minDrumDia + (minDrumDia+3.0))/2.0;
    goto exitDiaComp;
  }

  if((encoderOutputCounter)>=9600 && (encoderOutputCounter)<12000)  {   //5 rows of cable deployed
    drumDia=(minDrumDia + (minDrumDia+4.0))/2.0;
    goto exitDiaComp;
  }


  if((encoderOutputCounter)>=12000)  {   //greater than 5 rows of cable deployed
    drumDia=maxDrumDia;
    goto exitDiaComp;
  }
exitDiaComp:

  cableDist = 3.14*(drumDia/12.00)*(encoderOutputCounter/PulseCountPerRev);
  // amount of cable in feet that have been deployed



  // save the current encoder state as the last encoder state
  // for the next time through the loop
  if(digitalRead(displayBlankPin)==HIGH)   {

    tv.println(originx,originy,encoderOutputCounter);
    tv.print(originx, originy+6, cableDist,1);
    tv.println("  FEET   ");
  }
  else {
    tv.fill(0);
  }
}




I appreciate any assistance that you can provide.
« Last Edit: February 02, 2013, 08:07:06 pm by rickl » Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 610
Posts: 49071
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
The number of rotations is then converted to the length of cable deployed.
Since one revolution of the spool feeds off more wire when the spool is full than when it is nearly empty, how are you relating revolutions to length?

Quote
I appreciate any assistance that you can provide.
Code should be posted between code tags. Modify your post. Select the code. Press the icon with the # on it (above the smiley faces). Save your changes.

If you want to be really nice, open the IDE, open your sketch, use Tools + Auto Format, and copy the properly indented code in place of what you posted.

Quote
Using the test code implementation, I am able to send a single ASCII byte over the interface, but I haven't found a way to send multiple bytes of integer data.
Your sender is sending the integer as ASCII data. Your reader is reading the data one character at a time, and assuming that each character is a full packet, and that each character is a byte, rather than a letter.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you for your response.  I appreciate your suggestions as to how to improve the format of my post.  I guess I didn't do too well on my first post.  I have re-posted my code based on your suggestions.  This is the latest version of the code for both processors that addresses some of the serial interface issues.  The communication between the processors now works some of the time.  I am adding filters to the encoder lines to help address noise pick-up issues.
The cable reel (sewer machine) feeds from the inside out.  It has approximately 5-6 layers of cable with each layer approx 12 turns.  I have included in the code a simplistic diameter compensation routine to partially compensate for the changing diameter of the reel.  The cable length measurement only needs to be within 5%.  After the prototype is completed, I plan to perform testing to improve the accuracy of this algorithm.
Any further comments/suggestions would be greatly appreciated.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 610
Posts: 49071
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Any further comments/suggestions would be greatly appreciated.
Places that sell wire by the foot, or carpet by the foot, don't count turns of the roll. They run the material being dispensed through a device with a pair (or trio for wire). The wheels press against the material on two (or three) sides. One of the wheels is equipped with an encoder that more accurately determines length that counting turns of the reel.

Quote
I can't use Interrupts on the video overlay processor since they cause interference with the TVOut library, so I am left with using the pollserial library.
I've never heard of this library, so a link would be nice.

On the UNO, none of those gotos are needed. Use if/else if/else to make sure only one block can possibly be true.

Quote
Please provide help in possible approaches to sending two bytes of integer data to the overlay processor within the constraints of having to use the pollserial library for serial interface approaches.
You are not sending two bytes of data. A long, signed or unsigned is 4 bytes, but you are sending the data as a string, not as a collection of bytes. Once the count exceeds 99, you are sending more than 2 bytes of data (4 counting the start and end markers).
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks to Nick Gammon for his post "How to send and receive numbers over serial interface".  I have embedded his code with minimal changes in my software and the counter works reliably for most conditions.  Counts are not missed when rapidly rotating the reel in either a clockwise or counterclockwise direction.  Only one problem remains:
Test case used to precipitate problem:

(1)  Rotate the reel in a clockwise direction for 2000 counts.
(2)  Without resetting the counter, rotate the reel in a counterclockwise direction.
(3)  This countdown of the pulse count works correctly from 2000 counts down to 1000 counts.
(4)  One additional countdown pulse causes the counter to go to 9999 (instead of 999).  The count rate at this point also increases by an order of magnitude (should be 200 counts per revolution, now 2000 counts per revolution).

It appears the counter needs to be shifted right by one digit.  The problem is in the processInput subroutine, but I don't have a clue as to how to solve it.  Any ideas?  The current code for the Arduino Uno processor is as follows:

Code:
#include <pollserial.h>
#include <TVout.h>
#include <fontALL.h>
#include <digitalWriteFast.h>

#define W 136
#define H 96
pollserial pserial;
TVout tv;
unsigned char originx = 4;
unsigned char originy = 78;
const int displayBlankPin = 10;   // the pin that the display blanking wwitch is attached to
const float minDrumDia = 22.0;  // the initial drum diameter in inches
const float maxDrumDia = 25.0;
const float PulseCountPerRev = 200.0;
const char startOfNumberDelimiter = '<';
const char endOfNumberDelimiter = '>';

// Variable definition
volatile long encoderOutputCounter = 0.0;   // counter for the number of encoder pulses
float drumDia=0.0;
float cableDist = 0.0;  //amount of cable deployed
unsigned long time;

void setup()  {
  tv.begin(NTSC, W, H);
  tv.select_font(font4x6);
  initOverlay();
  tv.fill(0);
  pinMode (displayBlankPin, INPUT);
  digitalWrite(displayBlankPin, HIGH);   // tie other side of display blanking switch to ground
  tv.set_hbi_hook(pserial.begin(9600));
}


// Initialize ATMega registers for video overlay capability.
// Must be called after tv.begin().

void initOverlay() {
  TCCR1A = 0;
  // Enable timer1.  ICES1 (Bit 6) is set to 0 for falling edge detection on input capture pin.
  TCCR1B = _BV(CS10);

  // Enable input capture interrupt
  TIMSK1 |= _BV(ICIE1);

  // Enable external interrupt INT0 on pin 2 with falling edge.
  EIMSK = _BV(INT0);
  EICRA = _BV(ISC01);
}

// Required to reset the scan line when the vertical sync occurs
ISR(INT0_vect) {
  display.scanLine = 0;
}

long processInput()
{
  static long receivedNumber = 0;
  static boolean negative = false;
  byte c = pserial.read();
  switch(c)
  {
  case endOfNumberDelimiter:
    if(negative)
      encoderOutputCounter = -receivedNumber;
    else
      encoderOutputCounter = receivedNumber;
  case startOfNumberDelimiter:
    receivedNumber = 0;
    negative = false;
    break;

  case '0'...'9':
    receivedNumber*=10;
    receivedNumber+=c-'0';
    break;

  case'-':
    negative=true;
    break;

  }   //end of switch

  return encoderOutputCounter;

}   //end of processInput


void loop() {
  if(pserial.available())
    processInput();

  // Drum diameter compensation loop; provides estimate of drum diameter as a function of cable deployed
  if((encoderOutputCounter)>=0 && (encoderOutputCounter)<2400)  {  //1 row of cable deployed
    drumDia=minDrumDia;
  }

  else if((encoderOutputCounter)>=2400 && (encoderOutputCounter)<4800)  {  //2 rows of cable deployed
    drumDia=(minDrumDia + (minDrumDia+1.0))/2.0;
  }

  else if((encoderOutputCounter)>=4800 && (encoderOutputCounter)<7200)  {   //3 rows of cable deployed
    drumDia=(minDrumDia + (minDrumDia+2.0))/2.0;
  }

  else if((encoderOutputCounter)>=7200 && (encoderOutputCounter)<9600)  {   //4 rows of cable deployed
    drumDia=(minDrumDia + (minDrumDia+3.0))/2.0;
  }

  else if((encoderOutputCounter)>=9600 && (encoderOutputCounter)<12000)  {   //5 rows of cable deployed
    drumDia=(minDrumDia + (minDrumDia+4.0))/2.0;
  }


  else if((encoderOutputCounter)>=12000)  {   //greater than 5 rows of cable deployed
    drumDia=maxDrumDia;
  }

  cableDist = 3.14*(drumDia/12.00)*(encoderOutputCounter/PulseCountPerRev);
  // amount of cable in feet that have been deployed



  // save the current encoder state as the last encoder state
  // for the next time through the loop
  if(digitalRead(displayBlankPin)==HIGH)   {

    tv.println(originx,originy,encoderOutputCounter);
    tv.print(originx, originy+6, cableDist,1);
    tv.println("  FEET   ");
  }
  else {
    tv.fill(0);
  }
}




Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Problem solved!  System is now fully functional.  The display counter anomaly was caused by an erroneous display presentation in that the far right number in the reel rotation counter was not overwriting the previous display presentation.  This left an extraneous number in the rightmost place that gave the appearance that the counter was in error by an order of magnitude.  This counter was deleted since it was for debug purposes only.  The cable distance display works correctly.
Logged

Pages: [1]   Go Up
Jump to: