PIC ADC scaling/conversion help please

Status
Not open for further replies.
You said:
of course it won't, Basic can't do negative numbers...
It can, but you need to control it.
I have indicators written in Oshon and I provide the logic glue..
 
Last edited:
After hours playing with a calculator following on from Mikes post #19
1. get your ADC value.
2. if above 491 calculate ADC-492 - this allows for the dead zone.
3. if below 483 calculate 482-ADC
4. if neither 2 or 3 true then calculated value=0
5. if not zero then lookup value in a table.
I think I might have a near enough solution:

Do 1-4 as above, but instead of running in to a look up table, multiply the result from 1 or 2 by 56 and divide by 10.

It will give direct numbers that are close enough to 10 - 2500 for the project at hand (any fractions generated should be ignored if integer computation is used).
 
Had some time this morning and thought I'd take a trip down memory lane and do some asm programming.

If you do decide to go the table route, this code will provide a value as you described,
Code:
        radix       dec
        errorlevel     -207    ;Skip found label after column 1
        errorlevel     -302    ;Skip out of bank nuisance messages
        errorlevel     -303    ;Skip program word too large. Truncated to core size
;
; *******************************************************************************
; * Configuration fuse information for 16F1827:                    *
; *******************************************************************************
;
        include   <P16LF1827.INC>

 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
 __CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_LO & _LVP_OFF

    CBLOCK    0x20
tempADC:2
flags
    ENDC         

    CBLOCK    0x70
acc:2                ;in common memory so can be accessed from bank 3 (where flash registers are)
    ENDC

    ORG     0x0000               
    goto    start

    ORG     0x0004

;--------------------------------------------------------------------------------

start
        banksel OSCCON
        movlw     b'01101000'
        movwf   OSCCON
        banksel    0
        call    getValue
        goto    $
    
#define valueADC 35
#define center 487

getValue
;first do ADC-487 to discover direction
        bcf        flags,0            ;clear the negative flag    
        movlw    low(valueADC)
        movwf    acc
        movlw    high(valueADC)
        movwf    acc+1
        movlw    high(center)
        subwf    acc+1,f
        movlw    low(center)
        subwf    acc,f
        btfss    STATUS,C
        decf    acc+1,f
        btfss    acc+1,7        ;is it negative
        goto    notNeg
;need to negate acc
        bsf        flags,0            ;set the negative flag
        movlw    0xff
        xorwf    acc,f
        xorwf    acc+1,f
        incf    acc,f
        btfsc    STATUS,Z
        incf    acc+1,f
notNeg
; have now got a valid lookup value
;and a flag to tell us if above or below 487
        banksel    EEADRL             ;Select Bank for EEPROM registers
        movfw    acc ;
        movwf    EEADRL
        movfw    acc+1
        addlw    0x0e
        movwf    EEADRH
        bcf        EECON1,CFGS     ;Do not select Configuration Space
        bsf        EECON1,EEPGD     ;Select Program Memory
        bsf        EECON1,RD         ;Initiate read
        NOP                     ;required 2 x NOPs
        NOP     
        ;EEDAT will now contain 0 - 2500 
        ;and flags,0 will be set if below center
        goto    $


        org 0xe00
        ;note, dead band incorporated into table
        DW     0,0,0,0,0,5,11,16,22,27,33,39,44,50,55,61,67,72,78,83,89
        DW  95,100,106,111,117,123,128,134,139,145,151,156,162,167,173,178
        DW  184,190,195,201,206,212,218,223,229,234,240,246,251,257,262,268
        DW  274,279,285,290,296,302,307,313,318,324,329,335,341,346,352,357
        DW  363,369,374,380,385,391,397,402,408,413,419,425,430,436,441,447
        DW  453,458,464,469,475,480,486,492,497,503,508,514,520,525,531,536
        DW  542,548,553,559,564,570,576,581,587,592,598,604,609,615,620,626
        DW  631,637,643,648,654,659,665,671,676,682,687,693,699,704,710,715
        DW  721,727,732,738,743,749,755,760,766,771,777,782,788,794,799,805
        DW  810,816,822,827,833,838,844,850,855,861,866,872,878,883,889,894
        DW  900,906,911,917,922,928,934,939,945,950,956,961,967,973,978,984
        DW  989,995,1001,1006,1012,1017,1023,1029,1034,1040,1045,1051,1057,1062,1068,1073
        DW  1079,1085,1090,1096,1101,1107,1112,1118,1124,1129,1135,1140,1146,1152,1157,1163
        DW  1168,1174,1180,1185,1191,1196,1202,1208,1213,1219,1224,1230,1236,1241,1247,1252
        DW  1258,1263,1269,1275,1280,1286,1291,1297,1303,1308,1314,1319,1325,1331,1336,1342
        DW  1347,1353,1359,1364,1370,1375,1381,1387,1392,1398,1403,1409,1414,1420,1426,1431
        DW  1437,1442,1448,1454,1459,1465,1470,1476,1482,1487,1493,1498,1504,1510,1515,1521
        DW  1526,1532,1538,1543,1549,1554,1560,1565,1571,1577,1582,1588,1593,1599,1605,1610
        DW  1616,1621,1627,1633,1638,1644,1649,1655,1661,1666,1672,1677,1683,1689,1694,1700
        DW  1705,1711,1717,1722,1728,1733,1739,1744,1750,1756,1761,1767,1772,1778,1784,1789
        DW  1795,1800,1806,1812,1817,1823,1828,1834,1840,1845,1851,1856,1862,1868,1873,1879
        DW  1884,1890,1895,1901,1907,1912,1918,1923,1929,1935,1940,1946,1951,1957,1963,1968
        DW  1974,1979,1985,1991,1996,2002,2007,2013,2019,2024,2030,2035,2041,2046,2052,2058
        DW  2063,2069,2074,2080,2086,2091,2097,2102,2108,2114,2119,2125,2130,2136,2142,2147
        DW  2153,2158,2164,2170,2175,2181,2186,2192,2197,2203,2209,2214,2220,2225,2231,2237
        DW  2242,2248,2253,2259,2265,2270,2276,2281,2287,2293,2298,2304,2309,2315,2321,2326
        DW  2332,2337,2343,2348,2354,2360,2365,2371,2376,2382,2388,2393,2399,2404,2410,2416
        DW  2421,2427,2432,2438,2444,2449,2455,2460,2466,2472,2477,2483,2488,2494,2500,2500
        DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
        DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
        DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
        DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
        end
You could use the code above the label notNeg to get the value to be multiplied by 56 and divided by 10.
Note, this will not make an allowance for the dead zone.

Mike.
 
That looks interesting Mike, will give it a bash in a PIC tomorrow (with EEDAT data shown on the display for testing).
 
One thing to bear in mind, the DW table only uses half a K of flash. You still have 3.5K for the rest of your code. I assume the original code fitted in a smaller pic so this might be a viable alternative.

Mike.
 
The original code was in a 12F629 so plenty of room in the 16F1827.

Only extra to add will to check if anything has changed since the last ADC read and skip it if nothings changed.

Hopefully this will be that last part of the puzzle.
 
Wrote quick test program to test code for display on LCD - doesn't work in Oshonsoft simulator (could be a problem of the simulator) - won't have time to test it in an actual PIC for a few days (shows 000 instead of table values).

Code below if anybody wants to mess around with it.

