[Solvedish]PIC16F54 Timer, XC8 issue

Thread Starter

Travm

Joined Aug 16, 2016
363
I've written several iterations of this and ran into some sort of road block that required a complete rewrite now. The initial problem was using too many functions and exceeding the stack, this required a complete rewrite of the program to make it more linear. I've done that, but now it seems to be giving me an error that suggests its out of memory. I've been able to narrow the problem down to what appears to be a single line of code the complier or linker doesn't like (line 161) If I comment that line out (there are similar lines also commented out, any of them will cause the error as well) the program compiles fine. You will see a lot of things commented out, this is me cutting back items to try to isolate the issue w/ the compiler, including messing around with the structures I created for some variables. I have the majority of the code confirmed working, but the timer logic does not work.

This is the complier output;
Code:
CLEAN SUCCESSFUL (total time: 4ms)
make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
make[1]: Entering directory 'D:/travm/Documents/MplabXprojects2/BensTimer.X'
make  -f nbproject/Makefile-default.mk dist/default/production/BensTimer.X.production.hex
make[2]: Entering directory 'D:/travm/Documents/MplabXprojects2/BensTimer.X'
"C:\Program Files\Microchip\xc8\v2.32\bin\xc8-cc.exe"  -mcpu=16F54 -c  -fshort-double -fshort-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -I"C:/Program Files/Microchip/xc8/v2.32/pic/include" -I"C:/Program Files/Microchip/xc8/v2.32/pic/include/c90" -I"C:/Program Files/Microchip/xc8/v2.32/pic/include/c99" -I"C:/Program Files/Microchip/xc8/v2.32/pic/include/legacy" -I"C:/Program Files/Microchip/xc8/v2.32/pic/include/proc" -mwarn=-3 -Wa,-a -DXPRJ_default=default  -msummary=-psect,-class,+mem,-hex,-file  -ginhx032 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -mc90lib   -std=c90 -gdwarf-3 -mstack=compiled:auto     -o build/default/production/newmain.p1 newmain.c
newmain.c:116:30: warning: (373) implicit signed to unsigned conversion
newmain.c:125:37: warning: (373) implicit signed to unsigned conversion
newmain.c:134:37: warning: (373) implicit signed to unsigned conversion
newmain.c:160:34: warning: (373) implicit signed to unsigned conversion
"C:\Program Files\Microchip\xc8\v2.32\bin\xc8-cc.exe"  -mcpu=16F54 -Wl,-Map=dist/default/production/BensTimer.X.production.map  -DXPRJ_default=default  -Wl,--defsym=__MPLAB_BUILD=1  -fshort-double -fshort-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -I"C:/Program Files/Microchip/xc8/v2.32/pic/include" -I"C:/Program Files/Microchip/xc8/v2.32/pic/include/c90" -I"C:/Program Files/Microchip/xc8/v2.32/pic/include/c99" -I"C:/Program Files/Microchip/xc8/v2.32/pic/include/legacy" -I"C:/Program Files/Microchip/xc8/v2.32/pic/include/proc" -mwarn=-3 -Wa,-a -msummary=-psect,-class,+mem,-hex,-file  -ginhx032 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -mc90lib -std=c90 -gdwarf-3 -mstack=compiled:auto      -Wl,--memorysummary,dist/default/production/memoryfile.xml -o dist/default/production/BensTimer.X.production.elf  build/default/production/newmain.p1     
:0:: error: (1347) can't find 0x105 words (0x105 withtotal) for psect "maintext" in class "CODE" (largest unused contiguous range 0xFF)
(908) exit status = 1
nbproject/Makefile-default.mk:137: recipe for target 'dist/default/production/BensTimer.X.production.hex' failed
make[2]: Leaving directory 'D:/travm/Documents/MplabXprojects2/BensTimer.X'
nbproject/Makefile-default.mk:90: recipe for target '.build-conf' failed
make[1]: Leaving directory 'D:/travm/Documents/MplabXprojects2/BensTimer.X'
nbproject/Makefile-impl.mk:39: recipe for target '.build-impl' failed
make[2]: *** [dist/default/production/BensTimer.X.production.hex] Error 1
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2

