As a parallel to the thread started by Prince, how about a clever and useful tricks thread?
I'll start...
Tip 1: The ability to output the CV ref voltage on a pin on the 18F4620 can be awesome for debugging. It gives you the ability to output 16 different analog voltages. I (like most embedded developers) often write state machines to get my work done. I sometimes use the CV ref pin to output a voltage indicating which state I'm in. That way I can track state transitions in real time using an oscilloscope.
Tip 2:
If you're maintaining a legacy product using a chip such as the 18F458, consider modifying one of your engineering samples to use the 18F4580. Porting the code isn't hard (use #ifdefs for the differences). Use the additional breakpoints available on the 18F4580 to find your problem, then fix it on the production boards using the 18F458's. The difference between having 3 breakpoints and one is huge. Those who have the bling to buy an ICE can safely ignore this tip.
RE: Clever and Useful tricks - May 26, 2006 10:27:01 AM
Guest
In C18, always declare local loop control variables as the first local var in the function, as unsigned char. The compiler will access the variable using INDF2, that is the fastest ACCESS mode that you have in the PIC18, faster even than a global var. All other local vars will require PLUSW2 addressing, much slower.
Posts: 813
Joined: Jan. 8, 2006
From: Sweden
Status: online
In many architectures, multiplication by a power of 2 can be optimized to a bit shift operation.
In the PIC18 though, multiple bit shifts can be optimized into a single multiplication instruction (MULLW). It's mostly useful for 8-bit variables, but in some cases it can be used for 16-bits as well. Implemention is compiler-specific, but here's a couple of examples in assembly:
Aha. Nice thread. Glad I could start something. Hah. Wow these are cool tricks. I guess i named my thread a bit wrong, nice catch. These might be a bit more useful than mine. Though the posts on the other... hah... started intresting and puzzling discussion. These will be good for people looking for optimization(sp).
_____________________________
ETA - Certified Student Electronics Technician Experience In 12F, 16F, 18F, and 30F
while (alive > 0)
{
HandleLife();
Intelligence++;
}
RE: Clever and Useful tricks - May 27, 2006 3:29:20 PM
Guest
This one I can claim originality:
Multiply 16x16->32 in C18 (similar to the 8x8->16):
int y,z;
long x;
// trick: get 16*16->32
y = z = 1000;
x = (y*z,*(long *)&AARGB3); // 63 cycles
This works very well in C18 v3.00. It is compiler-version specific, though: for higher versions, you need to use __AARGB3 or the MATHDATA variable used by the 16x16 lib multiply routine. This is almost twice as fast as doing the operation cast to long.
Building on one of his examples, here's a reasonably efficient PutHex subroutine using one level of recursion (2 stack levels total);
;
; Print byte in W as two ASCII nybbles
;
PutHex movwf TEMP ; save byte
swapf TEMP,W ; swap nybbles in W
call Hex2Asc ; process left nybble
movf TEMP,W ; process right nybble
Hex2Asc andlw b'00001111' ; mask off left nybble
addlw h'36' ;
btfsc STATUS,DC ;
addlw h'07' ;
addlw 0-6 ; ($FA)
goto Put232 ; print ASCII nybble
Have fun. Regards, Mike
< Message edited by K8LH -- Jul. 16, 2006 8:33:41 AM >
Posts: 1545
Joined: Mar. 26, 2004
From: Michigan, USA
Status: offline
I've always been amazed at the creative solutions for processing the quadrature A and B outputs from a Rotary Encoder (grin). One simple method for determining encoder direction is to exclusive-or the previous encoder A or B bit reading with the opposite bit of the current encoder reading.
Here's a simple 10 word (PIC16) polling routine that uses a single variable along with a few extra instructions for dealing with encoder detents;
;
; check the Rotary Encoder A and B switches (bits 0 and 1 in
; the debounced SWDAT2 variable) for a change
;
ISR_Encoder
movf SWDAT2,W ; load switch data |B0
andlw b'00000011' ; mask encoder B and A switches |B0
xorwf ENCOLD,W ; same as last reading? |B0
bz ISR_NextColumn ; yes, branch (no change), else |B0
xorwf ENCOLD,W ; restore encoder bits in W |B0
rrf ENCOLD,f ; prep for B-old ^ A-new |B0
xorwf ENCOLD,f ; ENCOLD bit 0 = direction |B0
rrf ENCOLD,f ; now Carry bit = direction |B0
movwf ENCOLD ; update ENCOLD (new BA bits) |B0
;
; encoder position has changed but we only act on a change
; that occurs when the encoder falls into one of the detent
; positions (we ignore the changes between detents)
;
xorlw b'00000011' ; detent position (BA = 11)? |B0
bnz ISR_NextColumn ; no, branch, else |B0
;
; set encoder pseudo 'DEC' or 'INC' switch bits in the SWITCH2
; variable based on the Carry bit for Main program processing
;
I've always been amazed at the creative solutions for processing the quadrature A and B outputs from a Rotary Encoder (grin). One simple method for determining encoder direction is to exclusive-or the previous encoder A or B bit reading with the opposite bit of the current encoder reading.
[/code] Have fun. Regards, Mike
Hello Mike,
I m working on an encoder project, I want to count the pulses for cw and ccw direction, I wrote a code but still counting wrong with this code, could you give me a short example in C language, I couldnt understand the logic of your assembly code.
RE: Clever and Useful tricks - Jun. 1, 2006 3:47:40 AM
Guest
quote:
ORIGINAL: K8LH
One simple method for determining encoder direction is to exclusive-or the previous encoder A or B bit reading with the opposite bit of the current encoder reading.
As always your code is clean and direct Mike. You seem to think in terms of digital logic circuitry.
RE: Clever and Useful tricks - Jun. 1, 2006 3:49:19 AM
Guest
quote:
ORIGINAL: gloin
I m working on an encoder project, I want to count the pulses for cw and ccw direction, I wrote a code but still counting wrong with this code, could you give me a short example in C language, I couldnt understand the logic of your assembly code.
Hello gloin. May I suggest you to create a thread to discuss your problem?
Posts: 1545
Joined: Mar. 26, 2004
From: Michigan, USA
Status: offline
A quick-n-dirty method for adding a serial port to that "one off" prototype board test circuit you're workin' on (the 3.5mm stereo jacks I use fit nicely in the prototype board 0.1" spaced holes).
Posts: 1545
Joined: Mar. 26, 2004
From: Michigan, USA
Status: offline
Software version of Xtal Oscillator Trimmer Capacitor
A 20-MHz crystal oscillator doesn't necessarily oscillate at exactly 20-MHz which can be a 'bummer' in RTC applications (grin). Normally I would use a ceramic trimmer capacitor to 'tune' the oscillator circuit but last year I tried a "software" trimmer capacitor in a couple projects. So far it's proven very reliable and accurate.
The "software" trimmer code consists of 8 instruction words in two locations within the ISR;
;
; ISR_Trim routine is used to adjust the RTC 1-second period to
; within plus or minus 200-nsecs to make up for a crystal which
; may be slightly off frequency. The routine adds or subtracts
; one 200-nsec count (1 Tcyc) from Timer 2 for the first 'CCTR'
; number of 1-msec interrupts each second. Theoretical accuracy
; to within 6.3 secs/year, not including temperature drift and
; crystal aging.
;
; variables: CCNT, correction count from EEPROM [00 to FF]
; CVAL, correction value from EEPROM [FF or 01]
; CCTR, correction counter reloaded from 'CCNT'
; variable each 1-second period
;
; range: ±255 200-nsec counts/second (±51.0 usecs/sec)
;
ISR_Trim
movf CCTR,W ; correction counter 0? |B0
bz ISR_Sw_Input ; yes, branch, else |B0
decf CCTR,f ; decrement counter |B0
movf CVAL,W ; get correction value FF or 01 |B0
addwf TMR2,f ; apply 1 Tcyc timer correction |B0
;
I reset the CCTR 'correction counter' var with the CCNT 'correction count' value after each 1-second RTC period a little further down in my ISR;
;
; the Real Time Clock 1-second 'heartbeat' timer/counter code
; counts 1,000 1-msec interrupts before performing all of the
; once-per-second procedures and functions
;
ISR_RTC
decfsz RTCL,f ; RTC counter lo = 0? |B0
goto ISR_Calibrate ; no, branch, else |B0
movlw d'250' ; |B0
movwf RTCL ; reset RTCL for 250-msecs |B0
decfsz RTCH,f ; all four 250-msec periods? |B0
goto ISR_Calibrate ; no, branch, else |B0
bsf RTCH,2 ; reset RTCH for 4 (x 250) |B0
;
; reload timer Trim correction counter
;
movf CCNT,W ; reload correction counter var |B0
movwf CCTR ; |B0
;
A couple caveats apply when using the code; (1) writing to TMR2 clears the prescaler and postscaler but I don't use the prescaler and the postscaler hasn't yet incremented when I write TMR2, (2) You can apply the correction to shorter overall periods (100-msecs, 200-msecs, etc.) as long as the correction to your particular crystal can be accomplished within the shorter period.
Adjusting the CVAL 'correction value' and CCNT 'correction count' values and saving them to EEPROM is entirely up to you. In one of my projects I use a PCB jumper to display the values, a rotary encoder to edit them, and then remove the PCB jumper to save the edited values back to EEPROM.