|
|
View previous topic :: View next topic |
Author |
Message |
valkyrie.wing
Joined: 12 Oct 2017 Posts: 10
|
PIC18F8722 Bootloader at End of Program Memory |
Posted: Thu Oct 12, 2017 6:07 pm |
|
|
Good day.
I am writing a bootloader firmware using a PIC18F8722 and I prefer the bootloader firmware be located at the end of the program memory space. Below is my suggested memory organization.
Code: |
|------------------------------------|
| Bootloader Boot Vector | 00000
|------------------------------------|
| |
|------------------------------------|
| Hi-Prio Interrupt Vector | 00008
|------------------------------------|
| |
|------------------------------------|
| Lo-Prio Interrupt Vector | 00018
|------------------------------------| -------
| | ^
| | |
| | |
| | |
| | |
| Application Code | |
| | |
| | |
| | Application
| | Memory Space
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | *
|------------------------------------| -------
| | ^ 1FB00
| | |
| | |
| Bootloader Code | |
| | |
| | |
| | Bootloader
| | Memory Space
|------------------------------------| |
| Application Boot Vector | |
|------------------------------------| |
| | *
|------------------------------------| ------- 1FFFF
|
My concern here is that I do not want my Bootloader Boot Vector to be overwritten by the Application Boot Vector. The reason is, I will write the commands in the Bootloader code section. Thus, below is the code that will make sure the address 0x00000 will not be overwritten, although I am not sure if it is the correct implementation and I need feedback from the group for their suggestions.
Did I miss something that it should be included in the code?
Code: |
if (Addr == 0x0)
{
write_program_eeprom(AppBootVector, Data[0]);
for (DataIdx = 1;
DataIdx < Count;
DataIdx++)
{
write_program_eeprom(Addr + DataIdx, Data[DataIdx]);
}
}
else
{
write_program_memory(Addr, Data, Count);
}
|
Below are my source files:
bootloader.h - a modified version that came with CCS
Code: |
#if !defined(__BOOT_LOADER_H__)
#define __BOOT_LOADER_H__
#define LOADER_END (getenv("PROGRAM_MEMORY") - 1)
#if (getenv("PROGRAM_MEMORY") % getenv("FLASH_ERASE_SIZE")) == 0
#define LOADER_SIZE (0x4FF)
#else
#define LOADER_SIZE (getenv("PROGRAM_MEMORY") % (getenv("FLASH_ERASE_SIZE")) - 1)
#endif
#define LOADER_ADDR (LOADER_END - LOADER_SIZE)
#ifndef _BOOTLOADER_
#org LOADER_ADDR, LOADER_END {}
#else
#org LOADER_ADDR, LOADER_END default
#endif
#endif
|
fwloader.c - my main() method including the modified real_load_program() method that came from CCS.
Code: |
#include <18F8722.h>
#device ADC=16
#FUSES H4 // High-speed crystal clock x4 multiplier
#FUSES NOPROTECT // No code protection from reading
#FUSES NOWDT // No Watch Dog Timer
#FUSES NOBROWNOUT // No brownout reset
#FUSES NOLVP // No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES PUT // power-up timer
#define BOOTLOADER_RUN (PIN_A1)
#define BOOTLOADER_LED (PIN_A2)
#define ACKLOD (0x06)
#define XON (0x11)
#define XOFF (0x13)
#define BUFFER_LEN_LOD (64)
#define _BOOTLOADER_
#include "bootloader.h"
#use delay(crystal=40000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8, errors)
unsigned int atoi_b16 (char *BuffPtr)
{
// Convert two hex characters to a int8
unsigned int Result = 0;
int Idx;
for (Idx = 0;
Idx < 2;
Idx++, BuffPtr++)
{
if (*BuffPtr >= 'A')
{
Result = 16 * Result + (*BuffPtr) - 'A' + 10;
}
else
{
Result = 16 * Result + (*BuffPtr) - '0';
}
}
return Result;
}
void real_load_program (unsigned int32 AppBootVector)
{
int BuffIdx;
char Buffer[BUFFER_LEN_LOD];
int1 DoAcklod, Done = FALSE;
int8 Checksum, LineType;
int16 Laddr, Haddr = 0;
int32 Addr;
#if getenv("FLASH_ERASE_SIZE") > 2
int32 NextAddr;
#endif
int8 DataIdx, Idx, Count;
int8 Data[32];
while (Done == FALSE) // Loop until the entire program is downloaded
{
// Read into the buffer until 0x0D ('\r')
// is received or the buffer is full
BuffIdx = 0;
do
{
if (BuffIdx >= BUFFER_LEN_LOD)
{
BuffIdx = 0;
}
Buffer[BuffIdx] = getc();
if (Buffer[BuffIdx] == ':')
{
Buffer[0] = Buffer[BuffIdx];
BuffIdx = 0;
}
} while ( (Buffer[BuffIdx++] != 0x0D)
&& (BuffIdx <= BUFFER_LEN_LOD));
putchar(XOFF); // Suspend sender
DoAcklod = TRUE;
// Only process data blocks that start with ':'
if (Buffer[0] == ':')
{
// Get the number of bytes from the buffer
Count = atoi_b16(&Buffer[1]);
// Get the lower 16 bits of address
Laddr = make16(atoi_b16(&Buffer[3]), atoi_b16(&Buffer[5]));
// Get the line type
LineType = atoi_b16(&Buffer[7]);
// Get the program memory address
Addr = make32(Haddr, Laddr);
// If the line type is 1, then data is done being sent
if (LineType == 1)
{
Done = TRUE;
}
else if ( ( Addr < LOADER_ADDR
|| Addr > LOADER_END)
&& Addr < 0x300000)
{
Checksum = 0; // Sum the bytes to find the check sum value
for (Idx = 1;
Idx < (BuffIdx - 3);
Idx += 2)
{
Checksum += atoi_b16(&Buffer[Idx]);
}
Checksum = 0xFF - Checksum + 1;
if (Checksum != atoi_b16(&Buffer[BuffIdx - 3]))
{
DoAcklod = FALSE;
}
else
{
if (LineType == 0)
{
// Loops through all of the data and stores it in data
// The last 2 bytes are the check sum, hence buffidx-3
for (Idx = 9, DataIdx = 0;
Idx < (BuffIdx - 3);
Idx += 2)
{
Data[DataIdx++] = atoi_b16(&Buffer[Idx]);
}
#if getenv("FLASH_ERASE_SIZE") > 2
if ( (Addr != NextAddr)
&& (Addr & (getenv("FLASH_ERASE_SIZE") / 2 - 1) != 0))
{
erase_program_eeprom(addr);
}
NextAddr = Addr + 1;
#endif
// (*) Modifing the Application Boot Vector
// The assembly commands in memory are called the boot
// vector. It resets the board and points to the address of
// the main function.
// Each HEX file has their own version of this command. But
// since we always want the boot loader to run before the
// user code, we must not overwrite the loader's boot vertor.
// Instead, we move the user's boot vector to another location
// within the boot loader's program area
// (see label "APP_BOOT_VECTOR" below).
// This 'if' command detects user's boot vector and redirects it
// The boot vector is at address 0x0000
if (Addr == 0x0)
{
write_program_eeprom(AppBootVector, Data[0]);
for (DataIdx = 1;
DataIdx < Count;
DataIdx++)
{
write_program_eeprom(Addr + DataIdx, Data[DataIdx]);
}
}
else
{
write_program_memory(Addr, Data, Count);
}
}
else if (LineType == 4)
{
Haddr = make16(atoi_b16(&Buffer[9]), atoi_b16(&Buffer[11]));
}
else
{
;;;; // just do nothing here
}
} // else
} // else if
else
{
;;;; // just do nothing here
}
} // end if
if (DoAcklod == TRUE)
{
putchar(ACKLOD);
}
putchar(XON);
} // end while
putchar(ACKLOD);
putchar(XON);
return;
}
void main (void)
{
unsigned int32 AppBootVector; // the application boot vector address
setup_adc_ports(NO_ANALOGS); // setup PortA to digital I/O
if (input(BOOTLOADER_RUN) == TRUE) // firmware upgrade button is enabled
{
// get the memory address of the application boot vector
AppBootVector = label_address(APP_BOOT_VECTOR);
output_high(BOOTLOADER_LED); // turn-ON bootloader LED
// write new firmware to program memory
real_load_program(AppBootVector);
output_low(BOOTLOADER_LED); // turn-OFF bootloader LED
}
//-----------------------------------------------------------------
// this is a reboot command written in assembly.
// these will be overwritten with the boot vector we obtain from
// the new firmware HEX file.
// See (*) above for more details
//-----------------------------------------------------------------
APP_BOOT_VECTOR:
#asm
GOTO 0x00000
NOP
NOP
NOP
#endasm
return;
}
|
Thank you. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Fri Oct 13, 2017 3:21 am |
|
|
If you look at the data sheet, the code protection options for boot-block all allow you to protect the low addresses of memory, not the high address (just to confuse they talk about 'high memory', as the memory nearest address 0).
Look at table 25-5, for the layout that MicroChip assume will be used.
The option to build the CCS code for high memory address bootloaders is normally reserved for chips that are designed for this (PIC24 etc..).
Now the advantage of the 'high address' bootloader, is that it saves a jump time in interrupt handling, when the interrupt code can be directly accessed, instead of needing to be re-vectored. The big downside is that if anything goes wrong when writing the bottom block, the chip is toast. It is a pity that the interrupt vectors are not in another memory page than the boot vector, which would avoid this problem. It isn't really any more logical, and brings some downsides:
Lack of memory protection for the bootloader.
Need to update the application address separately. If this is stored at the end of memory the booloader code can't be protected.
Costs an erasable of memory at the top of the ROM for this.
Now there is a way to avoid the second and third of these.
If you are happy to allocate permanently enough space for your interrupt routines, with some 'spare', you can alter your build for your program like:
Code: |
#build (interrupt = 0x008:0x3FF, reset=0x400)
|
The 'start' vector for your main code will then permanently be at address 0x400, with the interrupt code below this.
So long as you then just ensure that if anything reprograms the first block, the booloader entry vector is written, you have no need to store an 'application address'.
Obviously you potentially lose some memory between the area allocated for the interrupt handlers and the main code, but I think this is tidier than having the extra store at the top of memory. You can then set the code protection bit for the top block of your program memory and protect the bootloader code.
Even neater, the one write that has to change will only happen when the interrupt code is changed. You can distribute the 'main' program without any interrupt code for 99% of updates, and this doesn't need to happen. A lot safer.
A thought. |
|
|
valkyrie.wing
Joined: 12 Oct 2017 Posts: 10
|
|
Posted: Fri Oct 13, 2017 7:54 pm |
|
|
Hi Ttelmah,
I checked the PIC18F8722 datasheet and I can't find Table 25-5 and my guess is you are referring to Figure 25-5 for code protected program memory organization.
Now, back to your suggestion on your reply. If I understand it correctly, you suggest that my application code will have a start vector of 0x400 and the interrupt vector for the application code will be from 0x008 to 0x3FF via the #build CCS C compiler preprocessor.
What will be in address 0x000? Is this address the bootloader boot vector? What about my bootloader code? Where do I put it? Based on your comment below, the end part of the program memory is normally reserved for chips that are designed for this like PIC24.
Although, the way I understand it, the bootloader code will still be at the end but the application code will start at 0x400. See memory organization below.
Code: |
|------------------------------------|
| Bootloader Boot Vector | 0x00000
|------------------------------------|
| |
|------------------------------------|
| | 0x00008
| |
| Interrupt Vector |
| |
| | 0x003FF
|------------------------------------| ------- 0x00400
| Application Boot Vector | ^
|------------------------------------| |
| | |
| | |
| Application Code | |
| | |
| | |
| | Application
| | Memory Space
| | |
| | |
| | |
| | |
| | *
|------------------------------------| ------- 0x1FB00
| | ^
| | |
| | |
| Bootloader Code | |
| | |
| | |
| | Bootloader
| | Memory Space
| | |
| | *
|------------------------------------| ------- 0x1FFFF
|
Did I get your suggestion correctly?
Thanks. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Fri Oct 13, 2017 11:37 pm |
|
|
Exactly.
Key is that provided you make the interrupt area large enough (you may need more than 0x400 - depends what you are doing), the application entry need never move.
At address zero you would need a fixed jump to your bootloader code.
The other thing is though that if you are distributing the code, given how rarely interrupt stuff needs to change, you can simply not include the interrupt code with the distribution, so the interrupt (and the bootloader jump) are not re-programmed.
This is quite a common way of distributing stuff. |
|
|
valkyrie.wing
Joined: 12 Oct 2017 Posts: 10
|
|
Posted: Sat Oct 14, 2017 5:23 am |
|
|
Hi Ttelmah,
I created a simple application firmware that will align with what was discussed. Below is the application code and the bootloader header.
bootloader.h
Code: |
#if !defined(__BOOTLOADER_OP2_H__)
#define __BOOTLOADER_OP2_H__
#define LOADER_END (getenv("PROGRAM_MEMORY") - 1)
#if (getenv("PROGRAM_MEMORY") % getenv("FLASH_ERASE_SIZE")) == 0
#define LOADER_SIZE (0x4FF)
#else
#define LOADER_SIZE (getenv("PROGRAM_MEMORY") % (getenv("FLASH_ERASE_SIZE")) - 1)
#endif
#define LOADER_ADDR (LOADER_END - LOADER_SIZE)
#define APP_INT_VECTOR_START (0x008)
#define APP_INT_VECTOR_END (0x3FF)
#define APP_BOOT_VECTOR (APP_INT_VECTOR_END + 0x1)
#ifndef _BOOTLOADER_
#build (reset=APP_BOOT_VECTOR, interrupt=APP_INT_VECTOR_START:APP_INT_VECTOR_END)
#org LOADER_ADDR, LOADER_END {}
#else
#org LOADER_ADDR, LOADER_END default
#endif
#endif
|
application_fw.c
Code: |
#include <18F8722.h>
#device ADC=16
#FUSES H4 // High-speed crystal clock x4 multiplier
#FUSES NOPROTECT // No code protection from reading
#FUSES NOWDT // No Watch Dog Timer
#FUSES NOBROWNOUT // No brownout reset
#FUSES NOLVP // No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES PUT // power-up timer
#include "bootloader_op2.h"
#use delay(crystal=40000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8, errors)
void main (void)
{
unsigned int8 Numb = 0;
delay_ms(100);
printf("nApplication Version 1.0\n");
while (TRUE)
{
printf("%u ", ++Numb);
}
return;
}
|
After compiling the said sources, I look at the listing and it is really weird because address 0x0000 has been replaced with DATA. Below is the snippet of the C/ASM listing.
Code: |
00400: GOTO 0106
.................... #include <18F8722.h>
.................... //////////// Standard Header file for the PIC18F8722 device ////////////////
.................... ///////////////////////////////////////////////////////////////////////////
.................... //// (C) Copyright 1996, 2014 Custom Computer Services ////
.................... //// This source code may only be used by licensed users of the CCS C ////
.................... //// compiler. This source code may only be distributed to other ////
.................... //// licensed users of the CCS C compiler. No other use, reproduction ////
.................... //// or distribution is permitted without written permission. ////
.................... //// Derivative programs created using this software in object code ////
.................... //// form are not restricted in any way. ////
.................... ///////////////////////////////////////////////////////////////////////////
.................... #device PIC18F8722
*
00000: DATA 6E,41
00002: DATA 70,70
00004: DATA 6C,69
00006: DATA 63,61
00008: DATA 74,69
0000A: DATA 6F,6E
0000C: DATA 20,56
0000E: DATA 65,72
00010: DATA 73,69
00012: DATA 6F,6E
00014: DATA 20,31
00016: DATA 2E,30
00018: DATA 0A,00
*
00044: TBLRD*+
00046: MOVF FF5,F
00048: BZ 006C
0004A: MOVFF FF6,08
0004E: MOVFF FF7,09
00052: MOVFF FF8,0A
00056: MOVF FF5,W
00058: BTFSS F9E.4
0005A: BRA 0058
0005C: MOVWF FAD
0005E: MOVFF 08,FF6
00062: MOVFF 09,FF7
00066: MOVFF 0A,FF8
0006A: BRA 0044
0006C: GOTO 0144 (RETURN)
|
I do expect that the whole code will begin at address 0x400 until it reaches the end address depending on the size of the application firmware. Based from the build definition, the start vector of the application is 0x400 while the interrupt vector is from 0x008 to 0x3FF.
Did I miss something here? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Sat Oct 14, 2017 8:12 am |
|
|
You need to have at least one interrupt handler defined.
The code won't automatically locate upward unless the interrupts are being used....
With the interrupts 'unused', it'll use this unused area for storage. |
|
|
valkyrie.wing
Joined: 12 Oct 2017 Posts: 10
|
|
Posted: Sat Oct 14, 2017 10:40 pm |
|
|
Hi Ttelmah,
You are right, I need to have #int method defined in order for the #build preprocessor to work. Here's the final code of the application firmware.
Code: |
#include <18F8722.h>
#device ADC=16
#FUSES H4 // High-speed crystal clock x4 multiplier
#FUSES NOPROTECT // No code protection from reading
#FUSES NOWDT // No Watch Dog Timer
#FUSES NOBROWNOUT // No brownout reset
#FUSES NOLVP // No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES PUT // power-up timer
#use delay(crystal=40000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8, errors)
#include "bootloader_op2.h"
#bit TXIF = 0xF9E.4 // clear transmit buffer flag (empty)
#bit RCIF = 0xF9E.5 // clear receive buffer flag (empty)
#define SERIAL_BUFF_SZ (32) // Allocated serial comm buffer
byte TXin; // transmit data IN index
byte TXout; // transmit data OUT index
byte TXbuffer[SERIAL_BUFF_SZ]; // Serial TX buffer
#int_tbe
void serial_tx_isr (void)
{
// put char to TXbuffer then send
putc(TXbuffer[TXout]);
// increment & limit to max buffer size
TXout = (TXout + 1) % SERIAL_BUFF_SZ;
// if last in data, stop TX interrupt
if (TXin == TXout)
{
disable_interrupts(int_tbe); // disable transmit interrupt
}
return;
}
void serial_bputc (char Ch)
{
boolean Restart;
int Next;
// set restart flag at start
Restart = (TXin == TXout);
// store char to TXbuffer
TXbuffer[TXin] = Ch;
// limit to max buffer size
Next = (TXin + 1) % SERIAL_BUFF_SZ;
while (Next == TXout); // buffer is full
TXin = Next;
if (Restart == TRUE) // enable TX interrupt
{
enable_interrupts(int_tbe);
}
return;
}
void main (void)
{
unsigned int8 Numb = 0;
delay_ms(100);
//-----------------------------------------------------
// initialize mcu and peripherals
//-----------------------------------------------------
TXIF = 1; // clear transmit buffer flag (empty)
RCIF = 0; // clear receive buffer flag (empty)
enable_interrupts(global); // enable global interrupt
//-----------------------------------------------------
printf(serial_bputc, "nApplication Version 1.0\n");
while (TRUE)
{
printf(serial_bputc, "%u ", ++Numb);
}
return;
}
|
And here's the final code of the bootloader based from what we had discussed with application boot vector at 0x400 while the interrupt vector is from 0x008 to 0x3FF. The Bootloader boot vector is at 0x000 while the bootloader code is at the end of the program memory.
bootloader_op2.h
Code: |
#if !defined(__BOOTLOADER_OP2_H__)
#define __BOOTLOADER_OP2_H__
#define LOADER_END (getenv("PROGRAM_MEMORY") - 1)
#if (getenv("PROGRAM_MEMORY") % getenv("FLASH_ERASE_SIZE")) == 0
#define LOADER_SIZE (0x4FF)
#else
#define LOADER_SIZE (getenv("PROGRAM_MEMORY") % (getenv("FLASH_ERASE_SIZE")) - 1)
#endif
#define LOADER_ADDR (LOADER_END - LOADER_SIZE)
#define APP_INT_VECTOR_START (0x008)
#define APP_INT_VECTOR_END (0x3FF)
#define APP_BOOT_VECTOR (APP_INT_VECTOR_END + 0x1)
#ifndef _BOOTLOADER_
#build (reset=APP_BOOT_VECTOR, interrupt=APP_INT_VECTOR_START:APP_INT_VECTOR_END)
#org LOADER_ADDR, LOADER_END {}
#else
#org LOADER_ADDR, LOADER_END default
#endif
#endif
|
bootloader_op2.c
Code: |
#include <18F8722.h>
#device ADC=16
#FUSES H4 // High-speed crystal clock x4 multiplier
#FUSES NOPROTECT // No code protection from reading
#FUSES NOWDT // No Watch Dog Timer
#FUSES NOBROWNOUT // No brownout reset
#FUSES NOLVP // No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES PUT // power-up timer
#define BOOTLOADER_RUN (PIN_A1)
#define BOOTLOADER_LED (PIN_A2)
#define ACKLOD (0x06)
#define XON (0x11)
#define XOFF (0x13)
#define BUFFER_LEN_LOD (64)
#define _BOOTLOADER_
#include "bootloader_op2.h"
#use delay(crystal=40000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8, errors)
unsigned int atoi_b16 (char *BuffPtr)
{
// Convert two hex characters to a int8
unsigned int Result = 0;
int Idx;
for (Idx = 0;
Idx < 2;
Idx++, BuffPtr++)
{
if (*BuffPtr >= 'A')
{
Result = 16 * Result + (*BuffPtr) - 'A' + 10;
}
else
{
Result = 16 * Result + (*BuffPtr) - '0';
}
}
return Result;
}
void real_load_program ()
{
int BuffIdx;
char Buffer[BUFFER_LEN_LOD];
int1 DoAcklod, Done = FALSE;
int8 Checksum, LineType;
int16 Laddr, Haddr = 0;
int32 Addr;
#if getenv("FLASH_ERASE_SIZE") > 2
int32 NextAddr;
#endif
int8 DataIdx, Idx, Count;
int8 Data[32];
while (Done == FALSE) // Loop until the entire program is downloaded
{
// Read into the buffer until 0x0D ('\r')
// is received or the buffer is full
BuffIdx = 0;
do
{
if (BuffIdx >= BUFFER_LEN_LOD)
{
BuffIdx = 0;
}
Buffer[BuffIdx] = getc();
if (Buffer[BuffIdx] == ':')
{
Buffer[0] = Buffer[BuffIdx];
BuffIdx = 0;
}
} while ( (Buffer[BuffIdx++] != 0x0D)
&& (BuffIdx <= BUFFER_LEN_LOD));
putchar(XOFF); // Suspend sender
DoAcklod = TRUE;
// Only process data blocks that start with ':'
if (Buffer[0] == ':')
{
// Get the number of bytes from the buffer
Count = atoi_b16(&Buffer[1]);
// Get the lower 16 bits of address
Laddr = make16(atoi_b16(&Buffer[3]), atoi_b16(&Buffer[5]));
// Get the line type
LineType = atoi_b16(&Buffer[7]);
// Get the program memory address
Addr = make32(Haddr, Laddr);
// If the line type is 1, then data is done being sent
if (LineType == 1)
{
Done = TRUE;
}
else if ( ( Addr < LOADER_ADDR
|| Addr > LOADER_END)
&& Addr < 0x300000)
{
Checksum = 0; // Sum the bytes to find the check sum value
for (Idx = 1;
Idx < (BuffIdx - 3);
Idx += 2)
{
Checksum += atoi_b16(&Buffer[Idx]);
}
Checksum = 0xFF - Checksum + 1;
if (Checksum != atoi_b16(&Buffer[BuffIdx - 3]))
{
DoAcklod = FALSE;
}
else
{
if (LineType == 0)
{
// Loops through all of the data and stores it in data
// The last 2 bytes are the check sum, hence buffidx-3
for (Idx = 9, DataIdx = 0;
Idx < (BuffIdx - 3);
Idx += 2)
{
Data[DataIdx++] = atoi_b16(&Buffer[Idx]);
}
#if getenv("FLASH_ERASE_SIZE") > 2
if ( (Addr != NextAddr)
&& (Addr & (getenv("FLASH_ERASE_SIZE") / 2 - 1) != 0))
{
erase_program_eeprom(addr);
}
NextAddr = Addr + 1;
#endif
write_program_memory(Addr, Data, Count);
}
else if (LineType == 4)
{
Haddr = make16(atoi_b16(&Buffer[9]), atoi_b16(&Buffer[11]));
}
else
{
;;;; // just do nothing here
}
} // else
} // else if
else
{
;;;; // just do nothing here
}
} // end if
if (DoAcklod == TRUE)
{
putchar(ACKLOD);
}
putchar(XON);
} // end while
putchar(ACKLOD);
putchar(XON);
return;
}
void main (void)
{
setup_adc_ports(NO_ANALOGS); // setup PortA to digital I/O
if (input(BOOTLOADER_RUN) == TRUE) // firmware upgrade button is enabled
{
output_high(BOOTLOADER_LED); // turn-ON bootloader LED
// write new firmware to program memory
real_load_program();
output_low(BOOTLOADER_LED); // turn-OFF bootloader LED
}
// run the application firmware
#asm
GOTO APP_BOOT_VECTOR
NOP
NOP
#endasm
return;
}
|
BTW, the jump instruction is using GOTO 0x400 instead of CALL to run the application firmware after the bootloader code is executed either for firmware update or not.
Thanks once again for the help. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Sun Oct 15, 2017 12:09 am |
|
|
Well done.
When you posted the earlier code I suddenly 'remembered' that it doesn't reserve the interrupt area if no interrupt is used. Had met this myself ages ago when trying to do something similar....
Yes, goto makes sense. There is nothing to 'return' to.
Hopefully you will find it a nice reliable solution. |
|
|
|
|
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
|