PDA

View Full Version : 12 bit A2D on 18F46K80, need a bit of help for figuring TAD and getting correct value



ecoli-557
- 8th May 2014, 18:58
Hello All-
I am working with a new chip (to me) and I am having difficulty in understanding how to figure the A2D on the 18F46K80.
I looked into a post by Darrel on TAD but I don't seem to be able to get the LCD to display anything like I think it should.
I am trying to monitor the 5 volt line to the proc. AN0 is fed from a voltage divider 4.53k to +5 and 8.25K to ground.
I have never used a 12 bit A2D and I am certain it is stupid silly, but I can't seem to get it.
Code is below, includes configs, defines, sub-routine, and test code.

Any advice would be greatly appreciated
-Steve
PS - going to have to try to get the formatting correct one of these days when posting!.......

'Define configuration
#CONFIG
CONFIG RETEN = ON ;Ultra-low regulator ON
CONFIG FOSC = EC3 ;External HS oscillator, port function on RA6
CONFIG INTOSCSEL = LOW ;LF-INTOSC in Low-power mode during Sleep
CONFIG SOSCSEL = DIG ;Digital (SCLKI) mode
;FCMEM doesn't seem to work here.....
; CONFIG FCMEM = OFF ;Fail Safe CLock Monitor is OFF
CONFIG IESO = OFF ;Internal/External oscillator switchover is OFF
CONFIG PWRTEN = ON ;Power Up timer is ON
CONFIG BOREN = OFF ;Brown Out Reset is OFF - for now
CONFIG BORV = 0 ;Brown Out level is 4.6v
CONFIG WDTEN = OFF ;Watchdog is OFF - for now
CONFIG MCLRE = ON ;MCLR pin is ENABLED
CONFIG STVREN = ON ;Stack FULL/UNDERFLOW reset is ENABLED
CONFIG BBSIZ = BB1K ;Boot Block size - 1K?
CONFIG XINST = OFF ;Extended Instructgion set is DISABLED
#ENDCONFIG

'---------------------------------------------------------------------------------------
'Define the oscillator and setup the INCLUDE files
DEFINE OSC 64 '64 MHz oscillator, external
INCLUDE "DT_INTS-18.bas" 'Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" 'Include if using PBP interrupts

'---------------------------------------------------------------------------------------
'OKAY, Lets set up the registers.....
OSCCON= %00001000 'Device enters SLEEP mode when SLEEP is executed
OSCCON2= %00000000 'System clock comes from other osc than internal
REFOCON.7=0 'Refrence oscillator is DISABLED
WPUB= %00000000 'PORTB pullups DISABLED
PADCFG1= %00000000 'ALL pull up resistors DISABLED on ports D,E
ANCON0= %00000001 'Selects AN1-AN7 as DIGITAL, AN0 as ANALOG
ANCON1= %00000000 'Selects remaining analog/digital inputs as DIGITAL
ADCON0= %00000000 'Turns OFF ADC - REMEMBER TO TURN IT BACK ON AFTER DEBUG-, selects AD0 as input
ADCON1= %10110000 'Vref is INTERNAL 4.1v and AVss
ADCON2= %10100110 'RIGHT justified, 8 TAD A2D aq time select, Fosc/64
T0CON.7=0 'DISABLES TMR0
CM1CON= %00000000 'Turns OFF CM1 comparator
CM2CON= %00000000 'Turns OFF CM2 comparator
CVRCON.7=0 'Comparator voltage ref is powered DOWN
HLVDCON.4=0 'High Low Voltage Detect is DISABLED
CCP1CON=%00000000 'Turns OFF capture, compare, pwm
CCP2CON=%00000000 'Turns OFF capture, compare, pwm
CCP3CON=%00000000 'Turns OFF capture, compare, pwm
CCP4CON=%00000000 'Turns OFF capture, compare, pwm
CCP5CON=%00000000 'Turns OFF capture, compare, pwm
CANCON=%00100000 'CAN disabled
CTMUCONH.7=0 'CTMU disabled
INTCON= %00000000 'INT0 and POIRTB change external interrupt is DISABLED
INTCON2= %01111100 'PORTB pullups enabled by TRIS, int are RISING edge, TMR0 hi and rest low priority
INTCON3.3=0 'Disables INT1 external interupt
IOCB= %00000000 'PORTB interrupt on change all bits DISABLED
SLRCON=%00000000 'Slew rate for all ports is STANDARD
SSPCON1.5=0 'MSSP port is DISABLED
ODCON=%00000000 'Open Drain capability is DISABLED
PSTR1CON= %00000000
' MDCON=%00000000 'Modulation Control is DISABLED
'---------------------------------------------------------------------------------------
'Direction registers
TRISA = %10001011 'Set PORTA for bit7 is clk in, 2 led out, 1 temp in, 1 A/D in, ext Vref
TRISB = %11001111 'PORTB is ICSP, 2 pins for LCD, low nibble for 4 button inputs
TRISC = %10001111 'Set RC7 (RX), rest are outputs, low 4 bits used as inputs for addressing
TRISD = %10000000 'Set PORTD for serial, 1 LED, 4 LCD data
TRISE = %00001000 'Set PORTE for MCLR, low 3 bits for addressing - high order not used
'----------------------------------------------------------------------------------------
'Constants here
sync con $54
Line1 CON 128 'Point to beginning of line 1 ($2)
Line2 CON 192 'Point to beginning of line 2 ($C0)
Line3 con 148 'Point to beginning of line 3 ($94)
line4 con 212 'Point to beginning of line 4 ($D4)

'----------------------------------------------------------------------------------------
'Define the Analog to Digital conversions
DEFINE ADC_BITS 12 '12-bit resolution
' DEFINE ADC_CLOCK 5 'Set clock source to Frc/16
DEFINE ADC_CLOCK 6 'FOSC/64, 0.8uS @ 64Mhz
DEFINE ADC_SAMPLEUS 50 'Sample time in uS before A/D conversion is started

'-----------------------------------------------------------------------------------------
ADCval var word 'Result of 12 bit ADC
ADCavg var word 'Running ADC average


