PDA

View Full Version : WRITE not working



servo260
- 22nd December 2004, 08:02
Hi, I'm usign the WRITE command with a 16f84a and I found that it doesn't work at 20mhz but it does a 4mhz.
I disable the interruptions but it is the same.
I need to used it at 20mhz, so if anyone know a solution plese help me.

NavMicroSystems
- 22nd December 2004, 10:35
Click here (http://www.picbasic.co.uk/forum/showthread.php?s=&threadid=1025)

mister_e
- 22nd December 2004, 15:14
Did you add..

DEFINE OSC 20

at the begining of your program??? Your code will be appreciate to help you on that issue.

regards

servo260
- 22nd December 2004, 15:22
This is the code i'm using, write works fine at 4mhz but not at 20.


define osc 20

data @0,4

clear

pulsos var byte
velocidad var word
circunferencia var byte
x var word ; valor del display
num var byte ; variable para la salida del display
boton var byte ; variable del comando button
demora var byte ; demora para button


wsave var byte $20 system
ssave var byte bank0 system
psave var byte bank0 system
r0save VAR WORD bank0
r1save VAR WORD bank0
r2save VAR WORD bank0
r3save VAR WORD bank0
fsave var byte bank0 system
rm1save var word bank0



TRISA = %11111111

TRISB = %00000000


goto start

define INTHAND display


start:

option_reg = %00000011
intcon = %00000000

read 0,circunferencia

intcon = %10100000


main:

if porta.2 = 0 then gosub setup

count porta.3,1000,pulsos

velocidad = pulsos * circunferencia
velocidad = velocidad * 36
velocidad = velocidad / 10

x = velocidad

goto main


setup:

x = circunferencia
pause 500

setup1:

x = circunferencia
if porta.2 = 0 then gosub config
if porta.1 = 0 then
intcon = %00000000
write 0,circunferencia
intcon = %10100000
return
endif

goto setup1

config:

circunferencia = circunferencia + 1
if circunferencia = 216 then circunferencia = 0
pause 100

return




asm
display
movwf wsave
swapf STATUS, W
clrf STATUS
movwf ssave
movf PCLATH, W
movwf psave
movf FSR,w
movwf fsave
endasm
r0save = R0
r1save = R1
r2save = R2
r3save = R3
rm1save = RM1

num = x dig 0
portb = num
high portb.4
pause 7
low portb.4
num = x dig 1
portb = num
high portb.5
pause 7
low portb.5
num = x dig 2
portb = num
high portb.6
pause 7
low portb.6

TMR0 = 0
INTCON.2 = 0

R0 = r0save
R1 = r1save
R2 = r2save
R3 = r3save
RM1 = rm1save
asm
bcf INTCON, 1
movf fsave,w
movwf FSR
movf psave, W
movwf PCLATH
swapf ssave, W
movwf STATUS
swapf wsave, F
swapf wsave, W
retfie
endasm

mister_e
- 22nd December 2004, 16:42
Reason can be simple here.

1. Count @ 4MHZ check pin state eache 20uSec, @20MHz 4uSec. can you send value of count to LCD or Serial and compare them after ???

2. Is your PIC support 20MHZ... PIC16F84-20 ??.. number after the dash is really important

3. Tie to ground every unused input pins

4. be sure of the 5V line... no noise. add the appropritate capacitor to the supply line

servo260
- 22nd December 2004, 18:34
The pic supports 20mhz and the 5v and capacitors are ok. I'll try puting the unused pins to ground, but I still don't understand why it work at 4 mhz and not at 20.

mister_e
- 22nd December 2004, 19:44
Did you set HS oscillator fuse when programming too???

servo260
- 22nd December 2004, 22:13
Yes, it's set in HS, otherwise it doesn't even start.

mister_e
- 22nd December 2004, 22:47
what about if you change prescaler to 1:128 or 1:64... timer is running under internal clock...

servo260
- 22nd December 2004, 23:06
I put the prescaler to 1 : 256 and it still doesn't work.

mister_e
- 22nd December 2004, 23:19
One thing i don't understand here... It's working @4MHz. I'm not really much familiar with timer but...


Original code:

1. Prescaler 1:8
2. interrupt on Timer overflow
3. Must interrupt each and every : (1/(4MHZ / 4))*8*256 = 2 mSec

BUT i read 3 pause 7 in your interrupt routine... longer than interrupt itself... this is why i don't understand why it was working without at least disable interrupts.

DISABLE or INTCON=0 at the begining of your interrupt routine may do something.


By the way, let's say you have 20Mhz:


1. prescaler 1:256
2. Interrupt on Timer overflow
3. Must interrupt each and every : (1/(20Mhz/4))*256*256 =13 mSec.


reduce your PAUSE to... let's say 3 or 4 and post what's happen now.

servo260
- 23rd December 2004, 01:45
I reduce the 3 "pause 7" to 3 "pause 3", but It's the same.
I't doesn't write to the eeprom, or, some times it erased it.

servo260
- 23rd December 2004, 03:26
How do I write the WRITE secuence in ASM? Perhaps that way it works.

Bruce
- 23rd December 2004, 10:21
Most PIC datasheets include short examples in assembler of how to read & write to onboard EEPROM.

You can also open the PBP library file PBPIC14.LIB, and scroll down to line 6018 to see how PBP WRITEs to EEPROM in assembler for the 14-bit core PIC.

However, I doubt this is the root of your WRITE problem.

You're using assembler interrupts with timer0 interrupting everything at 819uS (at 20MHz) or 4mS (at 4MHz) intervals.

You're using BASIC commands within your assembler interrupt without proper context saving or restoring of PBP system variables, and you're using BASIC commands that take much longer than your interrupt timing allows for your BASIC commands to operate properly, etc, etc,.

> count porta.3,1000,pulsos

At 20MHz, you will have around 1,221 timer0 interrupts in the time period it takes for this single BASIC line to perform its function.

Is this really what you want/need..?

TIP: To verify PBP commands like read, write, count, etc,, do it "without assembler interrupts". At least until you know a certain command is working as expected, at the oscillator speed you're using.

Once you're comfortable with, and know how (or if) a PBP command works, then move on to using these commands in larger more complex programs.

Example;

@ DEVICE HS_OSC, WDT_OFF
DEFINE OSC 20
EADD VAR BYTE ' EEPROM address
EDAT VAR BYTE ' EEPROM Data
X VAR BYTE ' GP Var

PORTB = 0
TRISB = 0

MAIN:
FOR EADD = 0 TO 25
EDAT = EADD
WRITE EADD,EDAT
GOSUB SHOW
NEXT EADD

DONE:
GOTO DONE

SHOW:
READ EADD,X
PORTB = X
PAUSE 250
RETURN
5 LED's on portb will show you if the READ & WRITE commands are working properly at 20MHz. If it is working at 4MHz, but not working at 20MHz, then I would suspect a timing issue.

This could be caused by using a 20MHz oscillator, and programming the config fuse for XT. At 20MHz be sure it's set for HS before programming your PIC. You may also want to verify your 20MHz crystal and load caps with a simple blink LED program if all else fails.

TIP #2: If you're going to use primarily BASIC commands, and you have to use interrupts, then look into using the PBP ON INTERRUPT option for BASIC interrupts. This gives your BASIC commands time to complete execution, and return what you really expect.

If you prefer to use assembler interrupts, then you really do need to get familiar with assembler, how to save/restore PBP system variables, and which system variables need attention based on the PBP commands used.

Hope this helps.

Acetronics2
- 23rd December 2004, 12:40
Hi, Servo to scissors ( ! gag ! )

It would certainly be very interesting to know a little more about your project ( What is it intended to do ...i.e ; bicycle speedometer or someting approaching ??? )

I think the answer to your problems is here ...really !!!

to my thoughts, and after reading your code, it's a simple event timing problem ...

Alain

servo260
- 23rd December 2004, 16:41
Yes, it's a bicycle speedometer, very simple and BIG, it's for learning purposes.
The problem I have is that I can't slow down the display rutine because I'm driving a multiplexed 7 segments display leds, and when I slow down it, the bright is very low.
Perhaps this question is stupid, but what is the problem in having an interrupt rutine longer than the interrupt itself?

mister_e
- 23rd December 2004, 17:06
Hi,
can you provide some info about your speedometer ?

I mean,

1. what goes in?
2. how?
3. How to interpret the pulses that goes in?
4. How many 7 segment you want.
5. Which PIC, Wich pins ... blah blah blah... or schematic.


I've time in front of me now to do something in that. Let me know, i'll have a try to this and keep you post.

regards

Bruce
- 23rd December 2004, 17:47
Hi Servo260,

Can you use RA4/TOCKI instead RA.3 to count your pulses?

If you can, then you can use Timer0 as your counter, and count pulses on RA4/TOCKI in the background without your count being interrupted during your display updates.

mister_e
- 23rd December 2004, 18:19
Bruce,
That's a great idea but... i'm not so familiar with those timer... For sure you can count with it... no problem with this part. The part i can't figure...

Let's say we want to count/sec... how to if F84a have only one timer module???

i can figure it easy with F628

1. timer0 to count input pulse
2. timer1 to get count/sec


but i figure we can do with F84a as follow

1. timer0 to count pulse
2. main loop must:
1.display last counter value, as long 1 sec delay is not finished
2.reload timer0 count to display
3. redo main loop.

let's say we have 3 X 7 segments and we display on each about 5msec = 15 msec. this loop must be done 1Sec/15mSec = 66 times. I am wrong or close... i figure it make sense ...

regards

Bruce
- 23rd December 2004, 20:05
Hi Steve,

I'm sure there are probably several ways to do something like this, but I would use the StopWatch in MPLAB to see how many clock cycles my sub-routines require.

Then use a variable (or several depending on the osc frequency) to keep track of how many times my code has run from start to return to start.

This variable (or variables) would tell me how long I had been accumulating counts on RA4/T0CKI during normal execution of my routines.


COUNTER VAR WORD ' # of passes through routines
MAX_COUNTS CON ? ' # of passes required for my time period
COUNTER = 0

Main:
COUNTER = COUNTER + 1
IF COUNTER = MAX_COUNTS THEN GOSUB COMPUTE
Do something

Display:
Refresh display, etc,,
GOTO Main

Compute:
Read TMR0
Compute result, etc,,
Clear COUNTER, timer0, etc,,
Return

Just loop through your program waiting for the variable/s to reach a value that is equal to the time period you need to accumulate your counts in. Then read TMR0, and do the math.

You can insert pauses all over the place as long as they don't interfere with the display refresh rate required.

The problem with using something like;

> count porta.3,1000,pulsos

with hundreds (or thousands) of interrupts before the BASIC command can execute is that even if you do manage to save & restore context, you'll never get a reliable count returned. Especially with all the pause statements & BASIC comands in the int handler.

The period for your counts returned is no longer valid which introduces a HUGE error factor in the final result.

mister_e
- 23rd December 2004, 20:28
Thanks Bruce,

That make enough sense for me to begin a try on this Timer issue.

Since i've never made anything, exept this dimmer code there...
http://www.picbasic.co.uk/forum/showthread.php?s=&threadid=1026

i'm not too familiar, for now, with those Timers.

i'll try to do something for the gentleman here when he will post more details on that project.

Is it possible to monitor as in MPLAB with microcode studio too???

thanks again Bruce.

Bruce
- 23rd December 2004, 20:42
Hi Steve,

Timer0 is very easy to setup & use to count external events. I posted a simple example in this thread http://www.picbasic.co.uk/forum/showthread.php?s=&threadid=1029 to increment TMR0 on high-to-low transitions on RA4/T0CKI pin.

> Is it possible to monitor as in MPLAB with microcode studio too???

MicroCode Studio ICD is nice, but it doesn't give you the "StopWatch" counter you have in MPLAB. The StopWatch shows you the cycle count which is what you'll need.

mister_e
- 23rd December 2004, 21:15
Hummm i see. Yep i know how to work *a bit* with timer. I just need to do some other things with those and few more basic knowledge. Why assign to WDT or else???


Here is one i want to try. Did you look to my dimmer code (the last post, code is in attachement) ???



How to work with MPLAB stop watch.. is it need any specific hardware to use it, i.e. MPLAB-ICD or something? i figure yes but not sure now.

I was figuring also something else to monitor counter... using external TimeBase to start and stop a counter variable. once it's stop send serial, or look in MicroCode studio a specific counter variable.... for sure it will not be 100% accurate but will be close.. no???

I agree it will not giving me the cycles but an amount of count of the time a procedure or a loop take. How does it sounds... don't be afraid to say me i'm totally out. I can take everything... i'm here to learn and help... when i can :)

regards

Bruce
- 23rd December 2004, 21:56
> Why assign to WDT or else???

With the prescaler assigned to the WDT, there is none assigned to timer0, counts will increment on every pulse rather than the default timer0 prescaler of 1:2 which would require 2 pulses on RA4/T0CKI per count increment. The best resolution is with no prescaler.

> Here is one i want to try. Did you look to my dimmer code (the > last post, code is in attachement) ???

I haven't yet, but I can take a look at it later this evening - time permitting.

> How to work with MPLAB stop watch.. is it need any specific
> hardware to use it, i.e. MPLAB-ICD or something? i figure yes
> but not sure now.

Nothing special required. Just click Debugger >> StopWatch, and it will show the number of instruction cycles your code uses during simulation. Very easy.

> I was figuring also something else to monitor counter... using
> external TimeBase to start and stop a counter variable. once
> it's stop send serial, or look in MicroCode studio a specific
> counter variable.... for sure it will not be 100% accurate but will
> be close.. no???

If you're going to trade-off for external hardware, a very simple option would just be to use an LED display driver that didn't require refreshing. Something like the MC14489B. We use this IC in our 4-digit, 7-segment Serial LED Display shown here http://www.rentron.com/Products/SLED4C.htm

You can find the MC14489B at DigiKey, Jameco, and several other places for a few $ each. Very handy little IC. Especially when using a controller with limited peripherals & I/O.

> I agree it will not giving me the cycles but an amount of count
> of the time a procedure or a loop take. How does it sounds...
> don't be afraid to say me i'm totally out. I can take everything...
> i'm here to learn and help... when i can :)

That won't work because PBP library commands, depending on the command, can be several pages of assembly code. Within the MicroCode Studio ICD, you have no idea of how many lines this is, or how many cycles it takes to execute a PBP command.

The MPLAB StopWatch will show you precisely how many instruction cycles it takes to get from point A to B. MicroCode Studio ICD will not. Big difference.

As you suggested earlier, dropping in a PIC with timer1 would be a great option, but if this isn't do-able, the MC14489B may be an option. It would certainly simplify the process, but there are other ways to use what's on hand. Just have to get a little creative...;o]

