PIC16F877A - Object Detection and Rejection - Timer Interrupt

Thread Starter

daljeet795

Joined Jul 2, 2018
295
It worked as expected but you need to set RD0 to zero or it'll boot high..
@Ian Rogers I didn't get where to set RD0 to zero in code?

To start the debugger, click on 'Debug Main Project' on the X toolbar. It will build and start. You can stop the code and set a breakpoint inside the interrupt routine. Reset and run. Rotate the encoder. If you don't get an interrupt, check your hardware.
Once you get the interrupt,.
@JohnInTX original code is post #239
Where to set a breakpoint inside interrupt routine
Code:
void __interrupt() IRQ(void){
  // CCP1 Interrupt
  if(CCP1IF == 1)  // if the CCP1 Interrupt flag is set...
  {
  CCP1IF = 0;
  gotIRQ = 1;  // 'Signal' main that an interrupt happened
  }
}
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
@JohnInTX original code is post #239
Where to set a breakpoint inside interrupt routine
Anywhere. All the lines get executed on each interrupt. The goal is to see if you get the interrupt at all. Fire it up and turn the encoder. If it breaks at all, you are getting encoder pulses into the timer/compare setup.

As a quick test, set the number of counts in CCPR1H/L to 5000 (1388h), rebuild, set the breakpoint, turn it on and rotate the encoder ONE turn (5000 counts). It should break right there.

If you don't get to the breakpoint, debug the hardware into the PIC.

Once that is working, set the breakpoint right after the if(gotIRQ) to see if the interrupt signal is being passed.
Then set the breakpoint at the point where you put a '1' in the shift register if(SENSOR==1).. It should run continuously until you trip the sensor then break. At that point, you can open the Variables window and watch the bit propagate through the shift register.
Same with the kicker, set a breakpoint where it turn on the kicker. If it breaks there, you should be good to go.
If any of this section does not work, you'll have to drill down into the software.

I seem to remember that the encoder is opto-coupled to the PIC. Be sure that the opto circuit is working with good logic levels and is fast enough for 5000PPM. Some optos are slower than others.

Have fun!

EDIT: the debugger will tell you if the PIC is running at all. Just set a breakpoint somewhere in the init code in main. If it breaks there, the PIC is at least running. If no break, it probably isn't. Check the usual suspects:
MCLR/ = 1
Oscillator running (use a scope or measure the voltage at OSC2, it should be about 1/2 Vdd)
Good power and ground on those pins - there are 2 of each that have to be connected.
 
Last edited:

Thread Starter

daljeet795

Joined Jul 2, 2018
295
EDIT: the debugger will tell you if the PIC is running at all. Just set a breakpoint somewhere in the init code in main. If it breaks there, the PIC is at least running. If no break, it probably isn't. Check the usual suspects:
MCLR/ = 1
Oscillator running (use a scope or measure the voltage at OSC2, it should be about 1/2 Vdd)
Good power and ground on those pins - there are 2 of each that have to be connected.
Good morning @JohnInTX

Have you seen screenshot #234

I did not know how to debug the code I followed some tutorials and tried to debug code

Am I debugging the code correctly?
 

JohnInTX

Joined Jun 26, 2012
4,787
To start the debugger, click on 'Debug Main Project' on the X toolbar. It will build and start. You can stop the code and set a breakpoint inside the interrupt routine. Reset and run. Rotate the encoder. If you don't get an interrupt, check your hardware.

Read the MPLAB-X manual for more info.
Note that you should be using a real PICkit from Microchip. Many clones do not support debugging. Also, with the '877A, you only get one breakpoint. That's enough for now but you'll have to remove the breakpoint before you can single step the program.
Have you done this?
 

Thread Starter

daljeet795