Volt:
ADCON0=%00000011 'Start ADC conversion, channel AN0
while ADCON0.1 = 1 :wend 'Wait for ADC DONE
ADCval.highbyte = ADRESH 'Move HIGH byte of result to adcVAL
ADCval.lowbyte = ADRESL 'Move LOW byte of result to adcVAL
ADCavg=ADCavg+ADCval 'Add latest ADC reading to running total
return

'-----------------------------------------------------------------
PB:
'Get PBs to work and print to LCD
gosub volt
if PB1=1 then lcdout $FE,line1,"+5 volt= ", dec ADCavg," "
lcdout $FE,line2," "
lcdout $FE,line3,DEC (temperature / 100), ".", DEC2 temperature, " F "
' lcdout $FE,line4," " 'Display done with init
pause 100 'Display for a while
goto PB

mark_s
- 8th May 2014, 20:36
Here are some snippets of code from a project of mine using a PIC18F45K80. I think you should remove this section from your code. When not using adcin, I prefer to manually set the adc registers.



'----------------------------------------------------------------------------------------
'Define the Analog to Digital conversions
DEFINE ADC_BITS 12 '12-bit resolution
' DEFINE ADC_CLOCK 5 'Set clock source to Frc/16
DEFINE ADC_CLOCK 6 'FOSC/64, 0.8uS @ 64Mhz
DEFINE ADC_SAMPLEUS 50 'Sample time in uS before A/D conversion is started

'-----------------------------------------------------------------------------------------


Maybe something here will help you get it running.



'ADC-----------------------------------------------------
ADCON0 = %00000001 ' A/D module enabled
ADCON1 = %01110000 '%01110000 Vref = INT 4096, TRIG ctmu, NEG REF VSS
ADCON2 = %10010100 ' Right justified, 12 Tad, Frc clock
ANCON1 = %00000000 'Select Digital or Analog AN8 - AN14

'-------------------------------------------------------------
TRISA = %00000011

ADC_AN0 var word
ADC_AN1 var word


'================================================= ======================
GET_AD0:
ADCON0 = %00000001 'AN0 ENABLED
ADCON0.1 = 1
@ nop
@ nop
@ nop
@ nop
@ nop
@ nop
@ nop
@ nop
WHILE ADCON0.1 = 1 ' A/D complete?
@ nop
@ nop
WEND
ADC_AN0.lowbyte =ADRESL
ADC_AN0.highbyte = ADRESH
'================================================= ==================
GET_AD1:
ADCON0 = %00000101 'AN1 ENABLED
ADCON0.1 = 1
@ nop
@ nop
@ nop
@ nop
@ nop
@ nop
@ nop
@ nop
WHILE ADCON0.1 = 1 ' A/D complete?
@ nop
@ nop
WEND
ADC_AN1.lowbyte =ADRESL
ADC_AN1.highbyte = ADRESH

'================================================= ==================

mark_s
- 8th May 2014, 21:51
Is this all of your code?

After a second look, this line looks like trouble?


ADCavg=ADCavg+ADCval 'Add latest ADC reading to running total


ADCavg will keep adding up until it overflows. I don't see where you clear it or divide the results. Normally you would do a loop say 8 times summing the readings, then divide ADCavg by 8 to get the average. A running average has a few more steps. I would try to get the adc running first without averaging or formating. Start by applying 0 to 4 volts on your input. You should see 0 to 4095 on your lcd. Then you can add the averaging and lcd formatting.

ecoli-557
- 8th May 2014, 23:16
Thanks Mark-
Yup, you found the stupid mistake, been looking at it too long.
Listed below and for all to see is what is working.
However, I can't seem to figure out how to normalize the A2D result which is accurate into the real world value.
Example: The result from the 12 A2D is 3272 and my meter says it is 3235 (actually 3.235). So, if I multiply 3272 by 1.547 I would get the correct real-world value of 5061.784. Then I would need to strip off some digits and add the decimal in the correct place.
All not too easy in PBP3 without real numbers..... Need to figure out how to get from 3272 to 5.06!

I can't seem to wrap my head around a solution, any ideas?

Updated code below so others might use what is working so far:
'Set for using PIC18F46K80
'Programmed using PicBasic Pro 3.0.7.4
'Environment MCSP 5.0.0.5

'Define configuration
#CONFIG
CONFIG RETEN = ON ;Ultra-low regulator ON
CONFIG FOSC = EC3 ;External HS oscillator, port function on RA6
CONFIG INTOSCSEL = LOW ;LF-INTOSC in Low-power mode during Sleep
CONFIG SOSCSEL = DIG ;Digital (SCLKI) mode
;FCMEM doesn't seem to work here.....
; CONFIG FCMEM = OFF ;Fail Safe CLock Monitor is OFF
CONFIG IESO = OFF ;Internal/External oscillator switchover is OFF
CONFIG PWRTEN = ON ;Power Up timer is ON
CONFIG BOREN = OFF ;Brown Out Reset is OFF - for now
CONFIG BORV = 0 ;Brown Out level is 4.6v
CONFIG WDTEN = OFF ;Watchdog is OFF - for now
CONFIG MCLRE = ON ;MCLR pin is ENABLED
CONFIG STVREN = ON ;Stack FULL/UNDERFLOW reset is ENABLED
CONFIG BBSIZ = BB1K ;Boot Block size - 1K?
CONFIG XINST = OFF ;Extended Instructgion set is DISABLED
#ENDCONFIG

'---------------------------------------------------------------------------------------
'Define the oscillator and setup the INCLUDE files
DEFINE OSC 64 '64 MHz oscillator, external
INCLUDE "DT_INTS-18.bas" 'Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" 'Include if using PBP interrupts