mister_e
- 23rd December 2004, 23:45
O.K thanks Bruce, that clear-up most of the things here. I'll try this MPLAB Stopwatch stuff now.

regards

servo260
- 24th December 2004, 00:51
Whell, there isn't much information that the one I already posted. The code was the complete code I wrote. The hardware uses a 16f84a as I mentioned before, a mc14511 to drive the 7 segments displays, thre bc548 to multiplexed them, and a photomicrosensor to count the pulses.
If you want to know anything else, just ask.

I will try the diferent aproaches you all mention.

I'll post the results when I have some code to begin the test.

mister_e
- 27th December 2004, 23:36
Hi,
Here's a code for you to count pulses in background then display on 3 X 7 segment display. I also use an PIC16F84A-20/P @ 20 MHZ. The only difference it's the use of the MC14511... I don't have this IC in stock so i use PORTB to drive them.

I use common anode display.

Code is well documented and it suppose to be easy to modify/adjust/understand. Hope This help you.




' Pulse counter
' =============
'
' File name : Count_Display.bas
' Company : Mister E
' Programmer : Steve Monfette
' Date : 27/12/2004
' Device : PIC16F84A-20/P
'
'
' This program display to 3 x 7 segments dislay the result of
' pulses count on PORTA.4 pin/sec.
'
'
' Hardware connection :
' ---------------------
' 1. 3 X 7 segments display on PORTB<7:0>
' 2. 3 X PNP transistor on PORTA<3:0> to drive common anode
' of each 7 segments display
'
'
' Programming mode and PIC define
' -------------------------------
'
@ __config _HS_OSC & _WDT_ON & _PWRTE_ON
' HS oscillator
' Enable watch dog timer
' Enable power up timer
'
DEFINE OSC 20 ' use 20 MHZ oscillator