C-like:
;
;********************************************************************************
;                                                                            
; Target Controller        PIC16F1827
;                          __________                                        
;           ADC-IN-----RA2 |1       18| RA1---------
;          -----------RA3 |2       17| RA0---------
;          -----------RA4 |3       16| RA7---------
;          -----------RA5 |4       15| RA6---------
;     Ground----------Vss |5       14| VDD---------+5 V
;     LCD D4----------RB0 |6       13| RB7---------
;     LCD D5----------RB1 |7       12| RB6---------LCD_rs  (LCD Pin 4)
;     LCD D6----------RB2 |8       11| RB5---------LCD_rw  (LCD Pin 5)
;     LCD D7----------RB3 |9       10| RB4---------LCD_e   (LCD Pin 6)
;                          ----------                                        
;                                                                            
; *******************************************************************************
; * Device type and options                            *
; *******************************************************************************
;
        processor 16F1827
        radix     dec
    errorlevel -207    ; Skip found label after column 1
    errorlevel -302    ; Skip out of bank nuisance messages
    errorlevel -303    ; Skip program word too large. Truncated to core size
;
; *******************************************************************************
; * Configuration fuse information for 16F1827:                    *
; *******************************************************************************
;
        include   <P16F1827.INC>

 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
 __CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_LO & _LVP_OFF
;
; *******************************************************************************
; Info for power-up display. Why was it done this way instead of with a table -
; because it is easier for beginners to visualise where the characters will
; appear on the LCD.

SIGN_ON_1    equ ' '        ; Position 1 on LCD - left most position
SIGN_ON_2    equ 'V'
SIGN_ON_3    equ 'K'
SIGN_ON_4    equ '5'
SIGN_ON_5    equ 'T'
SIGN_ON_6    equ 'M'
SIGN_ON_7    equ ' '
SIGN_ON_8    equ 'A'
SIGN_ON_9    equ 'D'
SIGN_ON_10    equ 'C'
SIGN_ON_11    equ ' '
SIGN_ON_12    equ 'T'
SIGN_ON_13    equ 'E'
SIGN_ON_14    equ 'S'
SIGN_ON_15    equ 'T'
SIGN_ON_16    equ ' '        ; Position 16 on LCD - right most position
;                                                                            
; *******************************************************************************
; * General equates.                                *
; *******************************************************************************
;
#define center 487
;
; *******************************************************************************
; * Assign names to IO pins.                            *
; *******************************************************************************
;
LCD_busy equ    0x03        ; LCD busy bit
LCD_e    equ     0x04        ; 0=disable, 1=enable
LCD_rw    equ     0x05        ; 0=write, 1=read
LCD_rs    equ     0x06        ; 0=instruction, 1=data
;
;
;    16F1827 Oscillator setup
    ; OSCCON - Oscillator control reg.
    ; ---------------------------------
    ; SPLLEN b7 enable PLL x4
    ; 1 = enabled 0 = disabled
    ; IRCF | b6-3 frequency selection
    ; 1111 = 16MHz HF
    ; 1110 = 8 or 32MHz HF
    ; 1101 = 4MHz HF
    ; 1100 = 2MHz HF
    ; 1011 = 1MHz HF
    ; 1010 = 500kHz HF
    ; 1001 = 250kHz HF
    ; 1000 = 125kHz HF
    ; 0111 = 500kHz MF (default)
    ; 0110 = 250kHz MF
    ; 0101 = 125kHz MF
    ; 0100 = 62.5kHz MF
    ; 0011 = 31.25kHz HF
    ; 0010 = 31.25kHz MF
    ; 000x = 31.25kHz LF
    ; Reserved B2 reserved, 0
    ; SCS      B1 0-: 1x = int. OSC.
    ; 01 = Timer1 oscillator
    ; 00 = determined by FOSC <2:0> in Configuratio
    ; POR default 00111-00 500 kHz (POR = Power On Reset)
OSCCONVAL EQU b'01101000'    ; 4MhZ CLOCK
;
; *******************************************************************************
; *           Allocate variables in general purpose register space        *
; *******************************************************************************
;
    CBLOCK    0x20        ; Start Data Block
    TenK
    Thou
    Hund
    Tens
    Ones
    freq_0
    freq_1
    freq_2
    freq_3
    byte2send        ;
    LCD_char        ; Character being sent to the LCD
    rs_value        ; The LCD rs line flag value
    timer1            ; Used in delay routines
    timer2            ;   "
    count            ; loop counter  (gets reused)
    temp            ; General reusable register
    CounterA
    CounterB
    CounterC
    CounterD
    CounterE
    flags
    ENDC            ; End of Data Block

    CBLOCK    0x70
    acc:2
    valueADC:2
    NumH
    NumL
    ENDC            ; End of Data Block
;
; *******************************************************************************
; * The 16F1827 resets to 0x00.                            *
; * The Interrupt vector is at 0x04.                        *
; *******************************************************************************
;
    ORG    0x0000              
    goto    start        ; Jump to main program

    ORG    0x0004
    goto    start        ; Jump to main program
;
; *******************************************************************************
; *                                        *
; * Purpose:  This is the start of the program.                    *
; *                                        *
; *******************************************************************************
;
start
    BANKSEL    PORTA
    clrf    PORTA
    clrf    PORTB

; Set PIC oscillator frequency
    banksel    OSCCON        ; Select OSCCON
    movlw    OSCCONVAL    ;
    movwf    OSCCON        ; Loads the wanted value
    banksel    PORTA

; Configures all I / O as digital
    Banksel    ANSELA
    bsf    ANSELA,ANSA2    ; set ra2 to analog
    clrf    ANSELB

; Disable all wakeup pull-ups
    Banksel    WPUA
    clrf    WPUA
    clrf    WPUB

    BANKSEL    OPTION_REG
    clrf    OPTION_REG    ;
;
        movlw   b'00110111'    ; PORTA (RA0:2,4:5 inputs, RA3,6:7 outputs)    
        movwf   TRISA        ;
        clrf    TRISB        ; Set port B to all outputs

;    BANKSEL    ADCON1        ;
    movlw    b'11000000'    ; right justify, FOSC/4 vdd and vss vref
    movwf    ADCON1        ;

;    BANKSEL    ADCON0        ;
    movlw    b'00001001'    ; select channel an2 & turn adc on
    movwf    ADCON0        ;

    BANKSEL    PORTA;
        call    init_LCD    ; Initialize the LCD
    call    busy_check    ; Wait for LCD to finish initialising
        call    display_version    ; Display title and version
;
; *******************************************************************************
; *                                        *
; * Purpose:      This is the Main Program Loop.                    *
; *                                        *
; *******************************************************************************
;
main
    movlw    100
    call    wait        ; acquisiton delay & loop delay

    BANKSEL ADCON0
    bsf    ADCON0,ADGO    ; start conversion
    btfsc    ADCON0,ADGO    ; is conversion done?
    goto    $-1        ; no, test again

    BANKSEL    ADRESH
    movf    ADRESH,w    ; read upper 2 bits
    movlb    0
    movwf    valueADC    ; store in gpr space

    BANKSEL    ADRESL
    movf    ADRESL,w    ; read lower 8 bits
    movlb    0  
    movwf    valueADC+1    ; store in gpr space

    call    getValue

    call    bin2BCD

    call    show_bcd

    goto    main        ; Continue main loop
