|
|
View previous topic :: View next topic |
Author |
Message |
escape_key
Joined: 18 Dec 2017 Posts: 2 Location: NZ
|
16F18855 ADC not working w/ CCS functions or reg writes |
Posted: Tue Dec 19, 2017 3:27 pm |
|
|
In trying to get the ADC working on my board, I've written a couple test programs that do just the ADC and RS232. The first is:
Code: |
#include <16F18855.H>
#device adc=10
#pragma use delay(clock = 8MHz)
#use rs232(stream = HOST, baud = 9600, xmit = PIN_B0, rcv = PIN_B3, errors)
void adc_init(void){
setup_adc_ports(sAN21);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(21);
}
void main(void){
adc_init();
while(1){
fprintf(HOST, "%ld \r\n", read_adc());
}
}
|
This code outputs a stream of numbers that are between 8 and 19 in a fairly random distribution, and the values have nothing to do with the voltage at the pin. A coworker went through the .lst file and determined that read_adc() was trying to set ADGO in the register before ADCON0. At that point we stopped looking through the assembly, assuming that we couldn't get it to work with the CCS supplied functions, and I wrote the following attempt at implementing my own functions:
Code: |
#include <16F18855.H>
#pragma use delay(clock = 8MHz)
#use rs232(stream = HOST, baud = 9600, xmit = PIN_B0, rcv = PIN_B3, errors)
#byte ADC_CON_0 = getenv("SFR:ADCON0")
#byte ADC_CON_1 = getenv("SFR:ADCON1")
#byte ADC_CON_2 = getenv("SFR:ADCON2")
#byte ADC_CON_3 = getenv("SFR:ADCON3")
#byte ADC_CLK = getenv("SFR:ADCLK")
#byte ADC_RES_H = getenv("SFR:ADRESH")
#byte ADC_RES_L = getenv("SFR:ADRESL")
#byte AN_SEL_C = getenv("SFR:ANSELC")
#byte ADC_PCH = getenv("SFR:ADPCH")
void setup_adc(void) {
ADC_CON_0 = 0x84; //turn on ADC and right justify it
ADC_CON_1 = 0x00;
ADC_CON_2 = 0x00;
ADC_CON_3 = 0x00;
ADC_CLK = 0x03; //gives Fosc/8, for 1us T_AD with 8MHz clock
//setting the input channel and telling the pin to be analogue
AN_SEL_C = 0x20; //set pin C5 to analogue input, all other pins on port C to digital input
ADC_PCH = 0x15; //0x15 = 21, analogue channel 21 is pin C5
}
int16 read_adc_custom_implementation(void){
ADC_CON_0 |= 0x01; //set ADGO bit to start conversion
while(ADC_CON_0 & 0x01){} //wait till conversion is finished (indicated by hardware reset of ADGO bit)
return make16(ADC_RES_H, ADC_RES_L); //read the result registers and return them combined into a 16bit integer
}
void main(void){
setup_adc();
fprintf(HOST, "0x%x\r\n", ADC_CON_0());
while(1){
fprintf(HOST, "%ld\r\n", read_adc_custom_implementation());
}
}
|
This code has two major problems:
1. Immediately after writing 0x84 to the ADCON0 register, the value read from it is 0x80. The difference is the bit that determines if the ADC result registers are left or right justified. I can't see any reason this would happen, the assembly is writing the correct value to what I think is the correct register.
2. When I call read_adc_custom_implementation(), the code gets stuck in the while loop forever, indicating that ADGO is never reset by hardware as I would expect it to be.
If anyone can help me get either piece of code to work it would be much appreciated. I don't particularly mind which method I get working, just so long as I can read the ADC.
EDIT: using CCS Version 5.065 |
|
|
Teriyaki
Joined: 07 Sep 2006 Posts: 7 Location: Munich, Germany
|
|
Posted: Tue Dec 19, 2017 4:07 pm |
|
|
I did test some time ago the 16F18855 analogue to digital converter in legacy mode, using a MPLABXpress board. You don't need to set the adc related registers by yourself. Just use the CCS functions. I did not yet test the advanced modes and CCS functions.
MPLABXpress.c
Code: |
#include <16F18855.h>
#device *=16 // use 16 bit pointers (for 14 bit parts)
#device adc=10 // read_adc() returns 10 bits of adc
#device WRITE_EEPROM = NOINT // allow interrupts occur during write eeprom operations
#include <fuses_PIC16F18855.h>
#include <MPLABXpress.h>
#pin_select U1TX=F188TXU
#pin_select U1RX=F188RXU
#use rs232(STREAM=USB, BAUD=9600, PARITY=N, XMIT=F188TXU, RCV=F188RXU, ERRORS)
void LED_sequence(void)
{
LED2_on();
delay_ms(1000);
LED3_on();
delay_ms(1000);
LED4_on();
delay_ms(1000);
LED5_on();
delay_ms(1000);
all_LEDs_off();
delay_ms(1000);
}
void main()
{
int16 adc_value_poti, adc_value_F188ANA1;
set_analog_pins(POTI, F188ANA1); // Set POTI and F188ANA1 as analogue input pins
setup_adc_reference(VSS_VDD); // Range 0-Vdd
setup_adc(ADC_LEGACY_MODE | ADC_CLOCK_DIV_32); // adc can also operate in advanced mode! (test this, probably then adc_read has to be used instead of read_adc)
while(1)
{
set_adc_channel(4); // Poti
delay_us(100);
adc_value_poti = read_adc();
set_adc_channel(8); // F188ANA1, AN pin of J5
delay_us(100);
adc_value_F188ANA1 = read_adc();
fprintf(USB, "%lu %lu\n\r", adc_value_poti, adc_value_F188ANA1); // print adc value for poti an F188ANA1 on USB
adc_value_poti = adc_value_poti >> 6; // only keep the upper 4 bits
if (bit_test(adc_value_poti, 0) == 1) // output the upper 4 bits of the adc result on the LEDs
output_high(LED2);
else
output_low(LED2);
if (bit_test(adc_value_poti, 1) == 1)
output_high(LED3);
else
output_low(LED3);
if (bit_test(adc_value_poti, 2) == 1)
output_high(LED4);
else
output_low(LED4);
if (bit_test(adc_value_poti, 3) == 1)
output_high(LED5);
else
output_low(LED5);
while(input(S2) == 0)
{
LED_sequence();
}
}
}
|
MPLABXpress.h
Code: |
/***************************Definitions for used Controller Pins****************************/
//
// MPLABXpress Board PIC Micro
// Signal Name Pin Description
// ----------- --------- ----------------------------------------------
#define LED2 PIN_A0 // red LED D2
#define LED3 PIN_A1 // red LED D3
#define LED4 PIN_A2 // red LED D4
#define LED5 PIN_A3 // red LED D5
#define POTI PIN_A4 // potentiometer for analogue input
#define S2 PIN_A5 // switch S2 input
#define F188TXU PIN_C0 // RS232 TX
#define F188RXU PIN_C1 // RS232 RX
#define F188ANA1 PIN_B0 // analogue input AN, pin 1 of J5
/****************************Definitions for LED switching *********************************/
#define LED2_on() output_high(LED2)
#define LED3_on() output_high(LED3)
#define LED4_on() output_high(LED4)
#define LED5_on() output_high(LED5)
#define all_LEDs_off() output_low(LED2); output_low(LED3); output_low(LED4); output_low(LED5)
|
fuses_pic16f18855.h
Code: | ///////////////////////////////////////////////////////////////////////////
//// fuses_PIC16F18855.h ////
//// ////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////
// Options for external oscillator fuse:
// LP Low power oscillator < 200 kHz
// XT Crystal oscillator <= 4 MHz
// HS High speed oscillator > 4 MHz
// NOEXTOSC External Oscillator not enabled
// ECL External clock with CLKOUT(PIC18), low power
// ECM External clock with CLKOUT(PIC18), medium power
// ECH External clock with CLKOUT(PIC18), high power
#define EXTERNAL_OSCILLATOR_FUSE NOEXTOSC
// Options for Startup Oscillator fuse:
// RSTOSC_HFINTRC On Power-up clock running from HFINTRC
// RSTOSC_HFINTRC_PLL On Power-up clock running from HFINTRC with 4x PLL
// RSTOSC_EXT_PLL On Power-up clock running from External Oscillator with 4x PLL
// RSTOSC_SOSC On Power-up clock running from SOSC
// RSTOSC_LFINTRC On Power-up clock running from LFINTRC
// RSTOSC_EXT On Power-up clock running from External Oscillator
#define STARTUP_OSCILLATOR_FUSE RSTOSC_HFINTRC
// Options for Clock Output on OSC2/CLKOUT fuse:
// CLKOUT Output clock on OSC2
// NOCLKOUT I/O function on OSC2
#define CLKOUT_FUSE NOCLKOUT
// Options for Clock Switching Enable fuse:
// NOCKS Clock Switching Disabled
// CKS Clock Switching Enabled
#define CLOCK_SWITCH_ENABLE_FUSE NOCKS
// Options for Fail-Safe Clock Monitor (for external oscillator) fuse:
// NOFCMEN Fail-safe clock monitor disabled
// FCMEN Fail-safe clock monitor enabled
#define EXT_OSCILLATOR_MONITOR_FUSE NOFCMEN
// Options for Master Clear pin fuse:
// NOMCLR Master Clear pin used for I/O
// MCLR Master Clear pin enabled
#define MCLR_FUSE MCLR
// Options for Power Up Timer fuse:
// PUT Power Up Timer
// NOPUT No Power Up Timer
#define PUT_FUSE PUT
// Options for Low Power Brownout Reset Enable fuse:
// LPBOR Low-Power Brownout reset is enabled
// NOLPBOR Low-Power Brownout reset is disabled
#define LOW_POWER_BOR_ENABLE_FUSE NOLPBOR
// Options for Brownout Reset fuse:
// NOBROWNOUT No brownout reset
// BROWNOUT_SW Brownout controlled by configuration bit in special file register
// BROWNOUT_NOSL Brownout enabled during operation, disabled during SLEEP
// BROWNOUT Reset when brownout detected
#define BROWNOUT_RESET_FUSE NOBROWNOUT
// Options for Brownout Reset Voltage selection fuse:
// Note: this fuse is just important if brownout detection is activated by the BROWNOUT_RESET_FUSE
// BORV27 Brownout reset at 2.70V
// BORV24 Brownout reset at 2.45V (PIC16F18877) or 1.90V (PIC16LF18877)
#define BROWNOUT_VOLTAGE_FUSE BORV24
// Optios for Zero-cross detect circuit enable at POR fuse:
// NOZCDDIS Zero-cross detect circuit is enabled at POR
// ZCDDIS Zero-cross detect circuit is disabled at POR
#define ZCD_ENABLE_FUSE ZCDDIS
// Options for Peripheral Pin Select reconfiguration fuse:
// NOPPS1WAY Allows multiple reconfigurations of peripheral pins
// PPS1WAY Allows only one reconfiguration of peripheral pins
#define PPS_CONFIGURATION_FUSE NOPPS1WAY
// Options for Stack Overflow/Underflow Reset Enable fuse:
// NOSTVREN Stack full/underflow will not cause reset
// STVREN Stack full/underflow will cause reset
#define STACK_RESET_FUSE NOSTVREN
// Options for Debug Mode fuse:
// DEBUG Debug mode for use with ICD, pins RB6 and RB7 used by the debugger
// NODEBUG No Debug mode for ICD, pins RB6 and RB7 are general purpose I/O pins
#define DEBUG_FUSE NODEBUG
// Options for Watchdog Timer Postscale fuse
// WDT32 Watch Dog Timer uses 1:32 Postscale
// WDT64 Watch Dog Timer uses 1:64 Postscale
// WDT128 Watch Dog Timer uses 1:128 Postscale
// WDT256 Watch Dog Timer uses 1:256 Postscale
// WDT512 Watch Dog Timer uses 1:512 Postscale
// WDT1024 Watch Dog Timer uses 1:1024 Postscale
// WDT2048 Watch Dog Timer uses 1:2048 Postscale
// WDT4096 Watch Dog Timer uses 1:4096 Postscale
// WDT8192 Watch Dog Timer uses 1:8192 Postscale
// WDT16384 Watch Dog Timer uses 1:16384 Postscale
// WDT32768 Watch Dog Timer uses 1:32768 Postscale
// WDT65536 Watch Dog Timer uses 1:65536 Postscale
// WDT131072 Watch Dog Timer uses 1:131072 Postscale
// WDT262144 Watch Dog Timer uses 1:262144 Postscale
// WDT524299 Watch Dog Timer uses 1:52499 Postscale
// WDT1048576 Watch Dog Timer uses 1:1048576 Postscale
// WDT2097152 Watch Dog Timer uses 1:2097152 Postscale
// WDT4194304 Watch Dog Timer uses 1:4194304 Postscale
// WDT8388608 Watch Dog Timer uses 1:8388608 Postscale
// WDTSW Watch Dog Timer Postscale settable in software
#define WDT_POSTSCALE_FUSE WDTSW
// Options for Watch Dog Timer fuse:
// NOWDT No Watch Dog Timer
// WDT_SW No Watch Dog Timer, enabled in Software
// WDT_NOSL Watch Dog Timer, disabled during SLEEP
// WDT Watch Dog Timer
#define WDT_FUSE WDT_SW
// Options for Watch Dog Window fuse:
// WDTWIN_12% Watchdog Window is 12.5% of WDT period
// WDTWIN_25% Watchdog Window is 25% of WDT period
// WDTWIN_37% Watchdog Window is 37.5% of WDT period
// WDTWIN_50% Watchdog Window is 50% of WDT period
// WDTWIN_62% Watchdog Window is 62.5% of WDT period
// WDTWIN_75% Watchdog Window is 75% of WDT period
// WDTWIN_100% Watchdog Window is 100% of WDT period
// WDTWIN_SW Watchdog Window is settable in software
#define WDT_WINDOW_FUSE WDTWIN_100%
// Options for Watch Dog Timer Clock Source fuse:
// WDTCLK_LFINTRC WDT uses 31.0 kHz LFINTRC as clock source
// WDTCLK_HFINTRC WDT uses 31.25 kHz MFINTRC(!) as clock source
// WDTCLK_SW WDT clock source settable in software
#DEFINE WDT_CLOCK_FUSE WDTCLK_LFINTRC
// Options for Program Memory Write Protection (self-writes) fuse:
// WRT Program Memory Write Protected
// WRT_4000 Program Memory Write Protected from 0 to 0x3FFF
// WRT_200 Program Memory Write Protected from 0 to 0x1FF
// NOWRT Program memory not write protected
#define PROGRAM_MEMORY_WRITE_PROTECT_FUSE NOWRT
// Options for memory scanner (for CRC) enable fuse:
// NOSCANE Scanner module is not available for use
// SCANE Scanner module is available for use
#define SCANNER_ENABLE_FUSE NOSCANE
// Options for low voltage programming fuse:
// NOLVP No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
// LVP Low Voltage Programming on B3(PIC16) or B5(PIC18)
#define LVP_FUSE NOLVP
// Options for Code Protection from external reading/writing fuse:
// PROTECT Code protected from external reads/writes
// NOPROTECT Code not protected from external reading/writing
#define CODE_PROTECT_FUSE NOPROTECT
// Options for Data EEPROM Code Protection from external reading/writing fuse:
// CPD Data EEPROM Code Protected
// NOCPD No EE protection
#define EEPROM_PROTECT_FUSE NOCPD
// All possible fuses (in configuration words 1 to 5):
#fuses EXTERNAL_OSCILLATOR_FUSE
#fuses STARTUP_OSCILLATOR_FUSE
#fuses CLKOUT_FUSE
#fuses CLOCK_SWITCH_ENABLE_FUSE
#fuses EXT_OSCILLATOR_MONITOR_FUSE
#fuses MCLR_FUSE
#fuses PUT_FUSE
#fuses LOW_POWER_BOR_ENABLE_FUSE
#fuses BROWNOUT_RESET_FUSE
#fuses BROWNOUT_VOLTAGE_FUSE
#fuses ZCD_ENABLE_FUSE
#fuses PPS_CONFIGURATION_FUSE
#fuses STACK_RESET_FUSE
#fuses DEBUG_FUSE
#fuses WDT_POSTSCALE_FUSE
#fuses WDT_FUSE
#fuses WDT_WINDOW_FUSE
#fuses WDT_CLOCK_FUSE
#fuses PROGRAM_MEMORY_WRITE_PROTECT_FUSE
#fuses SCANNER_ENABLE_FUSE
#fuses LVP_FUSE
#fuses CODE_PROTECT_FUSE
#fuses EEPROM_PROTECT_FUSE
#use delay(clock=32MHz, restart_wdt) // using the 16 MHz HFINTOSC with 2x PLL for 32 MHz internal clock
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19496
|
|
Posted: Wed Dec 20, 2017 1:48 am |
|
|
Critical question.
Compiler version.....
Now this is critical because there is an erratum for this chip, and some of the early compilers do not have the fix for this.
If you look at the listing generated (I think you were misinterpreting something on the CCS functions), you see:
Code: |
.................... fprintf(HOST, "%ld \r\n", read_adc());
00E0: MOVLB 01
00E1: BSF ADCON0.ADGO
00E2: NOP
00E3: BTFSC ADCON0.ADGO
00E4: GOTO 0E3
00E5: MOVF ADRESH,W
00E6: MOVWF @7A
00E7: MOVF ADRESL,W
|
If your code was talking to the wrong register, it might be a very early compiler.
However the critical thing is the 'NOP'. If this is not present, then you have an compiler before the fix was added.
The chip has a fault, that you must not test the ADGO bit immediately after setting it. If you do, you will think the conversion has completed, even though it hasn't actually started.....
On current compilers the CCS functions work fine.
If you want to do a homebrew version of the CCS function:
Code: |
#BIT ADGO=getenv("BIT:ADGO") //single bit for go
#WORD ADRES=getenv("SFR:ADRESL") //16bit register pair
int16 get_adc(void) //DIY read_adc replacement
{
ADGO=TRUE; //start the ADC
delay_cycles(1); //This is the bugfix
while (ADGO)
; //wait for the ADC to finish
return ADRES;
}
|
I see you have posted an amendment giving the version 5.065. This was the very first compiler release supporting the chip. Aaargh!.... Very much 'beta'.
5.064 won't compile the code. The fix is added in 5.069.
Have just checked with 5.065, and it uses the right register, but does not have the bugfix. The code I posted will give you a fix. |
|
|
escape_key
Joined: 18 Dec 2017 Posts: 2 Location: NZ
|
|
Posted: Wed Dec 20, 2017 4:03 pm |
|
|
Thank you very much for your help. I tried your work around, and it solved some of the problems but I was still getting very strange behaviour. I updated to 5.075 and now everything works perfectly with the built in functions. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19496
|
|
Posted: Thu Dec 21, 2017 2:25 am |
|
|
That suggests some of the problem was 'other things' in the chip setup.
The replacement posted produces identical code for this function.
As the first version supporting the chip I'm not surprised!...
The newer compiler should help things in other ways. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|