the voltage to the pot is probably fluxuating small amount....... add a good size cap to wiper arm (1 microfarad +-) to smooth voltage.
the voltage to the pot is probably fluxuating small amount....... add a good size cap to wiper arm (1 microfarad +-) to smooth voltage.
I added a 1uF, 63V electrolytic cap between the middle wiper arm input to the PIC and GND, but no change. I measured the voltage on the middle wiper arm of the pot and with a reading of '255' I see 4.99V and with '1' I see 0.00V.
Am I doing something wrong with using ADRESH in 8-bit mode?
Interestingly, if I switch to 10-bit resolution on ADC I get values from 5-1023. I think there must be something wrong with my config or code:
Code:#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use ' Vdd -> pin 1 -> +5V ' RC5/Rx -> pin 5 -> EUSART receive ' RC4/Tx -> pin 6 -> EUSART transmit (LCD) ' RA1 -> pin 12 -> trim pot input (1 uF electrolytic cap?) ' Vss -> pin 14 -> GND DEFINE OSC 16 ; Set oscillator 16Mhz DEFINE ADC_BITS 10 ; Set number of bits in result DEFINE ADC_SAMPLEUS 50 ; Set sampling time in uS (was 5) DEFINE ADC_CLOCK 3 ; Set clock source (3=rc) DEFINE HSER_TXSTA 20h ; Set transmit status and control register DEFINE HSER_BAUD 2400 ; Set baud rate ' *************************************************************** ' Device Fuses ' *************************************************************** #CONFIG __config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF __config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF #ENDCONFIG ' *************************************************************** ' Initialization ' *************************************************************** OSCCON = %01111000 ; 16MHz internal osc APFCON0.2 = 0 ; Tx on RC4 for LCD display APFCON0.7 = 0 ; Rx on RC5 BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin ANSELC = 0 ; Digital only for all PortC pins TRISC = 0 ; Make all PORTC pins output TRISA = %00000010 ; Make all pins output except for RA1 (trim pot input) ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only FVRCON = 0 ; Fixed Voltage Reference is disabled ADCON0 = %00000101 ; ADC (analog-to-digital) is enabled on AN1 (RA1) only PAUSEUS 20 ; wait for the analog switch 'glitch' to die down ADCON1.7 = 1 ; Right-justified results in 10-bits #IFDEF USE_LCD_FOR_DEBUG LCD_INST CON 254 ' instruction LCD_CLR CON 1 ' Clear screen LCD_L1 CON 128 ' LCD line 1 LCD_L2 CON 192 ' LCD line 2 #ENDIF ADCInVal VAR WORD ; stores ADCIN result read from trim pot compVal VAR WORD ; stores last-changed ADC value GOSUB Do_ADC compVal = ADCInVal ; set initial compare value Main: gosub Do_ADC pause 100 #IFDEF USE_LCD_FOR_DEBUG If (compVal > (ADCInVal + 1)) or (compVal < (ADCInVal - 1)) THEn ' If this method of reading ADC is as rock-solid as it appears. ' then I can remove the +/- 1 before doing anything; it would be ' IF ADCInVal <> compVal ' DO SOMETHING HERE compVal = ADCInVal endif HSEROUT [LCD_INST, LCD_CLR] pause 5 HSEROUT ["ADC val=", DEC ADCInVal, " ", 13, 10] ; Send text followed by carriage return and linefeed #ENDIF GOTO Main Do_ADC: PAUSEUS 50 ' Wait for A/D channel acquisition time ' (does this need to be the same as ADC_SAMPLEUS?) ADCON0.1 = 1 ' Start conversion WHILE ADCON0.1 ' Wait for it to complete WEND ADCInVal.HighBYTE = ADRESH ADCInVal.LOWBYTE = ADRESL return
If I remove the DEFINEs usually associated with the ADCIN function:
and use Fosc/8 as the timer:Code:'DEFINE ADC_BITS 10 ; Set number of bits in result 'DEFINE ADC_SAMPLEUS 50 ; Set sampling time in uS (was 5) 'DEFINE ADC_CLOCK 3 ; Set clock source (3=rc)
Then I get 0-255. If I do the same for 10-bit resolution, though, I now get 1-1023.Code:ADCON1 = %00010000 ; Left-justified results in 8-bits; Fosc/8 as timer
that's cool...... 1 count in 1023 is small..small error..... that error with 0 volts could be where pic ground is in relation to pot low end...... ground pot low end close to pic ground.....
what about the fluxuating readings ?
I found the math behind the Arduino map function and have replicated it in a subroutine in PBP:
It doesn't look like this is working as expected - any ideas?Code:PR2 = 62 ; For 16Mhz OSC the desired output freq of 15,873Hz is ; achieved with this PR2 value (8-bit resolution ; with 1:4 prescaler) ; PWM freq must be ~ 16-20kHz to reduce noise MinDuty CON 100 ; Minimum speed to rotate motor for this application MaxDuty VAR WORD ; According to Darrel: ; MaxDuty = (PR2 + 1) * 4 MaxDuty = (PR2 + 1) * 4 ; 252 but with prescaler resolution it's actually 250 MotorDuty VAR WORD ; Actual duty cycle for motor MaxADCVal CON 255 ; 255 for 8-bit; 1023 for 10-bit ADCInVal VAR BYTE ; stores ADCIN result read from trim pot compVal VAR BYTE ; stores last-changed ADC value Main: gosub Do_ADC pause 100 If ADCInVal <> compVal Then #IFDEF USE_LCD_FOR_DEBUG HSEROUT [LCD_INST, LCD_CLR] pause 5 HSEROUT ["new ADCInVal=", DEC ADCInVal, " ", 13, 10] ; Send text followed by carriage return and linefeed #ENDIF GOSUB Map_ADC_Val_to_PWM_Duty gosub ChngMotorHPWM compVal = ADCInVal endif GOTO Main Do_ADC: PAUSEUS 50 ' Wait for A/D channel acquisition time ADCON0.1 = 1 ' Start conversion WHILE ADCON0.1 = 1 ' Wait for it to complete WEND ADCInVal = ADRESH return Map_ADC_Val_to_PWM_Duty: ' Arduino Map function to emulate: ' =============================== ' map(value, fromLow, fromHigh, toLow, toHigh) ' long map(long x, long in_min, long in_max, long out_min, long out_max) ' { ' return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; ' } MotorDuty = (ADCInVal - 0) * (MaxDuty - MinDuty)/(MaxADCVal - 0) + MinDuty #IFDEF USE_LCD_FOR_DEBUG HSEROUT [LCD_INST, LCD_CLR] pause 5 HSEROUT ["MotorDuty=", DEC MotorDuty, " ", 13, 10] ; Send text followed by carriage return and linefeed pause 1500 HSEROUT [LCD_INST, LCD_CLR] #ENDIF RETURN
Hi,
I don't know exactly how you expect it to work but if Iäm not mistaken your formula, when re-written looks like:When ADCInVal = 1 Motor Duty will be 100Code:MotorDuty = ADCInVAL * 252 / 255 + 100
When ADCInVAL = 255 MotorDuty will be 352
Is that what you're seeing?
/Henrik.
Well, they were rock solid. Now the readings are varying quite a lot and even with Melanie's 'averaging' code to pick the middle 8 values from 16 it's still jumping around a lot (and still doesn't read 0 when the trim pot wiper is at the GND end). If I measure the voltage reaching pin 6 (AN1) with a voltmeter it seems accurate and steady, varying from +5V to 0. What am I doing wrong now?
Code:'**************************************************************** '* Name : Nacelle_Steady-On_Lights_12F1840_16Mhz_Int.pbp * '* Author : Ross A. Waddell * '* Notice : Copyright (c) 2016 * '* : All Rights Reserved * '* Date : 02/02/2016 * '* Version : 1.0 * '* Notes : Steady-on nacelle engine lights (TOS Enterprise) * '* : * '**************************************************************** ' TODO: ' Add gamma correction ' Increase ADC resolution to 1024 ' Update PWM frequency/prescaler to use higher resolution ' The PWM/fade works perfectly when **not** connected to a FET ' - if using a FET, the LEDs 'pulse' and blink but don't fade in/out ' - use a 2N2222A transistor DEFINE OSC 16 ' Set oscillator 16Mhz ' For production version of this code, comment out the line below re: ' LCD debugging and also update config fuses to have code protect on #DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use ' *************************************************************** ' Pin Connections ' *************************************************************** ' VDD -> pin 1 -> +5V ' RA5/Rc -> pin 2 -> EUSART receive ' RA2 -> pin 5 -> 1kohm -> 2N2222A transistor -> LEDs ' RA1 -> pin 6 -> Trim pot input ' RA0/Tx -> pin 7 -> EUSART transmit (LCD) ' VSS -> pin 8 -> GND ' *************************************************************** ' EUSART Settings for Tx/Rc (e.g. LCD) ' *************************************************************** ' > use Mister E PIC Multi-Calc application to get register/DEFINE settings ' > as the values are dependent on the OSC and desired baud rate DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1 DEFINE HSER_CLROERR 1 ' Clear overflow automatically DEFINE HSER_SPBRG 160 ' 9600 Baud @ 16MHz, -0.08% ' *************************************************************** ' Device Fuses ' *************************************************************** ' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite #CONFIG __config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF __config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF #ENDCONFIG ' *************************************************************** ' Initialization ' *************************************************************** OSCCON = %01111000 ' 16MHz internal osc pause 100 ' for EEPROM issue APFCON.2 = 0 ; Tx on RA0 for LCD display APFCON.7 = 1 ; Rc on RA5 APFCON.0 = 0 ; CCP1 on RA2 ' Some LCD serial modules need inverted data, some do not ' Enable the line below if needed, but for SparkFun SerLCD it should be ' commented out 'BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin ' From Mister E's Multi-Calc (EUSART): ' ***************************************************************** SPBRGH = 1 BAUDCON.3 = 1 ' Enable 16 bit baudrate generator ' ***************************************************************** FVRCON = 0 ' Fixed Voltage Reference is disabled ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only 'ADCON0 = %00000101 ' ADC (analog-to-digital) is enabled on AN1 (RA1) only 'PAUSEUS 20 ; wait for the analog switch 'glitch' to die down ADCON1 = %10010000 ; Right-justified results in 10-bits; Fosc/8 as timer; ; VREF is connected to VDD TRISA = %00100010 ' Make all pins output except for RA1 (trim pot input) ' and RA5 (EUSART Rc) ADCInVal VAR WORD ; stores ADCIN result read from trim pot LEDBrVal VAR WORD FadeInPause CON 25 ; Pause during LED fade in i VAR WORD ' *************************************************************** ' Set up PWM on CCP1 ' *************************************************************** CCP1CON = %00001100 ; Use CCP1 in PWM mode ' Set duty cycle registers initially to 0 CCP1CON.4 = 0 CCP1CON.5 = 0 CCPR1L = 0 ' Use Mister E's PICMultiCalc_1.3.1.exe application (Windows only) ' to determine prescaler and PR2 values for given OSC frequency (e.g. 16Mhz) ' and duty cycle (use 100% to see highest actual value) ' ************************ ' CCP1 uses TMR2 ' ************************ T2CON = %00000110 ; Timer2 on with 1:16 prescaler PR2 = 255 ; For 16Mhz OSC the desired output freq of 976.563Hz ; is achieved with this PR2 value (10-bit resolution ; with 1:16 prescaler) MaxDuty VAR WORD ; According to Darrel: ; MaxDuty = (PR2 + 1) * 4 MaxDuty = (PR2 + 1) * 4 ; 1024 MinDuty CON 0 ; Minimum brightness for this application MaxADCVal CON 1023 ; 255 for 8-bit; 1023 for 10-bit compVal VAR WORD ; stores last-changed ADC value CounterA var BYTE ' Just a BYTE Temporary working variable DataW var WORD ' Just a WORD Temporary working variable RawData var WORD [16] ' Array holding ADC Result #IFDEF USE_LCD_FOR_DEBUG ' Display control codes for SerLCD serial LCD (SparkFun part #LCD-09395) ' (see 'Dropbox\PBP Projects\PIC Datasheets\SerLCD_V2_5 Datasheet.pdf' ' for list of control codes) LCD_INST CON 254 ' instruction LCD_CLR CON 1 ' Clear screen LCD_L1 CON 128 ' LCD line 1 LCD_L2 CON 192 ' LCD line 2 LCD_BR_CMD CON 124 ' command character for adjusting backlight brightness LCD_BR_LVL CON 140 ' 140==40% #ENDIF ' Should only need to do this one time to adjust backlight brightness '#IFDEF USE_LCD_FOR_DEBUG ' HSEROUT [LCD_BR_CMD, LCD_BR_LVL] ' PAUSE 5 '#ENDIF #IFDEF USE_LCD_FOR_DEBUG pause 1000 HSEROUT [LCD_INST, LCD_CLR, "LCD Init"] pause 5 HSEROUT [LCD_INST, LCD_L2, "SerLCD_V2_5"] pause 500 #ENDIF GOSUB DO_ADCIN_Chk LEDBrVal = ADCInVal compVal = LEDBrVal 'compVal = ADCInVal 'GOSUB Map_VrefInVal_to_PWM_Duty #IFDEF USE_LCD_FOR_DEBUG HSEROUT [LCD_INST, LCD_CLR] pause 5 HSEROUT ["LEDBrVal=",DEC LEDBrVal," "] pause 500 #ENDIF ' Fade in LED to final brightness FOR i = 0 to LEDBrVal CCP1CON.4 = i.0 CCP1CON.5 = i.1 CCPR1L = i >> 2 pause FadeInPause #IFDEF USE_LCD_FOR_DEBUG HSEROUT [LCD_INST, LCD_CLR] pause 5 HSEROUT ["i=",DEC i," "] #ENDIF NEXT i Main: GOSUB DO_ADCIN_Chk PAUSE 100 IF ADCInVal <> compVal THEN #IFDEF USE_LCD_FOR_DEBUG HSEROUT [LCD_INST, LCD_CLR] pause 5 HSEROUT ["new ADCInVal", LCD_INST, LCD_L2, DEC ADCInVal, " "] #ENDIF LEDBrVal = ADCInVal compVal = LEDBrVal gosub ChngLEDBrightness ENDIF GOTO Main '*********** Read ADC or USART Rc inputs ******************************* Do_ADCIN_Chk: ' Stuff 16 Element WORD Array full of ADC values ' ---------------------------------------------- For CounterA=0 to 15 ADCON0 = %00000101 ' Select Channel, Turn-On A/D ' 7=0 Unused ' 6=0 Unused ' 5=0 ) ' 4=0 ) ' 3=0 ) selects AN1 ' 2=0 ) ' 1=0 Go-done Bit ' 0=1 switch-On ADC Module Pauseus 50 ' Wait for channel to setup ADCON0.1 = 1 ' Start conversion While ADCON0.1=1:Wend ' Wait for conversion DataW.HighByte=ADRESH ' Read variable from ADC and save DataW.LowByte=ADRESL RawData(CounterA)=DataW Next CounterA ' Sort ADC Input ' -------------- CounterA=0 GetADCSortLoop: If RawData(CounterA+1) < RawData(CounterA) then DataW=RawData(CounterA) RawData(CounterA)=RawData(CounterA+1) RawData(CounterA+1)=DataW If CounterA>0 then CounterA=CounterA-2 endif CounterA=CounterA+1 If CounterA < 15 then goto GetADCSortLoop ' Quanticise, discarding top and bottom FOUR elements ' ---------------------------------------------------- DataW=0 For CounterA=4 to 11 DataW=DataW+RawData(CounterA) Next CounterA ADCInVal=DataW>>3 ' Divide Result by EIGHT ' Read trim pot Vref to set LED brightness ' PAUSEUS 50 ' Wait for A/D channel acquisition time ' ADCON0.1 = 1 ' Start conversion ' WHILE ADCON0.1 = 1 ' Wait for it to complete ' WEND ' ADCInVal.HighBYTE = ADRESH ' ADCInVal.LOWBYTE = ADRESL return '*********** Set duty registers **************************************** ChngLEDBrightness: CCP1CON.4 = LEDBrVal.0 CCP1CON.5 = LEDBrVal.1 CCPR1L = LEDBrVal >> 2 RETURN
after setting adc input you need to Wait A/D channel acquisition time before you Start conversion
I removed the CCP stuff to leave just the ADC and still the value jumps around by +- 6 or more:
Code:DEFINE OSC 16 ' Set oscillator 16Mhz ' For production version of this code, comment out the line below re: ' LCD debugging and also update config fuses to have code protect on #DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use ' *************************************************************** ' Pin Connections ' *************************************************************** ' VDD -> pin 1 -> +5V ' RA5/Rc -> pin 2 -> EUSART receive ' RA2 -> pin 5 -> 1kohm -> 2N2222A transistor -> LEDs ' RA1 -> pin 6 -> Trim pot input ' RA0/Tx -> pin 7 -> EUSART transmit (LCD) ' VSS -> pin 8 -> GND ' *************************************************************** ' EUSART Settings for Tx/Rc (e.g. LCD) ' *************************************************************** ' > use Mister E PIC Multi-Calc application to get register/DEFINE settings ' > as the values are dependent on the OSC and desired baud rate DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1 DEFINE HSER_CLROERR 1 ' Clear overflow automatically DEFINE HSER_SPBRG 160 ' 9600 Baud @ 16MHz, -0.08% ' *************************************************************** ' Device Fuses ' *************************************************************** ' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite #CONFIG __config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF __config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF #ENDCONFIG ' *************************************************************** ' Initialization ' *************************************************************** OSCCON = %01111000 ' 16MHz internal osc pause 100 ' for EEPROM issue APFCON.2 = 0 ; Tx on RA0 for LCD display APFCON.7 = 1 ; Rc on RA5 APFCON.0 = 0 ; CCP1 on RA2 ' Some LCD serial modules need inverted data, some do not ' Enable the line below if needed, but for SparkFun SerLCD it should be ' commented out 'BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin ' From Mister E's Multi-Calc (EUSART): ' ***************************************************************** SPBRGH = 1 BAUDCON.3 = 1 ' Enable 16 bit baudrate generator ' ***************************************************************** FVRCON = 0 ' Fixed Voltage Reference is disabled ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only ADCON0 = %00000101 ' ADC (analog-to-digital) is enabled on AN1 (RA1) only PAUSEUS 20 ; wait for the analog switch 'glitch' to die down ADCON1 = %10010000 ; Right-justified results in 10-bits; Fosc/8 as timer; ; VREF is connected to VDD TRISA = %00100010 ' Make all pins output except for RA1 (trim pot input) ' and RA5 (EUSART Rc) ADCInVal VAR WORD ; stores ADCIN result read from trim pot compVal VAR WORD #IFDEF USE_LCD_FOR_DEBUG ' Display control codes for SerLCD serial LCD (SparkFun part #LCD-09395) ' (see 'Dropbox\PBP Projects\PIC Datasheets\SerLCD_V2_5 Datasheet.pdf' ' for list of control codes) LCD_INST CON 254 ' instruction LCD_CLR CON 1 ' Clear screen LCD_L1 CON 128 ' LCD line 1 LCD_L2 CON 192 ' LCD line 2 LCD_BR_CMD CON 124 ' command character for adjusting backlight brightness LCD_BR_LVL CON 140 ' 140==40% #ENDIF ' Should only need to do this one time to adjust backlight brightness '#IFDEF USE_LCD_FOR_DEBUG ' HSEROUT [LCD_BR_CMD, LCD_BR_LVL] ' PAUSE 5 '#ENDIF #IFDEF USE_LCD_FOR_DEBUG pause 1000 HSEROUT [LCD_INST, LCD_CLR, "LCD Init"] pause 5 HSEROUT [LCD_INST, LCD_L2, "SerLCD_V2_5"] pause 500 #ENDIF GOSUB DO_ADCIN_Chk compVal = ADCInVal #IFDEF USE_LCD_FOR_DEBUG HSEROUT [LCD_INST, LCD_CLR] pause 5 HSEROUT ["ADCInVal=",DEC ADCInVal," "] pause 500 #ENDIF Main: GOSUB DO_ADCIN_Chk PAUSE 100 IF ADCInVal <> compVal THEN #IFDEF USE_LCD_FOR_DEBUG HSEROUT [LCD_INST, LCD_CLR] pause 5 HSEROUT ["new ADCInVal", LCD_INST, LCD_L2, DEC ADCInVal, " "] #ENDIF compVal = ADCInVal ENDIF GOTO Main '*********** Read ADC or USART Rc inputs ******************************* Do_ADCIN_Chk: ' Read trim pot Vref to set LED brightness PAUSEUS 50 ' Wait for A/D channel acquisition time ADCON0.1 = 1 ' Start conversion WHILE ADCON0.1 = 1 ' Wait for it to complete WEND ADCInVal.HighBYTE = ADRESH ADCInVal.LOWBYTE = ADRESL return
Bookmarks