'---------------------------------------------------------------------------------------
'OKAY, Lets set up the registers.....
OSCCON= %00001000 'Device enters SLEEP mode when SLEEP is executed
OSCCON2= %00000000 'System clock comes from other osc than internal
REFOCON.7=0 'Refrence oscillator is DISABLED
WPUB= %00000000 'PORTB pullups DISABLED
PADCFG1= %00000000 'ALL pull up resistors DISABLED on ports D,E
ANCON0= %00000001 'Selects AN1-AN7 as DIGITAL, AN0 as ANALOG
ANCON1= %00000000 'Selects remaining analog/digital inputs as DIGITAL
ADCON0= %00000000 'Turns OFF ADC - REMEMBER TO TURN IT BACK ON AFTER DEBUG-, selects AD0 as input
ADCON1= %10110000 'Vref is INTERNAL 4.1v and AVss
ADCON2= %10010110 'RIGHT justified, 4 TAD A2D aq time select, Fosc/64
T0CON.7=0 'DISABLES TMR0
CM1CON= %00000000 'Turns OFF CM1 comparator
CM2CON= %00000000 'Turns OFF CM2 comparator
CVRCON.7=0 'Comparator voltage ref is powered DOWN
HLVDCON.4=0 'High Low Voltage Detect is DISABLED
CCP1CON=%00000000 'Turns OFF capture, compare, pwm
CCP2CON=%00000000 'Turns OFF capture, compare, pwm
CCP3CON=%00000000 'Turns OFF capture, compare, pwm
CCP4CON=%00000000 'Turns OFF capture, compare, pwm
CCP5CON=%00000000 'Turns OFF capture, compare, pwm
CANCON=%00100000 'CAN disabled
CTMUCONH.7=0 'CTMU disabled
INTCON= %00000000 'INT0 and POIRTB change external interrupt is DISABLED
INTCON2= %01111100 'PORTB pullups enabled by TRIS, int are RISING edge, TMR0 hi and rest low priority
INTCON3.3=0 'Disables INT1 external interupt
IOCB= %00000000 'PORTB interrupt on change all bits DISABLED
SLRCON=%00000000 'Slew rate for all ports is STANDARD
SSPCON1.5=0 'MSSP port is DISABLED
ODCON=%00000000 'Open Drain capability is DISABLED
PSTR1CON= %00000000
' MDCON=%00000000 'Modulation Control is DISABLED
'---------------------------------------------------------------------------------------
'Direction registers
TRISA = %10001011 'Set PORTA for bit7 is clk in, 2 led out, 1 temp in, 1 A/D in, ext Vref
TRISB = %11001111 'PORTB is ICSP, 2 pins for LCD, low nibble for 4 button inputs
TRISC = %10001111 'Set RC7 (RX), rest are outputs, low 4 bits used as inputs for addressing
TRISD = %10000000 'Set PORTD for serial, 1 LED, 4 LCD data
TRISE = %00001000 'Set PORTE for MCLR, low 3 bits for addressing - high order not used
'----------------------------------------------------------------------------------------
'Constants here
sync con $54
Line1 CON 128 'Point to beginning of line 1 ($2)
Line2 CON 192 'Point to beginning of line 2 ($C0)
Line3 con 148 'Point to beginning of line 3 ($94)
line4 con 212 'Point to beginning of line 4 ($D4)
HystLevel CON 7 ' 7 = .7 of the Least Significant Digit
' Valid HystLevel is from 6 to 13
AvgCount CON 16 ' = Number of samples to average. For best response
' times, keep AvgCount as small as you can
FAspread CON 50 ' = Fast Average threshold +/-
' FAspread should be larger than the worst possible
' noise in the A/D conversion.

'----------------------------------------------------------------------------------------

ADCval var word 'Result of 12 bit ADC
ADCavg var word 'Running ADC average
k var byte
AVGchanged VAR BIT ' 1 indicates that the Average Value has changed
' you have to clear this bit after using it
ADavg VAR WORD ' Stores the current Average
ADhyst VAR WORD ' Stores the current Value for Hysterisis
Value VAR WORD ' Input / Output variable to be averaged
spread CON FAspread * 10 ' adjust spread *10 for Hyst.

'################################################# ################################################## ######################
'# We go around the rest of the subroutines
'################################################# ################################################## ######################
Initialize:
goto startmain 'Jumps around subroutines
'################################################# ################################################## ######################
'# Subroutines
'################################################# ################################################## ######################

Volt:
ADCON0=%00000011 'Select channel AN0, and start sampling then after 4 TADs - convert
while ADCON0.1 = 1 :wend 'Wait for ADC DONE
ADCval.highbyte = ADRESH 'Move HIGH byte of result to adcVAL
ADCval.lowbyte = ADRESL 'Move LOW byte of result to adcVAL
' adcval=(adcval*1.567) '1.567 will convert voltage divider into real voltage
' Just don't know HOW to do it.........
'I have taken the ((ADCavg/1000)*1547)/1000 which works on the calculator, but here without REAL numbers????
'THEN add a decimal so ADCavg ends up being 5.06 given ADCavg of 3272.....
value=ADCval
gosub average
ADCavg=value
return

Average:'Thanks Darrel...via PBP forum.
Value = Value * 10 ' Scale value up for hysterisis
IF Value = ADavg Then NoChange ' if they're the same, nothing to do
IF ABS (Value - ADavg) > spread OR Value < AvgCount Then FastAvg
IF ABS (Value - ADavg) < AvgCount Then RealClose
ADavg = ADavg - (ADavg/AvgCount) ' Subtract 1 samples worth
ADavg = ADavg + (Value/AvgCount) ' Add in the new sample portion
GoTo AVGok
FastAvg: ' Catch up to the changing value
ADavg = Value ' Set Average to the current value
GoTo AVGok
RealClose: ' Reduce the sample size when
ADavg = ADavg - (ADavg/(AvgCount/4)) ' the average and the sample are
ADavg = ADavg + (Value/(AvgCount/4)) ' "Real Close"
AVGok:
IF ABS (ADavg - ADhyst) > HystLevel then ' If it changed > HystLevel +/-
ADhyst = ((ADavg + 5) / 10) * 10 ' Round ADavg to get new Value
AVGchanged = 1 ' Indicate that Average changed
ENDIF
NoChange:
Value = ADhyst / 10 ' Scale the result down
Return

