|
|
View previous topic :: View next topic |
Author |
Message |
EdWaugh
Joined: 07 Dec 2004 Posts: 127 Location: Southampton, UK
|
Updating firmware over RS232 |
Posted: Fri Jun 05, 2009 7:45 am |
|
|
Hi all,
I'm trying to sort out a kind of a bootloader based on the CCS supplied loader.c but I'm having a problem. What I want to do is not have a bootloader as such but allow the user to select an option through the menu system of the main application where they can choose to update firmware.
I am using loader.c much as it is supplied and call load_program() and reset_cpu after the write has been done.
This works fine when I program the application with itself or with just a minor change like a value in a string. However, if I do a big change like add or remove some large library function it gets stuck in write_program_memory() after a short while. The point it gets stuck is fixed the same on repeated attempts and I am able to write past that point normally. I don't think it is a comms problem, I have no buffer overflows or anything.
I suspect I may be having some kind of writing over executing code problem but I don't think that should be the case as loader.c will not attempt to write over the load_program() functions. I have included my modified version below which basically just has changes to make it easier for me to read/understand and debug.
any thoughts would be most welcome.
cheers
ed
Code: |
#pragma SEPARATE
unsigned int atoi_b16(char *s);
#pragma ORG LOADER_ADDR+10, LOADER_END default
void real_load_program (void)
{
int1 do_ACKLOD, done=FALSE;
int8 checksum, line_type;
int16 l_addr,h_addr=0;
int32 addr;
/*#if getenv("FLASH_ERASE_SIZE")>2
int32 next_addr;
#endif*/
int8 dataidx, i, count;
int8 data[32];
while (!done) // Loop until the entire program is downloaded
{
buffidx = 0; // Read into the buffer until 0x0D ('\r') is received or the buffer is full
do
{
buffer[buffidx] = fgetc(LOADER_STRM);
} while ( (buffer[buffidx++] != 0x0D) && (buffidx <= BUFFER_LEN_LOD) );
fputc(XOFF, LOADER_STRM); // Suspend sender
//fprintf(rda2_strm,"XF");
output_low(LED1);
do_ACKLOD = TRUE;
// Only process data blocks that start with ':'
if (buffer[0] == ':')
{
count = atoi_b16 (&buffer[1]); // Get the number of bytes from the buffer
//fprintf(rda2_strm,"Got line, %u\r", count);
// Get the lower 16 bits of address
l_addr = make16(atoi_b16(&buffer[3]),atoi_b16(&buffer[5]));
line_type = atoi_b16 (&buffer[7]);
// At the first time through h_addr is zero as we are assuming the high bytes of the addr are zero
// until we get a type 4 command
addr = make32(h_addr,l_addr);
// If the line type is 1, then data is done being sent
if (line_type == 1)
{
done = TRUE;
fprintf(rda2_strm,"Done\r");
}
else if ((addr < LOADER_ADDR || addr > LOADER_END) && addr < 0x300000)
{
checksum = 0; // Sum the bytes to find the check sum value
for (i=1; i<(buffidx-3); i+=2)
{
checksum += atoi_b16 (&buffer[i]);
}
checksum = 0xFF - checksum + 1;
if (checksum != atoi_b16 (&buffer[buffidx-3]))
{
do_ACKLOD = FALSE;
fprintf(rda2_strm,"CS Fail\r");
}
else
{
if (line_type == 0)
{
// Loops through all of the data and stores it in data
// The last 2 bytes are the check sum, hence buffidx-3
for (i = 9,dataidx=0; i < buffidx-3; i += 2)
{
data[dataidx++]=atoi_b16(&buffer[i]);
}
/* #if (getenv("FLASH_ERASE_SIZE") > getenv("FLASH_WRITE_SIZE"))
fprintf(rda2_strm,"Erase\r");
if ((addr!=next_addr)&&(addr&(getenv("FLASH_ERASE_SIZE")/2-1)!=0))
{
erase_program_eeprom(addr);
}
next_addr = addr + 1;
#endif*/
fprintf(rda2_strm,"Write 0x%lx, %u, ", addr, count);
write_program_memory(addr, data, count);
fprintf(rda2_strm,"DN\r");
}
else if (line_type == 4)
{
h_addr = make16(atoi_b16(&buffer[9]), atoi_b16(&buffer[11]));
fprintf(rda2_strm,"HAddr 0x%x\r", h_addr);
}
}
}
}
if (do_ACKLOD)
{
fputc(ACKLOD, LOADER_STRM);
}
fputc(XON, LOADER_STRM);
//fprintf(rda2_strm,"XO");
output_high(LED1);
}
fputc(ACKLOD, LOADER_STRM);
fputc(XON, LOADER_STRM);
//fprintf(rda2_strm,"XO");
// After writing a new program we always want to reset the CPU
fprintf(rda2_strm,"Reset\r");
reset_cpu();
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jun 05, 2009 1:16 pm |
|
|
1. Verify that your loader program is receiving and interpreting the
incoming data correctly.
Add printf statements to display the address and the bytes that come into
the loader routine. Comment out the code that erases and writes to
program memory. This will capture the entire incoming stream of
addresses and data. Copy and paste this information from the terminal
window, into MS Word and save it and print it. Now compare the
addresses and data that were received, to the original HEX file that
you sent to the PIC.
2. Determine the address at which it locks up.
Re-enable the erase and write program memory routines. Run the
above test again. You said it locks up at one point. Note the address.
Look at the .LST file for your original program (that's in the PIC). Look
at the code at the address where the problem occurs. This may
give you a clue.
You may be over-writing CCS library routines that are called by the
loader routine. The library code (putc, getc, delays, etc.) is not
guaranteed to be placed at the same ROM address by the compiler every
time you compile your program. So the loader may be calling library
routines that no longer exist at the addresses that its calling. One solution
is to make versions of the library code that are only for the loader
routine, so they can't be over-written. |
|
|
EdWaugh
Joined: 07 Dec 2004 Posts: 127 Location: Southampton, UK
|
cheers |
Posted: Sat Jun 06, 2009 4:52 am |
|
|
thanks for the great answer, I hadn't considered the routines I am linking in. I will give that all a try.
cheers
ed |
|
|
EdWaugh
Joined: 07 Dec 2004 Posts: 127 Location: Southampton, UK
|
Making a second copy of library routines |
Posted: Mon Jun 08, 2009 10:19 am |
|
|
I had more of a think about it and your suggestion about library functions sounds like the problem to me. It's not clear to me how to do as you suggest however, is there a trick to it?
I have a memory of an old post I think by Ttelmah where the math routines for use in an interrupt are duplicated by putting a #org statement around that interrupt, my memory is tho that the math routine copies are not then stored inside the #org just two copies are created (otherwise wouldn't what I have already work as I am calling the functions inside a #org).
I could also use multiple #use functions for fputc (I will eliminate fprintf as two copies would be silly). I guess if I just create a new stream this shouldn't affect my other code.
I feel like I should be able to figure out the solution but it is eluding me, sorry if I've missed something obvious!
cheers
ed |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Jun 08, 2009 10:38 am |
|
|
I created my own versions of the CCS library routines. Here is my
version of putc(). This code will reside in the "bootloader" area along
with the rest of the bootloader code. It can't be overwritten by new
firmware.
Code: | void putbyte(char c)
{
while(!TXIF_BIT);
TXREG_REG = c;
} |
Here is my version of getc().
Code: | char getbyte(void)
{
long timer;
timer = 0xffff; // A count of 65,535 gives timeout of approx. 0.65 seconds
// This loop takes 10 usec
while((char)(timer >> 8)) // Only check the MSB, for speed
{
if(RCIF_BIT)
{
return(RCREG_REG);
}
timer--;
}
return(0);
} |
This is what I mean when I said to create your own library routines.
There might be some other way to do it. I worked on this project
back in 2001. |
|
|
EdWaugh
Joined: 07 Dec 2004 Posts: 127 Location: Southampton, UK
|
Got it working |
Posted: Mon Jun 08, 2009 10:43 am |
|
|
After making that last post i had a go with just creating a new RS232 stream inside the #org directive and once I remembered to disable interrupts in the second application I was testing it all started to work very nicely. Tiny app loads giant app and then giant app loads tiny app.
I guess the CCS compiler must already be making second copies of those functions and placing them in the #org region for me.
I will tidy up the code tomorrow and post it here and in the completed code forum in case anyone finds it useful.
thanks
ed |
|
|
EdWaugh
Joined: 07 Dec 2004 Posts: 127 Location: Southampton, UK
|
Finally |
Posted: Tue Jun 09, 2009 7:55 am |
|
|
Hi all,
I have tidyed up and made a few modifications and posted my final code here:
http://www.ccsinfo.com/forum/viewtopic.php?p=116730#116730
It seems to work fine, I hand checked the .lst file and have confirmed there are no CALL, RCALL or GOTO statements that point outside of the #org defined area. Hopefully this means it is all self contained.
I have left it very big and very verbose as I think it makes it a nice demo for people trying to figure this out. The CCS built in functions all seem to get inlined, like:
make8()
make16()
make32()
reset_cpu()
write_program_memory()
read_program_memory()
C library functions like strncmp however had to be copied into the code. Putting the #use RS232 statement inside the #org seems to mean all serial port statements are also included in this code.
I am a bit concerned that a change in optimisation level might break this, so I may have a play with that now. Thanks to PCM Programmer for the suggestions.
Cheers
ed |
|
|
EdWaugh
Joined: 07 Dec 2004 Posts: 127 Location: Southampton, UK
|
Optimisation levels |
Posted: Tue Jun 09, 2009 8:04 am |
|
|
Hi again,
I just tried opt 0 and opt 10 and apart from getting a bit fatter the loader worked fine and when I checked the lst there were no references outside the #org.
I suspect this is because the built in functions aren't real C functions but just bits of asm that are pasted in at compile time. As long as this doesn't change there should be no problem.
ed |
|
|
jesconsa
Joined: 21 Jun 2010 Posts: 3
|
|
Posted: Mon Jun 21, 2010 6:07 am |
|
|
Try to put WRT on the FUSES!!!!!!!!!!!.........It works for me!!!!!!!!!! Ciao. Jesus |
|
|
|
|
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
|