Joined Jul 2, 2018
295
Try increasing the resolution of the system by using smaller segments on the belt and increasing the number of bits in the shift register by a corresponding amount. You may find that your objects span more than one segment meaning more than one consecutive bits per object but that doesn’t matter. As an exercise, consider what it would do with an infinite number of segments and bits. You would have an exact linear image of the objects on the belt. But that would be overkill considering that things like the kicker and sensor have limited resolution e.g. if the kicker blows everything within 2” off the belt, it’s probably pointless to have much more resolution in the system.
@LesJones @MaxHeadRoom @JohnInTX @djsfantasi @Ian Rogers

First of all, thank you very much

finally, I have tested code When sensor detects bad object kicker can kick on the belt

Working code
C:
#include <xc.h>
#pragma config FOSC = HS  // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF  // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON  // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = OFF  // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF  // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF  // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF  // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF  // Flash Program Memory Code Protection bit (Code protection off)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#define ENCODER RC0
#define SENSOR  RC1
#define KICKER  RD0
#define _XTAL_FREQ 20000000  // **you need this for delays**
__bit gotIRQ;  // signals main that an interrupt
void main(void)
{
  unsigned int ShiftReg = 0; //  Init to 0
  INTCON = 0; // No interrupts
  TRISC = 0b00000011; // RC0 Eencoder Pin RC1 Sensor Pin
  TRISD = 0b00000000; // RD0 Kicker pin
  //CCP1 MODULE INITIALIZATION
  T1CON = 0b00000011;  // 1:1 prescale, OSC disabled, Input Synchronized, External clock on T1CKI(RC0), Timer ON

  CCP1CON = 0b00001011;  // Compare mode using CCPR1H and CCPR1L as a 16 bit value

//Set CCPR1H and CCPR1L (16 bits) to the number of encoder pulses e.g. for 795
    CCPR1H = 0x03;
    CCPR1L = 0x84;

    CCP1IF = 0;
    CCP1IE = 1;  //Enabled CCP1 interrupt
    INTCON = 0xc0;  //Enabled Global interrupts & Peripherals interrupt
    while(1)
  {
  if (gotIRQ)
  {
     gotIRQ = 0;  // acknowledge the interrupt ** ALWAYS and before the delay **
    ShiftReg <<= 1;  // shift the integer shift register one bit left. LSbit = 0 after shift
  if (SENSOR == 1)
  ShiftReg |= 0x0001;  // bad object, put 1 in LSbit
  if(ShiftReg & 0x0400){  // and if the #10 bit after shift is '1', kick it
  KICKER = 1;  // set output pulse high
  __delay_ms(40);  // *groan* Delay to see output ( inbuilt delay)
  KICKER = 0;  // set output pulse low
  }// kicker
  }// gotIRQ
} // while
}// main
void __interrupt() IRQ(void){
  // CCP1 Interrupt
  if(CCP1IF == 1)  // if the CCP1 Interrupt flag is set...
  {
  CCP1IF = 0;
  gotIRQ = 1;  // 'Signal' main that an interrupt happened
  }
}
still, I am not getting the perfect rejection but I am to close for perfection and working on it

as described in post #165 @JohnInTX Your approach depends on the spacing between two objects This method will only work if there is fix space between each object

Edit : This method doesn't work If the gap between each object not fixed

I think we need to find way that doesn't depend on the gap between each object
 

JohnInTX

Joined Jun 26, 2012
4,787
as described in post #165 @JohnInTX Your approach depends on the spacing between two objects This method will only work if there is fix space between each object. This method doesn't work If the gap between each object not fixed
Based on your observations, do you think more resolution would help? You are working with settings I picked out of the air..

As a simple test you can double the resolution to see if 2X the belt segments would help. If so, you can put a pencil to it and see what you actually need. You currently are using 10 segments of the belt. Make it 20 smaller segments by:
  • Setting the number in CCP1RH/L to 1/2 its current value. That will halve the size of the segments on the belt.
  • Double the number of shift register bits to 20 from 10. That will require
    • Declaring the shift register as an unsigned long integer (32 bits)
    • Using 0x00100000 in place of 0x400 to pick off bit 20.

Hopefully, that will at least show if you are on the right track.

