PIC16F877A - Object Detection and Rejection - Timer Interrupt

Thread Starter

daljeet795

Joined Jul 2, 2018
295
Yep, you do.
I hope this code should work on conveyor
@JohnInTX If do you see anything wrong please tell me

I would test code when I will get conveyor today
C:
#include <xc.h>

#pragma config FOSC = XT  // 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

__bit gotIRQ;  // signals main that an interrupt

void main(void)
{
    unsigned int ShiftReg = 0; //  Init to 0
    INTCON = 0; // No interrupts
    PORTC = 0b00000000; //Set all Pin to Low
    TRISC0 = 1; // RC0 Eencoder Pin
    TRISC1 = 1; // RC1 Sensor Pin
    TRISD0 = 0; // 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

    // 65536- 2165 = 63,371 is eual to 0xF78B
    CCPR1H = 0xF7;
    CCPR1L = 0x8B;
    CCP1IF = 0;

    INTCON = 0xc0;  //Enabled Global interrupts & Peripherals interrupt
    CCP1IE = 1;  //Enabled CCP1 interrupt

  while(1)
  {
        if (gotIRQ)
        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 =~ KICKER ;
        gotIRQ = 0;  // acknowledge the interrupt
  }
}
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
  }
}
 

Thread Starter

daljeet795

Joined Jul 2, 2018
295
Yes. You don’t want to toggle the kicker, you want to turn it on and off according to the bit in the shift register.
I’m not where I can check the timer setup.
Good luck!
@JohnInTX There is air pusher rejection system as given in following video

I think I just need to set the pin high for air selonied coil according to the bit in the shift register. It would only hit the object one time

Edit: This is not the original system also I can't get original system for testing next two day's so I can't check program on the system because of two day's holidays

But I would like to continue next part
 
Last edited:

djsfantasi

Joined Apr 11, 2010
9,237
I don’t intend to confuse matters, but you can simplify the code a bit. Instead of this:
Code:
if(ShiftReg & 0x0400)
    KICKER = -KICKER;
You could simply write:
Code:
KICKER = (ShiftReg & 0x400);
This would ensure that KICKER is set properly for all iterations of the loop.
 

djsfantasi

Joined Apr 11, 2010
9,237
I don’t intend to confuse matters, but you can simplify the code a bit. Instead of this:
Code:
if(ShiftReg & 0x0400)
    KICKER = -KICKER;
You could simply write:
Code:
KICKER = (ShiftReg & 0x400);
This would ensure that KICKER is set properly for all iterations of the loop.
You may have to implement a short delay if KICKER is true. Depends on the reject mechanism.
 

Thread Starter

daljeet795

Joined Jul 2, 2018
295
@JohnInTX @djsfantasi I assume till the code is okay

I am adding one complex task because I am using the scanner on conveyor that directly connected to PC but I want to scan object when the sensor detects the object on the conveyor. If there is no object then don't scan anything


upload_2019-5-16_19-57-5.png


My program suppose to send a signal to PC if the sensor detects the object on the conveyor

I have to add this to my old code, but I find it very difficult That is why I want to write a sime program before that would send data to PC if sensor detects the object on the conveyor

This is my pseudo code
C:
#include <xc.h>

#define _XTAL_FREQ 20000000

#define ENCODER RC0
#define SENSOR  RC1
#define KICKER  RD0

// 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 UART_Init(void)
{
 
    TXSTA = 0b00100100;    // Transmit enabled = 1, BRGH=1  High speed
    RCSTA = 0b10010000;    // Serial Port, Continuous Receive enabled
    SPBRG = 0x81;          // baud rate 9600
}

void UART_TxChar(char ch)
{
    while(TXIF==0);       // Wait till the transmitter register becomes empty
    TXIF=0;               // Clear transmitter flag
    TXREG=ch;             // load the char to be transmitted into transmit reg
}

char UART_RxChar()
{
    while(RCIF==0);       // Wait till the data is received
    RCIF=0;               // Clear receiver flag
    return(RCREG);        // Return the received data to calling function
}

void main(void)
{
    TRISC0 = 1; // RC0 Eencoder Pin
    TRISC1 = 1; // RC1 Sensor Pin
    TRISD0 = 0; // RD0 Kicker Pin
 
 UART_Init;
        
  while(1)
  { 
       if (SENSOR == 1)
        send_signal_to_PC;
  }
}
 

JohnInTX

Joined Jun 26, 2012
4,787

