1

I am implementing the ADC for PIC18F47J53 using MPLAB v3.51.

Why is the value of the variable CH in the main function not passed correctly to the function ADC_Read with the argument channel?

unsigned int ADC_Read(unsigned char channel);
void ADC_Initialize();

#pragma config ADCSEL = BIT12                  //12 bit conversion mode enabled

#define ANCON_AiBi4         ANCON0bits.PCFG1   // Analog Input channels

int main(int argc, char** argv) 
{
    unsigned int j=0;
    unsigned char CH; 

    ADC_Initialize();

    while (1) 
    {
        CH = 1;
        j = ADC_Read(CH); //store the result of adc in j.    
    }  
    return (0);
}


void ADC_Initialize()
{
     ADCON0 = 0x01;   //00000001      ADC ON, AVss, AVdd 
     ADCON1 = 0xBD;   //10111101      R justified, Normal op, 20TAD, FOSC/16
}


/* Select the analog channel */
unsigned int ADC_Read(unsigned char channel)
{
    ADCON0 &= 0xC3;                     //11000011    Clearing the Channel Selection Bits
    ADCON0 |= channel<<2;               //Setting the required Bits
    ADCON0bits.GO_DONE = 1;
    while(ADCON0bits.GO_DONE);
    return ((ADRESH * 256) + ADRESL);   //Returns Result
}

Generated assembly code

!unsigned int ADC_Read(volatile unsigned char channel)
0xB0: MOVFF FSR2, POSTINC1
0xB2: NOP
0xB4: MOVFF FSR1, FSR2
0xB6: NOP
0xB8: SUBFSR 2, 0x2
!{
!    ADCON0 &= 0xC3;                     //11000011    Clearing the Channel Selection Bits
0xBA: MOVLW 0xC3
0xBC: ANDWF ADCON0, F, ACCESS
!    ADCON0 |= channel<<2;               //Setting the required Bits
0xBE: MOVF [0x0], W, ACCESS
0xC0: MULLW 0x4
0xC2: MOVF PROD, W, ACCESS
0xC4: IORWF ADCON0, F, ACCESS
!    ADCON0bits.GO_DONE = 1;
0xC6: BSF ADCON0, 1, ACCESS
!    while(ADCON0bits.GO_DONE);
0xC8: BTFSC ADCON0, 1, ACCESS
0xCA: BRA 0xC8
!    return ((ADRESH * 256) + ADRESL);   //Returns Result
0xCC: MOVF ADRESH, W, ACCESS
0xCE: MOVLB 0x0
0xD0: MOVWF 0xC, BANKED
0xD2: CLRF 0xD, BANKED
0xD4: MOVFF 0xC, 0xD
0xD6: NOP
0xD8: MOVLB 0x0
0xDA: CLRF 0xC, BANKED
0xDC: MOVF ADRES, W, ACCESS
0xDE: MOVLB 0x0
0xE0: ADDWF 0xC, W, BANKED
0xE2: MOVLB 0x0
0xE4: MOVWF __tmp_0, BANKED
0xE6: MOVLW 0x0
0xE8: MOVLB 0x0
0xEA: ADDWFC 0xD, W, BANKED
0xEC: MOVLB 0x0
0xEE: MOVWF 0xB, BANKED
0xF0: MOVFF __tmp_0, PROD
0xF2: NOP
0xF4: MOVFF 0xB, PRODH
0xF6: NOP
0xF8: BRA 0xFA
!}
0xFA: SUBFSR 1, 0x1
0xFC: MOVFF INDF1, FSR2
0xFE: NOP
0x100: RETURN 0

Configuration Bits source code

// PIC18F47J53 Configuration Bit Settings

// 'C' source line config statements

#include <p18F47J53.h>

// CONFIG1L
#pragma config WDTEN = ON       // Watchdog Timer (Enabled)
#pragma config PLLDIV = 1       // PLL Prescaler Selection (No prescale (4 MHz oscillator input drives PLL directly))
#pragma config CFGPLLEN = OFF   // PLL Enable Configuration Bit (PLL Disabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset (Enabled)
#pragma config XINST = ON       // Extended Instruction Set (Enabled)

// CONFIG1H
#pragma config CPUDIV = OSC1    // CPU System Clock Postscaler (No CPU system clock divide)
#pragma config CP0 = OFF        // Code Protect (Program memory is not code-protected)