;
getValue
;first do ADC-487 to discover direction
    bcf    flags,0        ; clear the negative flag  
    movlw    low(valueADC)
    movwf    acc
    movlw    high(valueADC)
    movwf    acc+1
    movlw    high(center)
    subwf    acc+1,f
    movlw    low(center)
    subwf    acc,f
    btfss    STATUS,C
    decf    acc+1,f
    btfss    acc+1,7        ; is it negative
    goto    notNeg
;need to negate acc
    bsf    flags,0        ; set the negative flag
    movlw    0xff
    xorwf    acc,f
    xorwf    acc+1,f
    incf    acc,f
    btfsc    STATUS,Z
    incf    acc+1,f
notNeg
; have now got a valid lookup value
; and a flag to tell us if above or below 487
    BANKSEL    EEADRL        ; Select Bank for EEPROM registers
    movfw    acc        ;
    movwf    EEADRL
    movfw    acc+1
    addlw    0x0e
    movwf    EEADRH
    bcf    EECON1,CFGS    ; Do not select Configuration Space
    bsf    EECON1,EEPGD    ; Select Program Memory
    bsf    EECON1,RD    ; Initiate read
    NOP            ; required 2 x NOPs
    NOP
        ; EEDAT will now contain 0 - 2500
        ; and flags,0 will be set if below center
    movf    EEDATH,w
    movwf    NumH        ; for BCD conversion routine
    movf    EEDATL,w
    movwf    NumL        ; for BCD conversion routine
    movlb    0
     return
;
; *******************************************************************************
; * Purpose:                                    *
; *    This subroutine converts a 16 bit binary number to decimal in       *
; *    TenK, Thou, Hund, Tens, Ones                        *
; *******************************************************************************
;
bin2BCD    ; Takes number in NumH:NumL and returns decimal in TenK:Thou:Hund:Tens:Ones

; Fast 16-Bit Binary to Decimal by Peter Hemsley
; Input: 16-bit binary in NumH and NumL
; Output: Decimal (BCD) digits in TenK,Thou,Hund,Tens,Ones
; No temporary variables required
; Code size: 46 instructions
; Execution time: Variable, approx 150 to 170 cycles

Bin2DecFast
    movf    NumH,w        ;Hex Digit 0X00 (H2)
    iorlw    0xF0        ;w=H2-16
    movwf    Ones        ;Ones=H2-16
    movwf    Tens        ;Tens=H2-16
    addwf    Tens,f        ;Tens=H2*2-32
    addwf    Tens,f        ;Tens=H2*3-48, C=1
    movwf    Hund        ;Hund=H2-16
    rlf    Hund,f        ;Hund=H2*2-31

    swapf    NumH,w        ;Hex Digit X000 (H3)
    iorlw    0xF0        ;w=H3-16
    addwf    Hund,f        ;Hund=H3+H2*2-47 Done!
    movwf    Thou        ;Thou=H3-16
    addwf    Thou,f        ;Thou=H3*2-32
    addlw    D'52'        ;w=H3+36
    addwf    Ones,f        ;Ones=H3+H2+20

    swapf    NumL,w        ;Hex Digit 00X0 (H1)
    iorlw    0xF0        ;w=H1-16
    addwf    Tens,f        ;Tens=H2*3+H1-64
    addwf    Ones,f        ;Ones=H3+H2+H1+4, C=1
    rlf    Ones,f        ;Ones=(H3+H2+H1)*2+9, C=0
    comf    Ones,f        ;Ones=-(H3+H2+H1)*2-10
    rlf    Ones,f        ;Ones=-(H3+H2+H1)*4-20

    movf    NumL,w        ;Hex Digit 000X (H0)
    andlw    0x0F        ;w=H0
    addwf    Ones,f        ;Ones=H0-(H3+H2+H1)*4-20 Done!
    rlf    Tens,f        ;C=0, Tens=H2*6+H1*2-128 Done!

    movlw    D'7'
    movwf    TenK        ;TenK=7

    addlw    D'3'        ;w=10, C=0
    rlf    Thou,f        ;Thou=H3*4-64 Done!
mod0
    addwf    Ones,f        ;D(X)=D(X)mod10
    decf    Tens,f        ;D(X+1)=D(X+1)+D(X)div10
    skpc
    goto    mod0
mod1
    addwf    Tens,f
    decf    Hund,f
    skpc
    goto    mod1
mod2
    addwf    Hund,f
    decf    Thou,f
    skpc
    goto    mod2
mod3
    addwf    Thou,f
    decf    TenK,f
    skpc
    goto    mod3

    return
;
; *******************************************************************************
; *    Display data on the LCD.                        *
; *******************************************************************************
;
show_bcd
    movlw    0xC1        ; Point the LCD to digit location
    call    cmnd2LCD    ; Send digit location to LCD

    movf    Thou,f        ; Test if 'file'is 0 or 1
    btfss    STATUS,Z    ; The Z or Zero bit is set to "1" when the result
    goto    not_zero    ; of an arithmetic or logical operation is zero

    movlw    ' '        ; Thou = 0, insert space
    call    data2LCD    ; Send byte in W to LCD
    goto    show_Hund

not_zero
    movfw    Thou        ; Thou is not 0, insert number
    addlw    0x30        ; Convert to ASCII character
    call    data2LCD    ; Send to LCD - rinse and repeat until all characters sent

show_Hund
    movfw    Hund
    addlw    0x30
    call    data2LCD

    movfw    Tens
    addlw    0x30
    call    data2LCD

    movfw    Ones
    addlw    0x30
    call    data2LCD

    return
;
; *******************************************************************************
; *                                        *
; * Purpose:    Send Command or Data byte to the LCD                *
; *        Entry point cmnd2LCD:  Send a Command to the LCD        *
; *        Entry Point data2LCD:  Send a Data byte to the LCD        *
; *                                        *
; * Input:    W has the command or data byte to be sent to the LCD.        *
; *                                        *
; *******************************************************************************
;
cmnd2LCD   ; ****** Entry point for commands ******
    clrf    rs_value    ; Remember to clear RS  (clear rs_value)
    goto    write2LCD    ; Go to common code
data2LCD   ; ****** Entry point for data ********
    bsf    rs_value,0    ; Remember to set RS (set bit 0 of rs_value)
write2LCD
    movwf    LCD_char    ; Save byte to write to LCD
    call    busy_check    ; Check to see if LCD is ready for new data
    movlw    2        ;
    movwf    count        ; SET UP LOOP COUNTER

write2LCD2
    swapf    LCD_char,f    ; SWAP MS & LS NIBBLES
    movf    LCD_char,w
    andlw    15        ; MAKE TOP 4 BITS OF W 0

    movwf    PORTB        ; SEND MS NIBBLE
    bcf    PORTB,LCD_rs    ; Guess RS should be clear - command mode

    btfsc    rs_value,0    ; Should RS be clear?  (is bit 0 == 0?)
    bsf    PORTB,LCD_rs    ; No, set RS - data mode
    nop
    bsf    PORTB,LCD_e    ; SET E HIGH
    nop
    bcf    PORTB,LCD_e    ; SET E LOW

    decfsz    count,F
    goto    write2LCD2    ; GO BACK AND SEND LS NIBBLE
    return