' I/O Definition
' --------------
'
TRISA = %11110000 ' PORTA : <2:0> outputs to common Anode of 7 segment
' 1. PORTA.0 More significant
' 2. PORTA.2 Less significant
' : PORTA.4 input for signal
'
TRISB = 0 ' PORTB connected to 7 segments
' B0 : segment a
' B1 : segment b
' B2 : segment c
' B3 : segment d
' B4 : segment e
' B5 : segment f
' B6 : segment g
' B7 : segment decimal dot


' Internal EEPROM definition
' --------------------------
'
data @0,192,249,164,176,153,146,130,248,128,144 ' table to convert
' numbers to 7 segments
' pattern output when
' drive invert



' Interrupt and register definition
' ---------------------------------
'
OPTION_REG = %1111000 ' TMR0 clock source : RA4/T0CKI
' increment on low to high transition
' Prescaler assign to WDT
' WDT rate 1:1
'
INTCON = %10100000 ' Enable global interrupt
' Disable EE write interrupt
' Enable TMR0 overflow interrupt


' Variable definition
' -------------------
'
DisplayPort var PORTB ' Port for 7 Segments
ClockInput var PORTA.4 ' Input pin for signal
_7Seg1 con 14 ' enable more significant 7 segment display
_7Seg2 con 13 ' enable mid significant 7 segment display
_7Seg3 con 11 ' enable less significant 7 segment display
Digit_1 var byte ' Hundreds digit
Digit_2 var byte ' Tenth digit
Digit_3 var byte ' Unit digit
ToBeDisplay var word ' Result of count to be send to 7 segment display
Display var byte ' Temp variable
DisplayLoop var byte '
Delay var word ' Variable for Delay loop
OverFlowVar var word '