'################################################# ################################################## ######################
'# Startmain - where the MAGIC starts!
'################################################# ################################################## ######################
startmain:
lcdout $FE,1
lcdout $FE,line3," @ startmain " 'Display code version
lcdout $FE,line4,"SMODE address= ",dec3 address," "'Display our ADDRESS
pause 2000 'Display for a while
PB:
'Get PBs to work and print to LCD
gosub volt
if PB1=1 then lcdout $FE,line1,"5 volt= ", dec ADCavg," ", dec ADCval
' if PB1=1 then lcdout $FE,line1,"5 volt= ", dec ADCavg," ", [#(ADCval/10,".",#(ADCval//10)]
lcdout $FE,line2," "
lcdout $FE,line3,DEC (temperature / 100), ".", DEC2 temperature, " F "
' lcdout $FE,line4," " 'Display done with init
pause 100 'Display for a while
goto PB

ecoli-557
- 8th May 2014, 23:21
Mark- How DO you do the formatting within the box?
I read a thread that said to save it in HTML and cut and paste, but that didn't seem to work this time.......
Just curious as it would make it easier to read.......

ecoli-557
- 8th May 2014, 23:28
Ha!
I am a DUMMY! It was SO easy...... just have to play with the forum software.
Ahem, here is the code in better formatted style:


'Set for using PIC18F46K80
'Programmed using PicBasic Pro 3.0.7.4
'Environment MCSP 5.0.0.5
'Program using the USB MELABS programmer - not yet


'Define configuration
#CONFIG
CONFIG RETEN = ON ;Ultra-low regulator ON
CONFIG FOSC = EC3 ;External HS oscillator, port function on RA6
CONFIG INTOSCSEL = LOW ;LF-INTOSC in Low-power mode during Sleep
CONFIG SOSCSEL = DIG ;Digital (SCLKI) mode
;FCMEM doesn't seem to work here.....
; CONFIG FCMEM = OFF ;Fail Safe CLock Monitor is OFF
CONFIG IESO = OFF ;Internal/External oscillator switchover is OFF
CONFIG PWRTEN = ON ;Power Up timer is ON
CONFIG BOREN = OFF ;Brown Out Reset is OFF - for now
CONFIG BORV = 0 ;Brown Out level is 4.6v
CONFIG WDTEN = OFF ;Watchdog is OFF - for now
CONFIG MCLRE = ON ;MCLR pin is ENABLED
CONFIG STVREN = ON ;Stack FULL/UNDERFLOW reset is ENABLED
CONFIG BBSIZ = BB1K ;Boot Block size - 1K?
CONFIG XINST = OFF ;Extended Instructgion set is DISABLED
#ENDCONFIG

'---------------------------------------------------------------------------------------
'Define the oscillator and setup the INCLUDE files
DEFINE OSC 64 '64 MHz oscillator, external
INCLUDE "DT_INTS-18.bas" 'Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" 'Include if using PBP interrupts

'---------------------------------------------------------------------------------------
'OKAY, Lets set up the registers.....
OSCCON= %00001000 'Device enters SLEEP mode when SLEEP is executed
OSCCON2= %00000000 'System clock comes from other osc than internal
REFOCON.7=0 'Refrence oscillator is DISABLED
WPUB= %00000000 'PORTB pullups DISABLED
PADCFG1= %00000000 'ALL pull up resistors DISABLED on ports D,E
ANCON0= %00000001 'Selects AN1-AN7 as DIGITAL, AN0 as ANALOG
ANCON1= %00000000 'Selects remaining analog/digital inputs as DIGITAL
ADCON0= %00000000 'Turns OFF ADC - REMEMBER TO TURN IT BACK ON AFTER DEBUG-, selects AD0 as input
ADCON1= %10110000 'Vref is INTERNAL 4.1v and AVss
ADCON2= %10010110 'RIGHT justified, 4 TAD A2D aq time select, Fosc/64
T0CON.7=0 'DISABLES TMR0
CM1CON= %00000000 'Turns OFF CM1 comparator
CM2CON= %00000000 'Turns OFF CM2 comparator
CVRCON.7=0 'Comparator voltage ref is powered DOWN
HLVDCON.4=0 'High Low Voltage Detect is DISABLED
CCP1CON=%00000000 'Turns OFF capture, compare, pwm
CCP2CON=%00000000 'Turns OFF capture, compare, pwm
CCP3CON=%00000000 'Turns OFF capture, compare, pwm
CCP4CON=%00000000 'Turns OFF capture, compare, pwm
CCP5CON=%00000000 'Turns OFF capture, compare, pwm
CANCON=%00100000 'CAN disabled
CTMUCONH.7=0 'CTMU disabled
INTCON= %00000000 'INT0 and POIRTB change external interrupt is DISABLED
INTCON2= %01111100 'PORTB pullups enabled by TRIS, int are RISING edge, TMR0 hi and rest low priority
INTCON3.3=0 'Disables INT1 external interupt
IOCB= %00000000 'PORTB interrupt on change all bits DISABLED
SLRCON=%00000000 'Slew rate for all ports is STANDARD
SSPCON1.5=0 'MSSP port is DISABLED
ODCON=%00000000 'Open Drain capability is DISABLED
PSTR1CON= %00000000
' MDCON=%00000000 'Modulation Control is DISABLED
'---------------------------------------------------------------------------------------
'Direction registers
TRISA = %10001011 'Set PORTA for bit7 is clk in, 2 led out, 1 temp in, 1 A/D in, ext Vref
TRISB = %11001111 'PORTB is ICSP, 2 pins for LCD, low nibble for 4 button inputs
TRISC = %10001111 'Set RC7 (RX), rest are outputs, low 4 bits used as inputs for addressing
TRISD = %10000000 'Set PORTD for serial, 1 LED, 4 LCD data
TRISE = %00001000 'Set PORTE for MCLR, low 3 bits for addressing - high order not used
'----------------------------------------------------------------------------------------
'Constants here
sync con $54
Line1 CON 128 'Point to beginning of line 1 ($2)
Line2 CON 192 'Point to beginning of line 2 ($C0)
Line3 con 148 'Point to beginning of line 3 ($94)
line4 con 212 'Point to beginning of line 4 ($D4)
HystLevel CON 7 ' 7 = .7 of the Least Significant Digit
' Valid HystLevel is from 6 to 13
AvgCount CON 16 ' = Number of samples to average. For best response
' times, keep AvgCount as small as you can
FAspread CON 50 ' = Fast Average threshold +/-
' FAspread should be larger than the worst possible
' noise in the A/D conversion.