BUILD FAILED (exit value 2, total time: 1s)
I'm still learning how these things work so bear with me.

Its confusing because I can set that variable in other places, but when I try to set the variable within the if statement it chokes. commenting out that one line (leaving the if statement) shows I still have almost 50% of my program and 40% of my data memory available. I'm not sure how to break this down so that it doesn't depend on contiguous memory (if that is even the problem).

I'm tempted at this point to just toss the hardware I built and rebuild on a more modern chip, but at the same time I'd like to understand some of these problems at the level this old school chip is forcing me to work at (no interrupts, etc).


Code:
/*
 * File:   newmain.c
 * Author: travm
 *
 * Created on August 10, 2021, 8:53 AM
 */
#pragma config OSC = XT         // Oscillator selection bits (RC oscillator)
#pragma config WDT = OFF         // Watchdog timer enable bit (WDT enabled)
#pragma config CP = OFF         // Code protection bit (Code protection off)

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#define _XTAL_ 4000000

/*
 *
 */

/****Global Vars****/
const char (seg7set[11]) = {
    0b00001000, //0
    0b01101011, //1
    0b01000100, //2
    0b01000001, //3
    0b00100011, //4
    0b00010001, //5
    0b00010000, //6
    0b01001011, //7
    0b00000000, //8
    0b00000001, //9
    0b01111111, //clear
};
bit ansel = 0;
bit tock = 0;
bit dottock = 0;

struct {
    //unsigned ansel : 1;
    unsigned tick : 7;  // 7 bits = 128, allows rollover @ 250ms
    unsigned lasttick :7;
    unsigned seccnt : 2; // counted @ tick rollover, makes 1s count @ rollover
    //unsigned tock : 1;
    //unsigned dottock : 1;
} clocks = {0,0,0};

bit mode = 0 ;
bit buttonstate = 0;
char buttontick = 0;
/*struct {
    unsigned state : 1;
    unsigned tick : 8;
} button = {0,0};
*/
char minutes = 4 ;

struct {
    unsigned secdigit1 : 4;
    unsigned secdigit2 : 4;
} time = {0,0};

