PDA

View Full Version : Maximum frequency output from a PIC



NickMu
- 20th August 2013, 23:49
Not too long ago there was a tread in this forum that wasdealing with maximum frequency output from a PIC for any given master clock. Unfortunatelymy searches did not return any good information.

I’m interested to get a small PIC device (10F or 12F type)to act as a high frequency clock generator using its internal clock and I wouldlike to know what to expect.
I have an application where the space is very tight and Ineed a modulated clock source with decent stability in the order of hundreds ofkHz but ideal will be 1MHz.

Using PBP I understand that something like:

main_loop:
Togglepin_x
GOTOmain_loop

should give maximum frequency output. Assuming that theToggle and GOTO commands will take two cycles each that will give me possibleoutput frequency of Main_clock/4/4 which will be 250 kHz for a 4MHz clock.
My question is if one uses Assembly (which is not my strongestfield) will it get any faster? Will itbe possible to make it run in the background?
Ideally I need a 1 MHz output that will be ON for 15 mS andOFF for 5 mS.

Thank you in advance for any input in this matter.

Dave
- 21st August 2013, 11:48
Nick, Here ia a program I wrote a few years ago to take a 10F222 and use it as a modulator for some serial data I was pushing to the surface about 200 feet below the water surface. It worked quite well. As you can see it is done in assembly wrapped inside PBP. I hope this will give you a starting point.

' SURFCOMM1.BAS
' WRITTEN FOR 10F222
' PROGRAM TO SEND:
'STATUS DATA READING,TEMPERATURE REDAING,SERVO POSITION READING and
'DEPTH SENSOR READING TO SURFACE @ 1 SECOND INTERVALS. TRANSMIT DATA
'@ 3000 BAUD MODULATED @ 125Khz.
'
' WRITTEN BY DAVID PUROLA 07/13/2006
'
' INCORPORATED INTO 216229 REV.D "NEW TUBSUB" (12/28/2006)
'
DEFINE OSC 8
'
' ************************************************** ******************
' Define Port Variables
' ************************************************** ******************
DATA_OUT VAR GPIO.0 '0-MODULATED DATA TO HEAD UNIT
TXENAB VAR GPIO.1 '1-TRANSMIT ENABLE INPUT
DATA_IN VAR GPIO.2 '1-INPUT DATA FROM SOURCE
SPARE3 VAR GPIO.3 '1-

' ************************************************** ******************
' Declare Variables
' ************************************************** ******************
' DO NOT CHANGE THESE LINES
' ************************************************** ******************
TRISIO = %11111110 'SET PORT DIRECTION REGISTER
OPTION_REG = %11000000 'DISABLE PULL-UPS,PSA to TMR0,PSA 1:2
ADCON0 = %00000000 ' LEFT JUSTIFIED,REF = VDD,Channel 0,A/D Off

'************************************************* ********************
LOOP: 'MAIN LOOP FOR PROGRAM
'************************************************* ********************
ASM
BCF GPIO,0 ;DISABLE TRANSMITTER OUTPUT INITIALLY
NXTPLS BTFSS GPIO,1 ;TEST DATA ENABLE INPUT PIN BIT STATE
GOTO NXTPLS ;DON'T WAIT FOR PERIOD TO EXPIRE BEFORE TESTING ENABLE BIT
BTFSC GPIO,2 ;TEST DATA INPUT PIN BIT STATE
GOTO NXTPLS ;DON'T WAIT FOR PERIOD TO EXPIRE BEFORE TESTING BIT
BSF GPIO,0 ;IF NOT SET THEN MODULATE OUTPUT (ENABLE OUTPUT PIN)
NOP ;BIT ON TIME
NOP
NOP
NOP
NOP
NOP
NOP
BCF GPIO,0 ;(DISABLE OUTPUT PIN)
NOP
GOTO NXTPLS
ENDASM
GOTO LOOP
STOP

NickMu
- 21st August 2013, 13:20
Hi Dave,

Thank you for your sample code.
I do expect some PIC10F222 any day now and I will start testing with.
Just to make things easier for me understanding your code what is the output of this piece of code?

Regards,

Nick

amgen
- 21st August 2013, 13:53
Dave, that was a cool project. Did you use a 'depth sounder transducer' to modulate ? They are generally used at 52 and 125KHZ. How far in water do you suppose the signal can travle at that freq ?
don

NickMu
- 22nd August 2013, 02:06
While waiting for my 10F PICs I had a little time today and did some experiments with a PIC12F683.
Using the internal oscillator set for 4MHz and a close loop with Toggle GPIO.x I was getting in the neighborhood of 70 kHz output and 143 kHz for 8 MHz clock.
Interestingly enough with simple High / Low GPIO.x loop I got about 200 kHz.

Then I realized that one of the clock options is to use internal clock with clock/4 on clock out pin. This method got me the 1 MHz output for the 4 MHz internal clock and 2 MHz for the 8 MHz internal clock.

The oscilloscope shows a pretty steady output and my frequency counter gives about 0.15% error.

If this will prove to be my path I will use a one gate chip to buffer and modulate the output.

I still wish I will be able to solve it with only one PIC10F and I will try my best to find a solution.

Nick

HenrikOlsson
- 22nd August 2013, 07:30
Hi,
The toggle command first makes the pin an output, then it reads the state of the pin, then it inverts the state, then it writes the new state to the pin - takes time.
High/Low first makes the pin an outout then sets the pins state - takes a little less time.
You'll get more speed of you write to the pin directly

TRISO.0 = 0 'Make pin an output
DoIt:
GPIO.0 = 1
GPIO.0 = 0
Goto DoIt
If you can handle some jitter / discontinuity in the output then something likw this will give you even higher average frequency with the drawback that every 6th pulse will be "missing" due to the GOTO.

DoIt:
GPIO.0 = 1
GPIO.0 = 0
GPIO.0 = 1
GPIO.0 = 0
GPIO.0 = 1
GPIO.0 = 0
GPIO.0 = 1
GPIO.0 = 0
GPIO.0 = 1
GPIO.0 = 0
Goto DoIt

Another aproach would be to use the CCP module, though I'm not sure if the 10F series have that built in.

Of course if all you need is stable 1MHz (or whatever that can be derived from the oscillator) signal then use clock-out option of the chips oscillator.

/Henrik.

Dave
- 22nd August 2013, 11:42
Amgen, The project was a cannon ball replacement for COHO fishing. The sub would track the temperature gradients and depth then control the lure depth as it was connected to the rear of the sub. The data link was the stainless tow wire going up to the boat. The remote readout would display the depth, temperature as well as plot the depth during the tow on an VFD dot matrix display.

NickMu
- 22nd August 2013, 12:13
Thank you Henrik for your suggestions.
I will try the tonight and report the findings.
If I must I will stay with 12F series which might give me the corect output.
The goal is to get 1MHz with output with a 15 mS ON and 5 mS OFF from one single chip.

Regards,

Nick

NickMu
- 22nd August 2013, 13:22
One other choice will be to have the PIC12f output (the 1MHz) controlled ON and OFF by one of its input pins kind like Dave did in his application. This will give me more flexibility with the rest of the concept which is not completely defined yet.
Once I have a clearer picture I will present it to the forum.

Nick

NickMu
- 22nd August 2013, 22:56
If anyone is interested here are the results of the speed test using the two methods suggested by Henrik:
• Writing directly to the output pin at 8MHz internal clock I got a little over 500 kHz but the duty cycle is 25% or 75% depending on the order you write to the pin 0 or 1.
• Using x consecutive writes to the pin (but with a gap at the end of the sequence) increases the burst frequency to about 1 MHz with a duty cycle of 50%. BTW I used 10 cycles.

So far I learned a lot in the process.