Good luck!
 
Last edited:

jpanhalt

Joined Jan 18, 2008
11,087
@post #226:
<snip>An object that is perturbed moving down the conveyor belt.<snip>
It seems that if there is a variable number of items between detect and kick, and if an item can be displaced along the length of the belt during transit from detect to kick, another approach might be warranted.

@daljeet795 How much might an item move between detector and kicker?
 

Thread Starter

daljeet795

Joined Jul 2, 2018
295
Based on your observations, do you think more resolution would help? You are working with settings I picked out of the air..
@JohnInTX Take look at my calculation

The total distance between the sensor and kicker is 1200 mm

16500 count need to reach from sensor to kicker

Then one count = 1200mm /16500 = 0.073mm.

Object length = 190 mm

If 5 objects fit between the sensor and kicker

That divides the distance between the sensor and kicker into 5 slots

We get 16500 / 5 = 3300 encoder pulses per object

For example, a 8 bit unsigned integer making a 5-bit shift register:

shift register is shifted once for every 3300

0b0000 0001 put 1 in LSbit

0b0010 0000 if the #5 bit after shift is '1', kick it

C:
    while(1)
  {
  if (gotIRQ)
  {
     gotIRQ = 0;  // acknowledge the interrupt ** ALWAYS and before the delay **
     ShiftReg <<= 1;  // shift the integer shift register one bit left. LSbit = 0 after shift
  if (SENSOR == 1)
  ShiftReg |= 0b0000 0001;  // bad object, put 1 in LSbit
  if(ShiftReg & 0b0010 0000){  // and if the #5 bit after shift is '1', kick it
  KICKER = 1;  // set output pulse high
  __delay_ms(40);  // *groan* Delay to see output ( inbuilt delay)
  KICKER = 0;  // set output pulse low
  }// kicker
  }// gotIRQ
} // while
}// main
Hope it would clear understanding

How much might an item move between detector and kicker?
@jpanhalt There may be 5 objects move between detector and kicker
 

djsfantasi

Joined Apr 11, 2010
9,237
There is another way. I showed how to do it in post#132.

It’s an event list or FIFO list. The object spacing is not relevant; the only parameter it is dependent on, is the belt speed.

C:
dim unsigned long queue[6];
volatile byte putIndex = 0;
volatile byte getIndex = 0;
volatile boolean emptyQueue = true;
// add an item to be rejected to the queue
void putQueue() {
const unsigned long positionDelay = 16500;
queue[putIndex] = getCounter + positionDelay;
putIndex=++putIndex%6;
emptyQueue = false;
}
// get the next item to be rejected from the queue
unsigned long getQueue() {
if(!emptyQueue) {
   byte tempIndex;
   tempIndex = getIndex
   getIndex=++getIndex%6;
   emptyQueue = (putIndex==getIndex);
   return queue[tempIndex]
   }
else {
   return 0
   }
}
// obtain the current counter value
unsigned long getCounter() {
/* insert code to get the current counter value and return it as the function value. No parameters are necessary. */
}
// scan or test the object to determine if it should be rejected
boolean testObject) {
/* insert code to test the objects and  return false if it is a reject */
}
// physically reject a failed item
void reject() {
/* code to physically reject the object. */
}
// main processing loop
void loop () {
unsigned long Counter;
boolean rejectionDone= true;
const unsigned long deltaCounter= 2;
if (! testObject()) putQueue( );
if (! emptyQueue && rejectionDone) {
   Counter = getQueue()); // next failed object
   rejectionDone = false;
   }
if (! rejectionDone &&
       abs(Counter-getCounter()) <= deltaCounter) {
   // failed item is at rejection point
   reject();
   rejectionDone= true;
   Counter = 0;
   }
}
 

Thread Starter

daljeet795

Joined Jul 2, 2018
295
There is another way. I showed how to do it in post#132.

It’s an event list or FIFO list. The object spacing is not relevant; the only parameter it is dependent on, is the belt speed.
@djsfantasi How this method depends on the belt speed and what happens if belt speed will change. Should speed be the constant?

