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

Error 71 out of ROM using 16F887

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



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

Error 71 out of ROM using 16F887
PostPosted: Mon Jun 26, 2017 7:54 pm     Reply with quote

My main question is why only half of the PIC16F887 is being used?

I am getting an Error71 Out of ROM, A segment or the program is too large MAIN
I was using a PIC16F884 which has 4k of program memory (I assume this is what the compiler is referring to). I switched to a PIC16F887 with 8k of program memory but could only add a few simple statements before the same error returned. The highest address in .lst is 0FE7 which is less than 4k, and nowhere near 8k. (edited; PIC16F874 and PIC 16F877 changed to PIC 16F884 and PIC 16F887).

In this forum I read “Error 71 Out of ROM problem” and “Error 71 ... Out of ROM, A segment or the program is too lar” and have some idea of where the problems are.
Is the code in .lst shown before the statement or after it? I see a lot of code at the start followed by “#include <math.h>"
I’m not shure what constitutes a function. MAIN is a function, does it have to all fit on one page? Much of my program is in a while(1) loop, is this a function to fit on one page?

The exact error is:
Executing: "C:\Program Files (x86)\PICC\Ccsc.exe" +FM "Console PIC_1_v5.1 CMPS11.C" #__DEBUG=1 +ICD +DF +LN +T +A +M +Z +Y=9 +EA #__16F887=TRUE
*** Error 71 "Console PIC_1_v5.1 CMPS11.c" Line 772(4,5): Out of ROM, A segment or the program is too large MAIN
Seg 00004-007FF, 0169 left, need 00761
Seg 00800-00FFF, 0800 left, need 00867
Seg 01000-017FF, 0800 left, need 00867
Seg 01800-01EFF, 0700 left, need 00867
Seg 00000-00003, 0000 left, need 00867 Reserved
Seg 00004-007FF, 0169 left, need 00867

All my functions are very short and simple and are at the end. I sort of understand that 0004 to 07FF needs more ROM but how do I "shorten" blocks of less than 10 statements, often less than 5?
Are segments the same as addresses? Are they in HEX? Why is nothing above 4k used (0FFF, below)?

The .lst file shows:
CCS PCM C Compiler, Version 5.055, xxxxx 26-Jun-17 16:26

Filename: C:\Users\user\Documents\ROV_2 Console\Software Console PIC 1\Console PIC_1_v5.1 CMPS11.lst

ROM used: 3699 words (47%)
Largest free fragment is 2048
RAM used: 93 (26%) at main() level
134 (38%) worst case
Stack used: 2 locations
Stack size: 7

and the addresses are below for continuous blocks with parts of the code and comments shown where they occur. I don't know what the code is after the data.
0004 to 00FD DATA
0228 to 02AA code
035F to 042E code
04B0 to 068A code
#include <math.h>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT, BROWNOUT, MCLR, NOLVP
#use delay (clock=8000000)
00FE to 0111 code
#use rs232 (baud=9600, parity=N, bits=8, enable = pin_E2, xmit=pin_C6, rcv=pin_C7, ERRORS)
0465 to 047C code
049F to 04A8 code
#use I2C (master, SCL=PIN_C3, SDA=PIN_C4)
0112 to 015B code
0314 to 035E code
#byte porta = getenv("SFR:PORTA") to void main(void)
0800 to 0911 code in various short blocks
while (1)
0912 to 0CD4 code
// Start of data transmission
0CD5 to 0D16 code
// Start of LCD display loop
0D17 to 0FE7 code
// end of while loop
// end of main loop

// functions
047D to 04AF code
0440 to 0454 code
042F to 043F code
0455 to 0464 code
015C to 019A code
01DF to 0227 code
019B to 01DE code
02AB to 0303 code
0304 to 0313 code


Last edited by rovtech on Tue Jun 27, 2017 7:48 am; edited 1 time in total
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Jun 26, 2017 9:15 pm     Reply with quote

Quote:
All my functions are very short and simple and are at the end.

Seg 00800-00FFF, 0800 left, need 00867

The compiler thinks you have a function which takes 0x0867 words.
It could be the main() function. The ROM page size in this PIC is 0x0800.
That's the problem. Identify what function is causing this error.
Pick a likely function (one with floating point math, for example) and
start commenting out lines of code in it until it compiles. Then if it's the
one, then break that function up into at least 3 new functions.

Also, what do you mean by "at the end". Do just mean that your
functions are placed in your source file after the end of main() ?


Quote:

I switched to a PIC16F877.

#__16F887=TRUE

You've got MPLAB or some IDE set for a different PIC than you
are compiling for.
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Tue Jun 27, 2017 8:17 am     Reply with quote

Thanks PCM Programmer.
My mistake. I was using 16F874 a few years ago then switched to 16F884 and of course meant 16F887. I just had 74 in my head. The compiler would not allow such a typo. I corrected the original post with a note.
I understand what you say and saw that in the previous posts I mentioned. I think the problem is in my MAIN function which is easy to make too long. The question is how do I make it shorter? I cannot have more than one MAIN, but I could try to move some statements to separate functions which seems dumb when they would only be called once.
The only floating point is shown below. Does the compiler install the entire math package or just what it needs? This is in MAIN.
Code:
// adjust units and display battery status
  volts = bat_volts * 0.0586;                          // scale battery voltage
  amps  = (polarity_adj(bat_amps))*0.175;              // scale battery current 20 amps FS