int main(void) {
/***********RUN ONCE**********/
    TRISA = 0b1010; //RA3 gets tri-stated to turn off AN1 and AN2,
                    //RA1 must be high imp. its connected straight to ground
    OPTION = 0b00000010;  //Options - Prescaler set at 1:8
    TRISB = 0b00000000;  //All PortB Pins are I/O
    char previousclk = 0 ;
    char currentclk = 0;
    char pba = 0b10000000;
    
    RA3=1;  //Anode 1 and 2 - tristate this pin to turn off
    RB7=1;  //Anode 3 start on Anode 3, or the if statement flow doesn't work
 
/*****Main Program Loop*****/   
    while(1)
    {   
//clocks
        currentclk = TMR0;
        if (previousclk > currentclk) //advance item clocks here
        {
            clocks.tick ++; // approx 2 ms tick, refer to option register to adjust
                    // 4 mhz clock / 1:4 instruction, 8 bit counter, 1:8 prescaler
            ansel = 1;
            tock = 0;
            previousclk = currentclk;
        }
        else
        {
            previousclk = currentclk;
        }
    // need dotflash routine here, must shrink size
        if (clocks.tick == 127 && tock == 0)
        {
            clocks.seccnt ++ ;
            tock = 1;
            dottock=0;
        }
        
        if ((clocks.seccnt == 1 || clocks.seccnt == 3)&& dottock == 0)
        {
            RA0 = RA0 ^ 1;
            dottock = 1;
        }
//Display       
        if (ansel==1)  // This is the display multiplexor
        {
            if (RB7==1)  //if anode RB7 is on - This services minutes
            {
                RB7=0; //clear the display
                pba=0b00000000; //set the portB anode bit
                //  --X-------; //portB anode bit off
                                //set portB to proper digit
                TRISA = 0b0010; //activate display
                RA3=1;
                PORTB = seg7set[minutes]+pba ;
            
            }
            else if (RA3==1)   //this services seconds 10's
            {
                
            //Set portB to proper digit
                RA3=0;  //clear the display _ activate display
                        //RA3 is connected to transistor,
                PORTB = seg7set[time.secdigit1]+pba ;
            }
            else if (RA3==0) //this services seconds 1's
            {
                TRISA = 0b1010;  //
                RB7=1;
                pba=0b10000000;  //set the portB anode bit
                //  --1-------;  // PortB anode bit on
                //activate display
                PORTB = seg7set[time.secdigit2]+pba ;
            }
           ansel=0;
        }
//Timer Logic
        if (clocks.seccnt == 3 && mode == 1)  // when in mode 1, count down
        {
            char placeholder = 0 ;
   /*      if (time.secdigit2 == 0)
            {
                placeholder = time.secdigit1 - 1;
                time.secdigit1 = placeholder ;
                time.secdigit2 = 9;
            }
            if (time.secdigit1 == 0)
            {
                placeholder = time.minutes - 1;
                time.minutes = placeholder ;
                time.secdigit1 = 9;
            }
            
            if ((time.secdigit1 == 0 && time.secdigit2 == 0) && time.minutes==0)
            {
                mode = 0;
            }  */
            
            placeholder = time.secdigit2 - 1 ;
        //    time.secdigit2 = placeholder ;
            mode = 0;
        }
        
//Button Logic       
        //detect a button press
        if (RA1==0 && buttonstate==0)
        {
            buttonstate=1;
            clocks.lasttick=clocks.tick;
        }
        
        //Debouncer and detect long vs short hold - this can be shrunk but is most accurate
        if (buttonstate==1)
        {
            if (clocks.lasttick==(clocks.tick+1))  // tick rollover, only counted while
            {
                if (buttontick < 8)
                {
                 buttontick++; //BUTTON TICK ADVANCES @ 122hz (250ms)
                 clocks.lasttick=clocks.tick;
                }
            }
            if ((RA1==1 && buttontick > 1) && buttontick < 8)
            {
                buttonstate=0;//Do Short Press stuff
                //time.minutes = (currentclk & 0b00000011)+ (currentclk>> 2 & 0b00000011) + 3 ; // logic for selecting initial timer setting
                
                //time.minutes=1;
                //time.secdigit1=5;
                //time.secdigit2=9;
                //if (clocks.mode==1) clocks.mode=0;
                //if (clocks.mode==0) clocks.mode=1;
                //clocks.mode = clocks.mode ^ 1 ;  //this should work, but doesnt?
                buttontick=0;
            }
            if (RA1==1 && buttontick == 8)
            {
                buttonstate=0;//Do long press stuff
                minutes=2;
                time.secdigit1=5;
                time.secdigit2=6;
                mode=0;
                buttontick=0;
            }
        
        }
    }
    
    return;
}
 

Papabravo

Joined Feb 24, 2006
21,159
I'm going to go out on a limb and speculate. Understanding what is happening is complex and it relates to the particular hardware you have chosen. It would appear that certain structures, like an "if-then-else" construct require a contiguous block of memory so that they can generate code in a particular way. The problem arises from the need to force the C-programming language on a processor for which it was NEVER intended. It is not your fault, and If you were writing this code in assembly language this would not be happening.
 

nsaspook