' Variable and software initialisation
' ------------------------------------
'
tobedisplay = 0 ' set initial value of count
TMR0 = 0 ' reset prescaller
on interrupt goto SetVarToBeDisplay

MainLoop:

' MainLoop
' ---------
'
' 1. display the result of the count on RA4 pin
' 2. refresh display
' 3. reset Timer0
' 4. reload prescaler.
'
' Duration of the procedure : 1 sec
' fine tuned by DelayBetweenEachDisplay Sub
'
' Looping 1 sec and get results of the pulse count in
' TMR0 + OverFlowVar
'
DisplayRefresh:
'
' convert digit to 7 segment output pattern
' -----------------------------------------
display=ToBeDisplay dig 2 ' Read hundreds digit
read display,digit_1 ' Convert hundreds
'
display=ToBeDisplay dig 1 ' Read tenths digit
read display,digit_2 ' Convert tenths
'
display=ToBeDisplay dig 0 ' Read units digit
read display,digit_3 ' Convert units
'
'
' Send digit to 7 segments
' ------------------------
for displayloop = 0 to 111 ' loop for about 1 sec

' display hundreds
' ----------------
PORTA=_7seg1 ' enable hundreds 7 segment
displayport=digit_1 ' display
gosub DelayBetweenEachDigit

' display tenth
' -------------
PORTA=_7seg2 ' enable tenth 7 segment
displayport=digit_2 ' display
gosub DelayBetweenEachDigit