Henrik, based on your prior experience with CCP module I have few questions that you might be able to answer:
1. What will be the maximum frequency out that can be achieved from a given master clock?
2. Can the output be turned ON and OFF at some predetermined intervals (let's say 15 mS ON, 5 mS OFF)?
3. What other requirements are there for the PIC (other the CCP module) to be able to get that?
4. Will PIC12F683 do the job?

I will start searching the forum for this kind of applications and I’m sure I might be able to find something to get me started.

Thank you to all the members of this forum that are making PIC programming accessible to more and more hobbyists.

Who said you can’t teach old dogs new tricks?:

Regards,

Nick

HenrikOlsson
- 23rd August 2013, 08:05
Hi Nick,

Henrik, based on your prior experience with CCP module I have few questions that you might be able to answer:
1. What will be the maximum frequency out that can be achieved from a given master clock?
As per the datasheet, PWMPeriod = (PR2+1) * 4 * Tosc so with an 8MHz oscillator and PR2=0 the maximum PWM frequency would be 2MHz (1/(1*4*0.000000125)

2. Can the output be turned ON and OFF at some predetermined intervals (let's say 15 mS ON, 5 mS OFF)?
Yes, that's what the PWM module does, however the number of available dutycycle ratios depends on the selected output frequency. At the maximum output frequency you have 2bits of resoultion, ie the dutycycle can be 0%, 33%, 66% or 100%.

You keep saying that you want 1MHz output frequency with 15ms on-time and 5ms off-time. That's a period of 20ms which equals a frequency of 50Hz - not 1MHz. If you mean 15us on-time and 5us off-time it'll be 50kHz, still not 1MHz.

3. What other requirements are there for the PIC (other the CCP module) to be able to get that?
No, not really.

4. Will PIC12F683 do the job?
I think it might, I'll have to look into it a bit further though.

/Henrik.

Dave
- 23rd August 2013, 12:21
Henrick, I think what NickMu means is, you have a carrier frequency of 1 Mhz. being generated by an oscillator and you modulate it with a gated signal of 15 Ms. on and 5Ms. off. What looks like OOK modulation. NickMu, am I right? Is this what you are looking for?

Dave
- 23rd August 2013, 12:24
NickMu, If this is what you are looking for then maybe you should look at using an 12F1840. It has a DATA SIGNAL MODULATOR module that is quite usefull for generating waveform like this.

NickMu
- 23rd August 2013, 17:46
Hi Henrik,

Thank you for your help and my apologies for not making myself clear.
I am in need of a modulated high frequency source. The main frequency (carrier) should be between 250 kHz and 4 MHz pending some results of testing in progress. I will start with 1 MHz and move up or down on carrier frequency.
I need to be able to turn this carrier ON and OFF in bursts of 15 mS ON and 5 mS OFF.

My project is still in its infant stages, so right now I’m at the point of poking around to see what can be done with a minimum amount of components.

The larger project this is part of is a non-contact power and data transfer. This week and next week I will be doing a lot of testing on the power transfer part to find an optimal frequency that will make it easier to do both with as little loss as possible.

My first power transfer test is encouraging (I can transfer about 1.5 W which is more than I need) with about 66% efficiency. For this I’m using a Mosfet driven by a variable gated oscillator. It serves the purpose for testing but in the final design I would like to have something much simpler especially at the slave unit side where I have a lot of limitations space being one of them.

My research also shows that for power transfer lower frequencies (100 – 400 kHz) are better. For data transfer I would prefer to use higher frequencies since I already tested a wired 4 MHz power and data transfer. Also I understand that higher carrier frequency will allow higher data transfer rate. I must have 9600 but 19200 will be better.

This week I will do some research on employing the CCP module to do most of this part.
I will also study Dave’s example which seems to be doing almost what I need only at lower frequencies.

Dave, you are right and thanks for the suggestion, I will look into it. I am familiar with PIC12F683 but if I must learn new tricks I will.

So many things, so little time.

Thanks again for your help.

Regards,

Nick

HenrikOlsson
- 23rd August 2013, 18:17
Hi Nick,
No problem, I didn't have a 12F683 at home but I did have a 12F1840. Now, the 1840 has an ECCP module while the 683 has a standard CCP module so the following code MAY need to be changed slightly (the CCP1CON register) in order to work properly on the 683 (I'm not using the data modulator here). It sets the CCP module up to output a 1MHz, 50% dutycycle PWM on PortA.2. The main program then simply toggles the timebase (TMR2) for the CCP module on and off to modulate the output.

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' RA0 -> Not used
' RA1 -> Not used
' RA2 -> CCP1 output
' RA3 -> Not used
' RA4 -> Not used
' RA5 -> Not used

' ************************************************** *************
' Device Fuses
' ************************************************** *************

#CONFIG
__config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG

' ************************************************** *************
' Compiler directives
' ************************************************** *************
DEFINE OSC 8 ; We're running at 16Mhz

' ************************************************** *************
' Initialization
' ************************************************** *************

OSCCON = %01110000 ' 8MHz internal osc
ANSELA = 0 ' Digital only for all PortA pins
TRISA = %00000000 ' Make PORTA outputs

CCP1CON = %00001100 ' Normal PWM
PR2 = 1 ' 1MHz output @8MHz system clock
CCPR1L = 0 ' 50% Dutycycle
CCP1CON.5 = 1
CCP1CON.4 = 1

' ************************************************** *************
' Actual program
' ************************************************** *************
Main:
T2CON.2 = 1 ' Timebase ON
PAUSE 15
T2CON.2 = 0 ' Timebase OFF
PAUSE 5
Goto Main

I have a scope screen grab of the output but for some reason the file upload manager on the forum refuses to work, the upload just hangs...aarghh....

Now, if you intend to do other things with the PIC as well then this simple aproach obviously won't work. But you could use a timer interrupt in order to turn on and off the carrier at the correct intervals. I haven't personally used the data signal modulator module in the 12F1840 but it sure sounds like the "correct" way forward. The 12F1840 also allows you to run at 32MHz using the internal oscillator and 4x PLL which would allow you to get higher output frequency using the CCP module than on the 12F683 which tops out at 8MHz.

/Henrik.

NickMu
- 23rd August 2013, 18:57
Hi Henrik,

I will try your code tonight. I only have 12F683 PICs and to change to 12F1840 will take some time for me (I’m still using an older version of PBP) which, more than sure, does not support this chip.
If I can get modulated 1 MHz from one chip for now it will save me a lot of time with the testing and a lot of space on the board.
I will post my findings as soon as I have results.

As always thank you for your help.

Regards,

Nick

NickMu
- 24th August 2013, 17:46
I had to make few minor changes to Henrik’s code to adapt it to a 12F683 and make the code compatible with my old PBP 2.47.

Here is the working code that generates bursts of 15 mS ON and 5 mS OFF with a carrier frequency of 1MHz (Sorry Henrik for mutilating your nicely commentated code but REMing some of the lines makes it easier on my eyes).


@ device pic12F683, intrc_osc_noclkout, wdt_on, mclr_off, protect_off
DEFINE OSC 8 ' We're running at 8 Mhz
OSCCON=%01110111 ' 8MHz internal osc
ANSEL = 0 ' Digital only for all PortA pins
TRISIO=%00100000 ' Make PORTA outputs
CCP1CON = %00001100 ' Normal PWM
PR2 = 1 ' 1MHz output @8MHz system clock
CCPR1L = 0 ' 50% Dutycycle
CCP1CON.5 = 1
CCP1CON.4 =1
Main:
T2CON.2 = 1 ' Timebase ON
PAUSE 15
T2CON.2 = 0 ' Timebase OFF
PAUSE 5
Goto Main

This will make my testing a lot easier and fine tune the power transfer part. In the real life I will replace the fixed timing of the T2CON.2 control with some other mechanism (something like Dave did). Right now my first priority is to decide on the final frequency and tune my LC circuits for maximum efficiency on power transfer and maximum reliable speed on data transfer.
Unfortunately I do not have too much time to experiment with the new 12F1840 which is by far the best way to go. It involves so many more variables for me including complete upgrade of my hardware and software and I’m not ready for it yet.

One question for Dave:
You used a data speed of 3000 bps. Did you try to push your system to see what will be the maximum data speed that you could get?
And two questions for everyone:
Is there a 10F, 12F or low pin count 16F PIC, supported by my older 2.47 PBP that will have a 16 MHz internal clock?
What kind of minimal hardware / software upgrade to be able use PIC12F1840?

Thank you in advance.

HenrikOlsson
- 24th August 2013, 21:58
Hi Nick,
Great, I'm glad you've got it working on the '683!

You'd need to check microchip.com and use the product selector tool they have to find which PICs have the oscillator options you want and then cross reference that with the supported devices list for the compiler version you have.
If you find yourself in a position where you need to consider upgrading to PBP3 I wouldn't hesitate a second. The big difference is how the CONFIG bits are handled - and it's a big step forward IMO. Then you have the conditional compilation features and (since you're even on pre 2.50) you'll get support for 32bit variables when using PIC18 series devices and a couple of new commands.

PBP doesn't really know much about the hardware but obviously your device programmer needs to support the chip you're programming for - you need to check the list for your particular device programmer. I have a PICKit3, it's cheap and it works.

/Henrik.

NickMu
- 25th August 2013, 15:23
Thanks Henrik for your support and encouraging.
Switching to the latest software and hardware is one of my next priorities.
I was going to order a PICKit3 just to get familiar with it until I will cross the bridge. Your sample code will be my first try.

MeLabs use to have a really helpful PIC selection system. What happened to it? I wasn’t aware that Microchip has the product selection tool. I will use it from now on.

I’m sure that other members of the forum will be interested in some of the aspects of my project and I pledge to keep everyone posted with my progress. It’s time to order some parts and start testing.

Regards,

Nick

NickMu
- 12th September 2013, 02:56
Despite the loss of few last posts this tread still has enough information to support my next hypothetical question:
As posted before I’m employing a PIC12F683 to generate a modulated 500KHz carrier.
The output is 15 mS carrier ON followed by 5 mS carrier OFF and it drives a LC resonant circuit to transfer power to a remote sensor. In my efforts to keep the part count and manual adjustments to a minimum I was wondering if a second PIC12F683 on the receiving side will be able to perform as a missing pulse detector and flip a pin high when the carrier is present and drive it low when the carrier is OFF?
I am aware that some jitter will be present but for my intended purposes even 100 uS will be fine.
How will one go about getting something like that to work?
I’m thinking that if an input is set for IOC and the interrupt is cleared as soon as possible. This indicates that the carrier is ON.
If no more interrupts that means carrier is OFF.
I never used interrupts so any input if this is a good approach will be appreciated. Some possible links to something like this will be more than helpful.

Regards,

Nick

HenrikOlsson
- 12th September 2013, 07:12
Hi,
It can probably be done with a PIC, not sure it's the best way though....

What does the signal actually look like at the receiving end? I imagine it's not a nice squarewave at logic levels so, depending on how you do this, you probably need to do some signal conditioning before feeding it further. Because of this I'm not sure a PIC or other logic device is the best tool for the job. Can't you just use a lowpass filter with a suitable time constant followed by a simple transistor buffer?

Or, if the signal IS conditioned to logic levels, perhaps use a retriggerable flip-flop with something like 2.5us time constant. The 500kHz carrier will then keep triggering the flip-flop, as soon as the carrier goes away the flip-flop output will fall withing 2.5us. If the signal isn't conditioned to logic levels perhaps using a 555 timer as the flip-flop could do it but again, it all depends.....

If you insist on using a PIC for the job I'm sure we can figure something out though.

/Henrik.

NickMu
- 12th September 2013, 13:08
Hi Henrik,

It is funny but I used both your suggestions in my younger days. I also used a frequency to voltage convertor (LM2917 if my memory serves me right). Each has its limitations and advantages.
Since I started using PICs I pledge to myself that I will replace any of the analog or logical components that can be replaced especially when it comes to timing circuits. In this case it is so easy to change one line in the code to generate a different frequency or duty cycle with much better accuracy than analog circuits. I also pledge to myself to learn as many special functions of the PICs as I possibly can. Like I said before I’m a slow learner but nobody bits me when it comes to forgetting things (LOL).
I am aware that some signal conditioning will be necessary but that is my last of the worries. I recently discovered those single gate chips in small packages and I’m sure that a couple resistor and capacitors combined with a trigger smith will do fine.
At least knowing now that it can be done using a regular PIC I will research this avenue and keep you posted.

Thank you again for the valuable input.

Regards,

Nick

HenrikOlsson
- 12th September 2013, 17:06
Hi Nick,
Ah, ok, it's the I've got a hammer now everything looks like a nail kind of thing ;-)

Well, as with most things there are more than one way to skin a cat. What I'd do is probably something like this (for 12F683 but not tested):

' CMCON, ANSEL, TRIS, whatever not shown here!

oldCount VAR BYTE
LED VAR GPIO.0

oldCount = 0 'Clear count
LED = 0 ' Turn off LED

' Set TMR0 up as a counter, signal fed into T0CKI pin
OPTION_REG.5 = 1

Main:
If TMR0 <> oldCnt THEN ' There has been pulses since last time we checked
LED = 1
ELSE
LED = 0
ENDIF

oldCnt = TMR0 ' Store current count

' Now wait a bit to allow any new pulses to "tickle" the counter.
' Shortest PauseUs time @8Mhz is 12us.
' If faster response is needed replace with a copule of @NOP (each taking 500ns @8MHz) or try without any delay at all.

PauseUs 25 ' Or go do something else usefull instead....

Goto Main


Anyway, that's one idea....

/Henrik.

NickMu
- 12th September 2013, 18:03
Hi Henrik,

Thank you for the sample code.
I will do some “hammering” tonight and report the results.

Regards,

Nick

NickMu
- 13th September 2013, 03:21
Results after the “hammering” session today.

Testing setup as follows:

One PIC12F683 acting as Tx module. It output bursts of 15 mS 500Khz carrier on CCP1 pin (pin 5) followed by 5mS no carrier. Pin 2 of the PIC is high when carrier ON and low when carrier is OFF.
Second PIC12F683 acting as Rx is connected to the same 5V supply and has its TOCKI pin (pin 5) connected to CCP1 pin of the Tx PIC. Pin 2 of this PIC drives a LED.

After adding some configuration lines and fix some oldCount speling I ran few simple tests to make sure the connections and configs are OK I ran the sample code.
Unfortunately it did not work and the problem was on the Rx side. Monitoring the signals from Tx they were what they should be. Rx will output some very random signals or no signals at all.

So I started messing with the code and try few alterations of the Rx program. I realized that some different handling of TMR0 and oldCount needs to be implemented. I have to confess that some of the results did not make sense to me at the time but by pure luck (I left a code line behind in the first WHILE / WEND loop got some meaningful results).
I do not know enough about the insides of the WHILE / WEND loop but, after violating its condition. I also think I broke few other rules of programming and according to the well known rule if something works there must be at least two mistakes that compensate each other.
The Rx and Tx codes that follow are working and my old analog scope shows perfectly matched signals on the Tx and Rx PICs.
Tx code:


@ device pic12F683, intrc_osc_noclkout, wdt_on, mclr_off, protect_off
DEFINE OSC 8 ' We're running at 8 Mhz

OSCCON=%01110111 ' 8MHz internal osc
ANSEL = 0 ' Digital only for all PortA pins
TRISIO=%00100000 ' Make PORTA outputs
out var GPIO.5
CCP1CON = %00001100 ' Normal PWM
PR2 = 3 ' 1MHz output @8MHz system clock
CCPR1L = 2 ' 50% Dutycycle
CCP1CON.5 = 0
CCP1CON.4 =0
T2CON.2 = 1
Main:
high out
T2CON.2=1 ' Osc ON
PAUSE 15
low out
T2CON.2=0 ' Osc OFF
PAUSE 5
Goto Main


Rx code:


@ device pic12F683, intrc_osc_noclkout, wdt_on, mclr_off, protect_off
DEFINE OSC 8 ' We're running at 8 Mhz

OSCCON=%01110111 ' 8MHz internal osc
ANSEL = 0 ' Digital only for all pins
TRISIO=%00100100 ' Make PORTA outputs

oldCount VAR byte
LED VAR GPIO.5

oldCount = 0 'Clear count
LED = 0 ' Turn off LED

' Set TMR0 up as a counter, signal fed into T0CKI pin

OPTION_REG.5 = 1

Main:
While TMR0 <> oldCount ' There has been pulses since last time we checked
high led
oldCount = TMR0 ' Store current count
Wend
low led
oldCount =0
TMR0=0
While oldCount=TMR0
low led
wend
GOTO main


Can somebody explain me why this code is working?

Regards,

Nick

HenrikOlsson
- 13th September 2013, 07:38
Hi Nick,
Sorry about the typo, told you I didn't test it though.

Currently I have no idea why my original code (less the typo) did not work while your new one does. I just tried it here, again with two 12F1840's so that MIGHT have something to do with it but I don't think so. I also figured out that once you've enabled PWM mode on the CCP pin you can't control that output manually. So in order to force the PWM output low during the OFF period of the carrier you have to disable the CCP module and THEN force it low, I've implemented that in the TX-code below:

' ************************************************** *************
' Device Fuses
' ************************************************** *************

#CONFIG
__config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG

' ************************************************** *************
' Compiler directives
' ************************************************** *************
DEFINE OSC 8 ; We're running at 8Mhz

' ************************************************** *************
' Variables and aliases
' ************************************************** *************
i VAR BYTE
LED VAR PortA.4

' ************************************************** *************
' Initialization
' ************************************************** *************

OSCCON = %01110000 ' 8MHz internal osc
ANSELA = 0 ' Digital only for all PortA pins
TRISA = %00000000 ' Make PORTA outputs

CCP1CON = %00001100 ' Normal PWM
PR2 = 3 ' 500kHz output @8MHz system clock
CCPR1L = 2 ' 50% Dutycycle
CCP1CON.5 = 0
CCP1CON.4 = 0

' ************************************************** *************
' Actual program
' ************************************************** *************

' Show that we're up and running.
For i = 0 to 9
Toggle LED
Pause 200
NEXT

Main:
CCP1CON = %00001100 ' Normal PWM mode
T2CON.2 = 1 ' Timebase ON
PAUSE 15

T2CON.2 = 0 ' Timebase OFF
CCP1CON = %00000000 ' Disable PWM to regain control of PortA.2
PortA.2 = 0 ' Force output low to prevent saturation of coil
PAUSE 5

Goto Main

For the RX-side I did this, which is pretty much exactly like I wrote it earlier. I found that I could ditch the delay to speed up the response time since the exection time for the loop is longer than the PWM-period. If you change the frequency you may need to add some delay.

' ************************************************** *************
' Device Fuses
' ************************************************** *************

#CONFIG
__config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG

' ************************************************** *************
' Compiler directives
' ************************************************** *************
DEFINE OSC 8 ; We're running at 8Mhz

' ************************************************** *************
' Variables and aliases
' ************************************************** *************
oldCnt VAR BYTE
i VAR BYTE
LED VAR PortA.4

' ************************************************** *************
' Initialization
' ************************************************** *************

OSCCON = %01110000 ' 8MHz internal osc
ANSELA = 0 ' Digital only for all PortA pins
TRISA = %00000100 ' T0CKI is input, rest outputs

' ************************************************** *************
' Actual program
' ************************************************** *************

OPTION_REG.5 = 1 ' TMR0 as counter
oldCnt = 0
TMR0 = 0

' Show the world that we're alive
For i = 0 to 9
Toggle LED
PAUSE 200
NEXT

Main:
IF TMR0 <> oldCnt THEN
LED = 1
ELSE
LED = 0
ENDIF

oldCnt = TMR0

Goto Main

I'm going to try to add two screen-grabs from the scope. Last time I did the uploader didn't work but hopefully that's fixed by now. Nope the bloody thing still doesn't work, sorry but you'll have to trust me that it does work here.

/Henrik.

NickMu
- 13th September 2013, 13:29
Hi Henrik,

Thanks again for your patience with me.
Your code makes much more sense to me so I will try it again tonight. I can only guess that as long as the execution time of the main loop is longer than the carrier period we should be fine. If that is not true there should be short glitches during the carrier ON period. Increasing the carrier frequency should be fine while going lower might require some delay.
I can only hope that first time it might have been a stupid mistake on my side.
Now that the setup is fine-tuned there should be no reason why it should not work.
I will report the findings as soon as I have results.

Regards,

Nick

HenrikOlsson
- 13th September 2013, 20:32
Hi Nick,
Yes, as long as the loop takes longer to exectute than the period time of the PWM signal (2us in this case) it'll work without additional delay in there. When running at 8MHz each ASM instruction takes 500ns so there's really only time 4 instructions in this case. The Goto Main takes 2 and the jump caused by the IF-THEN takes 2 so that's 4, then we have the rest so it's pretty safe to run it without any delay.

Here are the screenshots I mentioned:
7080

7081

/Henrik.

NickMu
- 13th September 2013, 20:43
Hi Henrik,

I just can’t wait to try the code again (that will be in a couple of hours after I get off work).
The signals look really good. Only about 12 uS delay on the falling edge which is better than I need. Is it the same at the rising edge?

Thanks,

Nick

HenrikOlsson
- 13th September 2013, 21:10
Hi Nick,
Yes and no....
Yes because it's going to be in the same ballpark.
No, because it's not going to be an absolute number (on the rising OR falling edge) because the carrier signal might "come on" (or off) at any time. If it happens to do that just before we check it the delay will be very short, if it happens to do it just after we just checked the delay will be a little longer.

I measured a couple of pulses here.
Delay on rising edge: 10, 7.5, 9.5, 10, 7.5, 6.5 us
Delay on falling edge: 9.5, 11.5, 12, 8, 8, 11 us

So I think you can consider 12us to be around the worst case for this particular code. Would you prefer constant delay at the cost of it being longer? (Not promising anything, just asking).

And, don't forget it's Programmers Day today - the 256th day of the year! :-)

/Henrik.

NickMu
- 13th September 2013, 21:39
Hi Henrik,

I only asked to confirm my theory about how it will look at the other edge which is exactly your explanation.
It does not make any difference to me for this application and as I stated before I could live with it even if it was longer. It is important though to know it so I can compensate for it.
Happy Programmer’s day!

Regards,

Nick

NickMu
- 14th September 2013, 01:04
As much as I hate to say it I managed to release a portion of the built in smoke from one of my PICs just before the load and test of the new codes.
I did not have the smoke pump to push some back in nor did I have a replacement PIC handy so it will have to wait until tomorrow.

Regards,

Nick

NickMu
- 14th September 2013, 18:21
After fixing the missing smoke problem and modifying the code for PIC12F683 (see my prior posts for configs) the code works without any glitches. I only added on the Tx side one other pin toggle so it made it easier on my scope to compare the Tx and Rx sync. I could see the small amount of jitter at each edge and by changing the time base of the scope I could read (as predicted) delays from about 6 uS to 12 uS. Sorry but I cannot post screen shots from my old analog scope but they are like Henrik’s samples.

I cannot explain what happened the first time and why it did not work. It might have been my scope that was confused by the complex signals (at that time I did not have the extra pin toggling) and did not latch properly on the trigger.

BTW Henrik what are you using as your scope? It looks pretty handy. I’m still using at least 10 years old technology and even though I’m getting close to retirement I’m planning on upgrading all my tools once again because this is going to be my favorite way of spending my free time. Hopefully good wife servicing this weekend will provide the necessary budget (LOL).

I can’t thank enough to Henrik for his help and patience. I learned at least two new things that I was afraid to play with before: PWM side of the CCP1 module and the using TMR0 as fast counter. I always need to start with a simple working sample code to get me started and this exercise gave me the opportunity to build a good foundation for future projects. I envision at least 25 PICs working 24/7 in my house for the next 5 years driving various gadgets. I know some might say that you can buy one gadget that will do the work of all little once but nothing gives me more satisfaction when I see things that I built long ago still working with no failures. And on top of that “I got the hammer” (LOL again).

Next step: interrupts.

Regards,

Nick

HenrikOlsson
- 14th September 2013, 19:08
Hi Nick,
No worries, I'm glad I could help and I'm glad you got it working.

My scope is a RIGOL DS4014 (http://www.rigolna.com/products/digital-oscilloscopes/ds4000/ds4014/), it's a 4-channel 100MHz bandwidth scope.
I've had it for a bit over a year now and I really do enjoy it. There's been a lot of development on the scope market in the last couple of years and there are manufacturers in the far east now churning out some pretty impressive machines (and some quite horrible ones as usual) at prices well below similar specs units from the name brands.

/Henrik.

NickMu
- 14th September 2013, 19:42
Thank you Henrik for everything.

Regards,

Nick

Art
- 15th September 2013, 01:14
Couldn't you use the external clock divided by 4 and no program at all?

or if you need a perfect square wave, come up with a 2MHz signal and drive a Flip-Flop with it?

NickMu
- 15th September 2013, 04:43
Hi Art,

Thank you for your input.
Please do not take my reply the wrong way and try to understand my points that follow.

The exercise was to keep the design to a minimum component count, explore unknown features of the PIC processors (unknown to me at the time), be able to modulate a high frequency carrier with the same PIC 10F or 12F family and finally have the most flexibility over the output frequency and duty cycle output. You can not bit the accuracy of the internal clock of the newer PICs if you are trying to use any RC timing circuits and employing any kind of external crystal oscillator defeats the purpose of the exercise.

As Henrik pointed it to me I love having the opportunity to use “the hammer” when it comes to replacing older ways of doing things. I grew up with doing things the hard way and I am convinced now that employing different caliber PICs the job is much easier.
Just to give you a stupid example: try designing an oscillator with a flexible frequency and duty cycle in a wide range using discrete components and you will find yourself in a deep trouble especially when one of the parameters changes.

Use the results of this thread exercise and you will find that all it takes to change one parameter is one or two lines of code. You can use the same hardware over and over for one or multiple projects and all it takes is code changes.

I’m old. My eyes are failing me and even though I can still replace a 100 pin SMD chip with 0.5 mm pitch (that means remove the old chip and install the new one) in less than 3 minutes with 100% success (my personal record about 15 years ago was 87 seconds) I do want to keep using this skill to a minimum.

So to answer your question:

The external clock divided by 4 gives only 50 / 50 duty cycle and only one output frequency for any given main clock. Second part of your question goes in the territory that I’m trying to escape from and gives very little flexibility when changing any parameter because it has to be done in the hardware.

I’m not being lazy I’m just trying to be more efficient when it comes to using my time especially when the clock is ticking for me on the down side.

Thanks for your suggestions and keep up the good work. I’ve been following your posts in this forum and I commend you for your great contributions.

Regards,

Nick

Art
- 15th September 2013, 12:36
Hi Nick,
Great post, and objective :) I've never tried to get out of using an Xtal before,
so hadn't previously considered some of the difficulties coming up with your own timing.

I know what you mean, playing with a phone that almost does everything doesn't motivate
me to build my own computers,
I do have to come back very soon for a project... kind of have to because I want the thing I'm making ;)

If it is just a perfectly linear program that can boss the chip out of running any other program,
such as for a power supply or something... I think as things warm up you lose timing of the internal clock,
same as if there was voltage fluctuation.
but for the exercise... with the new rules as I understand them,
I would try a program that can interrupt itself with an output pin
connected to the portB interrupt pin. Then you could introduce a linear delay at the interrupt
vector, and control the delay before turning the interrupt back on.
I don't know if the jump to the interrupt vector is essentially a GOTO or not.
It jumps to vector 4, which would not be as fast as resetting the program counter to zero vector.
and potentially some inaccuracy caused if there's any variation in the response time to the interrupt.
Cheers, B.

NickMu
- 15th September 2013, 14:11
Hi Art,

I agree with you on your points. One must be careful when simplifying things, like we’ve been doing in this thread, depending on the application.
For my application 10% accuracy on frequency and duty cycle will be fine. The beauty of what we did here is that instead of doing it the old fashion way, trying to match RC values for target frequency and duty cycle, we used the built in oscillator (which in my opinion is more precise and reliable than RC setup even when using 1% components) and use the code to make any changes. In the process the component count, board space and I’m sure cost went down. I didn’t dream when I started this that (excluding PSU section) with only two PICs, a couple of resistors and capacitors and one Mosfet I can wirelessly transfer power with good efficiency and have the grounds ready for data transfer.
As for your suggestion at the end of your post you used the magic word (again I’m talking for myself only) INTERRUPTS.
Yes, they are next on my list and I will start researching how to use them. As soon as I have a clearer image of interrupts I will try to see if they will do any better on my Rx module. I have the feeling that they will eliminate some if not all the jitter but not the delay.

Regards,

Nick

Art
- 15th September 2013, 15:06
If you are still going on it, you probably know not to bother looking at BASIC interrupt in this case.
Usually there is time taken saving and restoring context, but that could likely be avoided
if the program is under 2K, and doesn't care where it was up to when the interrupt was called.

I will be writing for a 16F877 soon, and would see how fast I could just cycle a pin,
but don't have anything to measure it with.

NickMu
- 15th September 2013, 16:00
As the wise man once said “necessity is the mother of invention”.
I’m sure that it will take you no time to build a “tool” to solve your problem. What about employing one more PIC to do the job for you? Set it up to count incoming pulses from your DUT for a set period of time and send the results to a LCD or to your preferred PC communication terminal for a visual. You just built yourself a home made, dirty cheap frequency counter ready to solve your problem now and help you in the future.
Just a thought.
Some experimenting and results in that matter were done in the beginning of this thread but unless you are starting with a really high clock flexibility is not that great. I’m not sure if the Assembly approach will bring any more advantage but, again it is out of my league.


Regards,

Nick

Art
- 15th September 2013, 23:50
Looks like I'll have to.
I always thought if I have a 5 Volt square wave,
that my multimeter would measure it as 2.5 Volts.
I never considered the sampling frequency of the multimeter has
no connection with the frequency of the signal being measured.

NickMu
- 19th September 2013, 01:07
Hi again,

I’m trying to move what I learned last week to a larger PIC (PIC16F819) which I have a lot of leftovers and I’m trying to make use of. I’m also trying to increase my output frequency by using a 16MHz oscillator.

Based on what the data sheet shows I should have about the same setup as I had with PIC12F683 except that I could select between RB2 and RB3 as my output.

With the above code I get good output on RB1 (led out which proves that the system is running) but I do not get any output on RB2 or RB3. I was hoping that it will go by default on one of them.


@ device pic16F819, wdt_off, hs_osc, pwrt_on, mclr_off, lvp_off, protect_on
DEFINE OSC 16 ' We're running at 16 Mhz

ADCON1=%01001110 ' Digital only for all pins

led var PORTB.1
i var byte

CCP1CON = %00001100 ' Normal PWM
PR2 = 3
CCPR1L =0
CCP1CON.5 = 1
CCP1CON.4 =1

For i = 0 to 9
Toggle LED
Pause 500
NEXT

Main:
high led
CCP1CON = %00001100 ' Normal PWM mode
T2CON.2 = 1 ' Timebase ON
PAUSE 15

low led
T2CON.2 = 0 ' Timebase OFF
CCP1CON = %00000000 ' Disable PWM to regain control of PortA.2
PAUSE 5
Goto Main


The data sheet states:

The CCP module’s input/output pin (CCP1) can be
configured as RB2 or RB3. This selection is set in bit 12
(CCPMX) of the Configuration Word register.

I have no idea how to change this setting and if this is the reason why I get no output.
Is there anything that I should do differently?

Thank you in advance for any input.

Nick

HenrikOlsson
- 19th September 2013, 06:52
Hi Nick,

1) Add _CCP1_RB2 or _CCP1_RB3 to your line of config directives in order to select which pin to be used as the CCP1-pin. But yes it WILL default to one of them.