;
; *******************************************************************************
; *                                        *
; * Purpose:    Check if LCD is done with the last operation.            *
; *        This subroutine polls the LCD busy flag to determine if        *
; *        previous operations are completed.                *
; *                                        *
; *******************************************************************************
;
busy_check
    clrf    PORTB        ; Clear all outputs on PORTB
    banksel    TRISB        ; Switch to bank for Tris operation
    movlw    b'00001000'    ; Set RB3 input, others outputs
    movwf    TRISB        ;

    banksel    PORTB
    bcf    PORTB,LCD_rs    ; Set up LCD for Read Busy Flag (RS = 0)
    nop
    bsf    PORTB,LCD_rw    ; Set up LCD for Read (RW = 1)

LCD_is_busy
    bsf    PORTB,LCD_e    ; Set E high
    nop
    bcf    PORTB,LCD_e    ; Drop E again

    btfss    PORTB,LCD_busy    ; Is Busy Flag (RB3) clear?
    goto    not_busy    ; Yes - exit
    nop            ; No
    nop            ; Wait a while

    bsf    PORTB,LCD_e    ; Pulse E high (dummy read of lower nibble),
    nop            ; wait,
    bcf    PORTB,LCD_e    ; and drop E again

    goto    LCD_is_busy    ; If not, it is busy so jump back

not_busy
    banksel    TRISB        ; Switch to bank 1 for Tristate operation
    clrf    TRISB        ; All pins (RB7..RB0) are back to outputs
    banksel    PORTB        ; Switch to bank 0
    clrf    PORTB        ; Clear all of Port B (inputs and outputs)
    return            ;
;

;
; *******************************************************************************
; *                                        *
; * Purpose:    Power on initialization of Liquid Crystal Display.  The LCD    *
; *        controller chip must be equivalent to an Hitachi 44780.        *
; *                                        *
; *******************************************************************************
;
init_LCD
     movlw    100
    call    wait        ; Wait for LCD to power up
;        Put 4-bit command on RB3..RB0                                        
;        PIC's RB3..RB0 lines connect to LCD's DB7..DB4 lines

        movlw   0x03        ; LCD init instruction (First)
        movwf   PORTB        ; Send to LCD via RB3..RB0                  
        bsf     PORTB,LCD_e    ; Set the LCD E line high,

     movlw    100
        call    wait        ; wait a "long" time,

    bcf     PORTB,LCD_e    ; and then Clear E
        movlw   0x03        ; LCD init instruction (Second)
        movwf   PORTB        ; Send to LCD via RB3..RB0                  
        bsf     PORTB,LCD_e    ; Set E high,

     movlw    32
        call    wait        ; wait a while,

        bcf     PORTB,LCD_e    ; and then Clear E
        movlw   0x03        ; LCD init instruction (Third)
        movwf   PORTB        ; Send to LCD via RB3..RB0                  
        bsf     PORTB,LCD_e    ; Set E high,              

     movlw    32
        call    wait        ; wait a while,

        bcf     PORTB,LCD_e    ; and then Clear E
        movlw   0x02        ; 4-bit mode instruction
        movwf   PORTB        ; Send to LCD via RB3..RB0                  
        bsf     PORTB,LCD_e    ; Set E high,

     movlw    16
        call    wait        ; wait a while,

        bcf     PORTB,LCD_e    ; and then Clear E

        movlw   0x28        ; 1/16 duty cycle, 5x8 matrix
        call    cmnd2LCD    ; Send command in w to LCD
        movlw   0x08        ; Display off, cursor and blink off
        call    cmnd2LCD    ; Send command to LCD
        movlw   0x01        ; Clear and reset cursor
        call    cmnd2LCD    ; Send command in w to LCD
        movlw   0x06        ; Set cursor to move right, no shift
        call    cmnd2LCD    ; Send command in w to LCD
        movlw   0x0E        ; Display on, cursor on and blink off
;        movlw   0x0F        ; Display on, cursor on and blink on
;        movlw   0x0C        ; Display on, cursor and blink off
        call    cmnd2LCD    ; Send command in w to LCD
        return            ;
;
; *******************************************************************************
; *                                        *
; * Function:    clear_lcd                            *
; *                                        *
; * Purpose:    Clears the LCD and homes the cursor.                *
; *                                        *
; * Input:    None                                *
; *                                        *
; * Output:    None                                *
; *                                        *
; * Revisions:    Ver 2.0: Added function for convenience and to reduce code    *
; *        space 4-4-04 W3CD.                        *
; *                                        *
; *******************************************************************************
;
clear_lcd
    movlw    0x01        ; Prepare to display a new message on LCD
    call    cmnd2LCD    ; by clearing the current one
    return
;
; *******************************************************************************
; *                                        *
; * Purpose:    Display version and other info on LCD for 1 second        *
; *        upon power-up                            *
; *                                        *
; *   Input:    SIGN_ON_1 through SIGN_ON_16                    *
; *                                        *
; *  Output:    LCD displays info                        *
; *                                        *
; *******************************************************************************
;
display_version
    call    clear_lcd
        movlw   SIGN_ON_1    ; digit 1            
        call    data2LCD    ;
        movlw   SIGN_ON_2    ; digit 2                
        call    data2LCD    ;
        movlw   SIGN_ON_3    ; digit 3              
        call    data2LCD    ;
        movlw   SIGN_ON_4    ; digit 4              
        call    data2LCD    ;
        movlw   SIGN_ON_5    ; digit 5            
        call    data2LCD    ;      
        movlw   SIGN_ON_6    ; digit 6            
        call    data2LCD    ;
        movlw   SIGN_ON_7    ; digit 7              
        call    data2LCD    ;
        movlw   SIGN_ON_8    ; digit 8              
        call    data2LCD    ;
        movlw   SIGN_ON_9    ; digit 9            
        call    data2LCD    ;
        movlw   SIGN_ON_10    ; digit 10                
        call    data2LCD    ;
        movlw   SIGN_ON_11    ; digit 11              
        call    data2LCD    ;
        movlw   SIGN_ON_12    ; digit 12              
        call    data2LCD    ;
        movlw   SIGN_ON_13    ; digit 13            
        call    data2LCD    ;      
        movlw   SIGN_ON_14    ; digit 14            
        call    data2LCD    ;
        movlw   SIGN_ON_15    ; digit 15              
        call    data2LCD    ;
        movlw   SIGN_ON_16    ; digit 16              
        call    data2LCD    ;

        call    wait_a_sec    ; Wait one second
    return
;
; *******************************************************************************
; *                                        *
; * Purpose:  Delay routines                            *
; *                                        *
; *******************************************************************************
;
wait_a_sec ;PIC Time Delay = 0.99999900 s with Osc = 4000000 Hz
    movlw    D'6'
    movwf    CounterC
    movlw    D'19'
    movwf    CounterB
    movlw    D'172'
    movwf    CounterA
loop
    decfsz    CounterA,f
    goto    loop
    decfsz    CounterB,f
    goto    loop
    decfsz    CounterC,f
    goto    loop
    return

wait_100us    ;PIC Time Delay = 0.00009800 s with Osc = 4MHz
    movlw    D'31'
    movwf    CounterA
loop2
    decfsz    CounterA,1
    goto    loop2
    return

wait    ; Enter with 'w' holding number of times to execute 1mS delay routine
    movwf    timer1        ; Set up counter
    call    wait_1ms    ; Go to wait loops
    decfsz    timer1,f
    goto    $-2
    return

wait_1ms     ;PIC Time Delay = 0.00099800 s with Osc = 4MHz
    movlw    D'2'
    movwf    CounterD
    movlw    D'74'
    movwf    CounterE