'------------------------------------------------------------------------------------------
'Variable List
ADCval var word 'Result of 12 bit ADC
ADCavg var word 'Running ADC average
k var byte
AVGchanged VAR BIT ' 1 indicates that the Average Value has changed
' you have to clear this bit after using it
ADavg VAR WORD ' Stores the current Average
ADhyst VAR WORD ' Stores the current Value for Hysterisis
Value VAR WORD ' Input / Output variable to be averaged
spread CON FAspread * 10 ' adjust spread *10 for Hyst.


'---------------------------------------------------------------------------------------------
'################################################# ################################################## ######################
'# We go around the rest of the subroutines
'################################################# ################################################## ######################
Initialize:
goto startmain 'Jumps around subroutines
'################################################# ################################################## ######################
'# Subroutines
'################################################# ################################################## ######################

Volt:
ADCON0=%00000011 'Select channel AN0, and start sampling then after 4 TADs - convert
while ADCON0.1 = 1 :wend 'Wait for ADC DONE
ADCval.highbyte = ADRESH 'Move HIGH byte of result to adcVAL
ADCval.lowbyte = ADRESL 'Move LOW byte of result to adcVAL
' adcval=(adcval*1.567) '1.567 will convert voltage divider into real voltage
' Just don't know HOW to do it.........
'I have taken the ((ADCavg/1000)*1547)/1000 which works on the calculator, but here without REAL numbers????
'THEN add a decimal so ADCavg ends up being 5.06 given ADCavg of 3272.....
value=ADCval
gosub average
ADCavg=value
return

Average:'Thanks Darrel...via PBP forum.
Value = Value * 10 ' Scale value up for hysterisis
IF Value = ADavg Then NoChange ' if they're the same, nothing to do
IF ABS (Value - ADavg) > spread OR Value < AvgCount Then FastAvg
IF ABS (Value - ADavg) < AvgCount Then RealClose
ADavg = ADavg - (ADavg/AvgCount) ' Subtract 1 samples worth
ADavg = ADavg + (Value/AvgCount) ' Add in the new sample portion
GoTo AVGok
FastAvg: ' Catch up to the changing value
ADavg = Value ' Set Average to the current value
GoTo AVGok
RealClose: ' Reduce the sample size when
ADavg = ADavg - (ADavg/(AvgCount/4)) ' the average and the sample are
ADavg = ADavg + (Value/(AvgCount/4)) ' "Real Close"
AVGok:
IF ABS (ADavg - ADhyst) > HystLevel then ' If it changed > HystLevel +/-
ADhyst = ((ADavg + 5) / 10) * 10 ' Round ADavg to get new Value
AVGchanged = 1 ' Indicate that Average changed
ENDIF
NoChange:
Value = ADhyst / 10 ' Scale the result down
Return
'################################################# ################################################## ######################
'# Startmain - where the MAGIC starts!
'################################################# ################################################## ######################
startmain:
lcdout $FE,1
lcdout $FE,line3," @ startmain " 'Display code version
lcdout $FE,line4,"SMODE address= ",dec3 address," "'Display our ADDRESS
pause 2000 'Display for a while
PB:
'Get PBs to work and print to LCD
gosub volt
if PB1=1 then lcdout $FE,line1,"5 volt= ", dec ADCavg," ", dec ADCval
' if PB1=1 then lcdout $FE,line1,"5 volt= ", dec ADCavg," ", [#(ADCval/10,".",#(ADCval//10)]
lcdout $FE,line2," "
lcdout $FE,line3,DEC (temperature / 100), ".", DEC2 temperature, " F "
' lcdout $FE,line4," " 'Display done with init
pause 100 'Display for a while
goto PB

ecoli-557
- 9th May 2014, 01:09
I do have a solution, I can read the ADC, strip off 1 digit, multiply the ADC value by a constant needed for the resistor (voltage) divider, strip off 2 LSBs and format the result for the LCD.
Seems to be accurate down as low as 4.55 - didn't want to go any lower!
Posted the module's final code here:
Thanks for the wealth of info on this forum, as well as the responses.
Regards.



'Set for using PIC18F46K80
'Programmed using PicBasic Pro 3.0.7.4
'Environment MCSP 5.0.0.5

'Define configuration
#CONFIG
CONFIG RETEN = ON ;Ultra-low regulator ON
CONFIG FOSC = EC3 ;External HS oscillator, port function on RA6
CONFIG INTOSCSEL = LOW ;LF-INTOSC in Low-power mode during Sleep
CONFIG SOSCSEL = DIG ;Digital (SCLKI) mode
;FCMEM doesn't seem to work here.....
; CONFIG FCMEM = OFF ;Fail Safe CLock Monitor is OFF
CONFIG IESO = OFF ;Internal/External oscillator switchover is OFF
CONFIG PWRTEN = ON ;Power Up timer is ON
CONFIG BOREN = OFF ;Brown Out Reset is OFF - for now
CONFIG BORV = 0 ;Brown Out level is 4.6v
CONFIG WDTEN = OFF ;Watchdog is OFF - for now
CONFIG MCLRE = ON ;MCLR pin is ENABLED
CONFIG STVREN = ON ;Stack FULL/UNDERFLOW reset is ENABLED
CONFIG BBSIZ = BB1K ;Boot Block size - 1K?
CONFIG XINST = OFF ;Extended Instructgion set is DISABLED
#ENDCONFIG

'---------------------------------------------------------------------------------------
'Define the oscillator and setup the INCLUDE files
DEFINE OSC 64 '64 MHz oscillator, external
INCLUDE "DT_INTS-18.bas" 'Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" 'Include if using PBP interrupts