Joined Aug 27, 2009
13,086
That code compiles using MPLABX 5.50 and XC8 V2.31 with a few modifications.
First change the 'bit' variables to bool
add a return code from main
set XC8 linker libraries to C90
set XC8 compiler optimizations to 2
Code:
CLEAN SUCCESSFUL (total time: 50ms)
make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
make[1]: Entering directory '/xxxx/pic18_k42/mem_test_pic16.X'
make  -f nbproject/Makefile-default.mk dist/default/production/mem_test_pic16.X.production.hex
make[2]: Entering directory '/xxxx/pic18_k42/mem_test_pic16.X'
"/opt/microchip/xc8/v2.31/bin/xc8-cc"  -mcpu=16F54 -c   -mdfp="/opt/microchip/mplabx/v5.50/packs/Microchip/PIC16Fxxx_DFP/1.2.33/xc8"  -fno-short-double -fno-short-float -O2 -fasmfile -maddrqual=ignore -xassembler-with-cpp -mwarn=-3 -Wa,-a -DXPRJ_default=default  -msummary=-psect,-class,+mem,-hex,-file  -ginhx32 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -mc90lib   -std=c99 -gdwarf-3 -mstack=compiled:auto     -o build/default/production/newmain.p1 newmain.c
"/opt/microchip/xc8/v2.31/bin/xc8-cc"  -mcpu=16F54 -Wl,-Map=dist/default/production/mem_test_pic16.X.production.map  -DXPRJ_default=default  -Wl,--defsym=__MPLAB_BUILD=1   -mdfp="/opt/microchip/mplabx/v5.50/packs/Microchip/PIC16Fxxx_DFP/1.2.33/xc8"  -fno-short-double -fno-short-float -O2 -fasmfile -maddrqual=ignore -xassembler-with-cpp -mwarn=-3 -Wa,-a -msummary=-psect,-class,+mem,-hex,-file  -ginhx32 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -mc90lib -std=c99 -gdwarf-3 -mstack=compiled:auto      -Wl,--memorysummary,dist/default/production/memoryfile.xml -o dist/default/production/mem_test_pic16.X.production.elf  build/default/production/newmain.p1   

Memory Summary:
    Program space        used    F4h (   244) of   200h words   ( 47.7%)
    Data space           used    14h (    20) of    19h bytes   ( 80.0%)
    EEPROM space         None available
    Configuration bits   used     1h (     1) of     1h word    (100.0%)
    ID Location space    used     0h (     0) of     4h bytes   (  0.0%)

make[2]: Leaving directory '/xxxx/pic18_k42/mem_test_pic16.X'
make[1]: Leaving directory '/xxxx/pic18_k42/mem_test_pic16.X'

BUILD SUCCESSFUL (total time: 1s)
Loading code from /xxxx/pic18_k42/mem_test_pic16.X/dist/default/production/mem_test_pic16.X.production.hex...
Program loaded with pack,PIC16Fxxx_DFP,1.2.33,Microchip
Loading completed
 

Attachments

Thread Starter

Travm

Joined Aug 16, 2016
363
That code compiles using MPLABX 5.50 and XC8 V2.31 with a few modifications.
First change the 'bit' variables to bool
add a return code from main
set XC8 linker libraries to C90
set XC8 compiler optimizations to 2
Seems compiler optimizations to 2 was the magic switch.
also leaving the "bool" vars as bits, seems to still work, and saves me a small pile of Data memory, will try this on the actual hardware later, thanks.

Before I go chasing the manual and internet any chance I can cheat and get told by someone who just knows what exactly that did? I'm still using the free version of XC8, but what exactly does changing the optimization # in XC8 do?
 

Thread Starter

Travm

Joined Aug 16, 2016
363
I'm going to go out on a limb and speculate. Understanding what is happening is complex and it relates to the particular hardware you have chosen. It would appear that certain structures, like an "if-then-else" construct require a contiguous block of memory so that they can generate code in a particular way. The problem arises from the need to force the C-programming language on a processor for which it was NEVER intended. It is not your fault, and If you were writing this code in assembly language this would not be happening.
Yes, I fully understand the C to assembler issue with this dinosaur chip. Unfortunately I bought it, and designed the hardware under the assumption it would be similar to the enhanced midrange chips. Its not, and now I know. Hence the considering just scrapping all this and buying a cheap low end enhanced mid range chip. Interrupts and instructions designed to work well with C must be worth the extra $0.15.