2) When the PIC starts all pins are set to be input. You need to set RB2 or RB3 to be an output by clearing the coresponding bit in the TRISB register. The reason the LED works is because commands like TOGGLE, HIGH and LOW handles clears the TRIS-bit for you - each and every time the command is executed.....

3) Not that it matters but anyway, as far as I can see the 16F819 can not do 16MHz on its internal oscillator, up goes the partcount you said you want to keep low. If running at 16MHz is the main reason for switching to the 16F819 you could just as well stuck that 16MHz oscillator on the 12F683.

/Henrik.

NickMu
- 19th September 2013, 13:21
Hi Henrik,

Thank you for pointing out my stupid mistake. I knew it must be something I overlooked.
In my defense I have to say that after copying the code from 683 I made the necessary changes on the top of the code to apply it to 819. In the process I removed the TRISIO line from the old code and look up the corresponding line for 819. I got distracted (which is quite easy at my age) and did not finish it. My mind was set that I did it so I was looking for something else.

I will add extra configs and test it again after work tonight.

To answer your question why I’m using an external oscillator: I would like to experiment with higher frequencies and I have plenty of 819 (some of them on PCBs ready to use as prototyping boards).
I will order some more oscillators (or try to locate the ones I have somewhere in my garage) for all PBP supported frequencies and have a wide range of available frequencies in the range of 250KHz to 5MHz. Also I could not find any PIC supported by my setup that will do more than 8MHz on internal oscillator.
After Art’s post I realized that my signal generator outputs only signals lower than 2MHz so I have to compensate for the difference.
I’m doing the same thing with the 683 (recycling old rev boards) but the 683 version is extremely small (less than 1² inch) and they do not have the padding for the oscillator.
For the working project I’m still using the low part count concept.

