PIC18 How to make the dot move on a 8x8 matrix display? - pic

I am trying to make a dot move up and down an 8x8 matrix display of column RA1. I use switches on a microcontroller to make the dot to move Up(RB5) and Down (RB0). I have the dot start in the position 0x08. The code works fine but the dot moves down when both switches are on. I was thinking that something like XORWF would help? Any ideas?
#include "p18F4520.inc"
;:::::CONFIG::::::::::::::::::::::::
CONFIG OSC = HS
CONFIG PWRT = OFF
CONFIG WDT = OFF
CONFIG PBADEN = OFF
CONFIG LVP = OFF
org 0x000000
PORST GOTO MAIN
org 0x000020
;:::::DELAY:::::::::::::::::::::::::
IDSHORT equ 0x20
IDLONG equ 0x21
ED10MS CLRF IDSHORT
MOVLW 0XFF
MOVWF IDLONG
LDLOOP DECFSZ IDSHORT
GOTO LDLOOP
DECFSZ IDLONG
GOTO LDLOOP
RETURN
;:::::::::::::::::::::::::::::::::::
MAIN CLRF TRISD
CLRF PORTD
CLRF TRISA
CLRF PORTA
BCF PORTA,RA1
BSF PORTA,RA1
SETF TRISB
MOVLW 0X08
MOVWF PORTD
LOOP BSF PORTA,RA1
CALL ED10MS
BCF PORTA,RA1
BTFSS PORTB,RB0 ;CHECKS IF SWHITCH IS ON - MOVE DOWN IF TRUE
GOTO DOWN
BTFSS PORTB,RB5 ;CHECKS IF SWHITCH IS ON - MOVE UP IF TRUE
GOTO UP
GOTO LOOP
UP MOVLW 0X80
CPFSEQ PORTD,W ;CHECKS IF IT REACHED THE TOP
RLNCF PORTD
GOTO LOOP
DOWN MOVLW 0X01
CPFSEQ PORTD,W ;CHECKS ID IT REACHED THE BOTTOM
RRNCF PORTD
GOTO LOOP
END

You can copy the bits in the PORTB reg and use XORLW. If all your other bits on PORTB are zero, ignore the ANDLW. If not, clear the other bits (other than RB0 and RB5) by using the ANDLW line. In LOOP, replace your bit tests with this. Whenever your switches are either both off or both on, this code will simply fall through and loop. STATUS and Z should be defined in the INC file, but you'll define TEMP. Note, this will work on one "snapshot" of PORTB, if you will. And this maintains your use of GOTOs for UP and DOWN:
MOVF PORTB,W ; put PORTB bits in w
ANDLW b'00100001' ; clr unused bits
MOVWF TEMP ; save w in TEMP
XORLW 0X20 ; look for RB0 'on', RB5 'off'
BTFSC STATUS,Z ; if untrue, Z bit clr, skip
GOTO DOWN ; if true, goto DOWN
MOVF TEMP,W ; get saved copy of PORTB bits
XORLW 0X01 ; look for RB0 'off', RB5 'on'
BTFSC STATUS,Z ; if untrue, Z bit clr, skip
GOTO UP ; if true, goto UP
GOTO LOOP ; any other case, goto LOOP

Related

How do I clear the RAM correctly when using indirect addressing with PIC16F1829

