CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug Reports on this forum. Send them to support@ccsinfo.com

16F18855 ADC not working w/ CCS functions or reg writes

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
escape_key



Joined: 18 Dec 2017
Posts: 2
Location: NZ

View user's profile Send private message

16F18855 ADC not working w/ CCS functions or reg writes
PostPosted: Tue Dec 19, 2017 3:27 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Dec 19, 2017 4:07 pm     Reply with quote

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: 12947

View user's profile Send private message

PostPosted: Wed Dec 20, 2017 1:48 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Dec 20, 2017 4:03 pm     Reply with quote

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: 12947

View user's profile Send private message

PostPosted: Thu Dec 21, 2017 2:25 am     Reply with quote

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. Smile
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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