loop1
    decfsz    CounterE,f
    goto    loop1
    decfsz    CounterD,f
    goto    loop1
    return
;      
       org 0xe00
        ;note, dead band incorporated into table
        DW  0,0,0,0,0,5,11,16,22,27,33,39,44,50,55,61,67,72,78,83,89
        DW  95,100,106,111,117,123,128,134,139,145,151,156,162,167,173,178
        DW  184,190,195,201,206,212,218,223,229,234,240,246,251,257,262,268
        DW  274,279,285,290,296,302,307,313,318,324,329,335,341,346,352,357
        DW  363,369,374,380,385,391,397,402,408,413,419,425,430,436,441,447
        DW  453,458,464,469,475,480,486,492,497,503,508,514,520,525,531,536
        DW  542,548,553,559,564,570,576,581,587,592,598,604,609,615,620,626
        DW  631,637,643,648,654,659,665,671,676,682,687,693,699,704,710,715
        DW  721,727,732,738,743,749,755,760,766,771,777,782,788,794,799,805
        DW  810,816,822,827,833,838,844,850,855,861,866,872,878,883,889,894
        DW  900,906,911,917,922,928,934,939,945,950,956,961,967,973,978,984
        DW  989,995,1001,1006,1012,1017,1023,1029,1034,1040,1045,1051,1057,1062,1068,1073
        DW  1079,1085,1090,1096,1101,1107,1112,1118,1124,1129,1135,1140,1146,1152,1157,1163
        DW  1168,1174,1180,1185,1191,1196,1202,1208,1213,1219,1224,1230,1236,1241,1247,1252
        DW  1258,1263,1269,1275,1280,1286,1291,1297,1303,1308,1314,1319,1325,1331,1336,1342
        DW  1347,1353,1359,1364,1370,1375,1381,1387,1392,1398,1403,1409,1414,1420,1426,1431
        DW  1437,1442,1448,1454,1459,1465,1470,1476,1482,1487,1493,1498,1504,1510,1515,1521
        DW  1526,1532,1538,1543,1549,1554,1560,1565,1571,1577,1582,1588,1593,1599,1605,1610
        DW  1616,1621,1627,1633,1638,1644,1649,1655,1661,1666,1672,1677,1683,1689,1694,1700
        DW  1705,1711,1717,1722,1728,1733,1739,1744,1750,1756,1761,1767,1772,1778,1784,1789
        DW  1795,1800,1806,1812,1817,1823,1828,1834,1840,1845,1851,1856,1862,1868,1873,1879
        DW  1884,1890,1895,1901,1907,1912,1918,1923,1929,1935,1940,1946,1951,1957,1963,1968
        DW  1974,1979,1985,1991,1996,2002,2007,2013,2019,2024,2030,2035,2041,2046,2052,2058
        DW  2063,2069,2074,2080,2086,2091,2097,2102,2108,2114,2119,2125,2130,2136,2142,2147
        DW  2153,2158,2164,2170,2175,2181,2186,2192,2197,2203,2209,2214,2220,2225,2231,2237
        DW  2242,2248,2253,2259,2265,2270,2276,2281,2287,2293,2298,2304,2309,2315,2321,2326
        DW  2332,2337,2343,2348,2354,2360,2365,2371,2376,2382,2388,2393,2399,2404,2410,2416
        DW  2421,2427,2432,2438,2444,2449,2455,2460,2466,2472,2477,2483,2488,2494,2500,2500
        DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
        DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
        DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
        DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
;
        END
 
You may need to use different instructions to read the table?
eg.

Also, the table could be bytes rather than words, as you need every value a multiple of ten. That could save some program space, if it's tight?
eg. Divide all by ten so you have 0-250 range in single bytes, then multiply the result by ten.
 
Also, the table could be bytes rather than words,
Absolutely no point storing it as bytes as they're 14 bit flash words, so still 1 location per entry - I.E. same size.
The DW table is simply constructed from the excel sheet I did that calculates the value as requested.
You may need to use different instructions to read the table?
The code was run (albeit in simulation) on the chip mentioned and referring to the datasheet for that chip. It works.
as you need every value a multiple of ten.
I suspect this was done as an attempt to simplify the code and not an actual requirement.

Mike.
Edit, the values increase by ~5.6 per step, far from the 10 multiple.
 
Last edited:
When I sim that code... The LCD busy craps out too fast.. If the busy come on fast, it misses the second nibble read and the RW bit is never cleared...

I will work on it later as now I have a LCD written while busy error..
 
In the Oshonsoft Sim I set the LCD setup values to 1 to stop it messing up.

I think it's just the Oshon sim doesn't understand the DW instruction.

If you use the Breakpoints manager and look at the code, it either says 'unrecognised' against parts of the table or lines of gibberish in amongst the table:-


The MPlab listing file is fine without any of the above garbage.

As for size, it only takes a total of 521 bytes.
 
The code assembles fine.... I'm going to swap the LCD routines for Nigels routines as I know they work.

I have NEVER had much luck with busy read on 4 bit interfaces...
 
Below code is a combination of various peoples input on this forum re ADC scaling, 16x8 multiply & 16x8 divide (from other thread viz AN617 problems) plus various other possibly useful bits.

It takes the ADC value, defines a center point and tells you whether the returned value is above or below the center.

That value is then multiplied by 56 and divided by 10 to scale it to a range of values between ~5 and 2500.

Thankyou to all for your input.

Note to Nigel - the divide routine came from Piclist

Code:
; *                                        *
; *******************************************************************************
; *    ADC scaling,16x8 multiply & 16x8 divide routines            *
; *    Plus various other bits of possibly useful bits of code            *
; *******************************************************************************
;
;********************************************************************************
;                                                                             
; Target Controller        PIC16F1827
;                          __________                                         
;           ADC-IN-----RA2 |1       18| RA1---------
;          -----------RA3 |2       17| RA0---------
;          -----------RA4 |3       16| RA7---------
;          -----------RA5 |4       15| RA6---------
;     Ground----------Vss |5       14| VDD---------+5 V
;     LCD D4----------RB0 |6       13| RB7---------
;     LCD D5----------RB1 |7       12| RB6---------LCD_rs  (LCD Pin 4)
;     LCD D6----------RB2 |8       11| RB5---------LCD_rw  (LCD Pin 5)
;     LCD D7----------RB3 |9       10| RB4---------LCD_e   (LCD Pin 6)
;                          ----------                                         
;                                                                             
; *******************************************************************************
; * Device type and options                            *
; *******************************************************************************
;
        processor 16F1827
        radix     dec
    errorlevel -207    ; Skip found label after column 1
    errorlevel -302    ; Skip out of bank nuisance messages
    errorlevel -303    ; Skip program word too large. Truncated to core size
;
; *******************************************************************************
; * Configuration fuse information for 16F1827:                    *
; *******************************************************************************
;
        include   <P16F1827.INC>

 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
 __CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_LO & _LVP_OFF
;
; *******************************************************************************
; Info for power-up display. Why was it done this way instead of with a table -
; because it is easier for beginners to visualise where the characters will
; appear on the LCD.

