MCU Examples.com
PIC Microcontroller Project Examples, free source codes and resources collection.


Automated checking weighing system

PIC assembly code listing of automated check weigher

;=============================================================================
;---Main Program code for the prototype developed for an automated check weigher
;---Date: 21 September 2010
;=============================================================================

 PROCESSOR 16F877a
 #include P16F877a.inc

 __config _CP_OFF & _DEBUG_ON & _LVP_OFF & _WDT_OFF & _HS_OSC

cblock 0x20    ; Defining all variables used in the program and in the 
            ; macros and functions
HIprt              
LOprt             
LOOPcnt1            
LOOPcnt2        
W_TEMP             
STATUS_TEMP        
PCLATH_TEMP        
data_pointer    
last_byte        
print_flg
complete_flg
mul_x            
mul_y            
mul_z    
weight_BIN_1        
weight_BIN_2        
weight_BIN_3        
weight_ASCII_1        
weight_ASCII_2        
weight_ASCII_3        
ASCII            
package_weight    
 endc
 cblock 0x40    ; Defining the starting address where a 40byte data string 
                ; is saved.
data_string        
 endc

org 0x0 goto main org 0x4 goto int org 0x10 ; Including the two macro files #include macro.inc #include uart.inc ;***************************************************************************** ; When Interrupted the program comes here where the values in the Working ; Register, STATUS Register and PCLATH register are saved to W_TEMP, ; STATUS_TEMP, and PCLATH_TEMP variables respectively. To do this the code ; given in the datasheet was used. int MOVWF W_TEMP ; Copy W to TEMP register SWAPF STATUS,W ; Swap status to be saved into W CLRF STATUS ; bank 0, regardless of current bank, MOVWF STATUS_TEMP ; Save status to bank zero STATUS_TEMP register MOVF PCLATH, W ; Only required if using pages 1, 2 and/or 3 MOVWF PCLATH_TEMP ; Save PCLATH into W CLRF PCLATH ; Page zero, regardless of current page ;*****************************************************************************; When the main program is interrupted the program does not know whether the ; interrupt came due to a reception of serial data or due to any other ; interrupt therefore the source of the interrupt needs to be checked but in ; this system the only serial data reception interrupt needs to be checked. banksel PIR1 ; PIR1 bank is selected since it contains the RCIF bit ; (receive interrupt flag bit) btfsc PIR1,RCIF ; RCIF bit is tested goto received_byte ; If set it means that data is received and receive ; buffer is full so the data in the receive buffer ; needs to be processed so the program jumps to ; received_byte goto exit_int ; If RCIF bit is not set the it means that the main ; program is interrupted by some other interrupt ; source and it is ignored and the program goes to ; exit_int to return from the interrupt. ;***************************************************************************** ; If a data byte is received the program comes here then it is needed to check ; for any errors the error types are overrun error and framing error. If there ; are no errors the program saves 40data bytes in 40 registers starting from ; 0x40 and ending in 0x68. received_byte banksel RCSTA ; Overrun and framing error can be checked by checking ; the status of the OERR and FERR bits in the RCSTA so ; first the bank containing RCSTA register is ; selected. btfsc RCSTA,OERR ; Checking the Overrun error bit (bit 1) goto uart_err_handler ; If an Overrun error occurs the FERR bit is set ; and the program jumps to uart_err_handler ; where the both Framing and Overrun errors are ; handled. btfsc RCSTA,FERR ; If there is no Overrun error the Framing Error ; bit (bit 2)checked. goto uart_err_handler ; If a Framing error occurs the FERR bit is set ; and the program jumps to uart_err_handler ; where the both Framing and Overrun errors are ; handled. banksel RCREG ; If there are no Overrun and Framing errors the ; data bytes received need to be saved. movf data_pointer,W ; Initially in the main program the data_point ; variable is given a literal value of 0x40 but ; the value wont remain as 0x40 as it is ; incremented each the data byte is saved. movwf FSR ; This value in data_point variable is put into ; the FSR register movf RCREG,W ; The received data byte is stored in the RCREG ; register and now it is taken to the W Reg movwf last_byte ; The value in W Reg is moved to the variable ; last_byte where the value is transmitted to ; the computer in the main program for testing ; purposes. bsf print_flg,0 ; when a byte is saved the print_flg 0th bit is ; set and it will be cleared in the main program ; after the value in the last_byte variable is ; transmitted. movwf INDF ; The data byte from the RCREG register is now ; in the W Reg and it is moved to the INDF ; register hence the data is written to the ; memory location which has an address equal to ; the value of the FSR register incf data_pointer,F ; After saving the value, the data_pointer value ; is incremented so that when the next data byte ; is received it will be saved to the address ; given by this incremented data_pointer value. movf data_pointer,W ; After incrementing the data_pointer value it ; is then moved to the Working Register to check ; whether a 40 byte data string is saved. xorlw 0x68 ; whether the string is completed or not is ; checked by performing a XOR logic operation ; between Working Register (which has the ; incremented value of the data_pointer) and ; literal 0x68 (difference between 0x68 and 0x40 ; is 0x28 = .40) btfsc STATUS,Z ; Result from XOR operation is tested by testing ; the Z bit of the STATUS Register goto completed_string ; If Z is set it means that the value in W Reg ; was 0x68 which means 40 bytes of data had been ; saved therefore the string is complete and the ; program jumps to completed_string goto exit_int ; If the XOR operation had a value it means that ; still the string is not complete and the ; program needs to exit from the interrupt ; routine to get the next byte of data so it ; jumps to exit_int where the program returns ; from interrupt. ;***************************************************************************** completed_string ; Once a complete string of data bytes are saved ; the program comes here. movlw 0x40 ; Since the data string is now complete the ; data_pointer value needs to be reset. movwf data_pointer ; To reset data_pointer value 0x40 is moved to ; the data_pointer variable. bsf complete_flg,0 ; To identify in the main program whether a ; complete string is saved or not the 0th bit in ; the complete_flg variable is set. banksel RCSTA ; RCSTA bank is selected to enabled continuous ; reception of data bytes bsf RCSTA,CREN ; Continuous reception of data bytes is enabled ; but the interrupt is disabled. banksel PIE1 ; Now that a complete string of data is saved ; the data needs to be processed in the main ; program so the USART Receive Interrupt is ; disabled. bcf PIE1,RCIE ; USART Receive Interrupt is disabled by ; clearing the RCIE bit in PIE1 register goto exit_int ; To process the data in the string the program ; goes to exit_int where it return from ; interrupt. ;***************************************************************************** ; If a Framing or an Overrun error occurs the program comes here. uart_err_handler bcf RCSTA,CREN ; Overrun error is cleared done by clearing CREN ; bit movf RCREG,W ; Framing error is cleared by putting the value ; in the RCREG to the Working Register bsf RCSTA,CREN ; When CREN bit is clear to clear the overrun ; error it disables the continuous reception of ; data bytes so it is enabled by setting CREN ; bit. movlw 0x40 ; 0x40 literal (which is the original value of ; the data_pointer) is moved to the working ; register. movwf data_pointer ; 0x40 in the working register is moved to the ; data_pointer variable thereby resetting the ; data_pointer value. This is done to get a new ; data string from the first. goto exit_int ; After the errors are handled the program exits ; from the interrupt to get a new string of ; data. ;***************************************************************************** ; Before returning from the interrupt the values in the variables W_TEMP, ; STATUS_TEMP, and PCLATH_TEMP are written to Working Register, STATUS ; Register and PCLATH register respectively. exit_int banksel PCLATH_TEMP MOVF PCLATH_TEMP, W ;Restore PCLATH MOVWF PCLATH ;Move W into PCLATH SWAPF STATUS_TEMP,W ;Swap STATUS_TEMP register into W ;(sets bank to original state) MOVWF STATUS ;Move W into STATUS register SWAPF W_TEMP,F ;Swap W_TEMP SWAPF W_TEMP,W ;Swap W_TEMP into W retfie ;----------------------------------------------------------------------------- ;-----------------------------------------------------------------------------