Can we start from code #post 88 there is one counter
 

LesJones

Joined Jan 8, 2017
4,511
Your reasoning in post #250 seems to suggest that you are still not thinking of the details of what is happening on the conveyor. You have gone from one extreme of a resolution of 0.07mm (1200/16500) to a resolution of 240mm. A 190mm long object may not even be in front of the detector that flags the item as good or bad at that point in time. You need to have a pulse occur about the time that the middle of the object is in front of the sensor (And also the kicker.) so I would choose a resolution that gave at least 3 pulses in 190mm. (I would probably choose 5 or possibly 7) Think about what would happen if the kicker hit the leading or trailing end of the object. It may just spin it round on the conveyor. If you ensure the kicker hits close to the centre of the object then it is more likely to knock it off the conveyor. If you give more details of the position detector for the reject detector and the way the reject detector works it would be a great help to the members trying to help you. In your profile you say that you are a design engineer. Is that a mechanical, electrical/electronic or software design engineer ? Do you have other design engineers in the company you work for that can help you in the aspects of design that you do not specialise in ?

Les.
 
Last edited:

Thread Starter

daljeet795

Joined Jul 2, 2018
295
. You need to have a pulse occur about the time that the middle of the object is in front of the sensor (And also the kicker.) so I would choose a resolution that gave at least 3 pulses in 190mm. (I would probably choose 5 or possibly 7) Think about what would happen if the kicker hit the leading or trailing end of the object.

If you give more details of the position detector for the reject detector and the way the reject detector works it would be a great help to the members trying to help you. Les.
@LesJones again, Thank's for your help

I have not used a scanner yet because I didn't want to increase the complexity of project I am just using the object sensor so it becomes high when detecting the leading end of the object. you are correct kicker should be kicking at the center of the object.

@LesJones I would like to discuss on resolutions. How do you choose a resolution?

How did you calculate a resolution of 240mm.?

I think you choose resolution on the basis of object length if yes then how do you choose the resolution for 25mm and 100mm object if the distance between detection and rejection is 1200 mm ?

What do you think about the method suggested by @djsfantasi ? Do you think it's a better way than the distance base method?

as you advise me for low resolutions I had contact to supplier low PPR of encoder not available in my place
I have contacted online and got following specification

upload_2019-5-28_15-59-28.png

Will it be useful ?
 
Last edited:

djsfantasi

Joined Apr 11, 2010
9,237
@djsfantasi How this method depends on the belt speed and what happens if belt speed will change. Should speed be the constant?
If speed is constant, then this method will work. There usually is some constraints imposed on a system. Let’s call this solution EL1.

Design EL2 removes the constant speed constraint. If speed is not constant, then you change the definition of the list to include entries for all objects. If the list entry is false, then the object is good. Otherwise, the object must be rejected. The constraint here is the number of objects between sensing and rejection must be constant. And it requires object detection at the reject station. There is no need in this scheme to count using an encoder. And the event list becomes a list of boolean values.

You could even remove the constraint of a fixed number of objects between stations, by counting the objects and storing the count of a rejected item in the event list, instead of a time or counter value. This is design EL3

Design EL4 improves the reject process by triggering it only when the object is centered at the station. As far as detecting the object center for rejection, you could detect both the leading and trailing edge in the sensing station. Then, in my original scheme, you would adjust the calculation of the counter value when the object would be at the reject station, to add 1/2 of the counter difference between the leading and trailing edge counter values.

Design EL5, combines the two ideas by saving this 1/2 counter difference in the event list, instead of a boolean flag.

Finally, no speed or object count constraints can be eliminated in design EL6. Use leading and trailing edge detection as well as object status (good or reject) at the sensing station. Make the event list a structure which contains the sequential object number and the 1/2 counter or time (μs) which designates when the the object will be centered in the reject station. Then when object n is entering the reject station AND it’s bad, pause for the centering value and then reject.