SIGN_ON_1    equ ' '        ; Position 1 on LCD - left most position
SIGN_ON_2    equ 'V'
SIGN_ON_3    equ 'K'
SIGN_ON_4    equ '5'
SIGN_ON_5    equ 'T'
SIGN_ON_6    equ 'M'
SIGN_ON_7    equ ' '
SIGN_ON_8    equ 'A'
SIGN_ON_9    equ 'D'
SIGN_ON_10    equ 'C'
SIGN_ON_11    equ ' '
SIGN_ON_12    equ 'T'
SIGN_ON_13    equ 'E'
SIGN_ON_14    equ 'S'
SIGN_ON_15    equ 'T'
SIGN_ON_16    equ ' '        ; Position 16 on LCD - right most position
;                                                                             
; *******************************************************************************
; * General equates.                                *
; *******************************************************************************
;
#define center 486        ; For ADC scaling routine
;
; *******************************************************************************
; * Assign names to IO pins.                            *
; *******************************************************************************
;
LCD_busy equ    0x03        ; LCD busy bit
LCD_e    equ     0x04        ; 0=disable, 1=enable
LCD_rw    equ     0x05        ; 0=write, 1=read
LCD_rs    equ     0x06        ; 0=instruction, 1=data
;
;
                ; 16F1827 Oscillator setup
                ; OSCCON - Oscillator control reg.
                ; ---------------------------------
                ; SPLLEN b7 enable PLL x4
                ; 1 = enabled 0 = disabled
                ; IRCF | b6-3 frequency selection
                ; 1111 = 16MHz HF
                ; 1110 = 8 or 32MHz HF
                ; 1101 = 4MHz HF
                ; 1100 = 2MHz HF
                ; 1011 = 1MHz HF
                ; 1010 = 500kHz HF
                ; 1001 = 250kHz HF
                ; 1000 = 125kHz HF
                ; 0111 = 500kHz MF (default)
                ; 0110 = 250kHz MF
                ; 0101 = 125kHz MF
                ; 0100 = 62.5kHz MF
                ; 0011 = 31.25kHz HF
                ; 0010 = 31.25kHz MF
                ; 000x = 31.25kHz LF
                ; Reserved B2 reserved, 0
                ; SCS      B1 0-: 1x = int. OSC.
                ; 01 = Timer1 oscillator
                ; 00 = determined by FOSC <2:0> in Configuratio
                ; POR default 00111-00 500 kHz (POR = Power On Reset)
OSCCONVAL EQU b'01101000'    ; 4MhZ CLOCK
;
; *******************************************************************************
; *           Allocate variables in general purpose register space        *
; *******************************************************************************
;
    CBLOCK    0x20        ; Start Data Block
    TenK
    Thou
    Hund
    Tens
    Ones
    byte2send        ;
    LCD_char        ; Character being sent to the LCD
    rs_value        ; The LCD rs line flag value
    timer1            ; Used in delay routines
    count            ; loop counter  (gets reused)
    CounterA
    CounterB
    CounterC
    CounterD
    CounterE
    flags
    r1
    r2
    r3
    t
    ENDC            ; End of Data Block

    CBLOCK    0x70
    NumH
    NumL
    ENDC            ; End of Data Block
;
; *******************************************************************************
; * The 16F1827 resets to 0x00.                            *
; * The Interrupt vector is at 0x04.                        *
; *******************************************************************************
;
    ORG    0x0000               
    goto    start        ; Jump to main program

    ORG    0x0004
    goto    start        ; Jump to main program
;
; *******************************************************************************
; *                                        *
; * Purpose:  This is the start of the program.                    *
; *                                        *
; *******************************************************************************
;
start
    BANKSEL    PORTA
    clrf    PORTA
    clrf    PORTB

; Set PIC oscillator frequency
    banksel    OSCCON        ; Select OSCCON
    movlw    OSCCONVAL    ;
    movwf    OSCCON        ; Loads the wanted value
    banksel    PORTA

; Configures all I / O as digital
    Banksel    ANSELA
    bsf    ANSELA,ANSA2    ; set ra2 to analog
    clrf    ANSELB

; Disable all wakeup pull-ups
    Banksel    WPUA
    clrf    WPUA
    clrf    WPUB

    BANKSEL    OPTION_REG
    clrf    OPTION_REG    ;
;
        movlw   b'00110111'    ; PORTA (RA0:2,4:5 inputs, RA3,6:7 outputs)     
        movwf   TRISA        ;
        clrf    TRISB        ; Set port B to all outputs

    movlw    b'11000000'    ; right justify, FOSC/4 vdd and vss vref
    movwf    ADCON1        ;

    movlw    b'00001001'    ; select channel an2 & turn adc on
    movwf    ADCON0        ;

    BANKSEL    PORTA;
        call    init_LCD    ; Initialize the LCD
    call    busy_check    ; Wait for LCD to finish initialising
        call    display_version    ; Display title and version
;
; *******************************************************************************
; *                                        *
; * Purpose:      This is the Main Program Loop.                    *
; *                                        *
; *******************************************************************************
;
main
    movlw    100
    call    wait        ; acquisiton delay & loop delay

    BANKSEL ADCON0
    bsf    ADCON0,ADGO    ; start conversion
    btfsc    ADCON0,ADGO    ; is conversion done?
    goto    $-1        ; no, test again

    movf    ADRESH,w    ; read upper 2 bits
    movwf    NumH        ; store in gpr space

    movf    ADRESL,w    ; read lower 8 bits
    movwf    NumL        ; store in gpr space
    movlb    0

    call    getValue

    call    bin2BCD

    call    show_bcd

    goto    main        ; Continue main loop

;--------------------------------------------------------------------------------
;    ADC scaling routine
;--------------------------------------------------------------------------------
getValue
;first do ADC-487 to discover direction
    bcf    flags,0        ; clear the negative flag   
    movlw    high(center)
    subwf    NumH,f
    movlw    low(center)
    subwf    NumL,f
    btfss    STATUS,C
    decf    NumH,f
    btfss    NumH,7        ; is it negative
    goto    notNeg
;need to negate acc
    bsf    flags,0        ; set the negative flag
    movlw    0xff
    xorwf    NumL,f
    xorwf    NumH,f
    incf    NumL,f
    btfsc    STATUS,Z
    incf    NumH,f
notNeg
; 'flags' tells us if above or below center value

;--------------------------------------------------------------------------------
;    Multiply by 56 (multiplier is in register 't')
;--------------------------------------------------------------------------------
    clrf    r3
    clrf    r2
    clrf    r1
    bsf    r1,7        ; used to count to 8
    movlw    56        ; value to multiply by
    movwf    t
mult_loop
    rrf    t,f
    btfss    STATUS,C
    goto    noAdd
    movfw    NumL
    addwf    r2,f
    movfw    NumH
    btfsc    STATUS,C
    incfsz    NumH,w        ; w = w+1
    addwf    r3,f
noAdd
    rrf    r3,f
    rrf    r2,f        ; r2 is high byte of word
    rrf    r1,f        ; r1 is low byte of word
    btfss    STATUS,C
    goto    mult_loop

;--------------------------------------------------------------------------------
;    Divide by 10 (divisor is in 'w')- remainder is not calculated
;--------------------------------------------------------------------------------
    movlw    16
    movwf    count
    movlw    10        ; keep divisor value in accumulator
    clrf    r3        ; temporary register

divide
    rlf    r1,f        ; shift next msb into temporary
    rlf    r2,f
    rlf    r3,f
    rlf    count,f        ; carry has 9th bit of temporary
                ; copy carry to counter

    subwf    r3,f        ; substract Y (in w) from temporary
    skpnc            ; if no borrow, set Counter.0
    bsf    count,0
    btfss    count,0        ; if Counter.0 clear (borrow) restore temporary

    addwf    r3,f
    clrc            ; restore counter
    rrf    count,f