// CONFIG2L
#pragma config OSC = INTOSC     // Oscillator (INTOSC)
#pragma config SOSCSEL = HIGH   // T1OSC/SOSC Power Selection Bits (High Power T1OSC/SOSC circuit selected)
#pragma config CLKOEC = ON      // EC Clock Out Enable Bit  (CLKO output enabled on the RA6 pin)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor (Enabled)
#pragma config IESO = ON        // Internal External Oscillator Switch Over Mode (Enabled)

// CONFIG2H
#pragma config WDTPS = 32768    // Watchdog Postscaler (1:32768)

// CONFIG3L
#pragma config DSWDTOSC = INTOSCREF// DSWDT Clock Select (DSWDT uses INTRC)
#pragma config RTCOSC = T1OSCREF// RTCC Clock Select (RTCC uses T1OSC/T1CKI)
#pragma config DSBOREN = ON     // Deep Sleep BOR (Enabled)
#pragma config DSWDTEN = ON     // Deep Sleep Watchdog Timer (Enabled)
#pragma config DSWDTPS = G2     // Deep Sleep Watchdog Postscaler (1:2,147,483,648 (25.7 days))

// CONFIG3H
#pragma config IOL1WAY = ON     // IOLOCK One-Way Set Enable bit (The IOLOCK bit (PPSCON<0>) can be set once)
#pragma config ADCSEL = BIT12   // ADC 10 or 12 Bit Select (12 - Bit ADC Enabled)
#pragma config MSSP7B_EN = MSK7 // MSSP address masking (7 Bit address masking mode)

// CONFIG4L
#pragma config WPFP = PAGE_127  // Write/Erase Protect Page Start/End Location (Write Protect Program Flash Page 127)
#pragma config WPCFG = OFF      // Write/Erase Protect Configuration Region  (Configuration Words page not erase/write-protected)

// CONFIG4H
#pragma config WPDIS = OFF      // Write Protect Disable bit (WPFP<6:0>/WPEND region ignored)
#pragma config WPEND = PAGE_WPFP// Write/Erase Protect Region Select bit (valid when WPDIS = 0) (Pages WPFP<6:0> through Configuration Words erase/write protected)
#pragma config LS48MHZ = SYS48X8// Low Speed USB mode with 48 MHz system clock bit (System clock at 48 MHz USB CLKEN divide-by is set to 8)

MPLAB variables view

ADCON0 and channel unexpected values

