Rich D.
Active Member
It seems I have a stubborn PIC. Try as I might, it seems to always want to run at an instruction cycle of 4 MHz, although I was hoping for 16 MHz.
Details: I am using the internal FRC oscillator, fixed at about 8 MHz (7.37 actually). I would like it to clock the processor at 30 MHz for maximum performance since there are no batteries involved. According to the specs, I should be able to run it with a 4X PLL, giving me a frequency of 29.48MHz. From that clock, the instruction time (which is /2) works out to 67.843 nSec. As a side note, the specs define a min instruction cycle time of 62.5 nSec, so that is about as fast as it can perform. All cool, I've read over the Specs and Family Reference Manual about four times now, so I thought I had this in the bag.
Programming this chip using a PicKit 3, I get it to run ok, but an output pin set to low then high in the next instruction gives me a pulse on an oscilloscope of 245 nSec, or about 1/4 the desired speed. I get it, that means I made a mistake somewhere in my config bits or registers used to set the clocks up. But here's the really confusing part: No matter what I change in the program, it never changes on the processor's hardware! Not faster, not slower, but rock-solid at 245 nSec.
This is what I am doing, these values are pre-set with constants:
The FOSCSEL configuration register is the first thing defined in the software,
with the IESO bit high to enable clock switching.
Also the PLLMODE bits set to 1100 for a 4X PLL. Also the FNOSC bits are set to 001 to define the FRC with postscaler and PLL as the initial oscillator.
All together that is a byte of 1 1100 001 or 0xE1.
The other configuration register is FOSC.
Here I have the FCKSM bits set to 00 to enable clock switching and fail safe monitor.
IOL1WAY is set to 0 so that the IOLOCK bit can be set and cleared multiple times.
PLLSS is 0 so that the PLL is driven by the FRC.
SOSCSEL is also set 0 but my understanding is it doesn't matter here.
OSCIOFCN is also set 0 but I believe that also isn't relevant here.
POSCMOD is set to 11 so that the primary oscillator (external, crystal, etc.) is disabled.
That gives me a byte of 00 0 0 x x 11 or a value of 0x3.
As far as I know, these two configuration bytes are all that determines the clock used at startup, FOSCSEL = 0xE1 and FOSC = 0x3.
But there's more, there's always more.
After the processor starts running, I do the usual of setting the stack pointer W15, write to all the registers with a word to eliminate that reg not initialized oddity, then I start to set the various oscillator registers as needed.
First, the CLKDIV register is loaded with 0x0020 which defines the following:
The ROI bit is set to 0, which I believe is not relevant.
The DOZE bits are set to 000 for a /1, and the DOZEN bit set to 0 to turn off the DOZE divider.
The RCDIV bits are set to 000 so the RC postscaler is /1
The CPDIV bits are set to 00 for a /1
The PLLEN bit is set to 1 so it is always active.
Next I set the OSCTUN register bits to all zero for no tuning change. (BTW, changing this value does produce a slight frequency change in my Fcy measurements.)
Now as I understand it I shouldn't have to change the clock at this time, but if I were to do it anyways it should either ignore the change or make a difference within a microsecond or two. Here I used the following code for what I think is the right unlock sequence as described in the specs.
In this code that follows, NEW_CLOCK_MODE is defined as 0x01, so that the high byte of the OSCCON register gets the bits 001 to use the FRC oscillator with prescaler and PLL.
disi #16
mov #NEW_CLOCK_MODE, w0 ;value to move
mov #OSCCONH, w3 ;where to move it
mov #0x78, w1 ;unlock bits
mov #0x9A, w2 ;more unlock bits
mov.b w1, [w3] ;un...
mov.b w2, [w3] ;...lock
mov.b w0, [w3] ;new mode set here
;
;WRITE the OSWEN bit to start switching
mov #OSCCONL, w3 ;where to move it
mov #0x46, w1 ;unlock bits
mov #0x57, w2 ;more unlock bits
mov.b w1, [w3] ;un...
mov.b w2, [w3] ;...lock
bset OSCCON, #0 ;clear starts clock switching
;
;unlock sequence completed, clock switch started. OSCCON bit 0 will be low when completed
not_switched:
btst OSCCON, #0 ;if not switched, it will hang here
bra NZ, not_switched ;forever and appear busted.
There is a booby-trap there so that if the clock switching doesn't happen, it should hang and nothing should execute after that. But it does continue,
so I assume the switching either worked or it was ignored because the config setting already had the clock set where I wanted it.
The next thing I do is set up an output pin to flash an LED on the G port's bit #8.
mov TRISG, w0
bclr w0, #8
mov w0, TRISG
;drive HIGH for ON
mov LATG, w0
bset w0, #8
mov w0, LATG
This seems to work ok because the output goes high and the LED illuminates.
And the last thing I do is to set up the processor to bang a single-cycle low-high to determine the instruction cycle timing.
mov LATG, w0
mov LATG, w1
bclr w0, #8 ;w0 data will drive low/off
bset w1, #8 ;w1 data will drive high/on
mov w0, LATG ;driven low
mov w1, LATG ;driven high again 67.842nS spent
mov w0, LATG ;driven low *67.842nS spent
mov w1, LATG ;driven high again *67.842nS spent
...just for fun I do it twice, so I can measure two more exact low to high pulses that are not skewed by the different rise-fall and fall-rise times.
The scope shows this to take 496 or 498 nSec, so divided by two gives me the instruction cycle time.
I believe this code executes without crashing because after this I have a software delay and toggle the LED state once each 1/2 second in an endless loop, and the led continues to flash for hours.
What I CAN'T UNDERSTAND, is why I can change all the bits in the various config registers and CLKDIV register, and I always get the same LED timing pulses, and see the LED flash at a consistent 1Hz rate, even after power cycles, reprogramming, and various reboots-of-desperation. What could I possibly be missing?!?
Any help would keep me from pulling out my remaining hairs.
Details: I am using the internal FRC oscillator, fixed at about 8 MHz (7.37 actually). I would like it to clock the processor at 30 MHz for maximum performance since there are no batteries involved. According to the specs, I should be able to run it with a 4X PLL, giving me a frequency of 29.48MHz. From that clock, the instruction time (which is /2) works out to 67.843 nSec. As a side note, the specs define a min instruction cycle time of 62.5 nSec, so that is about as fast as it can perform. All cool, I've read over the Specs and Family Reference Manual about four times now, so I thought I had this in the bag.
Programming this chip using a PicKit 3, I get it to run ok, but an output pin set to low then high in the next instruction gives me a pulse on an oscilloscope of 245 nSec, or about 1/4 the desired speed. I get it, that means I made a mistake somewhere in my config bits or registers used to set the clocks up. But here's the really confusing part: No matter what I change in the program, it never changes on the processor's hardware! Not faster, not slower, but rock-solid at 245 nSec.
This is what I am doing, these values are pre-set with constants:
The FOSCSEL configuration register is the first thing defined in the software,
with the IESO bit high to enable clock switching.
Also the PLLMODE bits set to 1100 for a 4X PLL. Also the FNOSC bits are set to 001 to define the FRC with postscaler and PLL as the initial oscillator.
All together that is a byte of 1 1100 001 or 0xE1.
The other configuration register is FOSC.
Here I have the FCKSM bits set to 00 to enable clock switching and fail safe monitor.
IOL1WAY is set to 0 so that the IOLOCK bit can be set and cleared multiple times.
PLLSS is 0 so that the PLL is driven by the FRC.
SOSCSEL is also set 0 but my understanding is it doesn't matter here.
OSCIOFCN is also set 0 but I believe that also isn't relevant here.
POSCMOD is set to 11 so that the primary oscillator (external, crystal, etc.) is disabled.
That gives me a byte of 00 0 0 x x 11 or a value of 0x3.
As far as I know, these two configuration bytes are all that determines the clock used at startup, FOSCSEL = 0xE1 and FOSC = 0x3.
But there's more, there's always more.
After the processor starts running, I do the usual of setting the stack pointer W15, write to all the registers with a word to eliminate that reg not initialized oddity, then I start to set the various oscillator registers as needed.
First, the CLKDIV register is loaded with 0x0020 which defines the following:
The ROI bit is set to 0, which I believe is not relevant.
The DOZE bits are set to 000 for a /1, and the DOZEN bit set to 0 to turn off the DOZE divider.
The RCDIV bits are set to 000 so the RC postscaler is /1
The CPDIV bits are set to 00 for a /1
The PLLEN bit is set to 1 so it is always active.
Next I set the OSCTUN register bits to all zero for no tuning change. (BTW, changing this value does produce a slight frequency change in my Fcy measurements.)
Now as I understand it I shouldn't have to change the clock at this time, but if I were to do it anyways it should either ignore the change or make a difference within a microsecond or two. Here I used the following code for what I think is the right unlock sequence as described in the specs.
In this code that follows, NEW_CLOCK_MODE is defined as 0x01, so that the high byte of the OSCCON register gets the bits 001 to use the FRC oscillator with prescaler and PLL.
disi #16
mov #NEW_CLOCK_MODE, w0 ;value to move
mov #OSCCONH, w3 ;where to move it
mov #0x78, w1 ;unlock bits
mov #0x9A, w2 ;more unlock bits
mov.b w1, [w3] ;un...
mov.b w2, [w3] ;...lock
mov.b w0, [w3] ;new mode set here
;
;WRITE the OSWEN bit to start switching
mov #OSCCONL, w3 ;where to move it
mov #0x46, w1 ;unlock bits
mov #0x57, w2 ;more unlock bits
mov.b w1, [w3] ;un...
mov.b w2, [w3] ;...lock
bset OSCCON, #0 ;clear starts clock switching
;
;unlock sequence completed, clock switch started. OSCCON bit 0 will be low when completed
not_switched:
btst OSCCON, #0 ;if not switched, it will hang here
bra NZ, not_switched ;forever and appear busted.
There is a booby-trap there so that if the clock switching doesn't happen, it should hang and nothing should execute after that. But it does continue,
so I assume the switching either worked or it was ignored because the config setting already had the clock set where I wanted it.
The next thing I do is set up an output pin to flash an LED on the G port's bit #8.
mov TRISG, w0
bclr w0, #8
mov w0, TRISG
;drive HIGH for ON
mov LATG, w0
bset w0, #8
mov w0, LATG
This seems to work ok because the output goes high and the LED illuminates.
And the last thing I do is to set up the processor to bang a single-cycle low-high to determine the instruction cycle timing.
mov LATG, w0
mov LATG, w1
bclr w0, #8 ;w0 data will drive low/off
bset w1, #8 ;w1 data will drive high/on
mov w0, LATG ;driven low
mov w1, LATG ;driven high again 67.842nS spent
mov w0, LATG ;driven low *67.842nS spent
mov w1, LATG ;driven high again *67.842nS spent
...just for fun I do it twice, so I can measure two more exact low to high pulses that are not skewed by the different rise-fall and fall-rise times.
The scope shows this to take 496 or 498 nSec, so divided by two gives me the instruction cycle time.
I believe this code executes without crashing because after this I have a software delay and toggle the LED state once each 1/2 second in an endless loop, and the led continues to flash for hours.
What I CAN'T UNDERSTAND, is why I can change all the bits in the various config registers and CLKDIV register, and I always get the same LED timing pulses, and see the LED flash at a consistent 1Hz rate, even after power cycles, reprogramming, and various reboots-of-desperation. What could I possibly be missing?!?
Any help would keep me from pulling out my remaining hairs.