So how would I get this out of MAIN, or any other code out of MAIN?
I typically have many blocks of code such as this which is one of the longer ones:
Code:
// LCD #1, display attitude, Pitch and Roll
  if(j==3)
  {
  text_position(0,3);                           // start fifth line
  if (bit_test (pitch,7))                       // check direction
   {
   pitch = pitch & 0x7F;                        // clear direction
   sprintf(buff, "%U deg down         ", pitch);
   }
  else
   sprintf(buff, "%U deg up           ", pitch);
   send_str(buff);
   text_position(0,4); 
  if (bit_test (roll,7))
   {
   roll = roll & 0x7F;                          // clear direction
   sprintf(buff, "%U deg Port         ", roll);
   }
  else
   sprintf(buff, "%U deg Stbd         ", roll);
   send_str(buff);                               // display text array
  }

But it is not a function, just part of MAIN which is a function.
It still does not explain why only half of the 16F887 is being used. I will take another look at the program and see if there is some way the wrong chip is specified.
I cannot find any problems, the correct chip is selected and the correct files are selected.
Yes, I place all the functions at the end of my program and have function prototypes near the beginning. The segment ranges are shown above.
Perhaps my style of writing C is wrong and I should be putting everything into functions. That would make the program hard to read.
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Tue Jun 27, 2017 8:44 am     Reply with quote

The wise counsel from PCM_P is to
break up your main();
into a number of smaller functions-

just don't get tricky and use the
#inline directive or your work will go for naught.....


Last edited by asmboy on Tue Jun 27, 2017 1:10 pm; edited 1 time in total
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Tue Jun 27, 2017 11:02 am     Reply with quote

I spoke to a C programmer and he set me straight. I am doing it all wrong and will pass on his words of wisdom for others who might be as confused as I am.
A 'C' program should be a collections of calls to functions that perform certain tasks. Think of it like building a house:

Buy the land.
Dig the basement.
Build the basement.
Install the floor.
Build the walls.
Install the roof.
Wire the house.
Plumb the house, etc.

The above would be your MAIN program and each line would be a function call.
The function "Install the roof" would consist of "draw the trusses, make the trusses, hoist them onto the walls, position them, etc.

So what I need to do is break down my MAIN program into:
Initialize the 3 LCDs.
Read switches and pots
Send commands to arm
Display arm positions, etc

I always thought subroutines (or functions) should only be written for actions that need to be called more than once. I suppose K&R say this but I just flipped thru the first few pages and it is not that clear to me. The only formal training I had was a Fortran course in the '70s but that did not leave me with a lasting impression of putting everything into subroutines. I guess having to declare the variables in each function was not as easy as declaring them all in MAIN. I knew to limit global variables. Fortunately what I have is what I thought was 'structured programming' so big blocks of my program can be easily made into functions. The light just went on!
I need to reduce my 7 pages of MAIN to less than a page.
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Sat Jul 01, 2017 8:58 am     Reply with quote

My basic problem was that my MAIN function grew too big to fit on a page of the PIC16Fxx and doubling the number of same-size pages did not solve the problem.
My next problem was a lack of understanding pointers. Although I re-read the manual, when I attempted to convert the following code to a function, I hit a brick wall because a function can only return one variable. I needed to return five.

Code:
// read switches and pack into variables, invert as required so select = true
  consw_1 = input_a()^0xc0;                                    // read port A, invert A7,A6
  consw_2 = (input_b() & 0x3f)^0x20;                      // mask out B7,B6, invert B5
  consw_3 = (input_c() & 0x27)^0x27;                     // mask out C7,C6,C4,C3. Invert C5,C2,C1,C0
  consw_4 = (input_d() & 0xef)^0x6f;                      // mask out D4, invert D6,D5,D3,D2,D1,D0

// ROV control E1,E0,A5,A4,A3,A2,A1,A0
// Bits 76543210: camera select, main thr, side thr, charge, +24v, stbd lamp, port lamp, ROV camera
  consws = ((input_e() & 0x03)<<6)|(consw_1 & 0x3f);

// base control A7,A6,B5
// Bits 765: arm left, arm right, deploy/retract
  base_ctrl = (consw_1 & 0xc0) | (consw_2 & 0x20);

// upper arm control B5,D2,D1,C1,C0
// Bits 53210: arm in, arm out, arm up, arm down
  arm_ctrl = (CONSW_2 & 0X20)|((consw_4 & 0x06)<<1)|(consw_3 & 0x03);

// fore arm control B5,B3,C2,D0,
// Bits 5320: deploy/retract, scan V/H, wrist in, wrist out
  forearm_ctrl = (consw_2 & 0x28)|(consw_3 & 0x04)|(consw_4 & 0x01);

// pod control D5,D3,B2,B1,B0
// Bits 53210: open grip, close grip, arm camera, arm lamps, arm lasers
  pod_ctrl = (consw_4 & 0x28)|(consw_2 & 0x07);


A friend explained the solution so well that I am adding his reply here. It solves many of my other problems as well. I hope this helps others.

In answer to your questions and comments, a function can only ever return a single value, typically representing the success or failure of the purpose of the function. If you need to return several quantities to a function, you simply pass in pointers to where each value is stored from the calling function and then assign the required values to each of the pointers.

For example, the calling function might look like this:
Code:

status = someFunction(&xCoord, &yCoord, &zCoord, &speed);

and someFunction would look like this:
Code:

int someFunction(*xpos, *ypos, *zpos, *speed)
{
*xpos = posx;
*ypos = posy;
*zpos = posz;
*speed = velocity;

return(1); // success, etc
}

I think the code you mentioned is an ideal candidate to be in a function. The compiler should be able to optimize the data acquisition loop if you are concerned about processing speed and efficiency. Main's role is simply to orchestrate the sequencing of the calls to the functions that are doing the work in the program.

Avoid global variables like the plague! The reason you are needing to restructure your program is to reduce the memory requirements. A pointer takes very little space, it simply holds the address from the function that instantiated the variables in question.
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