'---------------------------------------------------------------------------------------
'OKAY, Lets set up the registers.....
OSCCON= %00001000 'Device enters SLEEP mode when SLEEP is executed
OSCCON2= %00000000 'System clock comes from other osc than internal
REFOCON.7=0 'Refrence oscillator is DISABLED
WPUB= %00000000 'PORTB pullups DISABLED
PADCFG1= %00000000 'ALL pull up resistors DISABLED on ports D,E
ANCON0= %00000001 'Selects AN1-AN7 as DIGITAL, AN0 as ANALOG
ANCON1= %00000000 'Selects remaining analog/digital inputs as DIGITAL
ADCON0= %00000000 'Turns OFF ADC - REMEMBER TO TURN IT BACK ON AFTER DEBUG-, selects AD0 as input
ADCON1= %10110000 'Vref is INTERNAL 4.1v and AVss
ADCON2= %10010110 'RIGHT justified, 4 TAD A2D aq time select, Fosc/64
T0CON.7=0 'DISABLES TMR0
CM1CON= %00000000 'Turns OFF CM1 comparator
CM2CON= %00000000 'Turns OFF CM2 comparator
CVRCON.7=0 'Comparator voltage ref is powered DOWN
HLVDCON.4=0 'High Low Voltage Detect is DISABLED
CCP1CON=%00000000 'Turns OFF capture, compare, pwm
CCP2CON=%00000000 'Turns OFF capture, compare, pwm
CCP3CON=%00000000 'Turns OFF capture, compare, pwm
CCP4CON=%00000000 'Turns OFF capture, compare, pwm
CCP5CON=%00000000 'Turns OFF capture, compare, pwm
CANCON=%00100000 'CAN disabled
CTMUCONH.7=0 'CTMU disabled
INTCON= %00000000 'INT0 and POIRTB change external interrupt is DISABLED
INTCON2= %01111100 'PORTB pullups enabled by TRIS, int are RISING edge, TMR0 hi and rest low priority
INTCON3.3=0 'Disables INT1 external interupt
IOCB= %00000000 'PORTB interrupt on change all bits DISABLED
SLRCON=%00000000 'Slew rate for all ports is STANDARD
SSPCON1.5=0 'MSSP port is DISABLED
ODCON=%00000000 'Open Drain capability is DISABLED
PSTR1CON= %00000000
' MDCON=%00000000 'Modulation Control is DISABLED
'---------------------------------------------------------------------------------------
'Direction registers
TRISA = %10001011 'Set PORTA for bit7 is clk in, 2 led out, 1 temp in, 1 A/D in, ext Vref
TRISB = %11001111 'PORTB is ICSP, 2 pins for LCD, low nibble for 4 button inputs
TRISC = %10001111 'Set RC7 (RX), rest are outputs, low 4 bits used as inputs for addressing
TRISD = %10000000 'Set PORTD for serial, 1 LED, 4 LCD data
TRISE = %00001000 'Set PORTE for MCLR, low 3 bits for addressing - high order not used
'----------------------------------------------------------------------------------------
'Constants here
sync con $54
Line1 CON 128 'Point to beginning of line 1 ($2)
Line2 CON 192 'Point to beginning of line 2 ($C0)
Line3 con 148 'Point to beginning of line 3 ($94)
line4 con 212 'Point to beginning of line 4 ($D4)
HystLevel CON 7 ' 7 = .7 of the Least Significant Digit
' Valid HystLevel is from 6 to 13
AvgCount CON 16 ' = Number of samples to average. For best response
' times, keep AvgCount as small as you can
FAspread CON 50 ' = Fast Average threshold +/-
' FAspread should be larger than the worst possible
' noise in the A/D conversion.

'----------------------------------------------------------------------------------------
'Additional I/O Definitions
error var PORTA.5 'ERROR output, ACTIVE HIGH
heart var PORTA.6 'Heartbeat to know we are online - ACTIVE HIGH
warn var PORTD.4 'WARNING output, ACTIVE HIGH
status1 var PORTC.4 'STATUS output, ACTIVE HIGH
NCTS Var PORTC.5 'Goes high to xmit on COMMAND RS485
SCTS VAR PORTD.5 'Goes high to xmit on STUB RS485
PB1 var PORTB.3 'Pushbutton1 (far left)
PB2 var PORTB.2 'Pushbutton2
PB3 var PORTB.1 'Pushbutton3
PB4 var PORTB.0 'Pushbutton4 (on far right)
addr_dig1 var PORTE.0 'LSB of address
addr_dig2 var PORTE.1 'Digit 2 of address
addr_dig3 var PORTE.2 'MSB of address
DQ Var PORTA.1 'One-wire data pin

'------------------------------------------------------------------------------------------
'Variable List
i var byte 'Loop counter var
j var byte 'Loop counter var
Node_rxbyte var byte 'NODE serial receive byte
Stub_rxbyte var byte 'STUB serial receive byte
temperature Var Word 'Temperature storage
count_remain Var Byte 'Count remaining
count_per_c Var Byte 'Count per degree C
In_Count VAR BYTE 'Where we are in the 8 byte packet
Buff VAR BYTE[4] 'Array for the data
CMD_PORT VAR Buff[0] '2nd byte (after sync byte ($54) contains the 4 bits for STATUS/ 4 bits for PORT
BOARD VAR Buff[1] 'Board number on STUB network
SUMCHK VAR Buff[2] 'Simple sumcheck to see if payload is good
SyncRcvd VAR BIT 'Flag set when we get a proper sync byte
PacketRcvd VAR BIT 'Flag set when we get a whole packet
address var word 'Address variable
bcd1 VAR BYTE 'lsb of BCD address
bcd10 var byte '10's of BCD address
bcd100 var byte 'MSB of BCD address
ADCval var word 'Result of 12 bit ADC
ADCavg var word 'Running ADC average
k var byte
AVGchanged VAR BIT ' 1 indicates that the Average Value has changed
' you have to clear this bit after using it
ADavg VAR WORD ' Stores the current Average
ADhyst VAR WORD ' Stores the current Value for Hysterisis
Value VAR WORD ' Input / Output variable to be averaged
spread CON FAspread * 10 ' adjust spread *10 for Hyst.