Some value in learning everything the hard way though.
 

nsaspook

Joined Aug 27, 2009
13,086
Seems compiler optimizations to 2 was the magic switch. Before I go chasing the manual and internet any chance I can cheat and get told by someone who just knows what exactly that did? I'm still using the free version of XC8, but what exactly does changing the optimization # in XC8 do?
XC8 'free' has level 2 optimization enabled with the latest XC8 versions.
 

Papabravo

Joined Feb 24, 2006
21,159
In the compiler documentation you can find information on the things that the optimizer will try to do. Each of the possible optimizations belongs to a group and the groups are selected by an "optimization" level. Usually the default is level "0" which is equivalent to no optimization. Selecting a higher level means that the compiler will ever more aggressively look for ways to generate either smaller code or faster code.

Generically speaking some typical optimizations might include things like:
  1. Creating and reusing the value of an expression
  2. Replacing an expression with a constant
  3. Rearranging a loop as inline code
You should refer to the compiler documentation for specific details.

One last thing. If you can get an assembly language listing of the compiler output you can compile the code with different setting and actually see the results. It is very instructive and something every programmer should do at least once in their career.
 

Thread Starter

Travm

Joined Aug 16, 2016
363
tried tweaking the program, and loading it onto the hardware. the chip gets lost in the woods. Tomorrow i'll look more, maybe i have a logic issue as well, or maybe I can simplify some of the if statements I have going on.
When I had it almost finished and still half the memory left I thought I had it.
might also order some pic16F15243's and forget about the baseline stuff.
might even splurge for a 15244 :D
 
Last edited:

Beau Schwabe

Joined Nov 7, 2019
155
PIC16F54 ? ! .... I use that all the time, but program it in Assembly. One thing to note and it is subtle, but I have come across compilers that are not aware of the difference..... The PIC16F54 does NOT have the ADDLW or SUBLW instruction. Now as I said it is subtle, and there are ways around it, but it can bite you if you are not aware.

Here is a YouTube of the PIC16F54 in action as a VU meter ...
 

nsaspook

Joined Aug 27, 2009
13,086
tried tweaking the program, and loading it onto the hardware. the chip gets lost in the woods. Tomorrow i'll look more, maybe i have a logic issue as well, or maybe I can simplify some of the if statements I have going on.
When I had it almost finished and still half the memory left I thought I had it.
might also order some pic16F15243's and forget about the baseline stuff.
might even splurge for a 15244 :D
Most of the time a program like that is easier (once you've done it a few times) to code using a state machine than nested if conditional statement logic.

https://barrgroup.com/Embedded-Systems/How-To/State-Machines-Event-Driven-Systems
https://barrgroup.com/embedded-systems/how-to/coding-state-machines
 

Thread Starter

Travm

Joined Aug 16, 2016
363
If / when i get around to learning assembly programming that will likely be what I use the 16F54's for... Trying to learn too many new things at once and getting burned.
 

Thread Starter

Travm

Joined Aug 16, 2016
363
Most of the time a program like that is easier (once you've done it a few times) to code using a state machine than nested if conditional statement logic.

https://barrgroup.com/Embedded-Systems/How-To/State-Machines-Event-Driven-Systems
https://barrgroup.com/embedded-systems/how-to/coding-state-machines
That was more or less where I was going with the code. You'll see it doesnt have any Else statements. Its all single level if's, designed to loop through the main code quickly checking (with if statements) the status of specific bits and timers. I'm struggling most likely because i'm used to having 3 or 4 software timers, and an interrupt routine.
 

Papabravo

Joined Feb 24, 2006
21,159
There are many ways to write a trivial task dispatcher on processors with an exchange instruction where the program counter low can be the destination and the address of the exchange instruction ends up in a useful place. Some people refer to it as threaded interpreted code.
 
Top