How do you clear the RAM and initialize the FSR0 register correctly when using indirect addressing with PIC16F1829?
The code works. My problem is that when debugging, the variables (i.e., Delay1 & Delay2 in this case) whose values should be stored at address 0x70 and 0x71 are being stored at 0x120 and 0x121 respectively, despite the FSR0 register holding the 0x70 address.
I do not know what I have missed, as there are not many examples of using a 16-bit FSR register around. Therefore, any assistance that anyone can provide will be very much appreciated.
LIST p=16f1829 ;list directive to define processor
#INCLUDE <p16f1829.inc> ;processor specific variable definitions
__CONFIG _CONFIG1, (_FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF);
__CONFIG _CONFIG2, (_WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _LVP_OFF);
;-------------------------------------------------------------------------
; UDATA declares a section of uninitialised data
VARIABLES UDATA ; VARIABLES is the name of the section of memory
Delay1 RES 1 ; uninitialised data, placed by linker in GPR's.
Delay2 RES 1 ; uninitialised data, placed by linker in GPR's.
;-------------------------------------------------------------------------
; RESET VECTOR
;-------------------------------------------------------------------------
RESET_VECTOR CODE 0x0000
GOTO START
;-------------------------------------------------------------------------
; INTERRUPT SERVICE ROUTINE
;-------------------------------------------------------------------------
INT_VECTOR CODE 0x0004 ; Interrupt vector location
GOTO START
;-------------------------------------------------------------------------
; MAIN PROGRAM
;-------------------------------------------------------------------------
MAIN_PROG CODE
START
;-------------------------------------------------------------------------
; SET OSCILLATOR TO FACTORY FREQUENCY AND CLEAR GPR's
;-------------------------------------------------------------------------
ERRORLEVEL -302 ; Disable warning accessing register not in bank 0
BANKSEL OSCTUNE ; Configure OPTION_REG and TMR0
MOVLW 0x00 ; Set oscillator to factory calibrated frequency
MOVWF OSCTUNE ;
BANKSEL STATUS
ERRORLEVEL +302 ; Enable warning accessing register not in bank 0
CLEAR_RAM ; code sequence initialises all GPR's to 0x00
MOVLW 0x70 ; initialise pointer to RAM
MOVWF FSR0L
CLRF FSR0H
NEXT
CLRF INDF0 ; clear INDF0 register
INCF FSR0L, F ; inc pointer
BTFSS FSR0L, 7 ; all done?
GOTO NEXT ; no clear NEXT
CONTINUE ; yes CONTINUE
NOP
;-------------------------------------------------------------------------
; REMAINDER OF PROGRAM
;-------------------------------------------------------------------------
; Setup main init
BANKSEL OSCCON ; Selects memory bank containing OSCCON register
MOVLW b'00111000' ; Set CPU clock speed of 500KHz -> correlates to (1/(500K/4)) for each instruction
MOVWF OSCCON ; OSCCON <- 0x38
; Configure the LEDs
BCF TRISC,0 ; Make I/O Pin C0 an output for DS1
BANKSEL LATC ; Selects memory bank containing LATC
CLRF LATC ; Start by turning off all of the LEDs
MAINLOOP:
BSF LATC, 0 ; Turn LED on
ONDELAYLOOP:
DECFSZ Delay1,f ; Waste time.
BRA ONDELAYLOOP ; The Inner loop takes 3 instructions per loop * 256 loops = 768 instructions
DECFSZ Delay2,f ; The outer loop takes an additional 3 instructions per lap * 256 loops
BRA ONDELAYLOOP ; (768+3) * 256 = 197376 instructions / 125K instructions per second = 1.579 sec.
BCF LATC,0 ; Turn off LED C0 - NOTE: do not need to switch banks with 'banksel' since bank2 is still selected
OFFDELAYLOOP:
DECFSZ Delay1,f ; same delay as above
BRA OFFDELAYLOOP
DECFSZ Delay2,f
BRA OFFDELAYLOOP
BRA MAINLOOP ; Do it again...
;-------------------------------------------------------------------------
; END OF PROGRAM
;-------------------------------------------------------------------------
END ; End of program
The answer is that your code:
; UDATA declares a section of uninitialised data
VARIABLES UDATA ; VARIABLES is the name of the section of memory
Delay1 RES 1 ; uninitialised data, placed by linker in GPR's.
Delay2 RES 1 ; uninitialised data, placed by linker in GPR's.
Tells the assembler to place these in banked memory.
To place data in common (non-banked) memory use this syntax with MPASM:
; UDATA_SHR declares a section of uninitialised data common to all banks
VARIABLES UDATA_SHR ; VARIABLES is the name of the section of memory
Delay1 RES 1 ; uninitialised data, placed by linker in GPR's.
Delay2 RES 1 ; uninitialised data, placed by linker in GPR's.

Reading state of push button to turn on LED in AVR using Atmega32

I have a program that's supposed to read input values from a dip switch connected to PORTA and when button on PC0 is pressed, then it outputs the bits to the LEDs on PORTB, or if PC7 is pressed, then it shows up in PORTD. When PC3 is pressed, it's supposed to multiply both bytes for a 16-bit value stored as a high and low bytes.
The issue is that once I press the button once, the result does show up in the LEDs. However, after the first button press, the logic stops working. For example, when switching input on PORTA, it still shows up in the LEDs regardless of whether the button is pushed or not.
I cannot pinpoint where the issue is exactly and I really need help debugging this.
I'm using an atmega32, programming in Atmel studio, and simulating in Proteus.
I appreciate any help.
.cseg
.org 0x0000
; set stack pointer
ldi r29, low(ramend)
out spl, r29
ldi r29, high(ramend)
out sph, r29
start:
ser r16
out ddrb, r16 ; portb output
out ddrd, r16 ; portd output
clr r16
out ddra, r16 ;porta input
out ddrc, r16 ;portc input
ser r16
out portc, r16 ;pull-up resistor on PORTC
rjmp main
main:
sbic pinc, 0 ;skip if button is not pressed on PC0
call Load_Low ;call subroutine function to load the lower bit
sbic pinc, 7
call Load_High ;call subroutine function to load the higher bit
sbic pinc, 3
call Multiply ;call subroutine function to multiply both stored values
rjmp main
Load_High:
in r20, pina ;read bits in PINA to R20
mov r30, r20 ;store copy
out portb, r30 ;output to LEDs on PORTB
cbi portc, 0 ;clear bit
ret
Load_High:
in r20, pina
mov r31, r20
out portd, r31
cbi portc, 7
ret
Multiply:
mul r31, r30
out portd, r0
out portb, r1
cbi portc, 3
ret
In the schematics there is no pull-down resistor which could make a low logical level when the button is released. Therefore voltage is undefined, in real life voltage will be floating causing unexpected logical level change on the inputs.
Instead of adding external pull-down resistors you can connect buttons to the "GND" and engage internal pull-up resistors (setting corresponding bits of PORTC to 1 while in DDRC those bits are 0)
Also in all your routines there are strange lines without explanation:
cbi portc, 0 ;clear bit
cbi portc, 7
cbi portc, 3
those are the button inputs, why you're clearing PORTC bits each time?