'---------------------------------------------------------------------------------------------
'################################################# ################################################## ######################
'# We are going to start this Pig!
'################################################# ################################################## ######################
init:
heart=0 'Turns off HEARTBEAT led
warn=0 'Turns off WARNING led
error=0 'Turns off ERROR led
status1=0 'Turns off STATUS1 led
heart=1 : warn=1 : error=1 : status1=1 'Turns ON leds
pause 20 'Hold here for a bit while things IPL......
gosub initlcd 'Initializes the lcd
pause 2000 'Display for a while
heart=0 : warn=0 : error=0 : status1=0 'Turns OFF leds
'################################################# ################################################## ######################
'# We go around the rest of the subroutines
'################################################# ################################################## ######################
Initialize:
heart=0 'Turns off HEARTBEAT led
warn=0 'Turns off WARNING led
error=0 'Turns off ERROR led
status1=0 'Turns off STATUS1 led
ncts=0 'NODE network 485 in receive mode
scts=0 'STUB network 485 in receive mode
temperature=0 'Zeros var
bcd1=0 : bcd10=0 : bcd100=0 'Zeros the address vars
gosub chirp 'Flashed ERROR before we start
goto startmain 'Jumps around subroutines
'################################################# ################################################## ######################
'# Subroutines
'################################################# ################################################## ######################

initlcd:lcdout $fe,1 'Inits the lcd
return

get_addr:
'Get the address and perform functions to get the digit data
'this is code for the use of rotary hex switches - 3 of them
addr_dig1=0 : addr_dig2=0 : addr_dig3=0 'Set all commons LOW
status1=1 'Turns ON the StATUS1 led
addr_dig1=1 'Set LSB HIGH in order to read
bcd1=PORTC & $0F 'Reads port and strips off high nibble, puts in bcd1
pause 5
addr_dig1=0 'Resets digit 1 common
pause 5
addr_dig2=1 'Sets digit 2 common
bcd10=PORTC & $0F 'Reads port and strips off high nibble, puts in bcd10
pause 5
addr_dig2=0 'Resets digit 2 common
pause 5
addr_dig3=1 'Sets digit 3 common
bcd100=PORTC & $0F 'Reads port and strips off high nibble, puts in bcd100
pause 5
addr_dig3=0 'Resets digit 3 common
address=(bcd100*100)+(bcd10*10)+bcd1 'Builds the address from the 3 digits
status1=0 'Turns OFF STATUS2 led
return

h2d: ' Convert Hex coded data -> decimal data
K = (K & $F )+((K>>4)*10)
Return
d2h: ' Convert Decimal -> Hex coded data
K = (K DIG 1) * $10 + (K DIG 0)
Return

chirp: for i=1 to 3
high error
pause 50
low error
pause 50
next i
return

get_temp: 'Gets temperature from DS1820, puts in degrees F
OWOut DQ, 1, [$CC, $44] 'Start temperature conversion
pause 1000
OWIn DQ, 4, [count_remain] 'Check for still busy converting
If count_remain = 0 Then
OWOut DQ, 1, [$CC, $BE] 'Read the temperature
OWIn DQ, 0, [temperature.LOWBYTE, temperature.HIGHBYTE, Skip 4, count_remain, count_per_c]
endif
temperature = (temperature */ 461) + 3200
return

Volt:
ADCON0=%00000011 'Select channel AN0, and start sampling then after 4 TADs - convert
while ADCON0.1 = 1 :wend 'Wait for ADC DONE
ADCval.highbyte = ADRESH 'Move HIGH byte of result to adcVAL
ADCval.lowbyte = ADRESL 'Move LOW byte of result to adcVAL
value=ADCval
gosub average
ADCavg=value
'Here we 'normalize' the ADC reading to real-world voltage
ADCavg=ADCavg/10 'Leaves us with 3 digits
ADCavg=ADCavg*155 'Our normalizing junk-yard constant
ADCavg=ADCavg/100 'Leaves us with 3 digits
return

Average:'Thanks Darrel...via PBP forum.
Value = Value * 10 ' Scale value up for hysterisis
IF Value = ADavg Then NoChange ' if they're the same, nothing to do
IF ABS (Value - ADavg) > spread OR Value < AvgCount Then FastAvg
IF ABS (Value - ADavg) < AvgCount Then RealClose
ADavg = ADavg - (ADavg/AvgCount) ' Subtract 1 samples worth
ADavg = ADavg + (Value/AvgCount) ' Add in the new sample portion
GoTo AVGok
FastAvg: ' Catch up to the changing value
ADavg = Value ' Set Average to the current value
GoTo AVGok
RealClose: ' Reduce the sample size when
ADavg = ADavg - (ADavg/(AvgCount/4)) ' the average and the sample are
ADavg = ADavg + (Value/(AvgCount/4)) ' "Real Close"
AVGok:
IF ABS (ADavg - ADhyst) > HystLevel then ' If it changed > HystLevel +/-
ADhyst = ((ADavg + 5) / 10) * 10 ' Round ADavg to get new Value
AVGchanged = 1 ' Indicate that Average changed
ENDIF
NoChange:
Value = ADhyst / 10 ' Scale the result down
Return


'################################################# ################################################## ######################
'# Startmain - where the MAGIC starts!
'################################################# ################################################## ######################
startmain:
gosub initlcd 'Initializes the lcd
gosub get_addr 'Gets board address
gosub get_temp 'Gets temp from DS18S20 - not quite working
gosub volt 'Gets the +5 volt rail voltage
pause 200
lcdout $FE,1
lcdout $FE,line1," CLUSTER CONTROLLER " 'Display who we are
lcdout $FE,line2,"SMODE address= ",dec3 address," "'Display our address
lcdout $FE,line3,"5 volt= ", DEC ADCavg/100,".",DEC2 ADCavg//100 'Display +5 volt rail
lcdout $FE,line4,DEC (temperature / 100), ".", DEC2 temperature, " F " 'Display our temp
pause 2000 'Display for a while
goto startmain