Thread Starter

daljeet795

Joined Jul 2, 2018
295
I show how to use printf with the USART in #32 and #34 here. You have to rename your low level read/write to the USART getch and putch and then it will work with the standard C conio.h library. There is also an excerpt from the XC8 manual that describes the function in detail.

https://forum.allaboutcircuits.com/threads/uart-register-configuration.158219/page-2#post-1376190
https://forum.allaboutcircuits.com/threads/uart-register-configuration.158219/page-2#post-1377037
@JohnInTX Does it make sense ?
C:
#include <xc.h>
#include <stdio.h>
#include <conio.h>

#define _XTAL_FREQ 20000000

#define ENCODER RC0
#define SENSOR  RC1
#define KICKER  RD0

// 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 UART_Init(void)
{

    TXSTA = 0b00100100;    // Transmit enabled = 1, BRGH=1  High speed
    RCSTA = 0b10010000;    // Serial Port, Continuous Receive enabled
    SPBRG = 0x81;          // baud rate 9600
}

void putch(char data){                        
    while(!TRMT);
    TXREG = data;
}

unsigned char getch(){                
    while(!RCIF);
    return RCREG;
}


void main(void)
{
    TRISC0 = 1; // RC0 Eencoder Pin
    TRISC1 = 1; // RC1 Sensor Pin
    TRISD0 = 0; // RD0 Kicker Pin
    UART_Init;
      
    while(1)
    {
      if (SENSOR == 1)
      {
           printf("\rThe Sensor is: %d", SENSOR); 
      }
      else 
     {
           printf("\rThe Sensor is: %d", SENSOR); 
     }
    }
}
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
IO:
You're not initializing all of your IO, including the unused pins. Stop that. Initialize every pin, even the one's you are not using.
You should initialize the UART TX to an output 1 and RX to an input. That way if you have to re init the UART, you don't have transients on the lines.
Avoid initializing TRIS bit by bit. Always use full bytes i.e. TRISC = 0bxxxxxxxx; That will avoid problems in the PIC hardware.

Line 47 does not call UART_Init the way it is written.

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.
 

joeyd999

Joined Jun 6, 2011
6,300
Always use full bytes i.e. TRISC = 0bxxxxxxxx; That will avoid problems in the PIC hardware.
I agree that writing full 8 bit values to the TRIS registers is optimal (at least with regard to code size) -- when possible, as in this case.

But I know of no PIC hardware limitations that prevent the use of bitwise operations on these registers. Similarly, the LAT registers -- but not the PORT registers.

I often use bitwise operations on TRIS registers when I want to toggle a port between high or low and Hi-Z.
 

jpanhalt

Joined Jan 18, 2008
11,087
@joeyd999
I believe that is Microchip's recommendation as TRIS is subject to RMW corruption. Nevertheless, when you look at the Microchip forums, you can find examples of the XC8 compiler doing bit-wise writes and how it should be coded to avoid that.

The problem is apparently most common when using I2C:
https://www.microchip.com/forums/m1064930.aspx (XC8 bit-wise)
http://ww1.microchip.com/downloads/en/DeviceDoc/80131e.pdf (under the MSSP section, p.4)

Edit: As for your last point, I have not seen reports of a problem doing that.
 
Last edited:

joeyd999

Joined Jun 6, 2011
6,300
@joeyd999
I believe that is Microchip's recommendation as TRIS is subject to RMW corruption. Nevertheless, when you look at the Microchip forums, you can find examples of the XC8 compiler doing bit-wise writes and how it should be coded to avoid that.

The problem is apparently most common when using I2C:
https://www.microchip.com/forums/m1064930.aspx (XC8 bit-wise)
http://ww1.microchip.com/downloads/en/DeviceDoc/80131e.pdf (under the MSSP section, p.4)
Wow. I stand (slightly) corrected.

New rule: during IIC operations, do not use RMW instructions on the port with the IIC interface.

Thanks.
 

JohnInTX

Joined Jun 26, 2012
4,787
Don't use RMW on the TRIS register on a port using MSSP I2C either. It's not for nothing that uCHIP now gently advises against RMW on TRIS in the later datasheets. Other peripherals can have issues as well.

EDIT: @jpanhalt The errata doc you reference in the 2ed link is interesting, thanks. The I2C issue described is because the MSSP can change the direction of the SDA line to ACK a slave and if that happens coincident with your r-m-w, it can change SDA to an output which fouls the bus. What is interesting is that I have personally observed that very problem on a lot of other PNs that are not on that list, including some 18F. I don't know the extent of the problem because I shadow TRIS operations on all 8-bit PICs and PORT ops on midrange/baseline, too.