' display units
' -------------
PORTA=_7seg3 ' enable unit 7 segment
displayport=digit_3 ' display
gosub DelayBetweenEachDigit

next
tobedisplay = OverFlowVar + TMR0
OverFlowVar = 0 ' Reset OverFlowVar
TMR0 = 0 ' reset prescaller
goto DisplayRefresh


DelayBetweenEachDigit:

' DelayBetweenEachDigit
' ---------------------
' Produce delay of about 3 mSec
'
' Fine tuned with MPLAB StopWatch to get MainLoop = 1 sec
'
for delay=1 to 307
@ nop
next
@ nop
@ nop
@ nop
@ nop
@ nop
@ nop
@ nop
return


disable
SetVarToBeDisplay:
'
' SetVarToBeDisplay
' -----------------
' interrupt routine of TMR0 overflow
'
' Reset prescaller
' Reset overflow flag
'
OverFlowVar = OverFlowVar + 256
INTCON.2 = 0 ' clear overflow flag
TMR0 = 0 ' reload TMR0
resume
enable

servo260
- 28th December 2004, 07:06
Thanks a lot for that code.
Could you explain how to calculate the mainloop time? Because I want to try with 250ms.
BTW, here is the code I modified from yours. I basicaly change the display rutine so I can use the 4511, but I think I modified the main loop time.

