Scaling from power of 2 to multiple of 10 in ASM

Status
Not open for further replies.

BobW

Active Member
Frequently it's necessary to take a value that has a range from zero to some power of two, and then scale it into a range of zero to some multiple of ten for display. For example, I have a 10 bit analog input 0..1023 that I'd like to display on a 3 1/2 digit display (0..1999). The following simple PIC assembly code does this, and can be easily adapted for inputs that have a different power of two range, and outputs that are different multiples of ten. This routine multiplies the input value by 1.953125 (2000/1024)

Code:
;Registers used
    ;X_H, X_L, X_F - high byte, low byte and fraction byte of input value
    ;Y_H, Y_L, Y_F - high byte, low byte and fraction byte of output result
    ;count - temporary counter register
;
Scale
    ;Scale 0..1024 input to 0..2000 display count 2000/1024=1.953125
    ;X_H, X_L, X_F should be set to input value before calling this routine
    clrf Y_F
    clrf Y_L
    clrf Y_H
    movlw 5
    movwf count
    ;Shift and add loop
sLoop
    call AddXY  ;Add x to y.
    call ShiftX
    decfsz count,f
    goto sLoop
    call ShiftX
    ;Add final adjustment
    call AddXY
    ;Result is in Y_H,Y_L with fractional part in Y_F
    return
;
;The following subroutines use midrange instructions
ShiftX
    bcf status,c
    rrf X_H,f
    rrf X_L,f
    rrf X_F,f
    return
AddXY
    ;Add x reg to y reg - 3 bytes
    incf Y_H,f
    movf X_F,w
    addwf Y_F,f
    btfsc status,c
    incfsz Y_L,f
    decf Y_H,f
    movf X_L,w
    addwf Y_L,f
    btfsc status,c
    incf Y_H,f
    movf X_H,w
    addwf Y_H,f
    return
;
;The following subroutines use enhanced midrange instructions
ShiftX
    lsrf X_H,f
    rrf X_L,f
    rrf X_F,f
    return
AddXY
    ;Add x reg to y reg - 3 bytes
    movf X_F,w
    addwf Y_F,f
    movf X_L,w
    addwfc Y_L,f
    movf X_H,w
    addwfc Y_H,f
    return

Note that the main routine Scale calls two subroutines ShiftX and AddXY. I've included two versions of the subroutines, one for the regular midline PICs and one for the Enhanced Midline PICs.

For anyone wondering why I'm scaling by 2000/1024 rather than 1999/1023, please refer to this discussion:

For other input ranges, simply left or right shift the input value the appropriate number of positions. For example if the input range is 0..4096 call ShiftX twice at the very beginning of the Scale routine.

For other output ranges: 0..1000, 0..500, 0..250 etc., can be had by right shifting the result.

Edit:
I should also mention that the calculation algorithm is an exact multiplication by 1.953125. If more precision is needed for wider ranges, more bytes can be used with no other changes. As presented, the calculation uses three bytes: two for the integer part and one for the fractional part which may occur during intermediate calculations. So, there's little to no loss of accuracy due to truncation of calculation results.
 
Last edited:
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…