Regards,

Nick

NickMu
- 20th September 2013, 01:39
I've added the two extra configs and I got output on RB2.
I did not have time to play more but I will do as soon as I will return from a two weeks vacation.

Regards,

Nick

Art
- 20th September 2013, 12:16
You could try putting the code you want to be called the fastest, closest to zero vector.
Labels called with a Goto command causes the program counter to start looking for your label from zero,
so the closest it is to zero, the faster it is found, and run.

So without looking at anything else about your code:





// the following is not run time code, so who cares…
@ device pic16F819, wdt_off, hs_osc, pwrt_on, mclr_off, lvp_off, protect_on
DEFINE OSC 16 ' We're running at 16 Mhz
led var PORTB.1
i var byte

// jump to config vector
Goto Config

Main:
high led
CCP1CON = %00001100 ' Normal PWM mode
T2CON.2 = 1 ' Timebase ON
PAUSE 15

low led
T2CON.2 = 0 ' Timebase OFF
CCP1CON = %00000000 ' Disable PWM to regain control of PortA.2
PAUSE 5
Goto Main


Config:
ADCON1=%01001110 ' Digital only for all pins
CCP1CON = %00001100 ' Normal PWM
PR2 = 3
CCPR1L =0
CCP1CON.5 = 1
CCP1CON.4 =1

