peter002
Member level 3
- Joined
- Mar 18, 2013
- Messages
- 56
- Helped
- 0
- Reputation
- 0
- Reaction score
- 0
- Trophy points
- 1,286
- Location
- Hindusthaan
- Activity points
- 1,765
TIMR1 BCF PIR1,TMR1IF ;Reset the TMR1IF flag bit
MOVLW H'FF'
MOVWF TMR1H ; Set initial value for the Timer1
MOVLW H'ED'
MOVWF TMR1L
BSF T1CON,3
BCF T1CON,TMR1CS ; Timer1 counts pulses from internal oscillator
BSF T1CON,T1CKPS0 ; Assigned prescaler rate is 1:8
BSF T1CON,T1CKPS1
BSF PIE1,TMR1IE ; Enable interrupt on overflow
MOVLW H'C0'
MOVWF INTCON ; Enable interrupt (bits GIE and PEIE)
BSF T1CON,TMR1ON ; Turn the Timer1 on
BTFSS PIR1,TMR1IF
GOTO $-1
RETURN
I changed your quote tags to code tags.
I'm not really sure what you are trying to do there. A speedometer of that type measures the number of pulses in a given period. The best way to do that is to use an interrupt to establish a time period, and count the number of pulses between interrupts. You seem to be using the interrupt registers but do not have an interrupt service routine.
Basics:
1. write an ISR. Inside it, snapshot the current hall effect pulse count, zero the original count, reset the timer for the next period then exit.
2. in the main code, read the hall sensor pulses as they arrive and display the 'snapshot' number the ISR generated.
For example, if your timer is set to generate an interrupt every second, the hall sensor produces say 15 pulses per second so the counter has reached 15. The ISR copies the '15' somewhere safely, sets the counter back to zero so it is ready for the next interrupt. The main code picks up the '15' from where you stored it and displays it. What you get is a display showing the number of pulses per second which you can convert to a real speed if you wish with some math.
Question: Can you really cycle so fast that you need a six-digit speedometer? :grin:
Brian.
Don't try to repeat the ISR, that won't work.
Keep a counter inside the ISR to keep track of how many times it has been called. For example if the timer causes an interrupt 10 times a second, load '10' into a variable, count it down and if it has reached zero, set a flag (another variable or a bit inside a variable) to say the period has finished the reload the counter with 10 again.
In the main code, check the variable (or bit) to see if the period has elapsed, then do whatever you have to do and finally reset the variable for next time.
If you do that, the timing is controlled by the ISR and it will never be slowed down by any other routines you run.
Brian.
and to add to that... I still can't see any ISR.
I'm also trying to work out why an ultrasonic range finder is being used on a bike!
I can't tell what language it is written in, possibly MPSIM which went obsolete over 20 years ago. I would strongly advise re-writing it to work with XC8 or a similar modern assembler.
Brian.
@peter002,
What you have posted in your topic so far looks like an assignment for students of an assembly language programming course.
The code file you have attached shows a badly organized implementation of:
There are many bugs and serious problems with your assembly language source code.
- An 8-bit interface to an ASCII character LCD module using an HD44780 controller chip.
- An asynchronous UART at 9600 baud.
- Various width pulses on PORTD bit 0.
- Use of hardware TIMER1 to assert an interrupt.
- Functions to write ASCII digits to the character LCD module.
It would seem that you do not yet have enough understanding of the fundamentals of how to work with microcontrollers using assembly language.
Has the course instruction covered these topics:
If you can please tell me more about the tools you have available and the tasks you need to complete.
- Essential ideas for embedded controller application design and implementation.
- Architecture of a process loop.
- Hardware timer initialization and usage.
- Handling interrupt requests.
- Declaring symbols for storing data in RAM.
@peter002,
What you have posted in your topic so far looks like an assignment for students of an assembly language programming course.
The code file you have attached shows a badly organized implementation of:
There are many bugs and serious problems with your assembly language source code.
- An 8-bit interface to an ASCII character LCD module using an HD44780 controller chip.
- An asynchronous UART at 9600 baud.
- Various width pulses on PORTD bit 0.
- Use of hardware TIMER1 to assert an interrupt.
- Functions to write ASCII digits to the character LCD module.
It would seem that you do not yet have enough understanding of the fundamentals of how to work with microcontrollers using assembly language.
Has the course instruction covered these topics:
If you can please tell me more about the tools you have available and the tasks you need to complete.
- Essential ideas for embedded controller application design and implementation.
- Architecture of a process loop.
- Hardware timer initialization and usage.
- Handling interrupt requests.
- Declaring symbols for storing data in RAM.
and to add to that... I still can't see any ISR.
I'm also trying to work out why an ultrasonic range finder is being used on a bike!
I can't tell what language it is written in, possibly MPSIM which went obsolete over 20 years ago. I would strongly advise re-writing it to work with XC8 or a similar modern assembler.
Brian.
TIMR1 BCF T1CON,TMR1CS ; Timer1 counts pulses from internal oscillator
BSF T1CON,T1CKPS0 ; Assigned prescaler rate is 1:8
BSF T1CON,T1CKPS1
BSF PIE1,TMR1IE ; Enable interrupt on overflow
RETURN
TIMR11 BCF PIR1,TMR1IF ;Reset the TMR1IF flag bit
MOVLW H'FF'
MOVWF TMR1H ; Set initial value for the Timer1
MOVLW H'9D'
MOVWF TMR1L
MOVLW H'C0'
MOVWF INTCON ; Enable interrupt (bits GIE and PEIE)
BSF T1CON,TMR1ON ; Turn the Timer1 on
BTFSS PIR1,TMR1IF
GOTO $-1
RETURN
This is really bad programming technique and enabling interrupts without an ISR makes it extremely likely to crash. Every time T1 overflows it will force a call to address 0x004, if you are lucky that address may contain code it can survive from but each time you run the assembler it may change the code at that address.
Also the "GOTO $-1" should be avoided if possible as it is non-portable. Use a label at the jump target and "GOTO" that instead. It will ensure the assembler goes to the right place in all eventualities.
I strongly suggest you structure your code like this:
1. At 'org 0x0000' place a jump to the initializing code.
2. At 'org 0x0004' write the ISR. End it with a 'retfie' instruction.
3. Write the initialization code next, this is where step 1. jumps to.
Use the timer and ISR to generate regular timing periods and count them if you need longer times. The other advantage in this method is you can create more than one delay from the same routine and they can be for different periods and even overlap each other.
Brian.
ORG 0
GOTO RESET
This is really bad programming technique and enabling interrupts without an ISR makes it extremely likely to crash. Every time T1 overflows it will force a call to address 0x004, if you are lucky that address may contain code it can survive from but each time you run the assembler it may change the code at that address.
Also the "GOTO $-1" should be avoided if possible as it is non-portable. Use a label at the jump target and "GOTO" that instead. It will ensure the assembler goes to the right place in all eventualities.
I strongly suggest you structure your code like this:
1. At 'org 0x0000' place a jump to the initializing code.
2. At 'org 0x0004' write the ISR. End it with a 'retfie' instruction.
3. Write the initialization code next, this is where step 1. jumps to.
Use the timer and ISR to generate regular timing periods and count them if you need longer times. The other advantage in this method is you can create more than one delay from the same routine and they can be for different periods and even overlap each other.
Brian.
No! The 16F876A only has one level of interrupts so you cannot write more than one ISR.u mean all the routines tht i ll use for interrupts must be written from the address 0x0004 and so on and they must be ended with the command retfie.. so wen i call then in the place of goto $-1 command they shud be terminated with retfie...???
ORG 0
GOTO RESET 000
;LCD INITIALISATION TABLE------------------------------------------
LCDINITTBL ADDWF PCL,F 0002
RETLW D'56' 0003
RETLW D'12' 0004
RETLW D'6' 0005
RETLW D'1' 0006
LCDINIT MOVLW D'0' 0007
MOVWF INTC 0008
IO MOVF INTC,W
CALL LCDINITTBL
MOVWF PORTB
CALL SENDINST
ORG 0
GOTO RESET
ORG 4
< Place the ISR code here and end it with a 'retfie' instruction >
;LCD INITIALISATION TABLE------------------------------------------
LCDINITTBL ADDWF PCL,F
RETLW D'56'
RETLW D'12'
RETLW D'6'
RETLW D'1'
LCDINIT MOVLW D'0'
MOVWF INTC
IO MOVF INTC,W
CALL LCDINITTBL
MOVWF PORTB
CALL SENDINST
MOVWF INTCON ; Enable interrupt (bits GIE and PEIE)
BSF T1CON,TMR1ON ; Turn the Timer1 on
TIMR1_LOOP
BTFSS PIR1,TMR1IF
GOTO TIMR1_LOOP
RETURN
It would be convenient for displaying speeds up to 100 Kilometers per hour with a resolution reaching to millimeters.Question: Can you really cycle so fast that you need a six-digit speedometer? :grin:
Actually, the easiest way to write code by far is to copy the template from the assembler files and build upon it. The template already has a skeleton ISR with the context saving and restoration inside it.
The problem I have is I use Linux and MPLABX, if Peter002 is using MPASMWIN it implies they are using Windows. I think (but please check) it is at: C:/program files/Microchip/mpasm/templates/code/16f876TEMP.ASM but obviously with a different file structure my copy is elsewhere.
Brian.
Actually, the easiest way to write code by far is to copy the template from the assembler files and build upon it. The template already has a skeleton ISR with the context saving and restoration inside it.
The problem I have is I use Linux and MPLABX, if Peter002 is using MPASMWIN it implies they are using Windows. I think (but please check) it is at: C:/program files/Microchip/mpasm/templates/code/16f876TEMP.ASM but obviously with a different file structure my copy is elsewhere.
Brian.
;***** VARIABLE DEFINITIONS
w_temp EQU 0x7D ; variable used for context saving
status_temp EQU 0x7E ; variable used for context saving
pclath_temp EQU 0x7F ; variable used for context saving
;**********************************************************************
ORG 0x000 ;processor reset vector
nop ;nop required for icd
call setup ;initialise registers
goto main ;go to beginning of program
ORG 0x004 ;interrupt vector location
[COLOR=#FF0000] movwf w_temp ;save off current W register contents
movf STATUS,w ;move status register into W register
movwf status_temp ;save off contents of STATUS register
movf PCLATH,w ;move pclath register into w register
movwf pclath_temp ;save off contents of PCLATH register[/COLOR]
banksel PIR1
[COLOR=#008000]btfss PIR1,TMR1IF ;check if TMR1 roll over interrupt
goto ISR_restore ;no, so restore and exit
[/COLOR]
call step_to_target ;advance all the present values
movlw 0xF6 ;preset for timer1 MSB register (250Hz when xtal = 20MHz)
movwf TMR1H
movlw 0x3C ;preset for timer1 LSB register
movwf TMR1L
bcf PIR1,TMR1IF ;clear the interrupt flag
ISR_restore
[COLOR=#FF0000]movf pclath_temp,w ;retrieve copy of PCLATH register
movwf PCLATH ;restore pre-isr PCLATH register contents
movf status_temp,w ;retrieve copy of STATUS register
movwf STATUS ;restore pre-isr STATUS register contents
swapf w_temp,f
swapf w_temp,w ;restore pre-isr W register contents
retfie ;return from interrupt
[/COLOR]
setup
movlw 0x55 ;seed the random number generator
movwf random
That code is horrible!
That is not an ISR and you can't loop back to the entry point of an ISR either. You are enabling interrupts but have no ISR to service it.
An ISR MUST comply with the following:
1. It MUST be located at address 0x0004, it is built into the silicon, you can't change it.
2. when you enter it you almost certainly have to save at least the current W and STATUS register values.
3. you then do whatever the ISRs purpose is, in your case to make a delay.
4. you MUST then restore the registers saved in step 2.
5. you end the ISR with a 'retfie' instruction.
What you must consider is that an interrupt can occur at any time, for example whenever TMR1 rolls over. You don't ever call the ISR directly in your program, it gets called automatically by whatever triggers the interrupt. When an interrupt occurs, the present location in the program (the PCL register contents) is saved to the stack and the next instruction to be executed is at address 4. When the 'retfie' is reached at the end of the ISR, the PCL is restored and the program continues from where it was before. The reason for steps 2 and 4 is that if you change the values in W or STATUS they will be different when you return to normal program flow and this will obviously cause problems.
Here is a sample of some code I wrote for a 16F877A (nearly 10 years ago!):
Code:;***** VARIABLE DEFINITIONS w_temp EQU 0x7D ; variable used for context saving status_temp EQU 0x7E ; variable used for context saving pclath_temp EQU 0x7F ; variable used for context saving ;********************************************************************** ORG 0x000 ;processor reset vector nop ;nop required for icd call setup ;initialise registers goto main ;go to beginning of program ORG 0x004 ;interrupt vector location [COLOR=#FF0000] movwf w_temp ;save off current W register contents movf STATUS,w ;move status register into W register movwf status_temp ;save off contents of STATUS register movf PCLATH,w ;move pclath register into w register movwf pclath_temp ;save off contents of PCLATH register[/COLOR] banksel PIR1 [COLOR=#008000]btfss PIR1,TMR1IF ;check if TMR1 roll over interrupt goto ISR_restore ;no, so restore and exit [/COLOR] call step_to_target ;advance all the present values movlw 0xF6 ;preset for timer1 MSB register (250Hz when xtal = 20MHz) movwf TMR1H movlw 0x3C ;preset for timer1 LSB register movwf TMR1L bcf PIR1,TMR1IF ;clear the interrupt flag ISR_restore [COLOR=#FF0000]movf pclath_temp,w ;retrieve copy of PCLATH register movwf PCLATH ;restore pre-isr PCLATH register contents movf status_temp,w ;retrieve copy of STATUS register movwf STATUS ;restore pre-isr STATUS register contents swapf w_temp,f swapf w_temp,w ;restore pre-isr W register contents retfie ;return from interrupt [/COLOR] setup movlw 0x55 ;seed the random number generator movwf random
It is only a small part of much bigger code, I have colored the context saving in red to make it obvious. The green part is to check that the interrupt really was caused by TMR1 because all the different interrupt triggers cause a jump to the same ISR on that processor.
Your BCD to decimal code is correct, but consider that BCD does not include numbers greater than 9. I think you really want code to convert hexadecimal to ASCII so you have to add the difference between ASCII 9 and ASCII A if the value is greater than 9.
Brian.
ORG 0
GOTO RESET
ORG 4
MOVWF W_TMP
MOVF STATUS,W
MOVWF STATUS_TMP
MOVF PCLATH,W
MOVWF PCLATH_TMP
BANKSEL PIR1
BTFSS PIR1,TMR1IF
GOTO NO_INT
GOTO ISR
ISR CALL TIMR11
INCF TSTTMP,F
BTFSC PORTC,5
GOTO ISR
MOVF PCLATH,W
MOVWF PCLATH
MOVF STATUS_TMP,W
MOVWF STATUS
SWAPF W_TMP,F
SWAPF W_TEMP,W
RETFIE
NO_INT MOVF PCLATH,W
MOVWF PCLATH
MOVF STATUS_TMP,W
MOVWF STATUS
SWAPF W_TMP,F
SWAPF W_TEMP,W
RETFIE
That is correct. A subroutine is called from within your program, an interrupt is triggered by some hardware event. In your program, that event is TMR1 rolling over. It means that whenever it rolls over, regardless of where the program is running at the time, an immediate call to address 4 is made. Inside the PIC the difference is that a 'call' instruction saves the address of the next instruction to be executed on the stack so that when you 'return', it continues from after the call. An interrupt is similar except that as well as the address being saved to the stack, the interrupts are also disabled. That is because on PIC16 devices you can't interrupt a running interrupt. When you use RETFIE it is like a normal 'return' instruction but it also re-enables any interrupts again.if the isr is automaticall called then it means i do not to call isr??
It might be possible to do it that way, it depends on your program but the safe way is to use that specific code using swap instructions. The reason is that because an ISR can be triggered at any time, it is essential that AFTER the ISR has finished (after the retfie), things carry on as before. If you change W, PCLATH or the STATUS register in the ISR code they will still be different when the main code resumes and that could have bad consequences in your program. Using the swap method rather than movwf does not change any of the bits in the STATUS register so it helps to preserve them before returning.onre more thing.. to push the contents of w_tmp in to the w register is that necessary that we must use swapf command??? i think using the swapf w_tmp firstly the contents of w_tmp are sent to w and then they are swapped and sent back to w_tmp and then another swapf w_tmp,w is used to keep the contents into the w.. why cant we simply use movf w_tmp,w??
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?