Problem with stack overflow is forcing device reset

I am doing a small voltmeter with PIC18f4550, I am using the ADC converter of the PIC and based on the binary value I receive I show an approximation in a 7 segment display, the problem is that the program stays cycled and does not perform any action. I enclose the code I am using.
list p=18f4550
include <p18f4550.inc>
;CONFIG FOSC = HS
CONFIG PWRT = ON
CONFIG BOR = OFF
CONFIG WDT = OFF
CONFIG MCLRE = OFF
CONFIG PBADEN = OFF
CONFIG LVP = OFF
CONFIG DEBUG = OFF
CONFIG XINST = OFF
;se utiliza para asignar variables de forma secuencial
CBLOCK 0x00
adclecturaH
adclecturaL
Tiempo
aux1
ENDC
org 0x00 ;incia desde posicion 0
goto Inicio
;
Inicio
CLRF PORTB
MOVLW 0x00
MOVWF TRISB ;asignar como salidas
CLRF PORTC
MOVLW 0x00
MOVWF TRISC ;asignar como salidas
MOVLW b'00011110'
MOVWF ADCON1
MOVLW b'00000001'
MOVWF ADCON0
Ciclo
BSF ADCON0, 1
BTFSC ADCON0, 1
MOVFF ADRESH,adclecturaH
movlw adclecturaH
movwf aux1
MOVLW 0X00
;-------------------------------------------------COMPROBACIONES
;movlw b'00010000'
COM0
movlw b'00000000'
sublw aux1
btfsc STATUS,Z
call COM1 ;otra comprobacion
call DIS0
COM1
movlw b'11001100'
sublw aux1
btfsc STATUS,Z
call COM2 ;otra comprobacion
call DIS1
COM2
movlw b'01100110'
sublw aux1
btfsc STATUS,Z
call COM3 ;otra comprobacion
call DIS2
COM3
movlw b'10011001'
sublw aux1
btfsc STATUS,Z
call COM4 ;otra comprobacion
call DIS3
COM4
movlw b'00110011'
sublw aux1
btfsc STATUS,Z
call COM5 ;otra comprobacion
call DIS4
COM5
movlw b'11111111'
sublw aux1
btfsc STATUS,Z
CALL COM1
call DIS5
;----------------------------------------------MOSTRAR VALORES
DIS0
MOVLW h'3f'
MOVWF PORTB
CALL RETARDO
GOTO Ciclo
DIS1
MOVLW h'06'
MOVWF PORTB
CALL RETARDO
GOTO Ciclo
DIS2
MOVLW h'5b'
MOVWF PORTB
CALL RETARDO
GOTO Ciclo
DIS3
MOVLW h'4f'
MOVWF PORTB
CALL RETARDO
GOTO Ciclo
DIS4
MOVLW h'66'
MOVWF PORTB
CALL RETARDO
GOTO Ciclo
DIS5
MOVLW h'6d'
MOVWF PORTB
CALL RETARDO
GOTO Ciclo
RETARDO
MOVLW D'245'
MOVWF Tiempo
DEC
decfsz Tiempo
GOTO DEC
RETURN
END
The error I receive in Proteus 7 is:
[PIC18 STACK] PC = 0x0082 stack overflow is forcing device reset
Every CALL needs a RET statment.
CALL will put the PC onto the stack and RET will get it back. Without RET the stack will overrun:
main:
CALL Subfunc ;CALL subfunction
..... ;continue here after finish subfunction
Sunfunc:
MOVLW 0x00 ;example
....
RET ;jump back to main function

Programming PIC18F4520 stopwatch (MPLAB)