For i = 0 to 9
Toggle LED
Pause 500
NEXT

// run main program
Goto Main

NickMu
- 20th September 2013, 13:51
This is a nice tip Art.
I will only have the chance to try it after my well overdue vacation, just for fun.

Just so I understand your tip correctly for the sample code presented it will make the GOTO jump in the main loop much faster in which case the main loop will be faster and the second half of the loop will be closer to the target 5 mS?

For my particular application this is not a big advantage but for time sensitive applications it might make a difference. All I need in this case is to create a 20 mS frame with a 15 / 5 mS ratio.
I can only guess that the Rx program might benefit from that but in my case the really fast action happens behind the scene and I think that the system is pushed to its maximum.

I was always under the impression that each PBP command has a fixed amount of execution time but your suggestion proves me wrong.

How is your project going?

One correction to my statements few posts ago: I mentioned that I will try to increase my available frequencies by employing a PIC16F819 with higher frequency external oscillators to cover 2 MHz – 5 MHz band but I just realized that my math was wrong. For some reason I was under the impression that you can get a maximum Fosc / 4 out of PWM module but the formula proves me wrong again, it is only Fosc / 8. I think that I was biased by the fact that the Fosc / 4 is available on clock out feature.
Anyway recycling few boards that will only collect dust in my garage is still good enough even though the expected results are a bit lower.