Note if the belt varies speed, this value may be off a μs in either direction, but the ratio of the object length to belt length will be small enough so this error will not greatly effect the reject process. No spinning boxes! Also note that an encoder is not necessary and your μP time can be used.

So, here are many ideas that address some of the changing requirements in this thread. Let’s review the real constraints and requirements (although the latter is pretty clear). I’m willing to clarify any of these ideas.
 

LesJones

Joined Jan 8, 2017
4,511
djsfantasi's method is the same as the counting method (If my understanding is correct.) you started with but is uses a number of counters in a sort of circular buffer configuration. The number of counters must be at least the maximum number of objects between the reject detector an the kicker point. (I think I would have one extra counter.) If I understand djsfantasi's code (Remember I am totally useless at "C" programming.) correctly he indexes through the counters. I think the comment about the speed of the conveyor is relating to the time required to increment and check if the counters have reached the value to indicate that the object has reached the kicker position. Both this and the shift register method work on distance travelled by the conveyor. The 240mm resolution value comes from the fact that there are 5 shift pulses between the detector and kicker positions. (1200mm / 5 = 240mm) I am not going to to give you the answer to choosing the resolution for 25mm and 100mm objects as the question indicated that you need to think more about what is happening in physical terms before thinking about writing code.

Les.
 

Thread Starter

daljeet795

Joined Jul 2, 2018
295
You should only printf when the sensor changes. The way you have it, it will send the line over and over as long as the sensor bit is 1.
@JohnInTX Back to this point

If the sensor detects an object, this program should suppose to send sensor data to the port of PC
C:
#include <xc.h>
#include <stdio.h>
#include <conio.h>

#define SENSOR  RC1

// CONFIG
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF
//END CONFIG
void putch(char data){                   
    while(!TRMT);
    TXREG = data;
}

unsigned char getch(){                            
    while(!RCIF);
    return RCREG;
}
void main(void) {
  
    TXSTA = 0b00100100;                           // TX enabled, BRGH=1
    RCSTA = 0b10010000;                           // Serial Port, Continuous Receive enabled
    SPBRG = 0x81;                                 // baud rate 9600
  
}
void main(void)
{

   
    while(1)
    {
      if (SENSOR == 1)
      {
           printf("\rThe Sensor is: %d", SENSOR);
      }
      else
     {
           printf("\rThe Sensor is: %d", SENSOR);
     }
    }
}
Problem is I don't receive any data on hyper terminal
 
Last edited:

Thread Starter

daljeet795

Joined Jul 2, 2018
295
So, here are many ideas that address some of the changing requirements in this thread. Let’s review the real constraints and requirements (although the latter is pretty clear). I’m willing to clarify any of these ideas.
@djsfantasi I did not respond to your ideas because I was thinking for an idea of FIFO queue. JohnInTX's method is also used queue (shift register )

I have not ignored your advice I thought I should respond after spending some time on searching
 

JohnInTX

Joined Jun 26, 2012
4,787
For the comms, read this thread and try the sample codes.
https://forum.allaboutcircuits.com/threads/uart-register-configuration.158219/

EDIT: a good first test for serial comms is to hold the PIC in reset (or remove it from the socket) and jumper the RX and TX pins (RC6 and RC7) together. Then type on the terminal. You should see what you type on the screen. That says that the PC's serial interface is working as far as the PIC. You can go from there.

If you are using a USB-RS-232 adapter keep in mind that
1) you need something like a MAX-232 to convert the +/- RS-232 to logic levels.
2) Some cheapo knock-off adapters don't work very well. Get a good one.
3) Check for ground continuity between the PIC and PC. Some cheapo adapters don't carry the signal ground all the way through. I've had to add ground bonding between PIC and PC on occasion (at least until the good-quality adapters arrived).

When the hardware looks good, write a quickie code that just echos the characters received. That will confirm that the UART setup is compatible with Hyperterminal's.

Good luck!
 
Last edited:
Top