|
|
View previous topic :: View next topic |
Author |
Message |
kuriken
Joined: 21 Oct 2006 Posts: 19
|
Bootloading Using Software UART |
Posted: Sat Oct 21, 2006 2:18 pm |
|
|
Hello I'm relatively new to imbedded programming and am working on a project to impliment bootloading to our current device.
I use a PIC18f4620 and configured it for a software UART which my laptop PC can talk to through the USB chip (FT232RL).
I've compiled the EX_load.c from the example files with the changes in #use rs232 and <18F4620.h> to match my hardware. However I'm not having any luck in bootloading. I'm starting to wonder if the UART needs to be hardware...
Please help. |
|
|
jma_1
Joined: 08 Feb 2005 Posts: 147 Location: Wisconsin
|
|
Posted: Sun Oct 22, 2006 9:41 am |
|
|
Greetings,
The bootloader is two parts. The first being a hex file which is loaded into the pic chip with a device programmer or icd. The second part being your application code. Did you load the bootloader into the pic? What triggers your micro entering boot mode? If memory serves correctly, the CCS example specifically examines a port pin for some state before entering boot mode (then waits to receive your code) . Is this condition satisfied in your code? Did you include the bootloader header file in your code when it was compiled? Also, please note if you compile the source code with the bootloader h file, and load the code with a device programmer, there is a good chance your code will not operate properly.
Try to verify your device actually enters boot mode. Verify if boot mode is entered, are the records you send being Acked? Are you using siow.exe to load software? If not and I remember correctly (examples not loaded on the computer I'm typing this email from) I thought XON/XOFF is needed in your serial port setup protocol. Does your serial port work correctly on the pic & pc ends during normal operation without the bootloader?
Hopefully this at least gives you more things to look at to find the
problem.
Cheers,
JMA |
|
|
Guest
|
|
Posted: Mon Oct 23, 2006 2:06 pm |
|
|
JMA
Thanks for replying. I'm using the EX_loader.c example and not the EX_bootloader.c. Looks like the EX_bootloader.c is meant to run with an application. That suits me better, I will give it a try today.
Below is the code I use. I load this code into the PIC with a ICD-U40. But you have said.
"Also, please note if you compile the source code with the bootloader h file, and load the code with a device programmer, there is a good chance your code will not operate properly."
How else do I load the code? Could you give me details on how to do this?
As for debugging the code, I know I'm getting chracters through to the PC since I am able to send the "L" across to confirm the start of loading code. The loadprogram() function is hard to debugg since it doesn't like to have printfs in the code. I'm assuming this is because its written to some special memory in the PIC that is restricted in size.
Let me know if you have more suggestions.
Code: | #define defined(__PCH__)
#if defined(__PCH__)
#include <18F4620.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#use rs232(DEBUGGER, stream=MONITOR, ERRORS)
#use rs232(baud=115200,parity=N,xmit=PIN_D6,rcv=PIN_B0,bits=8,stream=USB)
#endif
#include "loader.c"
void main()
{
// Clears out the rs232 buffer
delay_ms (1000);
while (kbhit(USB))
{
fgetc(USB);
}
// Loop until 'L' is pressed
do
{
fprintf(MONITOR, "\r\nSoftware Version A!\r\n");
fprintf(MONITOR, "Press L to download new software.\r\n");
} while (fgetc(USB) != 'L');
// Let the user know it is ready to accept a download
fprintf(MONITOR, "\r\nWaiting for download...\r\n");
// Load the program
load_program();
} |
|
|
|
jma_1
Joined: 08 Feb 2005 Posts: 147 Location: Wisconsin
|
|
Posted: Mon Oct 23, 2006 7:30 pm |
|
|
Greetings,
What I meant was the application hex file (not the bootloader) if compiled for use with the bootloader will not work if you program it into the pic with the ICD (assuming you are using interrupt routines) instead of serially.
I originally wanted my bootloader to work in the manner in which your code is setup. However, the requirements of the final application could not have a long delay in the startup (delayms(1000)).
I would remove all of the different streams and focus on your USB communication (virtual COM port; still include ERRORS). I have never used the 'monitor' for debugging, but I would try to make the code as bare as possible to limit any potential problems. I might also remove the do while loop and just wait for the correct character (repeatedly prints out software version, etc; print directly to the USB port for debugging).
Debugging->add a putc( ) into the loader application. Signal a correct record and checksum with one special character and a different character if the checksum does not match. The putc() function should take up substantially less space than your printf() and still fit in your defined boot block. If not, increase the boot block size. You might also look at the loader.c internals as to the programming protocol followed in the example.
Make sure the serial port setup with your #use RS233 agrees between the loader and your application (going from memory here -> not at my normal computer).
What are you using to load the software? I have had good success with the 'siow' application included with the compiler. I would strongly avoid Hyperterminal.
If none of these suggestions lead you in the right direction, start again with a simple test. Use the provided example and get it to work (single input pin in a certain state on powerup; send the code using Siow). Once the example is verified working correctly, then try and alter it for needs.
Hopefully I am not leading you in the wrong direction. I do not have any of the CCS examples available to me until later in the week when I am back at my normal computer.
Cheers,
JMA |
|
|
kuriken
Joined: 21 Oct 2006 Posts: 19
|
Some Progress and More Questions |
Posted: Wed Oct 25, 2006 1:55 pm |
|
|
JMA
Thanks for the prior suggestions. I got something working now. However I cannot understanding why its working, it would be great if you could take a look at what I have now.
First I'm listing some of the changes I made to get EX_load.c to work .
////////////////////////////////////////////////////////////////////////////////////
One of my problems was related to the fact that I was using fputc() instead of putchar() in the load_program(). Before the #use232 declaration was located in lower memory so I was over writing it as I was updating the ROM then the program would stop debugging. I redefined the #use RS232 line just before the load_program() function.
Quote: |
What are you using to load the software?
|
I'm writing my own C# Windows Application which reads a HEX file as a binary and sends it to the PIC through a FL232 chip (USB to RS232 translator). The major mistake I found here was that I was not getting rid of the line feeds, which the load_program() function does not handle. Also I changed my code so I wait for the Acklog before sending the next line of the HEX file.
Finally I changed the ROM address of the RESET vector to LOADER_END+1 for the HEX file I was trying to load. If I put the RESET vector anywhere else it simply didn't work. I found this out by mistake by accidentally including the bootloader.h file in the HEX file I was loading. I know this does not make sense but by doing so I am able to load software onto my PIC. If I don't do it the PIC resets but does not function properly. How could this be? Do you have any ideas?
So far, my understanding is that the load_program() function lives in high ROM at address LOAD_ADD~LOAD_END. Then in load_program() the new program is written to a lower memory starting at ROM address 0x00.
It would be great if you could explain to me what happens after reset_cpu() is called in load_program(). Does it jump to the new reset vector? or the old reset vector? If it does jump to the new reset vector why doesn't putting the reset vector at 0x00 ROM work? I could not figure this out by reading the datasheets.
Below is the code in entirety.
First the ex_loader.c and loader.c code. I have deleted some of the #if that did not apply and made necessary changes to accomidate my PIC18f4620.
ex_loader.c
Code: |
#define defined(__PCH__)
#if defined(__PCH__)
#include <18F4620.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#use rs232(DEBUGGER, stream=MONITOR, ERRORS)
#use rs232(baud=57600,parity=N,xmit=PIN_D6,rcv=PIN_B0,bits=8,stream=USB)
#endif
#include "loader_ken.c"
void main()
{
// Clears out the rs232 buffer
delay_ms (1000);
while (kbhit(USB))
{
fgetc(USB);
}
// Loop until 'L' is pressed
do
{
fprintf(MONITOR, "\r\nSoftware Version A!\r\n");
fprintf(MONITOR, "Press L to download new software.\r\n");
} while (fgetc(USB) != 'L');
// Let the user know it is ready to accept a download
fprintf(MONITOR, "\r\nWaiting for download...\r\n");
// Load the program
load_program();
}
|
loader_ken.c
Code: |
#ifndef LOADER_END
#define LOADER_END getenv("PROGRAM_MEMORY")-1
#define LOADER_SIZE 0x4FF //Increased size for fprintfs
#endif
#define LOADER_ADDR LOADER_END-LOADER_SIZE
#define BUFFER_LEN_LOD 64
int buffidx;
char buffer[BUFFER_LEN_LOD];
#define ACKLOD 0x06
#define XON 0x11
#define XOFF 0x13
#SEPARATE
unsigned int atoi_b16(char *s);
#ORG LOADER_ADDR+10, LOADER_END default
//redefined here so the use declarations are stored in higher ROM
#use rs232(DEBUGGER, stream=MONITOR, ERRORS)
#use rs232(baud=57600,parity=N,xmit=PIN_D6,rcv=PIN_B0,bits=8,stream=USB)
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(USB);
} while ( (buffer[buffidx++] != 0x0D) && (buffidx <= BUFFER_LEN_LOD) );
fputc(XOFF,USB); // Suspend sender
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
// Get the lower 16 bits of address
l_addr = make16(atoi_b16(&buffer[3]),atoi_b16(&buffer[5]));
line_type = atoi_b16(&buffer[7]);
addr = make32(h_addr,l_addr);
// If the line type is 1, then data is done being sent
if (line_type == 1)
{
done = TRUE;
}
else if ((addr <LOADER_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;
}
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> getenv("FLASH_WRITE_SIZE")
if ((addr!=next_addr)&&(addr&(getenv("FLASH_ERASE_SIZE")/2-1)!=0))
{
erase_program_eeprom(addr);
}
next_addr = addr + 1;
#endif
write_program_memory(addr, data, count);
fprintf(MONITOR, "%Lu \n\r", addr);
}
else if (line_type == 4)
{
h_addr = make16(atoi_b16(&buffer[9]), atoi_b16(&buffer[11]));
}
}
}
}
if (do_ACKLOD) fputc(ACKLOD,USB);
fputc(XON,USB);
}
fputc(ACKLOD,USB);
fputc(XON,USB);
reset_cpu();
}
unsigned int atoi_b16(char *s) { // Convert two hex characters to a int8
unsigned int result = 0;
int i;
for (i=0; i<2>= 'A')
result = 16*result + (*s) - 'A' + 10;
else
result = 16*result + (*s) - '0';
}
return(result);
}
#ORG default
#ORG LOADER_ADDR, LOADER_ADDR+9
void load_program(void)
{
real_load_program();
}
|
HEX code being loaded by my C# application. EX_bootload.c and bootloader_ken.h.
EX_bootload.c
Code: |
#define defined(__PCH__)
#include <18F4620.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#use rs232(baud=115200,parity=N,xmit=PIN_D6,rcv=PIN_B0,bits=8,stream=USB)
#include "bootloader_ken.h"
int i;
void main(void) {
int i=0;
while(TRUE)
{
fputc(i++,USB);
delay_ms(10);
}
}
|
bootloader_ken.h
Code: |
#define LOADER_END 64703
#define LOADER_SIZE 0x4FF
#define LOADER_ADDRESS LOADER_END-LOADER_SIZE
#build(reset=LOADER_END+1)
|
LOADER_END is hardcoded here with the value that I got from the LOADER_END variable in loader_ken.c the variable was defined as
getenv("PROGRAM_MEMORY")-1
I get 0xFFFF if I use the getenv() function in the bootloader_ken.h, which matches the datasheet. I'm confused why I don't get 0xFFFF when I call it in loader_ken.c. Please let me know if you have ideas.
Again many thanks for taking a look at this long post. |
|
|
jma_1
Joined: 08 Feb 2005 Posts: 147 Location: Wisconsin
|
|
Posted: Thu Oct 26, 2006 9:36 am |
|
|
Greetings,
The bootloader.h or similar file must be included in the real application code. This specifies the size boot boot block size, end, reset location, and interrupt vector. Without including this, your application will not work as you found out. This is the reverse of the warning I listed about compiling your application with this file include, but using an ICD (if not include, and programmed using bootloader-> will not work).
With the pic chip I'm using, the loader end and size are 0x4FF and 0x3FF respectively. The loader file (not your file, the sample boot.c or similar; same as loader_ken.c) specifically checks if 'LOADER_END' is defined -> defined in the bootloader.h file. If not, then it uses your get_env(program memory). This means the bootloader portion of the code, specifically the 'loader' portion is placed at 0x10A -> (0x4FF-0x3FF + 10) and must fit within the boot block ending address -> 0x4FF
#ORG LOADER_ADDR+10, LOADER_END auto=0 default
void real_load_program (void)
{
}
From this, I believe the function to program the code is in the lower addresses only. The loader function must be contained in the boot block or it would continually get overwritten if you didn't specify another reserved portion of memory. The new code is always written above the boot block (assuming you don't change the bootloader).
If the boot conditions are not met, the example bootloader makes a jump to the normal 'main' of your application (after boot block)
#org LOADER_END+2,LOADER_END+20
void application(void) {
while(TRUE);
}
I believe interrupts have latency time due to a jump to the normally specified interrupt location, which then makes a jump to the re-mapped location.
I would recommend re-powering the pic (hardware reset) following the reset_cpu() due to all the registers not being properly reset following a software reset.
With your lengthy post, I do not know if I've answered all your questions. I would strongly recommend working out in detail exactly where your functions are placed. Look through the LST file for your bootloader application and your normal application. This might be pain to do with the LST file. Try printing out the CCS example -> most likely much smaller LST files to wade through.
On a side note, how will you structure your bootloader to work with production code? Your code your posted specifically waits to load code and does allow the normal application to run. Will you wait a few seconds for the correct character sequence to be transmitted? Normally you will want this delay to be as short as possible.
Cheers,
JMA |
|
|
kuriken
Joined: 21 Oct 2006 Posts: 19
|
Got it to work |
Posted: Wed Nov 01, 2006 12:54 pm |
|
|
JMA,
Thanks for all the help. I abandoned ex_loader.c but finally got ex_bootloader.c to work for me.
The tricky part using software UART was that the compiler would place my #use statements after the LOADER_END address in ROM.
I made space for the #use statements and shifted my reset vector, interrupt vector and application() address and Viola it works!
If anybody is interested, below is the final version of the code.
bootloader_kk.c
Code: |
#include <18F4620.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,BROWNOUT
#use delay(clock=20000000)
#use rs232(DEBUGGER, stream=MONITOR, ERRORS)
#use rs232(baud=115200,parity=N,xmit=PIN_D6,rcv=PIN_B0,bits=8,stream=USB)
#define _bootloader
#include "bootloader_kk.h"
#include "loader_kk.c"
//moved over 0x100 to accomidate #use statements
#org LOADER_END+2+USE_STATEMENT_SIZE,LOADER_END+20+USE_STATEMENT_SIZE
void application(void) {
while(TRUE);
}
#org 0x40,0xFF //upper limit is LOADER_END-LOADER_SIZE otherwise will not compile
void main(void)
{
unsigned int8 updateflag;
updateflag = read_eeprom(FIRMWARE_UPDATE_FLAG_ADDRESS);
if(updateflag == 0x01)
{
fputc('R',USB);
load_program();
}
else if(updateflag == 0xFF)
{
do{
}while(fgetc(USB) != 'L');
fputc('R',USB);
write_eeprom(FIRMWARE_UPDATE_FLAG_ADDRESS, 0x01);
write_eeprom(PRODUCTION_CHECK_FLAG_ADDRESS, 0x01);
load_program();
}
application();
}
#ORG default
#int_global
void isr(void) {
jump_to_isr(LOADER_END+5*(getenv("BITS_PER_INSTRUCTION")/8)+USE_STATEMENT_SIZE);
}
|
bootloader_kk.h
Code: |
#ifndef bootloader_kk_H
#define bootloader_kk_H
#define LOADER_END 0x5FF
#define LOADER_SIZE 0x4FF
#define USE_STATEMENT_SIZE 0x100 //ROM size alotted for #use statements in ex_bootloader.c
#ifdef _bootloader
#define FIRMWARE_UPDATE_FLAG_ADDRESS 0x50 // 1 byte make sure these match with eeprom.h
#define PRODUCTION_CHECK_FLAG_ADDRESS 0x51 // 1 byte
#else
#build(reset=LOADER_END+1+USE_STATEMENT_SIZE, interrupt=LOADER_END+9+USE_STATEMENT_SIZE)
#org 0, LOADER_END+USE_STATEMENT_SIZE {}
#endif
#endif
|
|
|
|
Darren Kattan
Joined: 26 Nov 2006 Posts: 1
|
|
Posted: Thu Dec 21, 2006 4:21 am |
|
|
This thread has been infinitely helpful, as I am working on a very similar project involving C#, Software UARTS and the FT232R.
I tried using Siow.exe at first, but it shoots the data down the line too fast for the Soft UARTS to get it. I eventually broke down and wrote a a C# interface, and added a few thread.sleep(10); lines in crucial spots, and I was able to give the micro enough time to process the buffer, and come back for another shot.
My question though, is as I'm debugging, I'm watching the Hex Echo through another software uart, and I'm noticing that there is a big jump in addresses, and then the loader function actually stops writing.
I'm assuming this is because the loader code itself is held in the upper portion of memory, and overwriting this code would cause the chip to crash...
Is it not supposed to rewrite the loader code?
It works for the most part right now, I'm still working some other kinks out, but I was curious about that.
--Darren |
|
|
grasspuddle
Joined: 15 Jun 2006 Posts: 66
|
|
Posted: Thu Dec 21, 2006 9:24 am |
|
|
Around october I finished a software UART bootloader, but I seem to have missed this whole topic.
I used visual basic instead of C# since it took about a day of programming for an app to work with visual basic. Its a noncustomer program so its a small app and I personally didn't feel the need to use C#.
I started with CCS' bootloader code and took out the hardware handshaking. I had some kind of throttle control, but can't remember too well. It programs stably at 57600 speed which means it can program my whole pic18f452 chip in under a minute. And I did use putchar.
I only declared the software usart in the bootprogram so I didn't need to specifically define which stream to use and don't know if that would even affect it. Also, no ftdi chip was used, just straight serial so there might be delays between that chip?
One difference I did see is that I commented out my XOFF command. When I used it there were timeouts after only a few lines written and the problem disappeared when that one command was commented out. I still don't know why it works that way.
Good Luck |
|
|
|
|
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
|