I'm making a code for PIC18F4520 stopwatch using a single 7 segment LED and 8 buttons. Button 1 to stop and button 2 to reset. The following is my code.
LIST p=18F4520
#include <P18F4520.INC>
CONFIG OSC = XT
CONFIG WDT = OFF
CONFIG LVP = OFF
CBLOCK 0x000
DELAY_H
DELAY_L
DELAY_U
input
endc
ORG 0x0000
goto Main
ORG 0x0800
BTFSC INTCON,INT0IF
call INT0_ISR
BTFSC INTCON3,INT1IF
call INT1_ISR
RETFIE
ORG 0x0100
Main
movlw 0x0F
movwf ADCON1
clrf TRISD
clrf PORTD
setf TRISB
BCF INTCON,INT0IF
BSF INTCON,INT0IE
BCF INTCON2,INTEDG0
BCF INTCON3,INT1IF
BSF INTCON3,INT1IE
BCF INTCON2,INTEDG1
BSF INTCON,GIE
movlw 0x00
movwf input
call bcd_7seg
movwf PORTD
count equ 0x25
movlw 0x0A
movwf count
sec
incf input, F
call bcd_7seg
movwf PORTD
call Delay
decf count, F
bnz sec
goto count
bcd_table:
db 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x3F
bcd_7seg:
movlw low bcd_table
movwf TBLPTRL
movlw high bcd_table
movwf TBLPTRH
movlw upper bcd_table
movwf TBLPTRU
movf input, W
addwf TBLPTRL, F
movlw 0
addwfc TBLPTRH
addwfc TBLPTRU
TBLRD*
movf TABLAT, W
RETURN
Delay:
MOVLW d'10'
MOVWF DELAY_U
LOP1_0:
MOVLW 0x80
MOVWF DELAY_H
LOP1_1:
MOVLW 0xFF
MOVWF DELAY_L
LOP1_2:
DECF DELAY_L, F
BNZ LOP1_2
DECF DELAY_H, F
BNZ LOP1_1
DECF DELAY_U, F
BNZ LOP1_0
return
INT0_ISR : ?
INT1_ISR : ?
delay:
MOVLW D'100'
MOVWF 1
back
MOVLW D'0'
MOVWF 2
here
NOP
NOP
NOP
NOP
NOP
NOP
decf 2, F
BNZ here
decf 1, F
BNZ back
return
end
Even though I didn't put any instructions in INT0_ISR and INT1_ISR, both button1 and button changes the number on LED to 1. What instructions should I put in INT0_ISR and INT1_ISR to give out the correct result? Thank you for your help!

I want to run my motors connected to PIC16F628A ..? I need PIC BASIC code?

I have a PIC16F628A, and I have 2 motors coonnected to pins of that PIC. Pins RB0 and RB1 are connected to right motor. Pins RB2 and RB3 are connected to left motor. I'm using MPLAB as compiler from .pbp to .hex. I want to write a PIC BASIC program (.pbp) that will move one of these motors( for ex: left one) ... I have just written a simple code segment like this.
TRISB= %11110000
PORTB.2=0
PORTB.3=0
MAINLOOP:
PORTB.2=1
PAUSE 50
PORTB.3=1
PAUSE 50
GOTO MAINLOOP
END
Compiler compiles it correctly, after I burn the hex file successfully, I place the pig, then I start it, I see nothing...! I'm really so amateur in this programming language, I just want to ask that do I need to write any other code segment to run my motors? Or is there anybody who had such kind of project before?
list p=16F628A
include <P16F628A.INC>
cblock 0x20
COUNT1
COUNT2
endc
org 0x00
init movlw .50
movwf COUNT1
movwf COUNT2
;;;SET PWM FREQUENCY;;;
bank1 ;SELECT BANK 01
movlw D'128' ;SET PR2 TO 128 DECIMAL SO THE PWM PERIOD = 2064uS => PWM FREQUENCY = 484Hz
movwf PR2
bank0 ;SELECT BANK 00
clrf CCPR1L ;SET PWM STARTING DUTY CYCLE;;;
comf CCPR1L
movlw B'00001100' ;SET PWM MODE, BITS 5 AND 4 ARE THE TWO LSBs OF THE 10BIT DUTY CYCLE REGISTER (CCPR1L:CCP1CON<5:4>)
movwf CCP1CON ;SET PWM PIN TO OUTPUT MODE;;;
bank1 ;SELECT BANK 01
bcf TRISB, 3 ;SET RB3 AS OUTPUT, TO USE FOR PWM
bank0 ;SELECT BANK 00
movlw B'00000010' ;SET TIMER 2 PRESCALE VALUE;;;PRESCALE = 16 SO THE PWM PERIOD = 2064uS => PWM FREQUENCY = 484Hz
movwf T2CON
clrf TMR2 ;CLEAR TIMER 2 MODULE;;;
bsf T2CON, TMR2ON ;ENABLE TIMER 2 MODULE;;;
main call DELAY
goto main
DELAY
loop1 decfsz COUNT1,1
goto loop1
decfsz COUNT2,1
goto loop1
return
end

Resources