DEFINE OSC 20


TRISA = %11111111
TRISB = %00000000


OPTION_REG = %1111000
INTCON = %10100000

ToBeDisplay var word
num var byte
displayloop var byte
OverFlowVar var word
delay var word


ToBeDisplay = 0
tmr0 = 0
on interrupt goto SetVarToBeDisplay


MainLoop:



DisplayRefresh:

for displayloop = 0 to 111

num = ToBeDisplay dig 0
portb = num
high portb.4
gosub DelayBetweenEachDigit
low portb.4
num = ToBeDisplay dig 1
portb = num
high portb.5
gosub DelayBetweenEachDigit
low portb.5
num = ToBeDisplay dig 2
portb = num
high portb.6
gosub DelayBetweenEachDigit
low portb.6

next

ToBeDisplay = OverFlowVar + tmr0
OverFlowVar = 0
tmr0 = 0

goto DisplayRefresh


DelayBetweenEachDigit:

for delay=1 to 307
@ nop
next
@ nop
@ nop
@ nop
@ nop
@ nop
@ nop
@ nop
return


disable
SetVarToBeDisplay:

OverFlowVar = OverFlowVar + 256
intcon.2 = 0
tmr0 = 0
resume
enable

mister_e
- 28th December 2004, 13:35
to get 250 ms count :

1. change for displayloop = 0 to 28
2. change for delay=1 to 313


simulation is really close to 250ms you need. Use Stopwatch in MPLAB to adjust it. place your breakpoint just before the Goto DisplayRefresh, place RunToCursor or SETPC at Display refresh.. that's it

Acetronics2
- 28th December 2004, 14:18
Hi, every body ...

There's a small failure in the problem ...
Let's see the original code : it is intended to be one pulse every wheel turn; let's assume speed is 36 km/h ...good speed no ?

that gives 10 m/s; as the wheel diameter is 500 to 700 mm, that makes a maximum of 1.5 m per rev.

so, maximum pulse freq. is 6Hz ...as PBP works with integers, that makes 7 different speeds to display !!!

Yeah, you've understood ... that is the period of rotation to be measured and converted further to speed. not the number of pulses to be counted.

So you need a time counter to run and to be read every pulse, I think then a 16F84 is not the right processor to have a separate free running timer in the backwards ...
and as the timer has to count roughly to 1.5 seconds ... interrupts will be really busy ...

Honestly, I don't have the solution for that ...with a 16F84.

Alain

servo260
- 28th December 2004, 16:18
i'm thinking in using 2 16f84, one that count the pulses and convert them to km/h, and the other to handle the display.
I know there must be some other pic who can handle this alone, but where I live this is the cheapest.
What do you think, is that posible?

mister_e
- 29th December 2004, 02:02
i think everything can be done with only one of those 16F84. We want to count time... not a big problem then....

Use interrupt (RB0/INT) to start/stop TMR0 counts of the internal clock ticks.

how does it sounds now???

But i agree, maybe easier with other PIC like PIC16F627,F628 with 3 internal timer.