;at this point carry is the next bit of result
    decfsz    count,f        ; repeat 16 times to find integer part
    goto    divide
    rlf    r1,f
    rlf    r2,f

    movfw    r2
    movwf    NumH
    movfw    r1
    movwf    NumL
    return
;
; *******************************************************************************
; * Takes number in NumH:NumL and returns decimal in TenK:Thou:Hund:Tens:Ones    *
; * Fast 16-Bit Binary to Decimal by Peter Hemsley                *
; * Input: 16-bit binary in NumH and NumL                    *
; * Output: Decimal (BCD) digits in TenK,Thou,Hund,Tens,Ones            *
; * No temporary variables required                        *
; * Code size: 46 instructions                            *
; * Execution time: Variable, approx 150 to 170 cycles                *
; *******************************************************************************
;
bin2BCD

Bin2DecFast
    movf    NumH,w        ;Hex Digit 0X00 (H2)
    iorlw    0xF0        ;w=H2-16
    movwf    Ones        ;Ones=H2-16
    movwf    Tens        ;Tens=H2-16
    addwf    Tens,f        ;Tens=H2*2-32
    addwf    Tens,f        ;Tens=H2*3-48, C=1
    movwf    Hund        ;Hund=H2-16
    rlf    Hund,f        ;Hund=H2*2-31

    swapf    NumH,w        ;Hex Digit X000 (H3)
    iorlw    0xF0        ;w=H3-16
    addwf    Hund,f        ;Hund=H3+H2*2-47 Done!
    movwf    Thou        ;Thou=H3-16
    addwf    Thou,f        ;Thou=H3*2-32
    addlw    D'52'        ;w=H3+36
    addwf    Ones,f        ;Ones=H3+H2+20

    swapf    NumL,w        ;Hex Digit 00X0 (H1)
    iorlw    0xF0        ;w=H1-16
    addwf    Tens,f        ;Tens=H2*3+H1-64
    addwf    Ones,f        ;Ones=H3+H2+H1+4, C=1
    rlf    Ones,f        ;Ones=(H3+H2+H1)*2+9, C=0
    comf    Ones,f        ;Ones=-(H3+H2+H1)*2-10
    rlf    Ones,f        ;Ones=-(H3+H2+H1)*4-20

    movf    NumL,w        ;Hex Digit 000X (H0)
    andlw    0x0F        ;w=H0
    addwf    Ones,f        ;Ones=H0-(H3+H2+H1)*4-20 Done!
    rlf    Tens,f        ;C=0, Tens=H2*6+H1*2-128 Done!

    movlw    D'7'
    movwf    TenK        ;TenK=7

    addlw    D'3'        ;w=10, C=0
    rlf    Thou,f        ;Thou=H3*4-64 Done!
mod0
    addwf    Ones,f        ;D(X)=D(X)mod10
    decf    Tens,f        ;D(X+1)=D(X+1)+D(X)div10
    skpc
    goto    mod0
mod1
    addwf    Tens,f
    decf    Hund,f
    skpc
    goto    mod1
mod2
    addwf    Hund,f
    decf    Thou,f
    skpc
    goto    mod2
mod3
    addwf    Thou,f
    decf    TenK,f
    skpc
    goto    mod3

    return
;
; *******************************************************************************
; *    Display data on the LCD.                        *
; *******************************************************************************
;
show_bcd
    movlw    0xC1        ; Point the LCD to digit location
    call    cmnd2LCD    ; Send digit location to LCD

;    movf    Thou,f        ; Test if 'file'is 0 or 1
;    btfss    STATUS,Z    ; The Z or Zero bit is set to "1" when the result
;    goto    not_zero    ; of an arithmetic or logical operation is zero
;
;    movlw    ' '        ; Thou = 0, insert space
;    call    data2LCD    ; Send byte in W to LCD
;    goto    show_Hund

    movfw    TenK        ; Thou is not 0, insert number
    addlw    0x30        ; Convert to ASCII character
    call    data2LCD    ; Send to LCD - rinse and repeat until all characters sent

not_zero
    movfw    Thou        ; Thou is not 0, insert number
    addlw    0x30        ; Convert to ASCII character
    call    data2LCD    ; Send to LCD - rinse and repeat until all characters sent

show_Hund
    movfw    Hund
    addlw    0x30
    call    data2LCD

    movfw    Tens
    addlw    0x30
    call    data2LCD

    movfw    Ones
    addlw    0x30
    call    data2LCD

    return
;
; *******************************************************************************
; *                                        *
; * Purpose:    Send Command or Data byte to the LCD                *
; *        Entry point cmnd2LCD:  Send a Command to the LCD        *
; *        Entry Point data2LCD:  Send a Data byte to the LCD        *
; *                                        *
; * Input:    W has the command or data byte to be sent to the LCD.        *
; *                                        *
; *******************************************************************************
;
cmnd2LCD   ; ****** Entry point for commands ******
    clrf    rs_value    ; Remember to clear RS  (clear rs_value)
    goto    write2LCD    ; Go to common code
data2LCD   ; ****** Entry point for data ********
    bsf    rs_value,0    ; Remember to set RS (set bit 0 of rs_value)
write2LCD
    movwf    LCD_char    ; Save byte to write to LCD
    call    busy_check    ; Check to see if LCD is ready for new data
    movlw    2        ;
    movwf    count        ; SET UP LOOP COUNTER

write2LCD2
    swapf    LCD_char,f    ; SWAP MS & LS NIBBLES
    movf    LCD_char,w
    andlw    15        ; MAKE TOP 4 BITS OF W 0

    movwf    PORTB        ; SEND MS NIBBLE
    bcf    PORTB,LCD_rs    ; Guess RS should be clear - command mode

    btfsc    rs_value,0    ; Should RS be clear?  (is bit 0 == 0?)
    bsf    PORTB,LCD_rs    ; No, set RS - data mode
    nop
    bsf    PORTB,LCD_e    ; SET E HIGH
    nop
    bcf    PORTB,LCD_e    ; SET E LOW

    decfsz    count,F
    goto    write2LCD2    ; GO BACK AND SEND LS NIBBLE
    return
;
; *******************************************************************************
; *                                        *
; * Purpose:    Check if LCD is done with the last operation.            *
; *        This subroutine polls the LCD busy flag to determine if        *
; *        previous operations are completed.                *
; *                                        *
; *******************************************************************************
;
busy_check
    clrf    PORTB        ; Clear all outputs on PORTB
    banksel    TRISB        ; Switch to bank for Tris operation
    movlw    b'00001000'    ; Set RB3 input, others outputs
    movwf    TRISB        ;

    banksel    PORTB
    bcf    PORTB,LCD_rs    ; Set up LCD for Read Busy Flag (RS = 0)
    nop
    bsf    PORTB,LCD_rw    ; Set up LCD for Read (RW = 1) 

LCD_is_busy
    bsf    PORTB,LCD_e    ; Set E high
    nop
    bcf    PORTB,LCD_e    ; Drop E again

    btfss    PORTB,LCD_busy    ; Is Busy Flag (RB3) clear?
    goto    not_busy    ; Yes - exit
    nop            ; No
    nop            ; Wait a while

    bsf    PORTB,LCD_e    ; Pulse E high (dummy read of lower nibble),
    nop            ; wait,
    bcf    PORTB,LCD_e    ; and drop E again

    goto    LCD_is_busy    ; If not, it is busy so jump back