Regards,

Nick

Art
- 20th September 2013, 14:21
Yes, as I understand, the major delay per cycle is the "Goto Main".
It's not my suggestion that proves you wrong, it's you timing it ;)

Then if you work out how long the Goto takes to execute, you can
subtract that from the 5 value in your PAUSE 5 command, and you're
fine until you put anything between the start of your program and the
end of main, or also if you use a beefy command like sound, or serin/out
etc., as they put code in the front of your program for the same reasons
(they want to execute fast also).

HenrikOlsson
- 20th September 2013, 16:21
Hi,
I'm definitely no expert on the low level stuff but I do think there's some misunderstaning / confusion going on here.

When you execute a GOTO Label it's NOT like the program starts from the top and then "searches" thru the code until it finds the Label to which you want to GOTO.

The physical adress of the piece of code starting "at" Label is known when the program is compiled so the GOTO Label is simply replaced with GOTO $xxxx where xxxx is the physical adress in program memory where the particular piece of code is located. A GOTO always takes TWO instruction cycles, no more no less.

But, some PICs have its program memory divided into pages and when a GOTO is executed it's important that it GOTO's the adress in the correct page. The compiler handles this for you when needed (that's the warning you see sometimes, Code crosses page boundries or something like that). Making sure you're in the correct page does take a couple of instructions as well so all in all the GOTO CAN take more than 2 instruction cycles but not much. So it CAN make a (small) difference rearanging the routines so it doesn't need to jump across page boundires when executing a GOTO but most of the time you simply wouldn't care.

In this particular case the GOTO Main will take 2 cycles. Only if the Main routine would end up starting in one page and ending in the next would it take more than 2 cycles.

Interrupt service routines are one thing where it can matter. Since the hardware interrupt vector is at adress $0004 (IIRC) it makes sense (if you're looking for the absolute best performance) to place your interrupt service routine at the top of the program (and jump over it of course). If you don't do this it MIGHT end up in another memory page and you'll loose a few cycles every time it needs to set the correct page bits before jumping to the ISR.

Again, I'm no expert so I might very well have some details wrong - I'm happy to be corrected.

/Henrik.

rsocor01
- 21st September 2013, 06:20
Henrik,

I don't have the definitive answer either, but I think that what Art is suggesting makes sense to me. I have tried moving to the beginning of the program subroutines that I use a lot in the program and the used programming code space is reduced by a good number of bytes. So, I assume that the program is also executing faster. I believe that this has something to do with the way the program memory is organized in pages in many PICs.

Robert

HenrikOlsson
- 21st September 2013, 10:51
Hi,
On the 12F683 and 16F819 there is only one page of program memory so it doesn't (or shouldn't if my reasoning is correct) matter where you put anything because there will never be a need to switch pages.

If I understand what Art wrote correctly he's basically claiming that the "further" the GOTO jump is, the longer it takes and the further "down the list" your subroutines are placed the longer it takes to GOSUB them because the program counter starts "searching for them" from the beginning of the program. I don't think that is correct - neither on PICs with a single memory page nor on those with multiple pages.

If using a PIC with the program memory spanning several pages and it has to jump (GOTO or GOSUB) from ona page to another then the compiler will insert code to make sure the correct page is selected before executing the GOTO but it either does that or it doesn't and that piece of code (to select the correct page) will be always take the same amount of time (a couple of cycles I imagine), it doesn't matter where or "how far" it is going to jump.

/Henrik.

EDIT: Robert, which PIC did you use in the project where you moved the subroutines?

Art
- 21st September 2013, 13:39
That is right sorry, I have my interpreted and compiled BASICs mixed up.

It's probably not bad practice anyway, so that your fast code is not kicked
out of the first page when you add picbasic's macro type commands such
as serin/out, DTMF, SOUND, pauseus, etc.
It also matters with C later on, to to avoid forward declarations.

It makes me wonder now, if every goto command also has the PCLATH
command with it, or if picbasic waits till the program extends past the 2K page (probably does).

You also turned off the watchdog, but didn't tell PBP not to insert clrwdt commands.
You would only need a single asm "@ clrwdt" inside the whole Main loop,
and calculate that into your subtract from the delay time,
but they are likely in the PAUSE commands, and factored into timing I think.

HenrikOlsson
- 21st September 2013, 14:55
Nope, not bad practice at all, I just don't see it making any difference in this application. But again, do try it - I might be wrong!

As for tweaking the delay time, it's a good thing to keep in mind for when timing is REALLY criticial but for this application, I don't know.... IMHO it's making things WAY more complicated than it is....

We're talking 20ms total period time here (ideal), 15ms 'carrier on', 5ms 'carrier off'. The PIC is running at 8MHz so each instruction is 500ns, it takes two instruction cycles to GOTO Main, add another couple of instructions for the rest, lets call it 20 in total - the actual period would be 20.01ms, an error in timing of 0.05%, I think that miniscule error will be swamped by the inacuracy of the internal oscillator. Of course, it MIGHT matter but then you need something better than the internal oscillator to run it.

/Henrik.

rsocor01
- 21st September 2013, 17:41
Henrik,

I think used a PIC16F727 when I did that. I think I got that idea of moving the most used subroutines to the beginning of the program when reading a post by Melanie. It had something to do with saving programming space in a PIC. I will try to find that thread later.

rsocor01
- 21st September 2013, 17:57
Ok, here is the post

http://www.picbasic.co.uk/forum/showthread.php?t=12105&p=80527#post80527

Art
- 22nd September 2013, 00:58
Henrik was right there, I was selling PBP short.
It shouldn't matter with PBP programs under 2K anyway,
but you'll appreciate the practice if you use another language.

You remember the old interpreted BASIC with line numbers?


10 CLRSCREEN
20 PRINT "HELLO WORLD"
30 GOTO 20


It's the goto in that one that searches a list to find the line/label it's after.
In this case (30 GOTO 20), it searches from the beginning, but if the destination
line is after the GOTO command, it searches from the GOTO command onward.
Some interpreted Pic BASIC language (not PBP) probably does the same thing
even if you don't need to use the line numbers anymore.

If you just want to check a byte variable is in BANK0 to access it without switching,
you can increment it with inline assembler.


@ incf _B1;

should be the same as


B1 = B1 + 1;

If byte B1 was not in BANK0, a different variable or some unassigned RAM will be incremented.

HenrikOlsson
- 22nd September 2013, 05:58
Hi,

Robert,
Yes, the 16F727 has its program memory divided into four pages so I can see it making a difference on that one. But still, I'm not cyet onvinced that having them "first" is what actually helps, I believe it's more like where they (the subroutines that is) are relative to where the calls TO them are. I mean, calling subroutines in page 0 from Main in page 1 should be no different from calling subroutines in page 1 from Main in page 0....it needs to switch page in both cases.

Darrel or anyone - are you listening? Care to share some details?

Art,
If that interpreted BASIC, whichever it is, really works like that it kind of sucks - big time IMO.
As for the rest, you're now talking about RAM and not program memory.

/Henrik.

rsocor01
- 22nd September 2013, 06:46
Yes, unfortunately I don't know the facts either. It would be good if somebody else clarifies this point here.

HenrikOlsson
- 22nd September 2013, 11:15
I've posted a question (http://support.melabs.com/threads/930-Placement-of-subroutines.?p=2000#post2000) on the MELABS support forum. Hopefully Darrel or someone else will give us some insight.

/Henrik.

Art
- 23rd September 2013, 02:53
Hi,
Art,
If that interpreted BASIC, whichever it is, really works like that it kind of sucks - big time IMO.
As for the rest, you're now talking about RAM and not program memory.

/Henrik.


The second part is in relation to the link posted (keeping vars in BANK0 - avoiding switch statements).

I will try to explain why it is important to put code you want to run fast at the beginning of your PBP program,
even though I was incorrect as to one of the reasons why, there are more, and PBP actually does this in the
compiled assembler program (with it's own function calls, not yours) whether you like it or not.

You want to keep most called functions (PBP routines) in the first 2K code space to avoid page switching.
You also want to keep most used variables in Bank0 to avoid bank switching (for RAM).

Say you fill up the first 2K page with code, and run into the second code page,
then introduce a serin or serout command in the second code page.
PBP doesn't put the serin command where you wrote it.
Every PBP macro command such as serial is placed at the beginning of the generated
assembler code, which kicks some of your your PBP code out of the first 2K code space.
What is placed where you wrote the serial command is a series of gosubs that pass
individual bytes to the assembler routine which has been placed at the beginning of the asm source.

You wil notice that if you use a second serial command, your code size doesn't massively increase again
because the original asm routine that was inserted by the first serial command is reused.

When you use a serin, and serout command, this is the decompiled assembler that is placed
at the beginning of your compiled code to deal with it:


SerialIN ;receive serial byte with pre-determined timeout
movf R6 ,W ;move timeout delay to temp
movwf R0 ;
movf R15 ,W ;
movwf R9 ;
movlw 0x01 ;
movwf R1 ;set inside loop counter
clrf R10 ;
serial_loop ;
clrwdt ;keep watchdog timer clear
call SerinX ;get one bit
btfss status ,C ;wait for start bit
goto serinstart ;
decfsz R1 ,F ;
goto serial_loop ;
movlw 0xFF ;
addwf R10 ,F ;
btfsc status ,C ;
goto serial_loop ;
addwf R0 ,F ;
btfss status ,C ;
addwf R9 ,F ;
btfss status ,C ;
goto DoneX ;time out
movlw 0x32 ;
movwf R1 ;reset inside loop counter
clrw ;
movwf R10 ;
goto serial_loop ;
SerialIN_notimeout ;
clrwdt ;receive byte serialy with no timeout
call SerinX ;get one bit
btfsc status ,C ;wait for start bit
goto SerialIN_notimeout ;
serinstart ;
call serialdelay ;wait 1/4 bit time
clrf R4 ;zero parity accumulator
movlw 0x08 ;number of bits in one byte
movwf R10 ;
serpar ;
call seroutdelay ;wait bit time
call SerinX ;get one bit
btfsc status ,C ;accumulate parity
incf R4 ,F ;
btfss R14 ,05 ;if no parity...
clrf R4 ;...keep it even
rrf R1 ,F ;move bit into byte
decfsz R10 ,F ;check for more bits
goto serpar ;
btfsc R14 ,05 ;if parity...
bcf R1 ,07 ;...clear top bit of result
call seroutdelay ;delay to stop bit
bsf status ,C ;carry shifts in clear
movf R1 ,W ;get char to W
return ;
SerinX ;
movf RR2 ,W ;receive one bit for serial in
movwf fsr ;
movf RR1 ,W ;
bsf fsr ,07 ;
iorwf indf ,F ;
bcf fsr ,07 ;
andwf indf ,W ;
btfsc R14 ,06 ;
xorwf RR1 ,W ;
addlw 0xFF ;
return ;
SerialIN_skipbyte ;
movf R13 ,W ;ignore received byte for serial in
bsf status ,C ;
btfsc status ,Z ;
goto DoneX ;
call skipX ;
btfss status ,C ;
return ;
decf R13 ,F ;
goto SerialIN_skipbyte ;

serialOUT ;transmit byte serialy from port pin
movwf R3 ;save byte to send
movf RR2 ,W ;get port to fsr
movwf fsr ;
movlw 0x80 ;zero parity accumulator (preserve blanking)
andwf RM2 ,F ;
movlw 0x08 ;1 start bit + 7 data bits
movwf R12 ;
bcf status ,C ;start bit low
seroutloop ;
btfsc status ,C ;accumulate parity bits
incf RM2 ,F ;
call seroutbit ;send a bit and delay bit time
rrf R3 ,F ;move to next bit
decfsz R12 ,F ;do next bit, if any
goto seroutloop ;
nop ;
btfsc R14 ,05 ;send parity
rrf RM2 ,W ;get parity to carry
call seroutbit ;send parity or last bit and delay bit time
call Done ;
bsf status ,C ;stop bit high
call seroutbit ;send stop bit and delay bit time
movf R16 ,W ;do pacing
movwf R10 ;
movf R7 ,W ;
goto DelayX ;
seroutbit ;
bcf fsr ,07 ;make sure we're pointing to the port
btfss R14 ,07 ;check for TRIS mode
goto seroutnorm ;handle normal mode
movf indf ,W ;get port data
iorwf RR1 ,W ;set bit on
btfss R14 ,06 ;bit matches invert mode
xorwf RR1 ,W ;set bit off
movwf indf ;write it out
bsf fsr ,07 ;point fsr to port tris
movf indf ,W ;get tris data
iorwf RR1 ,W ;make bit an input
btfss status ,C ;bit direction matches bit
xorwf RR1 ,W ;make bit an output
movwf indf ;write it out
goto seroutdelay ;wait remainder of bit time
seroutnorm ;
movf indf ,W ;get port data
iorwf RR1 ,W ;set bit on
btfss status ,C ;skip if bit should be on
xorwf RR1 ,W ;set bit off
btfsc R14 ,06 ;invert if 'N' mode
xorwf RR1 ,W ;flip for invert
movwf indf ;write it out
bsf fsr ,07 ;point fsr to port tris
comf RR1 ,W ;get complemented bit mask to W
andwf indf ,F ;clear tris bit to set output
goto seroutdelay ;wait remainder of bit time
serialdelay ;
bsf RM2 ,06 ;
seroutdelay ;
movf R14 ,W ;get high baud byte
andlw 0x1F ;mask to our bits
addlw 0xFF ;fix baud
movwf R9 ;save it
movf R5 ,W ;get low baud byte
addlw 0xF5 ;
btfsc status ,C ;bump up high byte if low overflowed
incf R9 ,F ;
btfss RM2 ,06 ;
goto pauseUSL ;
bcf RM2 ,06 ;clear short delay mark
movwf R0 ;save low part
movlw 0x02 ;divide time by 4
call shiftR ;
goto pauseUSL ;do 1/4 delay


That's quite a lot of code asking to be placed in the first 2K code space (and kicking your code out).
The serial always appears to be placed first, then SOUND, then EEPROM, then PAUSE.

If you use any PAUSE command, this is inserted:


Delay
clrf R10 ;delay 1
DelayX ;
movwf R1 ;delay 2
DelayXX ;
movlw 0xFF ;
addwf R1 ,F ;
btfss status ,C ;
addwf R10 ,F ;
btfss status ,C ;
goto DoneX ;
movlw 0x03 ;
movwf R9 ;
movlw 0xDF ;
call pauseUSL ;
goto DelayXX ;
clrf R9 ;
pauseUSL ;
addlw 0xE8 ;
movwf R0 ;
comf R9 ,F ;
movlw 0xFC ;
btfss status ,C ;
goto XpauseUSL ;
pauseX ;
addwf R0 ,F ;
btfsc status ,C ;
goto pauseX ;
XpauseUSL ;
addwf R0 ,F ;
clrwdt ;
incfsz R9 ,F ;
goto pauseX ;
btfsc R0 ,00 ;
goto pausedoneX ;
pausedoneX ;
btfss R0 ,01 ;
goto pausedone ;
goto Done ;


or on chip-EEPROM Read/Write:



EEPROMread ;read byte from on-chip EEPROM
bsf status ,rp0 ;for bank 1
movwf EEADR ;set the EEPROM address
bsf EECON1 ,0x0 ;EECON1 in bank 3
movf EEDATA ,W ;read from data memory
goto DoneX ;done

EEPROMwrite ;write byte to on-chip EEPROM
bsf status ,rp0 ;for EE in bank 1
movwf EEDATA ;set the EEPROM data
bsf EECON1 ,02 ;
movlw 0x55 ;unlock the door
movwf EECON2 ;
movlw 0xAA ;
movwf EECON2 ;
bsf EECON1 ,01 ;do the write
writeloop ;
btfsc EECON1 ,01 ;wait for the write to complete
goto writeloop ;
bcf EECON1 ,02 ;lock up when we're done
goto DoneX ;


So bottom line is if you are using any of these, insert them in your code once, straight away
before bothering with timing, or determining which bank you want your variables to be in.
Then you can add additional identical commands without worrying about anything else being added.
All of these commands reuse the code from the first time they were used in a PBP program.
Cheers, Art.

HenrikOlsson
- 23rd September 2013, 07:07
Hi Art,
I know most of that, the thing I'm having proplem with is this

You want to keep most called functions (PBP routines) in the first 2K code space to avoid page switching.
If the calling line (the GOSUB in your "main" routine) ends up in Page 1 while the sub is in Page 0 it STILL needs to switch pages - that's what I'm saying.

But...Darrel just responded to the post on the MELABS forum with some details and it turns out that GOSUBing from Page 1 to Page 0 does indeed take one instruction less than the other way around - go figure. But (again) if everything fits within the same page (Page 0) then that same GOSUB now takes 3 instructions instead of 2. Go figure...

/Henrik.

Art
- 23rd September 2013, 12:51
I always thought the PBP author put his routines closest to zero vector
for the reason I gave. Now, since you sorted me out on that,
I think it's to avoid bank or page switching in his asm code to both
make it smaller, and if he didn't always know the internal variables were in bank0,
timings of the routines would be a pain, just as it can be here.


If the calling line (the GOSUB in your "main" routine) ends up in Page 1 while the sub is in Page 0 it STILL needs to switch pages - that's what I'm saying.
There are often only a few loops where timing is an issue, like in the first example of this thread.
or, for example, something that is sending or receiving bytes at a time in a for next loop can be
called from anywhere and contained in the first 2K.



Page 1 to Page 0 does indeed take one instruction less than the other way around - go figure.

I haven't found the post, but it's cheaper to write a zero value than anything else,
that is done with a single clrf instruction, but writing a one takes two instructions.



if everything fits within the same page (Page 0) then that same GOSUB now takes 3 instructions instead of 2. Go figure.

That one's got me stumped. I would like to see the disassembly.
A jump from page 0 to page 0 should just be a gosub as if you are using a 2K chip.
You'll notice in the code above there are no asm gosubs.
Only returns to the gosubs that called the routines.
I think they must be more expensive, but that doesn't explain the difference.

Ioannis
- 23rd September 2013, 13:14
if everything fits within the same page (Page 0) then that same GOSUB now takes 3 instructions instead of 2. Go figure...

I think it is because it does not know if it is in page 0 or 1 or else, so a test must be done first?

Ioannis

HenrikOlsson
- 23rd September 2013, 18:21
Hi,
Here's a link (http://support.melabs.com/threads/930-Placement-of-subroutines.?p=2000#post2000) to the thread on MELABS forum. Darrel posted code there which he used to demonstrate, I'm sure you can disassemble that.

/Henrik.

rsocor01
- 23rd September 2013, 20:21
Hi,
Here's a link (http://support.melabs.com/threads/930-Placement-of-subroutines.?p=2000#post2000) to the thread on MELABS forum. Darrel posted code there which he used to demonstrate, I'm sure you can disassemble that.

/Henrik.

Hmm, I knew that the used programming space goes down, but I had no idea that it had to do with the GOSUBs. Also, it is good to know that the enhanced chips do not have this issue.

NickMu
- 5th December 2013, 18:25
Hi Team,

I am using the setup from post #43 and I need a clarification on how the duty cycle setting works.
If I understood the data sheet correctly for a 16 MHz main clock and an output frequency of 250 KHz I have access to 16 steps of duty cycle.
Also the manual states that you can use CCPR1L which is 8 bit wide and CCP1CON.5 plus CCP1CON.4 so you have access to a 10 bit wide setting.

My question is who keeps track of individual values and their place in the 10 bit variable.
Is the user supposed to do the logic or PBP takes care of it?
For example if I use something like this:

CCPR1L = 5
CCP1CON.5 = 1 (an 0 means 0 and an 1 means 2)
CCP1CON.4= 0 (an 0 means 0 and an 1 means 1)

Is the result 7 so we just add the two values or is it 21 which comes from 5 shifted twice and add 2 from CCP1CON.5 ?

Does one have to pick only multiple of 4 when choosing CCPR1L or whatever value is given is taken as a 10 bit value and added to CCP1CON 4 and 5?


Regards,

Nick

HenrikOlsson
- 5th December 2013, 19:05
Hi,
With 16MHz main clock there are three different prescaler ratios all giving you 250kHz but different number of bits of resolution.
If you set prescaler to 1:2 and load PR2 with 3 then you'll get 250kHz and 5 bits of resolution (not 5 "full" bits actually), ie you can set the dutycycle value anywhere between 0 and 16.

If you set the prescaler to 1:1 and load PR2 with 15 you'll get the same frequency but now you've got better resolution, 0-64 instead of 0-16.

Now, just write out whatever value you want in binary as a 10bit value: %0000010110
The two least significant bits goes to CCP1CON.5 and CCP1CON.4 and the remaining 8 bits goes to CCPR1L, in this case (as in your example) the result would be 21 which would result in either 100% or ~20% depending on the prescaler and PR2 setting as outlined above.

/Henrik.

NickMu
- 5th December 2013, 20:38
Thank you Henrik.
It makes more sense to me now.

Regards,

Nick