MFF
  • 21
  • 7
  • Why do you think it is not "forwarded" ? But I see a potential problem in not having the function prototype before `main`. – Eugene Sh. Jun 26 '17 at 20:57
  • I included the declaration in a separate .h file unsigned int ADC_Read(unsigned char channel); – MFF Jun 26 '17 at 21:01
  • I check the variables in the MPLAB it is always showing "channel" with a different value than "CH". So when added to the ADCON0 register to update the channel fields, a wrong channel is being processed. – MFF Jun 26 '17 at 21:05
  • Eugene, I added a screenshot to the original post. Thanks – MFF Jun 26 '17 at 21:14
  • Better examine the value of ADCON0 after written. This variable can be optimized away, so you see some junk instead. – Eugene Sh. Jun 26 '17 at 21:17
  • Checked the ADCON0 after written, it is shown unexpected value. Channel-1 (0b0001) is targeted and forwarded by the variable "CH" in the main, so the variable "channel" in the ADC_Read should hold the value 0x01, and ADCON0 should show 0x05. I uploaded a second screenshot after ADCON0 being written. – MFF Jun 26 '17 at 21:41
  • Weird. The code looks fine. Are you sure it is the same code you are running? I see something different on the screenshots. – Eugene Sh. Jun 26 '17 at 21:46
  • Yes Eugene, it is the same code. I just cleaned it up for posting here. – MFF Jun 26 '17 at 21:56
  • Does `CH` have a special meaning or macro definition? Try `ch`. – chux - Reinstate Monica Jun 27 '17 at 00:12
  • I thought the order of access of `ADRESH, ADRESL` was important. `(ADRESH * 256) + ADRESL` does not control that order. – chux - Reinstate Monica Jun 27 '17 at 00:13
  • Do you have compiler optimization enabled, so that the behaviour you see is not conform to your code lines? – Andre Kampling Jun 27 '17 at 06:00
  • Eugene, Chux, and Andre, thanks for the response, I changed the variable name of CH to ch and changed the order of access of ADRESH and ADRESL, but the problem still exist, however they are not related to assigning the value to the variable "channel". I also disabled the optimizer and the same problem still existed. Any further suggestions ? – MFF Jun 27 '17 at 15:11
  • Please try making `CH` **volatile** by: `volatile unsigned char CH;`. Do the same with the function argument: `volatile unsigned char channel`. If that does not work and as I have no more ideas, please also give us the generated assembler code (edit your question). Then we can see what's really going on there. – Andre Kampling Jun 27 '17 at 17:33
  • Andre, please find Generated Assembly Code added to the question. – MFF Jun 27 '17 at 19:42
  • Also when used `volatile` the same problem existed. – MFF Jun 27 '17 at 19:50
  • Wow really strange problem... Could you please add the assembly code of the call also, if I get it right you now add the code of the function only. – Andre Kampling Jun 27 '17 at 20:02
  • Also I have another question: What does the debugger says about `CH` before it will be passed? – Andre Kampling Jun 27 '17 at 20:07
  • The `CH` always has the right value, the problem is that the value is not passed correctly to the `channel` argument in the `unsigned int ADC_Read(unsigned char channel)` function. I added the assembly code to the question. – MFF Jun 27 '17 at 20:13
  • But it is not all code, isn't it? – Andre Kampling Jun 27 '17 at 20:17
  • Andre, the call to the function is also included, starting with `!{` and ending with `!}` . – MFF Jun 27 '17 at 20:33
  • @MFF: Just a random guess but did you perhaps forget to set the extended instruction set configuration bit (XINST)? The channel parameter here is read via a stack indexed `MOVF` instruction which would go off and index into thin air within the access bank with `XINST = 0`. – doynax Jun 27 '17 at 20:54
  • In project properties which compiler toolchain is set? – GJ. Jun 27 '17 at 21:17
  • @doynax: The extended instruction set is enabled using the Project Properties wizard. – MFF Jun 27 '17 at 21:18
  • @GJ: the Compiler Toolchains is C18 (v3.47) – MFF Jun 27 '17 at 21:19
  • Did you check the state of XINST bit? – GJ. Jun 27 '17 at 21:19
  • 1
    @MFF: Try defining the configuration bit in the code as well with `#pragma config XINST = ON`. The compiler option may simply inform the code generator of the intended target without letting the MCU in on it, though I'm far from certain. If nothing else verify the underlying configuration bit written to FLASH afterwards in the debugger or HEX image. – doynax Jun 27 '17 at 21:23
  • Check also the linker script how the Stack is initiated! – GJ. Jun 27 '17 at 21:32
  • @GJ, @doynax: It is working now but after disabling the extended instruction set and forcing it in the code using `#pragma config XINST = OFF`. I also changed the channel variables into `volatile`. But shouldn't I enable the extended instruction ? in that case the code will not work ! – MFF Jun 27 '17 at 21:40
  • Seems like HI-TECH C does not support the extended instruction set. XC8 is based on HI-TECH C, so I'd guess it is not currently supported either. But I am using C18 compiler which is supposed to support the extended instructions set ? am I missing something ? – MFF Jun 27 '17 at 21:47
  • 1
    @MFF: I'm still not quite following you I'm afraid. The `C18` compiler does support the extended instruction set and should preferably be used with it. Alas this an obsolete and generally awful C compiler, though beware that the non-optimizing `XC8` compiler may not be much of an improvement. In any event what happens when you enable the compiler option and set the `#pragma config` to on, and does the XINST bit read back as on when you examine it after programming? – doynax Jun 27 '17 at 21:56
  • @doynax: I am still using the `C18` compiler, however when I enable the extended instruction set using the Project Properties wizard and by enforcing it in the code using `#pragma config XINST = ON` the code is not working properly as stated in the question above, but when I disabled the extended instruction set, the variable is being forwarded to the `unsigned int ADC_Read(unsigned char channel)` correctly (still using the `C18`). – MFF Jun 27 '17 at 22:06
  • Fair enough. You may want to take @GJ's suggestion of verifying the linker script and resulting map file to watch out for collisions, though by the looks of the screenshot the variable address is well within RAM. Ideally single-step through the machine code from the push at the call site to the read-back to see if it gets trashed along the way. Beyond that I'd still suggest that you double-check the FLASH memory image at CONFIG1L/0x1FFF8 in FLASH (bit 6) by readback in the debugger and HEX image to make sure that the option isn't being lost along the way, say some debugger shenanigans. – doynax Jun 27 '17 at 22:18
  • Thanks guys. Will go through the last suggestion and get back to you with the result. – MFF Jun 27 '17 at 22:21
  • I added the screenshot of the Configuration Bits to the Question, the XINST bits seems fine and is ON for the Extended Instruction Set. Still the problem exist with the Extended Instruction Set enabled, the code is working fine with the Extend Instruction Set disabled. However, I should use the Extended Instruction Set or the program will fail later, as the bootloader has been compiled for extended instruction set and the loaded code has to have the same setting. – MFF Jun 28 '17 at 22:03
  • @MFF: You seem to have forgotten to attach the screenshot of the configuration bits. Nevertheless, perhaps it is time to step through the entire call sequence from machine instruction to machine instruction, monitoring the memory location storing the result (0xA14 in the screenshot). Somewhere between the initial push and `MOVF [0],W,A` and the fetch something is going lost. Also, the complete generated code for the application and final compiled HEX image would be helpful. – doynax Jun 29 '17 at 10:50
  • @doynax: I was allowed only to attach two files. However I added the Configuration Bits script to the the question content. – MFF Jun 29 '17 at 14:59
  • Why is there a banking switch in the beginning ? 0x3BC: MOVFF FSR2, POSTINC1 0x3BE: NOP 0x3C0: MOVFF FSR1, FSR2 – MFF Jun 29 '17 at 19:02
  • So what I found is that the correct value is stored at address `0xB14`. However, the variable `channel` points to a different address `0xA14`. I am not able to attach the screenshots since I am allowed to attach two pictures only. – MFF Jun 29 '17 at 20:40
  • 1
    @MFF: That's not bank switching. The compiler is creating a stack frame from FSR1 (the virtual stack pointer) into FSR2 (the base pointer) by pushing the previous base pointer onto the stack and assigning the new stack frame as the base pointer. Anyway, the single-bank stack model used here only bothers with the lower 8-bits and assumes fixed FSR1H/FSR2H at the stack page. Yet for whatever reason they have diverged. Perhaps there is an issue in the linker script (>256 bytes of stack?) or incorrect CRT start-up code. Please post the map/linker file and try tracing FSR1H/FSR2H for the divergence – doynax Jun 29 '17 at 22:12
  • How to enable the "Integer Promotions", it is disabled by default in C18, I tried to find out but did not find a clear description on how to do so ? – MFF Jul 05 '17 at 17:04
  • Ok, I found and enabled it in Project Properties>>C18>>MCC. However the problem still exist. Now I am using a global variable to pass the channel selection to the ADC function. – MFF Jul 05 '17 at 20:00
  • @doynax It was a stack error. However, it is working now after I enabled the “multi-bank stack model”, Still not clear why did the single-stack model did not work ? it is working for the same controller but with different code.Thanks guys. – MFF Jul 05 '17 at 23:01
  • @MFF: The small stack calling convention dictates that the high-byte of the `FSR1` pointer register stays in phase with `FSR2`, allowing the use of 8-bit arithmetic to improve performance. However as I mentioned above in your case `FSR1H`/`FSR2H` have drifter out of phase. Enabling multi-bank mode lifts the assumption by employing full 16-bit code, however depending on the nature of the root cause you may still be trashing memory and creating subtle bugs. My advice is to step through the early initialization to discover the mismatch origin and review the map/linker scripts and startup code. – doynax Jul 06 '17 at 03:33
  • 1
    Incidentally this is a significant limitation of complex architectures with uncomfortable support for the target language. A good compiler and optimizer may hide the warts and complexities when everything is working properly, however wherever it breaks down the programmer still needs low-level knowledge of the system to troubleshoot it. – doynax Jul 06 '17 at 03:37

1 Answers1

1

It was a stack error. However, it is working fine now after I enabled the “multi-bank stack model”. Thanks guys

MFF
  • 21
  • 7