HenrikOlsson
- 9th May 2014, 07:07
Hi,

Need to figure out how to get from 3272 to 5.06!
Couple of ideas:

ADValue VAR WORD
Temp VAR WORD
Result VAR WORD

ADValue = 3272
Temp = ADValue ** 35809 ' Multiply by ~0.5464
Result = ADValue + Temp ' Result now 5059

' Or, same as above but without using the Temp variable:
Result = ADValue + (ADValue ** 35809)

' Or, perhaps:
ADValue = 3272
Result = ADValue */ 396 ' Multiply by ~1.5469, Result now 5061

'Display the value
LCDOUT Result / 1000, ".", DEC3 Result // 1000


/Henrik.

Aussie Barry
- 9th May 2014, 08:50
Hi,

If you change your voltage divider to a 50%-50% configuration (say 4k7 and 4k7) and then set the reference voltage for the ADC to internal 4.096V, each step of the ADC will equate 0.5mV
Multiply your ADC reading by two and then manipulate the result to read "X.XXX Volts" per Henrik's LCDOUT routine.
Yes, I understand there may be some slight inaccuracies but I have found this approach to be quite effecting in recent projects.

Cheers
Barry
VK2XBP

ecoli-557
- 9th May 2014, 14:06
Henrik-
That looks like a cool way to do this, I have never used these operators, but I will try them all, just to see more about them.
It looks much more efficient than what I had done - multiple dividing......

Thanks! I will let you know how it turned out.
-Steve

ecoli-557
- 9th May 2014, 14:14
Barry-
I understand what you are saying, and yes, I am using the internal 4.1 reference - 1st time in fact to rely on an internal!. I was trying to use more of the available resolution on the ADC. If I do a 50% voltage divider, I have 2.5 volts against a 4.096 voltage reference. The divider I am using, uses more of the resolution of the 4.096 reference, 3.23 volts against 4.096 which gives me a little headroom above 5 volts.

I have done that in the past, but felt that I left some resolution on the table. This way does work, perhaps not straightforward, but I think it does give me more bits of resolution that can be used.

That being said, if I hadn't found a way (though it was more clunky than Henrik's) - I was sure thinking of doing just that!

Thanks and Regards to All,
Steve

ecoli-557
- 21st May 2014, 20:47
Henrik-
Sorry for the late reply, been out with a respiratory illness - geeesh.
In your example :
ADValue = 3272
Temp = ADValue ** 35809 ' Multiply by ~0.5464
Result = ADValue + Temp ' Result now 5059

Why are you using 'Top 16 bits' for multiplying? and how did you arrive at '35809' as the multiplier?

I really want to learn from this, Thanks.
-Steve

HenrikOlsson
- 21st May 2014, 21:25
Hi,
We want a "raw" display value of 5060 when the ADC returns 3272. That's a factor of 1.5464, right?
The ** operator does a 16*16bit multiplication and then returns the 16 top bits of the intermediate 32bit result which is the same as dividing that intermediate result by 65536.

So, on a calculator, 3272 * 35809 / 65536 = 1787 (truncated). Add in the "original" 3272 and we get 5059 which is pretty close to 5060.

Just think of the ** operators as multiplying by units of 1/65536.
65536*0.5464 = 35809...
35809 * (1/65536) = 0.5464...
And so on.

/Henrik.

ecoli-557
- 21st May 2014, 21:50
Henrik-
So, I kinda get the top 16 bits thing, but I still don't know where you got the number 35809?
65536 * 0.5464 ----> shouldn't this be 65535 * 1.5464?
-Steve

HenrikOlsson
- 22nd May 2014, 06:26
Hi,

65536 * 0.5464 ----> shouldn't this be 65535 * 1.5464?
Idealy, yes. But that would result in a value larger than 65535 which won't work without resorting to LONGs. So I treat what's on the right hand side of the decimal point separately from what's on the left hand side which is why I first multiply ADValue by 0.5464 and then ADD the ADValue to that result to get the 1.5464. See if this makes more sense:

Result = ADValue + (ADValue ** 35809) ' ~Same as ADValue * 1.5464

Since you're using an 18F part you atleast have the option to use LONGs so if you ARE then by all means go ahead and try Result = ADValue ** 101345, it should work.

Hope I don't make it more confusing....

/Henrik.

ecoli-557
- 22nd May 2014, 17:36
Is a bit more confusing........ BUT I will try what you had said. I just can't quite wrap my head about using sections of numbers....... I'll draw it out on paper and see if that helps....
I'll get it though, may take me a bit.
This forum is an amazing central clearing for knowledge and the fact that others are not only willing but patient is refreshing...
Thanks,
Steve

HenrikOlsson
- 22nd May 2014, 19:44
Hi Steve,

On paper, Result = X * 3 is the same as Result = X+X+X, right?
If the above is correct then Result = X * 1.5 is the same as Result = X + (X/2), right?
And if we agree that the above is correct then I think we'll agree that Result = X + (X*0.5) is the same as Result = X * 1.5 too?

Same thing with the formula in the previous message but the number is 1.5464 instead of 1.5.

Again, ideally, we would want to do Result = ADValue ** 101345 (where 101345 is 65536*1.5464) but that will result in a compile time error if LONGs aren't enabled (because 101345 is larger than 65536 so there's a risk of overflowing the 32bit intermediate result thus returning the wrong value) so we resort to first calculating what 0.5464 is and then add that to the original value, exactly as we did in the example above.

Don't worry, I'm sure you'll get it!

/Henrik.

ecoli-557
- 22nd May 2014, 19:58
Henrik-
Its probably a good thing you are not in Texas cuz your ears would be ringing over the loud 'Aha!' that I just said - Now I need to check my bathwater -(bad reference to Archimedes <grin>).
I think I can now see the light, just needed help with the switch......
Thanks Bud,
Steve