![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
colin382
Joined: 03 Jun 2020 Posts: 37 Location: UK
|
18F24J11 clock switch not working |
Posted: Tue May 11, 2021 4:13 am |
|
|
I have a logger application that must run for a long time on a small battery. The data acquired by the logger is written to an SD card once per day.
The logger runs on the internal clock at 31000Hz until the SD card is to be written. This action requires a serial port working at 2400 bps or higher, for which a much faster chip clock is needed. I use the 8MHz internal clock.
I have tried several ways of switching clock rates "on the fly" but none have worked. The attached code is my best effort so far, but although the code runs correctly, the current taken suggests that the chip is always using the high clock rate.
So, dear forum members, is there a way of switching clocks as needed? Code: |
/* Investigation of problem with clock change
Very little latency in the changeover, Microchip DS says 2 old clocks plus
four new. In this case about 60us, much less than the code timings
*/
// No specific header file, all is in this file
#include <18F24J11.h> // from CCS unchanged
#device ADC=10
#FUSES NOWDT //No Watch Dog Timer
// Default fuses
#FUSES T1DIG //Secondary Oscillator Source may be select regardless of T1CON.3 state
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES DSWDTOSC_INT //DSWDT uses INTRC as reference clock
#FUSES RTCOSC_T1 //RTCC uses Secondary Oscillator as reference source
#FUSES DSBOR //BOR enabled in Deep Sleep
#FUSES DSWDT //Deep Sleep Watchdog Timer enabled
#FUSES IOL1WAY //Allows only one reconfiguration of peripheral pins
#FUSES MSSPMSK7 //MSSP uses 7 bit Masking mode
#FUSES WPFP //Write/Erase Protect Page Start/End Location, set to last page or use WPFP=x to set page
#FUSES WPEND //Flash pages WPFP to Configuration Words page are write/erase protected
#FUSES WPDIS //All Flash memory may be erased or written
#use delay(internal=8M) // needed to define UART
#use RS232(UART1, stream = Logger)
setup_uart(baud = 9600, stream = Logger, internal = 8000000);
#use delay(internal = 31000) // start with slow clock
// variables
unsigned char Record[50] = "A long string of no particular interest\r\n";
// 43 bytes = 430 bits at 9600bps = 44.8 ms
void main() {
while(TRUE){
// Using the built-in UART functions
fprintf(Logger, Record);
#use delay(internal = 31000) // back to slow clock
delay_ms(500);
// code above works correctly, UART data is good, delay is good.
// but current 7.3mA, suggesting the clock does not return to 31000Hz.
// when all references to UART removed, current = 1.4mA.
}
} |
|
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9174 Location: Greensville,Ontario
|
|
Posted: Tue May 11, 2021 6:41 am |
|
|
re: I have a logger application that must run for a long time on a small battery. The data acquired by the logger is written to an SD card once per day.
Have you 'done the math' to see how big the 'small' battery needs to be to run for a 'long' time ? Depending on SD card mfr/size it might take a lot of power to store the data. Hmm..how much data ? What 'enviroment' (temperatures) is it located in ? Anything other then 'warm', and battery capacity can be 1/2 or even a 1/3rd of 'rated' aH.
At least look at Microchip's AN606 as a start to begin the 'math'.
When you get the 'number'... double it. 'Little' details like pullups, LEDs,enabled peripherals can steal a few electrons...over time,,,oopsy, battery died.... |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Tue May 11, 2021 7:12 am |
|
|
It will do.
You have the FSCM fuse set. If the clock speed drops below about 100KHz,
it'll switch to the default clock. You need this off.
You need to be setting the oscillator to 8MHz, before you use the UART,
wait a short while for it to stabilise, do your UART operation, then switch back
to 31K.
Also your 'setup_uart' line is doing nothing. A code line placed outside of
the code, has no effect at all. |
|
![](templates/subSilver/images/spacer.gif) |
colin382
Joined: 03 Jun 2020 Posts: 37 Location: UK
|
|
Posted: Tue May 11, 2021 11:19 am |
|
|
@temtronic
Thanks for your interest and observations. The code quoted is of course just the minimum to explain the problem. The project is to run from 3 * AA dry cells and consists of the micro, a SparkFun OpenLog that is only powered when needed (once per day)and one LED using 1mA that is pulsed for 30ms for each logged event- this happens about 2000 times a day on average. The hardware is in a heated residential property. Running from 31000Hz the quiescent current is about 100uA. The 1.4mA quoted originally includes the current taken by a Pickit4.
@Ttelmah
Thanks for the tip regarding the fail-safe fuse- I rather lazily assumed the CCS Wizard would read my mind and do what was necessary. As an aside, why would it step in when the code is running at a legitimate speed?
The code below implements your suggestions but sad to say the current is still high. I have attached the .lst file in case it helps.
Code: |
/* Investigation of problem with clock change
This scheme works, note clock speed determined entirely with #use delay
Very little latency in the changeover, Microchip DS says 2 old clocks plus
four new. In this case about 60us, much less than the output delay_ms() statements
*/
// No specific header file, all is in this file
#include <18F24J11.h> // from CCS unchanged
#device ADC=10
#FUSES NOWDT // No Watch Dog Timer
#FUSES NOFCMEN // no fail-safe (clock) monitor
void main() {
#use delay(internal=8M) // needed to define UART
#use RS232(UART1, baud = 9600, stream = Logger)
#use delay(internal = 31000) // start with slow clock
// variables
unsigned char Record[50] = "A long string of no particular interest\r\n";
// 43 bytes = 430 bits at 9600bps = 44.8 ms
while(TRUE){
// Using the built-in UART functions
#use delay(internal=8M) // needed for UART operation
delay_ms(1); // wait for things to stabilise
fprintf(Logger, Record);
#use delay(internal = 31000) // back to slow clock
delay_ms(500);
// code above works correctly, UART data is good, delay is good.
// but current 7.3mA, suggesting the clock does not return to 31000Hz.
// when all references to UART removed, current = 1.4mA.
// NB 1.3mA of currents above is going to the attached Pickit4
}
}
|
Listing: (In the interest of brevity I have removed most of the comments and Record string
CCS PCH C Compiler, Version 5.094, xxxxx 11-May-21 18:09
Filename: Clock change.lst
ROM used: 382 bytes (2%)
Largest free fragment is 15994
RAM used: 57 (2%) at main() level
58 (2%) worst case
Stack used: 0 locations
Stack size: 31
0000: GOTO 0080
..................... #include <18F24J11.h> // from CCS unchanged
.................... #device PIC18F24J11
*
0030: MOVF FEF,F
0032: BZ 0054
0034: MOVFF FEA,39
0038: MOVFF FE9,38
003C: MOVF FEF,W
003E: BTFSS F9E.4
0040: BRA 003E
0042: MOVWF FAE
0044: MOVFF 39,FEA
0048: MOVFF 38,FE9
004C: INCF FE9,F
004E: BTFSC FD8.2
0050: INCF FEA,F
0052: BRA 0030
0054: GOTO 016A (RETURN)
....................
.................... #list
....................
.................... #device ADC=10
....................
.................... #FUSES NOWDT // No Watch Dog Timer
.................... #FUSES NOFCMEN // no fail-safe (clock) monitor
....................
.................... void main() {
*
0080: CLRF FF8
0082: BCF FD0.7
0084: CLRF F9B
0086: MOVLW 70
0088: MOVWF FD3
008A: MOVF FD3,W
008C: BCF F7E.3
008E: MOVLW 0C
0090: MOVWF FB0
0092: MOVLW A2
0094: MOVWF FAD
0096: MOVLW 90
0098: MOVWF FAC
009A: CLRF 05
009C: CLRF 04
009E: MOVLW FF
00A0: MOVLB F
00A2: MOVWF x48
00A4: BCF FC2.6
00A6: BCF FC2.7
00A8: MOVF x49,W
00AA: ANDLW E0
00AC: IORLW 1F
00AE: MOVWF x49
00B0: MOVLW 07
00B2: MOVWF FB4
.................... #use delay(internal=8M) // needed to define UART
.................... #use RS232(UART1, baud = 9600, stream = Logger)
.................... #use delay(internal = 31000) // start with slow clock
....................
.................... // variables
.................... unsigned char Record[50] = "A long string of no particular interest\r\n";
00B4: MOVLW 41
(and the rest of the bytes)
0158: CLRF 2F
.................... while(TRUE){
.................... // Using the built-in UART functions
.................... #use delay(internal=8M) // needed for UART operation
*
0004: CLRF FEA
0006: MOVLW 38
0008: MOVWF FE9
000A: MOVF FEF,W
000C: BTFSC FD8.2
000E: GOTO 002C
0012: MOVLW 02
0014: MOVWF 01
0016: CLRF 00
0018: DECFSZ 00,F
001A: BRA 0018
001C: DECFSZ 01,F
001E: BRA 0016
0020: MOVLW 97
0022: MOVWF 00
0024: DECFSZ 00,F
0026: BRA 0024
0028: DECFSZ FEF,F
002A: BRA 0012
002C: GOTO 0162 (RETURN)
.................... delay_ms(1); // wait for things to stabilise
*
015A: MOVLW 01
015C: MOVWF 38
015E: MOVLB 0
0160: BRA 0004
.................... fprintf(Logger, Record);
0162: CLRF FEA
0164: MOVLW 06
0166: MOVWF FE9
0168: BRA 0030
.................... #use delay(internal = 31000) // back to slow clock
*
0058: CLRF FEA
005A: MOVLW 39
005C: MOVWF FE9
005E: MOVF FEF,W
0060: BZ 007C
0062: MOVLW 02
0064: MOVWF 01
0066: CLRF 00
0068: DECFSZ 00,F
006A: BRA 0068
006C: DECFSZ 01,F
006E: BRA 0066
0070: MOVLW 97
0072: MOVWF 00
0074: DECFSZ 00,F
0076: BRA 0074
0078: DECFSZ FEF,F
007A: BRA 0062
007C: GOTO 0174 (RETURN)
.................... delay_ms(500);
*
016A: MOVLW 02
016C: MOVWF 38
016E: MOVLW FA
0170: MOVWF 39
0172: BRA 0058
0174: DECFSZ 38,F
0176: BRA 016E
0178: MOVLB F
017A: BRA 015A
.................... // code above works correctly, UART data is good, delay is good.
.................... }
.................... }
017C: SLEEP
Configuration Fuses:
Word 1: F4A0 NOWDT STVREN NOXINST NODEBUG NOPROTECT
Word 2: FF98 INTRC_IO T1DIG NOLPT1OSC NOFCMEN IESO WDT32768
Word 3: F9FF DSWDTOSC_INT RTCOSC_T1 DSBOR DSWDT DSWDT_33SEC NOIOL1WAY MSSPMSK5
Word 4: F1CF WPFP WPEND NOWPCFG WPDIS |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue May 11, 2021 12:28 pm |
|
|
Look at the ASM code for your #use delay() statements inside main().
It's not switching the oscillator frequency. That code is the delay code
for delay_ms().
You need to use the CCS function designed for changing osc freq:
setup_oscillator(OSC_31KHZ);
and
setup_oscillator(OSC_8MHZ);
After each line above, you can put the appropriate #use delay()
statement to tell the compiler the current osc frequency.
You should be aware that the #use delay() statement only affects
code that occurs physically after it in the source file. The manual says:
Quote: | Any timing routines (delay_ms(), delay_us(), UART, SPI) that need
timing information will use the last defined #USE DELAY. |
|
|
![](templates/subSilver/images/spacer.gif) |
colin382
Joined: 03 Jun 2020 Posts: 37 Location: UK
|
|
Posted: Tue May 11, 2021 4:27 pm |
|
|
@PCM,
This is one of the many methods I have tried and failed with. I have now re-visited this solution after Ttelmah suggested turning off the FCMEN fuse, but still no luck.
After RTFM I also modified your suggestion to include OSC_INTRC, e.g. setup_oscillator(OSC_8MHZ | OSC_INTRC); etc. but with no benefit.
To be clear, I added the setup statement immediately before each pre-existing #use delay statement, with the appropriate argements.
In desperation I also tried only adding the setup statements in main(), and then only in while() but as one might expect, there was no improvement.
It's near midnight here in UK, so closing now and hoping for an overnight epiphany... |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Wed May 12, 2021 12:05 am |
|
|
The wizard is totally thick. It often generates incorrect values. This is why
all the 'old hands' here, 'scream' when people use the wizard....
For your clock switch, you need:
Code: |
#include <18F24J11.h>
#device ADC=10
#FUSES NOWDT //No Watch Dog
#FUSES NOFCMEN //disable FSCM
#use delay(internal=8000000)
#use RS232(UART1, baud = 9600, stream = Logger, ERRORS)
//You should _always _ use 'ERRORS' with a hardware UART. Not doing so, unless you
//add your own error handling code, will result in the UART becoming hung.
// variables
unsigned char Record[50] = "A long string of no particular interest\r\n";
// 43 bytes = 430 bits at 9600bps = 44.8 ms
void main()
{
setup_oscillator(OSC_31250 | OSC_INTRC);
#use delay(CLOCK=31250) //start at slow clock
while(TRUE)
{
// Using the built-in UART functions
setup_oscillator(OSC_8MHZ | OSC_INTRC);
#use delay(CLOCK=8M) // needed for UART operation
delay_ms(1); // wait for things to stabilise
fprintf(Logger, Record);
setup_oscillator(OSC_31250 | OSC_INTRC);
#use delay(CLOCK=31250) //back to slow clock
delay_ms(500);
// code above works correctly, UART data is good, delay is good.
// but current 7.3mA, suggesting the clock does not return to 31000Hz.
// when all references to UART removed, current = 1.4mA.
// NB 1.3mA of currents above is going to the attached Pickit4
}
}
|
The point is you need both a #use delay, saying what speed the clock
actually is at (if you want delays to work), and also a separate control
statement to physically switch the speed. When you put a #use delay
'inline' in the code, it _does not_ generate the code to change to this
speed. It only tells the compiler to use this speed in it's calculations
from this point. The actual clock switching has to be done 'as well'...
So if you code without the setup_oscillator, the listing is:
Code: |
.................... //setup_oscillator(OSC_8MHZ | OSC_INTRC);
.................... #use delay(internal=8M) // needed for UART operation
.................... delay_ms(1); // wait for things to stabilise
|
Note no switching code generated, while as I show, you get:
Code: |
.................... setup_oscillator(OSC_8MHZ | OSC_INTRC);
0126: CLRF F9B
0128: MOVLW 73
012A: MOVWF FD3
012C: MOVF FD3,W
.................... #use delay(CLOCK=8M) // needed for UART operation
*
0004: CLRF FEA
0006: MOVLW 39
0008: MOVWF FE9
000A: MOVF FEF,W
000C: BTFSC FD8.2
000E: GOTO 002C
0012: MOVLW 02
0014: MOVWF 01
0016: CLRF 00
0018: DECFSZ 00,F
001A: BRA 0018
001C: DECFSZ 01,F
001E: BRA 0016
0020: MOVLW 97
0022: MOVWF 00
0024: DECFSZ 00,F
0026: BRA 0024
0028: DECFSZ FEF,F
002A: BRA 0012
002C: GOTO 0136 (RETURN)
.................... delay_ms(1); // wait for things to stabilise
|
Showing both the switching code, and the new delay code for this speed. |
|
![](templates/subSilver/images/spacer.gif) |
colin382
Joined: 03 Jun 2020 Posts: 37 Location: UK
|
|
Posted: Wed May 12, 2021 1:45 pm |
|
|
Thanks Ttelmah, that works exactly as hoped. I did need to add a 2ms delay after the fprintf() to allow the last two bytes to be sent before switching back to the slow clock. My error was putting the initial setup_oscillator() and delay() before main().
There is one remaining issue: the current in the slow clock period is 1.7mA. If I remove all references to the RS232 and the associated setup_oscillator() and delay() so that the chip is always running at 31KHz the current comes down to 0.1mA. Next I left all the clock change stuff in place, but deleted the UART setup and the fprintf() that uses it. The current is then also 0.1mA.
It seems that invoking a UART enables a hardware module that runs continuously. Since there doesn't seem to be a #unuse RS232() built-in function, I will resurrect a bitbang UART TXD function I used some time ago. I don't need the RXD side.
Thamks again for your expertise and patience. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Wed May 12, 2021 11:21 pm |
|
|
If you look at the setup_UART function, you can use your #use RS232 like
this:
#use RS232(UART1, baud = 9600, ERRORS, NOINIT, stream = Logger)
Then when you want to use the UART (immediately after the clock switch),
use:
setup_UART(TRUE); //will enable the UART
Then once you have finished your printf, use:
Code: |
#BIT TRMT=getenv("BIT:TRMT")
//printf what you want here
while (TRMT==0)
; //this will wait for the UART to finish transmitting
setup_UART(FALSE); //now turns the UART off
//and now slow your clock speed
|
This allows you to turn the UART hardware off when you are not using it.
However the power consumption won't be coming from the UART hardware
itself, but probably what is connected. Remember the UART TX line will
be driven high when the UART is idle. It suggests something in the circuit
is putting a load on this line so when it is driven this way, there is significant
consumption.... |
|
![](templates/subSilver/images/spacer.gif) |
colin382
Joined: 03 Jun 2020 Posts: 37 Location: UK
|
|
Posted: Thu May 13, 2021 7:08 am |
|
|
Well I would never have thought of that in a million years.
It works and the current in slow clock is now 0.1mA. Brilliant.
Serial data is correct BUT the baud rate is 125K and doesnt change when I change the #use RS232() baud argument !
I think this is because the BRG register is left at default (=0), so the baud rate becomes Fosc/64. The list file has no reference to SPBRG1 (at register FB0h)
I could just write the appropriate value into SPBRG1, but is there a better way?
Code: |
#include <18F24J11.h>
#device ADC=10
#FUSES NOWDT //No Watch Dog
#FUSES NOFCMEN //disable FSCM
#use delay(internal=8000000)
#use RS232(UART1, baud = 9600, ERRORS, NOINIT, stream = Logger)
// Data transfer is correct but at 125Kbaud.
// #use RS232(UART1, baud = 2400, ERRORS, NOINIT, stream = Logger)
// attempt to modify baud rate, no effect
// variables
unsigned char Record[50] = "A long string of no particular interest\r\n";
// 43 bytes = 430 bits at 9600bps = 44.8 ms
void main()
{
setup_oscillator(OSC_31250 | OSC_INTRC);
#use delay(CLOCK=31250) //start at slow clock
while(TRUE)
{
setup_oscillator(OSC_8MHZ | OSC_INTRC);
#use delay(CLOCK=8M) // needed for UART operation
setup_UART(TRUE);
delay_ms(1); // wait for things to stabilise
#BIT TRMT=getenv("BIT:TRMT")
fprintf(Logger, Record);
while (TRMT==0); // wait for the UART to finish transmitting
setup_UART(FALSE); // now turn the UART off
setup_oscillator(OSC_31250 | OSC_INTRC);
#use delay(CLOCK=31250) // back to slow clock
delay_ms(5000); // enough time to measure current
}
}
|
|
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Thu May 13, 2021 7:36 am |
|
|
You can include the clock rate in the setup_UART. Normally it shouldn't be
needed, but I'd suspect it is getting confused by the clock changes.
setup_UART(9600, Logger, 8000000);
Should put the right BRG value in. ![Smile](images/smiles/icon_smile.gif) |
|
![](templates/subSilver/images/spacer.gif) |
colin382
Joined: 03 Jun 2020 Posts: 37 Location: UK
|
|
Posted: Thu May 13, 2021 7:59 am |
|
|
That fixed it. Once again, thanks for your help.
Until the next time... |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Thu May 13, 2021 11:40 am |
|
|
Nice to see progress forwards. ![Smile](images/smiles/icon_smile.gif) |
|
![](templates/subSilver/images/spacer.gif) |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|