not_busy
    banksel    TRISB        ; Switch to bank 1 for Tristate operation
    clrf    TRISB        ; All pins (RB7..RB0) are back to outputs
    banksel    PORTB        ; Switch to bank 0
    clrf    PORTB        ; Clear all of Port B (inputs and outputs)
    return            ;
;

;
; *******************************************************************************
; *                                        *
; * Purpose:    Power on initialization of Liquid Crystal Display.  The LCD    *
; *        controller chip must be equivalent to an Hitachi 44780.        *
; *                                        *
; *******************************************************************************
;
init_LCD
     movlw    100
    call    wait        ; Wait for LCD to power up
;        Put 4-bit command on RB3..RB0                                         
;        PIC's RB3..RB0 lines connect to LCD's DB7..DB4 lines

        movlw   0x03        ; LCD init instruction (First)
        movwf   PORTB        ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e    ; Set the LCD E line high,

     movlw    100
        call    wait        ; wait a "long" time,

    bcf     PORTB,LCD_e    ; and then Clear E
        movlw   0x03        ; LCD init instruction (Second)
        movwf   PORTB        ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e    ; Set E high,

     movlw    32
        call    wait        ; wait a while,

        bcf     PORTB,LCD_e    ; and then Clear E
        movlw   0x03        ; LCD init instruction (Third)
        movwf   PORTB        ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e    ; Set E high,               

     movlw    32
        call    wait        ; wait a while,

        bcf     PORTB,LCD_e    ; and then Clear E
        movlw   0x02        ; 4-bit mode instruction
        movwf   PORTB        ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e    ; Set E high,

     movlw    16
        call    wait        ; wait a while,

        bcf     PORTB,LCD_e    ; and then Clear E

        movlw   0x28        ; 1/16 duty cycle, 5x8 matrix
        call    cmnd2LCD    ; Send command in w to LCD
        movlw   0x08        ; Display off, cursor and blink off
        call    cmnd2LCD    ; Send command to LCD
        movlw   0x01        ; Clear and reset cursor
        call    cmnd2LCD    ; Send command in w to LCD
        movlw   0x06        ; Set cursor to move right, no shift
        call    cmnd2LCD    ; Send command in w to LCD
        movlw   0x0E        ; Display on, cursor on and blink off
;        movlw   0x0F        ; Display on, cursor on and blink on
;        movlw   0x0C        ; Display on, cursor and blink off
        call    cmnd2LCD    ; Send command in w to LCD
        return            ;
;
; *******************************************************************************
; *                                        *
; * Function:    clear_lcd                            *
; *                                        *
; * Purpose:    Clears the LCD and homes the cursor.                *
; *                                        *
; *******************************************************************************
;
clear_lcd
    movlw    0x01        ; Prepare to display a new message on LCD
    call    cmnd2LCD    ; by clearing the current one
    return
;
; *******************************************************************************
; *                                        *
; * Purpose:    Display version and other info on LCD for 1 second        *
; *        upon power-up                            *
; *                                        *
; *   Input:    SIGN_ON_1 through SIGN_ON_16                    *
; *                                        *
; *  Output:    LCD displays info                        *
; *                                        *
; *******************************************************************************
;
display_version
    call    clear_lcd
        movlw   SIGN_ON_1    ; digit 1             
        call    data2LCD    ;
        movlw   SIGN_ON_2    ; digit 2                 
        call    data2LCD    ;
        movlw   SIGN_ON_3    ; digit 3               
        call    data2LCD    ;
        movlw   SIGN_ON_4    ; digit 4               
        call    data2LCD    ;
        movlw   SIGN_ON_5    ; digit 5             
        call    data2LCD    ;       
        movlw   SIGN_ON_6    ; digit 6             
        call    data2LCD    ;
        movlw   SIGN_ON_7    ; digit 7               
        call    data2LCD    ;
        movlw   SIGN_ON_8    ; digit 8               
        call    data2LCD    ;
        movlw   SIGN_ON_9    ; digit 9             
        call    data2LCD    ;
        movlw   SIGN_ON_10    ; digit 10                 
        call    data2LCD    ;
        movlw   SIGN_ON_11    ; digit 11               
        call    data2LCD    ;
        movlw   SIGN_ON_12    ; digit 12               
        call    data2LCD    ;
        movlw   SIGN_ON_13    ; digit 13             
        call    data2LCD    ;       
        movlw   SIGN_ON_14    ; digit 14             
        call    data2LCD    ;
        movlw   SIGN_ON_15    ; digit 15               
        call    data2LCD    ;
        movlw   SIGN_ON_16    ; digit 16               
        call    data2LCD    ;

 ;       call    wait_a_sec    ; Wait one second
    nop ; for sim
    return
;
; *******************************************************************************
; *                                        *
; * Purpose:  Delay routines                            *
; *                                        *
; *******************************************************************************
;
wait_a_sec ;PIC Time Delay = 0.99999900 s with Osc = 4000000 Hz
    movlw    D'6'
    movwf    CounterC
    movlw    D'19'
    movwf    CounterB
    movlw    D'172'
    movwf    CounterA
loop
    decfsz    CounterA,f
    goto    loop
    decfsz    CounterB,f
    goto    loop
    decfsz    CounterC,f
    goto    loop
    return

wait_100us    ;PIC Time Delay = 0.00009800 s with Osc = 4MHz
    movlw    D'31'
    movwf    CounterA
loop2
    decfsz    CounterA,1
    goto    loop2
    return

wait    ; Enter with 'w' holding number of times to execute 1mS delay routine
    movwf    timer1        ; Set up counter
    call    wait_1ms    ; Go to wait loops
    decfsz    timer1,f
    goto    $-2
    return

wait_1ms     ;PIC Time Delay = 0.00099800 s with Osc = 4MHz
    movlw    D'2'
    movwf    CounterD
    movlw    D'74'
    movwf    CounterE
loop1
    decfsz    CounterE,f
    goto    loop1
    decfsz    CounterD,f
    goto    loop1
    return
;       
        END
 
Further on the "Good news front", I finally managed to get some time on the workbench and tried Mike's table code (posts #24 & #28) in an actual PIC.

It works , thankyou Mike - I will actually use that in the project as it provides a better dead zone than the straight multiply/divide option I came up with.
 
I intended to ask (at some time) if you had tried it in an actual pic.
Glad to hear it's now all working.
Wonder why the Oshonsoft simulator didn't like it?
It works fine in the MPLAB (NOT X) simulator.
BTW, if you want the spreadsheet (along with the VBA that produces the DW table), so you can tweek values, let me know.

Mike.
 
Thanks Mike, yes please, the spreadsheet would be welcome.

Re Oshonsoft sim, there are quite a few things it doesn't understand or can't decipher properly (like programs longer than 2048 words), really needs a massive overhaul to make it more useful.
 
Attached is the excel spreadsheet that produces the DW table. It's a txt file as the board won't allow xls files (and definitely not xlsm files), rename to a .xlsm file and the macro builtIt produces the table in the immediate window.
It does have a spurious comma at the end of the data but just delete it.
I think it's self explanatory but any questions just ask.

Mike.
P.S. Alt-F11 will activate the VB window to see the code.
Edit, I'm using excel 2013 so anything after 2007 should work.
 

Attachments

  • adc - xlsm.txt
    33.8 KB · Views: 294
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…