main ; The sensor is connected to PORTD first pin so it is made as input banksel TRISD bsf TRISD,1 ; Pneumatic arms are connected to PORTB third and fourth pins and since PORTB ; is not used for any other purpose whole of PORTB is set as output. banksel TRISB clrf TRISB banksel PORTB clrf PORTB ; PORTB is initially cleared. ; print_flg and complete_flg variables are cleared initially. banksel print_flg clrf print_flg clrf complete_flg ; Starting address (0x40) to write data bytes by indirect addressing method is ; saved in data_pointer variable movlw 0x40 ; literal 0x40 is written to the Working Register movwf data_pointer ; value in the working register is moved to ; data_pointer variable Initialise_UART ; Initializing Universal Asynchronous Receiver and ; Transmitter module using Initialise_UART macro ; Since an interrupt is used to detect a serial data reception Global and all ; Peripheral interrupts needs to be enabled and it is done by setting the 7th ; and 6th bits of the INTCON register. movlw b'11000000' movwf INTCON ; GIE (global interrupt enable bit) and PEIE ;(peripheral interrupt enable bit) are set i.e. ; enabling global and peripheral interrupts. ;***************************************************************************** ;***************************************************************************** ; The sensor to detect packages is modeled as a button switch therefore a ; button press is checked. If button switch is pressed it represents a package ; detected by the sensor. check_sensor banksel PORTD ; Bank containing PORTD is selected btfss PORTD,1 ; The input at the first pin of PORTD is ; checked. goto check_sensor ; If the switch is not pressed again the first ; pin of PORTD is checked. pausems .10 ; If the switch is pressed the switch is ; debounced btfss PORTD,1 ; Again the input at the first pin of PORTD is ; checked. goto check_sensor ; If the switch is not pressed again the first ; pin of PORTD is checked. ;***************************************************************************** ;***************************************************************************** ; If the switch is pressed it means that a package is detected by the sensor ; so a time delay is given for the package to come to the weighing scale. pausems .5000 ;***************************************************************************** ;***************************************************************************** start banksel print_flg ; bank containing print_flg is selected btfss print_flg,0 ; zero bit of the print_flg variable is checked ; (it is set in the interrupt routine if a data ; byte is received) goto start ; If the 0 bit of the print_flg variable is not ; set it means that no data byte is received so ; it jumps back to start bcf print_flg,0 ; If 0 bit of the print_flg variable is set it ; means that a data byte is received and the 0 ; bit is cleared movf last_byte,W ; The received data byte is saved in the ; last_byte variable and it is taken to the ; Working Register since the function ; UART_Print_Char transmits the value in Working ; register call UART_Print_Char ; Function prints the character in the Working ; Register in the computer's hyper terminal ; application btfss complete_flg,0 ; complete_flg zero bit will be set in the ; interrupt routine if a complete set of 40 data ; bytes are set. Therefore the status of the zero ; bit of the complete_flg is checked goto start ; If the zero bit of the complete_flg is not set ; the program jumps back to start where it waits ; for the next data byte reception. ; If the 0 bit of the complete_flg is set the 40 data bytes (ASCII Characters) ; needs to be printed in the HyperTerminal of the computer. This is done for ; testing purposes. movlw 0x40 ; If the 0 bit of the complete_flg is set 0x40 ; is moved to the Working Register movwf FSR ; Value in the Working Register is moved to the ; FSR register. 0x40 is the starting address ; location of the 40 data bytes saved. ; The 40 data bytes saved are read by using the indirect addressing method and ; printed each time one byte is read get_next_byte movf INDF,W ; Value in INDF register is taken to the Working Register call UART_Print_Char ; Value in Working Register is printed on the ; computer's Hyper Terminal using ; UART_Print_Char function incf FSR,F ; Value in FSR register is incremented to get ; the next data byte movf FSR,W ; Incremented value in FSR register is taken to ; the Working Register xorlw 0x68 ; Exclusive OR operation between the value in ; the Working Register and the literal 0x68 ; where 0x68 is the address value of the final ; byte of the 40 byte string is saved. btfss STATUS,Z ; Equality of the value in the Working Register ; and the literal 0x68 is checked by checking ; the status of the Z bit in the STATUS Register goto get_next_byte ; Z not set means that all the 40 bytes aren't ; yet printed so the program jumps to label ; get_next_byte bcf complete_flg,0 ; When the program comes here the Zero bit of ; the STATUS register is set which means the ; current register value is 0x68 this also means ; that the 40 ASCII characters are printed ; therefore the 0 bit of the complete_flg is ; cleared ; When 40 data bytes received were saved continuous reception and receive ; interrupt was disabled but once the 40 data bytes are printed these must be ; enabled. banksel RCSTA bsf RCSTA,CREN ; Enables continuous receive banksel PIE1 bsf PIE1,RCIE ; Enables the USART receive interrupt call String_start ; After the 40 bytes are saved the starting point of ; the data packet from the 40 bytes is identified by ; calling the String_start function ; Once the starting address of a complete data packet is identified the weight ; value is assumed to start from the third successive address from the ; starting address and it is also assumed that the weight value is in three ; registers from this location. process_weight_val banksel FSR ; Once the starting address of a complete data packet ; is identified the address is saved in the FSR ; register so bank containing FSR is selected. movlw .3 ; Literal 3 (decimal) is moved to the Working Register addwf FSR,F ; Since the weight value is assumed to start from the ; third successive address from the starting address ; literal 3 from working register is added to the FSR ; register banksel INDF ; bank containing INDF (virtual register) is selected movf INDF,W ; Value in INDF register written to the Working register movwf weight_ASCII_1 ; Value in Working register saved to ; weight_ASCII_1 variable. The value is an ASCII ; character representing the 100th place value ; of the weight value. call UART_Print_Char ; 1st ASCII character representing the hundredth ; place value is printed using UART_Print_Char ; function (done to test whether the proper ; value is taken correctly) incf FSR ; Value in FSR register is incremented in order ; to get the 10th place of the weight value. movf INDF,W ; Value in INDF register written to the Working register movwf weight_ASCII_2 ; Value in Working register saved to ; weight_ASCII_2 variable. The value is an ASCII ; character representing the 10th place value of ; the weight value. call UART_Print_Char ; 2nd ASCII character representing the tenth ; place value is printed using UART_Print_Char function incf FSR ; Value in FSR register is incremented in order ; to get the unit place of the weight value. movf INDF,W ; Value in INDF register written to the Working register movwf weight_ASCII_3 ; Value in Working register saved to ; weight_ASCII_3 variable. The value is an ASCII ; character representing the unit place value of ; the weight value. call UART_Print_Char ; 3rd ASCII character representing the unit ; place value is printed using UART_Print_Char ; function ; The ASCII value in the three variables weight_ASCII_1, weight_ASCII_2 and ; weight_ASCII_3 needs to be converted to a binary value and the resulting ; respective binary value is saved in weight_BIN_1, weight_BIN_2,and ; weight_BIN_3. Conversion is done by subtracting 0x30from the values in ; weight_ASCII_1, weight_ASCII_2 and weight_ASCII_3. ASCII_to_BIN banksel weight_ASCII_1 ; Proper bank is selected by selecting the ; bank containing weight_ASCII_1. movlw 0x30 ; Literal 0x30 is taken to the Working register. movwf ASCII ; Value in working register (0x30) is moved to ; the ASCII variable. movf weight_ASCII_1,F movf ASCII,W ; Value in the ASCII variable (0x30) is moved to ; the Working Register. subwf weight_ASCII_1,W ; weight_ASCII_1 - 0x30, result saved in the ; Working Register movwf weight_BIN_1 ; Result in the Working Register is written to ; weight_BIN_1 movf weight_ASCII_2,F movf ASCII,W ; Value in the ASCII variable (0x30) is moved to ; the Working Register. subwf weight_ASCII_2,W ; weight_ASCII_2 - 0x30, result saved in the ; Working Register movwf weight_BIN_2 ; Result in the Working Register is written to ; weight_BIN_2 movf weight_ASCII_3,F movf ASCII,W ; Value in the ASCII variable (0x30) is moved to ; the Working Register. subwf weight_ASCII_3,W ; weight_ASCII_3 - 0x30, result saved in the ; Working Register movwf weight_BIN_3 ; Result in the Working Register is written to ; weight_BIN_3 ; Now that the binary value is obtained the package weight now needs to be ; found and it is done by adding the value in 100th place with the value in ; the 10th place and with the value in the unit place. get_package_weight ; Binary value in weight_BIN_1 is the 100th place weight value of the package ; so it has to be multiplied by 100 to convert it to the 100th place value movf weight_BIN_1,W ; Value in weight_BIN_1 is taken to the Working ; Register movwf mul_x ; Value in Working register needs to be moved to ; the variable mul_x since the function ; 'multiply_by_100' multiplies the value in ; mul_x by 100 call multiply_by_100 ; Function 'multiply_by_100' is called which ; multiplies the value in mul_x by 100 and saves ; the result to the Working Register movwf weight_BIN_1 ; Resulting value in the Working Register after ; multiplication is written to the weight_BIN_1 ; variable ; Binary value in weight_BIN_2 is the 10th place weight value of the package ; so it has to be multiplied by 10 to convert it to the 10th place value movf weight_BIN_2,W ; Value in weight_BIN_2 is taken to the Working Register movwf mul_x ; As before value in Working register needs to ; be moved to the variable mul_x call multiply_by_10 ; Function 'multiply_by_10' is called which ; multiplies the value in mul_x by 10 and saves ; the result to the Working Register movwf weight_BIN_2 ; As before, resulting value in the Working ; Register after multiplication is written to ; the weight_BIN_2 variable ; Binary value in weight_BIN_3 is the unit place weight value of the package ; and no operation is made on this value movf weight_BIN_3,F ; Adding the three values in registers weight_BIN_1, weight_BIN_2 and ; weight_BIN_3 (weight_BIN_1 + weight_BIN_2 + weight_BIN_3) movf weight_BIN_1,W ; Value in weight_BIN_1 is taken to the Working Register addwf weight_BIN_2,W ; Value in the Working Register is added with ; weight_BIN_2 and the result is saved in the ; Working Register addwf weight_BIN_3,W ; Resulting value in the Working Register is ; added with weight_BIN_3 and the result is ; saved in the Working Register movwf package_weight ; the value in the Working Register is the ; weight of the package weighed and it is saved ; in the variable package_weight ; The weight limit that is to be checked is within 220g and 230g. So first the ; package weight is checked to see whether it is greater or equal or less than ; 220g and depending on the result the package is pushed to the proper ; conveyor check_weight_lower movlw .220 ; Lower limit of the weight limit (220g) is ; written to the Working Register subwf package_weight,0 ; Package Weight - 220g, result stored in ; Working Register ; Checking if Package Weight is equal to 220g check_equal_to_220g btfss STATUS,Z ; Equality is checked by checking the Zero bit ; of the STATUS Register. Z = 0 if the result of ; subtraction is not zero Z = 1 if the result of ; subtraction is zero goto check_neg_pos_lower ; If the result is non-zero the program ; jumps to check_neg_pos_lower to check ; whether the result is negative or ; positive. goto push_correct ; Result = 220 so the program goes to ; push_correct ; Checking if Package Weight is greater or less than 220g check_neg_pos_lower btfsc STATUS,C ; Whether the result is negative or positive is ; checked by checking the Carry bit of the ; STATUS Register goto check_weight_upper ; If the Carry bit is set it means that ; the package weight is more than 220g ; i.e. the result is positive so the ; program goes to check_upper goto push_incorrect ; If the Carry bit is clear it means that the ; package weight is less than 220g i.e. the ; result is negative so the program goes to ; push_incorrect ; The package weight is checked to see whether it is greater or equal or less ; than 230g and depending on the result the package is pushed to the proper ; conveyor check_weight_upper movlw .230 ; Upper limit of the weight limit (230g) is ; written to the Working Register subwf package_weight,0 ; Package Weight - 230g, result stored in ; Working Register check_equal_to_230g btfss STATUS,Z ; Equality is checked by checking the Zero bit ; of the STATUS Register goto check_neg_pos_upper ; If the result is non-zero the program ; jumps to check_neg_pos2 to check whether ; the result is negative or positive. goto push_correct ; Result = 230 so the program goes to ; push_correct check_neg_pos_upper btfsc STATUS,C ; Checking the Carry bit of the STATUS Register goto push_incorrect ; If the Carry bit is set it means that the ; package weight is more than 230g i.e. the ; result is positive so the program goes to ; push_incorrect goto push_correct ; If the Carry bit is clear it means that the ; package weight is less than 220g i.e. the ; result is negative so the program goes to ; push_correct ;***************************************************************************** ;***************************************************************************** ; Activating pneumatic arm 1 so that the package is pushed to the conveyor ; carrying packages with correct weight push_correct banksel PORTB ; PORTB bank is selected bsf PORTB,4 ; 4th pin of PORTB is set to activate Pneumatic Arm 1 pausems .2000 ; A delay is given to represent the time taken ; for the pneumatic arm movement bcf PORTB,4 ; 4rd pin of PORTB is cleared to deactivate ; Pneumatic Arm 1 goto check_sensor ; The program again goes back to chk_sensor to ; check the input from the sensor. ; Activating pneumatic arm 2 so that the package is pushed to the conveyor ; carrying packages with incorrect weight push_incorrect banksel PORTB ; PORTB bank is selected bsf PORTB,3 ; 3th pin of PORTB is set to activate Pneumatic ; Arm 2 pausems .2000 ; A delay is given to represent the time taken ; for the pneumatic arm movement bcf PORTB,3 ; 3rd pin of PORTB is cleared to deactivate ; Pneumatic Arm 2 goto check_sensor ; The program again goes back to chk_sensor to ; check the input from the sensor. end ; End of the program