I also wonder about the workaround presented:
Code:
MOVF TRISC, W ; Example for a 40-pin part such as the PIC16F877A
IORLW 0x18 ; Ensures <4:3> bits are ‘11’
ANDLW B’11111001’ ; Sets <2:1> as output, but will not alter other bits
; User can use their own logic here, such as IORLW, XORLW and ANDLW
MOVWF TRISC
I can't say for sure if MOVF TRISC,W picks off the TRIS bits from a different place than a bsf/bcf or if either is isolated from a shared peripheral but for my money (informed by brutal experience), that code is still r-m-w; it definitely is r-m-w if you are doing that with a midrange or baseline PORT. In any case, it would not be the first time that an errata document was inaccurate or incomplete. YMMV.

Thanks for the link..
 
Last edited:

Thread Starter

daljeet795

Joined Jul 2, 2018
295
IO:
You should initialize the UART TX to an output 1 and RX to an input.
@JohnInTX
Code:
    // bit0 Eencoder Pin input, bit1 Sensor Pin input, bit 6 TX Pin set as output,  bit 7 RX Pin set as input
       TRISC = 0b10000011;
That way if you have to re init the UART, you don't have transients on the lines.
Line 47 does not call UART_Init the way it is written.
How should i init UART ?

correct it should be UART_Init();

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.
That's what I am doing but it's under of while loop so it will repeat
 

JohnInTX

Joined Jun 26, 2012
4,787
That's what I am doing but it's under of while loop so it will repeat
OK but it still writes and re-writes one of those messages continuously because you are looking at the static value of the sensor rather than detecting and responding to changes.

Yes, you picked up on the parenthesis for UART_Init(); It will compile without them but all it does is evaluate the address of the function without doing anything further. XC8 will probably optimize the statement out completely. So, yes, you need those ().
// bit0 Eencoder Pin input, bit1 Sensor Pin input, bit 6 TX Pin set as output, bit 7 RX Pin set as input
TRISC = 0b10000011;
That's how to do it. Now, init ALL the IO, even the pins you are not using.

I don't have pic18F series so I want to purchase this https://in.element14.com/microchip/ma180034/module-pic18f87j94-plug-in-80pin/dp/2313811

Will it be right to buy
I don't really know. I would always recommend 18F over 16F but I haven't used that particular part or evaluation board. The one on that board is 3.3Vmax, will that do? Note that there are many 18F's and 16F1xxx Enhanced Midrange that will drop into the same socket as your '877A. You can proceed now with the 877A then upgrade as your requirements become more apparent. As before, 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.

On another topic, the PC connection via UART is something new again. Rather than another separate project using the UART, you should be thinking how it hooks in with your existing code. It's pointless to code and debug something out of context only to find that all of your work comes to nothing when it won't integrate with the main project.

Just my .02
 

Thread Starter

daljeet795

Joined Jul 2, 2018
295
On another topic, the PC connection via UART is something new again.. It's pointless to code and debug something out of context only to find that all of your work comes to nothing when it won't integrate with the main project.
@JohnInTX Back to the first topic

I have tested following code on the conveyor belt but it doesn't work as expected even it doesn't reject a single object
C:
#include <xc.h>
#pragma config FOSC = XT  // 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
__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 = ob11111110; // 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
    // 65536- 2165 = 63,371 is eual to 0xF78B
    CCPR1H = 0xF7;
    CCPR1L = 0x8B;
    CCP1IF = 0;
    INTCON = 0xc0;  //Enabled Global interrupts & Peripherals interrupt
    CCP1IE = 1;  //Enabled CCP1 interrupt
  while(1)
  {
        if (gotIRQ)
        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(30);    // Delay to see ouput ( inbuilt delay)
        KICKER = 0;            // set output pulse low
        gotIRQ = 0;  // acknowledge the interrupt
  }
}
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
  }
}
I checked kicker respond on 20 ms I have added 30ms short delay. I don't understand why program doesn't reject object?
Edit: made mistake while editing code on notepad

#define _XTAL_FREQ 20000000 //Specify the XTAL crystall FREQ
TRISC = 0b00000011; // RC0 Eencoder Pin RC1 Sensor Pin
 
Last edited:
Top