that encoder gives four option 400 500 5000 1024 PPR and previously suggested to use low resolutions encoder and It would work on 5V DC That's why I think it's best choice to useWhy is 400PPR a better choice?
100K Pulses/secWhat is the calculated input frequency at the desired max speed?
4 options in one encoder? Part number and datasheet please.that encoder gives four option 400 500 5000 1024 PPR and previously suggested to use low resolutions encoder and It would work on 5V DC That's why I think it's best choice to use
100K Pulses/sec. Where is that from and what is the significance? I was kind of expecting that you work backwards from the belt speed, roller diameter, PPR to reach a max pulse freq for the max belt speed.100K Pulses/sec
------------------- * 60 sec/min = 1200RPM max rotational speed.
5000PP/Rev
100K input frequency at the desired max speed?
Thanks @JohnInTX for saving me from the big problemEdit: the PIC should count a little over 4MHz so I don’t think that is your problem. The 4N25 should be good enough but your circuit could be better.
No, I thought PIC16f877A is not enough fast for this project and its counter, not counting pulses of the encoder at high speed But I was totally wrongCan you afford 2 new encoders?
#define _XTAL_FREQ 2000000
#include <xc.h>
#include <stdio.h>
#include <conio.h>
// BEGIN 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;
}
char getch ()
{
if(OERR == 1){ // reset UART on overflow error
CREN = 0;
CREN = 1;
}
while(RCIF==0); // Wait till the data is received
return(RCREG); // Return the received data to calling function
}
void send (char *s)
{
while (*s != 0)
putch(*s++);
}
void main(void) {
char data;
PORTD = 0b00000000; // All OFF
TRISD = 0b00000000;
TXSTA = 0b00100100; // TX enabled, BRGH=1
RCSTA = 0b10010000; // Serial Port, Continuous Receive enabled
SPBRG = 0x81; // baud rate 9600
while (1)
{
data = getch();
putch(data);
switch(data){
case '1':
RD4 = 1; // Relay ON (start belt)
RD2 = 1; // Green Light ON
break;
case '0':
RD4 = 0; // Relay OFF (stop belt)
RD2 = 0; // Red Light OFF
}
}
}
Boy, who didn't see this coming?I want to start/ stop the belt from PC
#include <xc.h>
#include <stdio.h>
#include <conio.h>
// 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
#define ENCODER RC0
#define SENSOR RC1
#define KICKER RD0
#define _XTAL_FREQ 20000000
__bit gotIRQ; // signals main that an interrupt occurred
__bit PCisScanning; // 1 = we sent a scan request to the PC and expect a reply
unsigned char PCreply; // holds reply from PC
enum {STOP, RUN} OpMode; // belt can be running or stopped
// PC commands
#define PC_SCAN_REQ '1'
#define PC_IRQ_TICK '0'
#define PC_BAD_OBJECT 'B'
#define PC_RUN_BELT 'R'
#define PC_STOP_BELT 'S'
void putch(unsigned char data){
while(!TRMT);
TXREG = data;
}
unsigned char getch()
{
if(OERR == 1){ // reset UART on overflow error
CREN = 0;
CREN = 1;
}
while(RCIF==0); // Wait till the data is received
return(RCREG); // Return the received data to calling function
}
// checkPC: checks UART, returns a char received or '\0' if none - does not wait on receive
unsigned char checkPC(void){
if(OERR == 1){ // reset UART on overflow error
CREN = 0;
CREN = 1;
}
if(RCIF) // if a char has been received
return RCREG; // return the char
else
return ('\0'); // else, return 00
}
void startBelt(void)
{
// add whatever starts the belt <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
void stopBelt(void)
{
// add whatever stops the belt <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
void main(void)
{
char data;
unsigned int ShiftReg = 0; // Init to 0
OpMode = STOP; // init to stopped
stopBelt();
INTCON = 0; // No interrupts
TRISC = 0b00000011; // RC0 Encoder Pin RC1 Sensor Pin
TRISD = 0b00000000; // RD0 Kicker pin
TXSTA = 0b00100100; // TX enabled, BRGH=1
RCSTA = 0b10010000; // Serial Port, Continuous Receive enabled
SPBRG = 0x81; // baud rate 9600
//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
CCPR1H = 0x03;
CCPR1L = 0x84;
CCP1IF = 0;
CCP1IE = 1; //Enabled CCP1 interrupt
INTCON = 0xc0; //Enabled Global interrupts & Peripherals interrupt
while (1)
{
//---------------- STOPPED ---------------------------
while (OpMode == STOP)
{
while ((PCreply = getch())!= PC_RUN_BELT); // wait for PC
OpMode = RUN; // set to run mode
startBelt(); // and fire it up
}// while STOP
//------------------ RUNNING ---------------------------
while (OpMode == RUN)
{
if (gotIRQ) // belt segment has passed
{
gotIRQ = 0; // acknowledge the interrupt ** ALWAYS and before any delay **
ShiftReg <<= 1; // shift the integer shift register one bit left. LSbit = 0 after shift
if (SENSOR == 1) // if there is an object present
{
putch(PC_SCAN_REQ); // ask the PC to scan the object
PCisScanning = 1; // note that we ARE expecting a reply from PC
}
else
{
putch(PC_IRQ_TICK); // you only need this if the PC needs to know about each segment on the belt
// it also tells the PC that the comms are still working
PCisScanning = 0; // note that we are NOT expecting a reply from PC
}
// while the PC is (maybe) scanning, use that time to see if you need to kick
if(ShiftReg & 0x0400) // if the #10 bit after shift is '1', kick the object at end of belt
{
KICKER = 1; // set output pulse high
__delay_ms(40); // *groan* Delay to see output ( inbuilt delay)
KICKER = 0; // set output pulse low
}// kicker
// now if we expect a reply from PC wait for it but not forever
// if not, just back to top of loop
if(PCisScanning == 1)
{
//wait for the PC reply OR another interrupt
while(((PCreply = checkPC()) == '\0') && !gotIRQ);
if(PCreply == PC_BAD_OBJECT) // if got a reply before an interrupt AND it's 'B'..
ShiftReg |= 0x0001; // bad object, put 1 in LSbit
if(PCreply == PC_STOP_BELT) // received STOP instead of BAD
OpMode = STOP;
// here you can add some error processing if you got the next
// interrupt before the PC replied i.e. dead PC or belt too fast
// you can also decode other responses from the PC here. Note
// that you don't have to process a 'G' response if all you want
// to know is if the scan was bad. You could decode 'G' to know
// that the PC was working and the comms were good if you want.
} // process wait for PC
} // gotIRQ
// no IRQ or done processing IRQ, check for STOP
if((PCreply = checkPC()) == PC_STOP_BELT) // PC command between IRQs?
OpMode = STOP;
if (OpMode == STOP) // if anything generated STOP..
stopBelt(); // stop belt and do other loop
}// while RUN
} // main while loop
}//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
}
}
@JohnInTX Thanks again. you have helped me so muchBoy, who didn't see this coming?
I’m still watching. I think the right path is to concentrate on the base problem. When you have other ideas like a Fire Alarm, document them and don’t think about them until you have accomplished your primary goal.@JohnInTX Thanks again. you have helped me so much
Am I running fast because I think Sometimes we go astray from our path So we need a person who brings us to the right path?
I wanted to add some of the safety features like fire Alarm And what can I add to the new features to make efficient system
You got help from a lot of very generous members here and you should thank them as well but from me, you're welcome, I need the exercise sometimes.@JohnInTX Thanks again. you have helped me so much
Agreed as long as you have laid out a path to follow. The design lays out the path. If you have carefully solved the problem with your design and documented the solution, you will find that the actual coding is easy and usually works first time. That is why I kept on you about the speed issue. My design (and I did have one, with sketches and calculations even) said that it should keep up. By having faith in that preliminary work and staying on the path as it were, we not only avoided lots of random coding trial and error but also avoided purchasing some expensive but unneeded encoders. So yes, it's handy to have someone who will keep you focused on the path but really that someone should be you. And it will be as you get some experience and confidence.@JohnInTX Am I running fast because I think Sometimes we go astray from our path So we need a person who brings us to the right path?
OK, I did not see that one coming.I wanted to add some of the safety features like fire Alarm but I did not even try to do that. And what can I add to the new features to make an efficient system?
I think that is an excellent idea. As I mentioned before, a lot of skilled and experienced members here gave you some really good information. A careful read in the context of how this project bounced around and how it could have been more directed would be invaluable.Edit :I thought I should go back once more. I'm reading all the advice given to me and studying codes. I learned a lot of things and I also enjoyed a lot
I mean thank you all,You got help from a lot of very generous members here and you should thank them as well but from me, you're welcome, I need the exercise sometimes.
1) I don't think I need to replace 877A but I would like to collect 18F for learning. I saw PIC18F4331 has Quadrature Encoder Interface (QEI) feature. What would be the advantage (QEI) feature in PIC18F4331?I don't really know. I would always recommend 18F over 16F if you are looking to upgrade have at it but consider looking at some that have built-in quadrature encoder modules. A parametric search on Microchip.com will help you there. But in any case, you'll have to have a firm idea of the features/performance that your application requires and use those as a guide.
Just my .02
2) I am interested to know How can we reject object without encoder becauseSo a simple photo cell detector could be used as a counter from detection position to rejection position.
Going back to my sensor on the conveyor itself, it would be a simple solution as the conveyor position required to move the item to the reject position could be measured exactly by rotating proximity flag etc.
Which can also be termed an encoder, I just prefer, Rotary Sensor
Max.
You need to focus the the result. Follow your spirit later.I mean thank you all,
@LesJones gave me a good start @MaxHeadRoom given good advice @Ian Rogers helped me to convert assembly code into c code @djsfantasi suggested queue idea @JohnInTX You helped me on coding
everyone's work is appreciated
Here are few points want to discuss
1) I don't think I need to replace 877A but I would like to collect 18F for learning. I saw PIC18F4331 has Quadrature Encoder Interface (QEI) feature. What would be the advantage (QEI) feature in PIC18F4331?
@jpanhalt @@JohnInTXYou need to focus the the result. Follow your spirit later.
#include <xc.h>
#include <stdio.h>
#include <conio.h>
// 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
#define ENCODER RC0
#define SENSOR RC1
#define KICKER RD0
#define BELT RD1
#define SCANNER_TRIGGER RD2
#define _XTAL_FREQ 20000000
__bit gotIRQ; // signals main that an interrupt occurred
unsigned char PCreply; // holds reply from PC
enum {STOP, RUN} OpMode; // belt can be running or stopped
// PC commands
#define PC_IRQ_TICK '0'
#define PC_BAD_OBJECT 'B'
#define PC_RUN_BELT 'R'
#define PC_STOP_BELT 'S'
void putch(unsigned char data){
while(!TRMT);
TXREG = data;
}
unsigned char getch()
{
if(OERR == 1){ // reset UART on overflow error
CREN = 0;
CREN = 1;
}
while(RCIF==0); // Wait till the data is received
return(RCREG); // Return the received data to calling function
}
// checkPC: checks UART, returns a char received or '\0' if none - does not wait on receive
unsigned char checkPC(void){
if(OERR == 1){ // reset UART on overflow error
CREN = 0;
CREN = 1;
}
if(RCIF) // if a char has been received
return RCREG; // return the char
else
return ('\0'); // else, return 00
}
void startBelt(void)
{
BELT == 1;
}
void stopBelt(void)
{
BELT == 0;
}
void main(void)
{
char data;
unsigned int ShiftReg = 0; // Init to 0
OpMode = STOP; // init to stopped
stopBelt();
INTCON = 0; // No interrupts
TRISC = 0b00000011; // RC0 Encoder Pin RC1 Sensor Pin
TRISD = 0b00000000; // RD0 Kicker pin
TXSTA = 0b00100100; // TX enabled, BRGH=1
RCSTA = 0b10010000; // Serial Port, Continuous Receive enabled
SPBRG = 0x81; // baud rate 9600
//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
CCPR1H = 0x03;
CCPR1L = 0x84;
CCP1IF = 0;
CCP1IE = 1; //Enabled CCP1 interrupt
INTCON = 0xc0; //Enabled Global interrupts & Peripherals interrupt
while (1)
{
//---------------- STOPPED ---------------------------
while (OpMode == STOP)
{
while ((PCreply = getch())!= PC_RUN_BELT); // wait for PC
OpMode = RUN; // set to run mode
startBelt(); // and fire it up
}// while STOP
//------------------ RUNNING ---------------------------
while (OpMode == RUN)
{
if (gotIRQ) // belt segment has passed
{
gotIRQ = 0; // acknowledge the interrupt ** ALWAYS and before any delay **
ShiftReg <<= 1; // shift the integer shift register one bit left. LSbit = 0 after shift
if (SENSOR == 1) // if there is an object present
{
SCANNER_TRIGGER == 1;
}
else
{
SCANNER_TRIGGER == 0;
}
if(ShiftReg & 0x0400) // if the #10 bit after shift is '1', kick the object at end of belt
{
KICKER = 1; // set output pulse high
__delay_ms(40); // *groan* Delay to see output ( inbuilt delay)
KICKER = 0; // set output pulse low
}// kicker
//wait for the PC reply OR another interrupt
while(((PCreply = checkPC()) == '\0') && !gotIRQ);
if(PCreply == PC_BAD_OBJECT) // if got a reply before an interrupt AND it's 'B'..
ShiftReg |= 0x0001; // bad object, put 1 in LSbit
if(PCreply == PC_STOP_BELT) // received STOP instead of BAD
OpMode = STOP;
} // process wait for PC
} // gotIRQ
// no IRQ or done processing IRQ, check for STOP
if((PCreply = checkPC()) == PC_STOP_BELT) // PC command between IRQs?
OpMode = STOP;
if (OpMode == STOP) // if anything generated STOP..
stopBelt(); // stop belt and do other loop
}
}//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
}
}
@JohnInTX I will try myself to get a solutionRight now, I don't have a lot of time to help you, sorry. But to me, that seems the most direct solution. Others may have a better idea.
Does extending the shift register make sense based on your understanding of the problem?@JohnInTX I will try myself to get a solution
Yes because I don't see any other wayDoes extending the shift register make sense based on your understanding of the problem?
@JohnInTX actually, I don't understand how the first shift register will work (10 to 14)?I see that your new sketch is different than your old one - the method I described will work for either.
I don’t want to confuse things, but you have to make sure that a new requirement is met.So as you've drawn it:
Sensor picks up an object at #11 but scanner is at #10. The system has to 'remember' that there was an object at #11 and trigger the scanner at #10 yes?
You can make the shift register do double-duty here - examine the SR for '1' at more than one place:
- At #11, if there is an object, put a '1' in the shift register in bit #11.
- At the next interrupt (meaning that the belt has travelled ONE object length), that '1' bit shows up at #10. Examine the shift register at that point.
- If there is a '1' there, trigger the scanner.
- When you get the result of the scan, REPLACE the '1' bit that triggered the scanner with a '1' or '0' representing the result of the scan. That '1' or '0' will propagate through the SR as before to control the kicker.
Note that now you have one physical shift register that is treated as two logical shift registers. The original one runs from bit #10 to bit #1 as before. The new one runs from bit #11 to bit #10. #10 is the boundary point where the meaning of the bits in the SR change from 'object detected' to 'result of the scan'.
The shift register is longer by one bit. You'll have to take that into account with your bit set/clear and bit detection operations. You've numbered the bits backwards in your sketch but if #11 is the LSbit of the shift register then (based on the earlier bit assignments):
The 'object detected' bit is 0x0001
The 'scan request' bit is 0x0002
The 'scan result' bit is ALSO 0x0002, the SR has NOT shifted between scan request and scan result
The 'kick control' bit has moved one bit to 0x08000
On each interrupt:
If you detect an object you do (ShiftReg |= 0x0001) t0 set that bit. (Object detected)
If (ShiftReg & 0x0002) request a scan (previous detected object is now under scanner)
If (scan is GOOD)
(ShiftReg &= ~0x0002) // CLEAR the kick bit, just leave it set if BAD
If (ShiftReg & 0x0800) // the previously detected BAD object is now at the kicker
Kickit();
Right now, I don't have a lot of time to help you, sorry. But to me, that seems the most direct solution. Others may have a better idea.
Good luck!
#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 SCANNER RD0
#define _XTAL_FREQ 20000000
__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 Scanner
//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; // object, put 1 in LSbit
if(ShiftReg & 0b00100000){ // and if the #5 bit after shift is '1', enable scanner
SCANNER = 1; // set output pulse high
}
}// 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
}
}