4
\$\begingroup\$

Now its time to send the application code to the bootloader, that's been programmed into an ATmega32A controller. It has to be sent in hex format, but I have no idea of "How to send it?"

The hex file compiled for an application code from atmel studio is as follows:

    :100000000C942A000C943F000C943F000C943F0089
    :100010000C943F000C943F000C943F000C943F0064
    :100020000C943F000C943F000C943F000C943F0054
    :100030000C943F000C943F000C943F000C943F0044
    :100040000C943F000C943F000C943F000C943F0034
    :100050000C943F0011241FBECFE5D8E0DEBFCDBF1A
    :1000600010E0A0E6B0E0E0E2F2E002C005900D9200
    :10007000A23EB107D9F70E94BD000C940E010C946A
    :100080000000D09A899A88988AB188618AB980B527
    :100090008F7B80BD529880B5866080BD80B5877F9C
    :1000A00080BD80B58F7C80BD89E189B910BC089581
    :1000B0005D9BFECF8CB908950F931F93CF93DF9371
    :1000C000FC0101900020E9F73197E81BF90B51F092
    :1000D000EC018C010E0F1F1F89910E945800C01760
    :1000E000D107D1F7DF91CF911F910F9108955F9BB9
    :1000F000FECF8CB190E008950E9477008335E1F740
    :100100000E9477008935C1F70E9477008335A1F7F7
    :100110000E947700833581F70E947700843561F76C
    :100120000E947700813441F70E947700823521F7E1
    :100130000E947700843501F70895CF93DF93EC0197
    :100140000E945C000E9477008F3421F00E947700AB
    :100150008F34E1F70E9477008B3461F02FE78AE15A
    :1001600096E0215080409040E1F700C00000CE01B1
    :100170000E949D00DF91CF9108950E9441002FE7DA
    :100180008AE196E0215080409040E1F700C00000F5
    :100190000E947C002FE382E49FE021508040904049
    :1001A000E1F700C0000080E690E00E949D008DE62F
    :1001B00090E00E949D002FE78AE196E02150804068
    :1001C0009040E1F700C0000087E790E00E945C00EB
    :1001D0002FE78AE196E0215080409040E1F700C08F
    :1001E000000084E990E00E945C002FE78AE196E03D
    :1001F000215080409040E1F700C000008EEB90E07D
    :100200000E945C002FE78AE196E0215080409040F8
    :10021000E1F700C0000080E090E00895F894FFCF7F
    :1002200041545E4E5754494D453F0D0A0041542BF1
    :1002300043474D490D0A0041545E4950494E4954C7
    :100240003D2261697274656C677072732E636F6DA5
    :10025000220D0A0041545E49504F50454E3D312C0D
    :1002600022544350222C223132322E3136352E3256
    :1002700033302E3137222C373836390D0A004154AD
    :100280005E495053454E443D312C22646576696386
    :10029000652D69642C6770735F64617461220D0A57
    :0202A00000005C
    :00000001FF

As per the standard the first line has data 0C942A000C943F000C943F000C943F00 and checksum 89. Do I have to just send the 16 bytes of data followed by checksum? In some programs, some lines don't even have 16 bytes of data. It only has 10 bytes with checksum at the end, eg: :0A0B4000CDBFED010895F894FFCF3A.

There is no clear examples anywhere. I expect someone out there must have some experience in it to help me.

\$\endgroup\$
4
  • \$\begingroup\$ You may want to swap this to embedded.SE \$\endgroup\$
    – Dzarda
    Commented May 30, 2014 at 11:36
  • \$\begingroup\$ Hi Dzarda, Could you please elaborate \$\endgroup\$
    – gzix
    Commented May 30, 2014 at 11:39
  • 3
    \$\begingroup\$ I don't have time to write a full answer at the moment but it's an Intel hex format and normally the easiest thing is to send the whole lot and decode in the bootloader. Then you can just use a regular terminal program to send it. \$\endgroup\$
    – PeterJ
    Commented May 30, 2014 at 11:48
  • \$\begingroup\$ Hi PeterJ, I'm sure i can decode it. But do i have to include the checksum at each line end. And how to pad if some line got less data \$\endgroup\$
    – gzix
    Commented May 30, 2014 at 12:00

3 Answers 3

2
\$\begingroup\$

This will need some further work on your part to integrate into your code but should give you some ideas. The first step in bootloader will be to include headers related to programming of the FLASH:

#include <avr/boot.h>
#include <avr/pgmspace.h>

A few other defines that help define things later and define some response codes at the end of processing each line are:

#define HEX2DEC(x)  (((x < 'A') ? ((x) - 48) : ((x) - 55)))
#define SPM_PAGEMASK ((uint32_t) ~(SPM_PAGESIZE - 1))
enum response_t {RSP_OK, RSP_CHECKSUM_FAIL, RSP_INVALID, RSP_FINISHED};

Then a routine like the following can be used to process the Intel hex format and write to FLASH. It is written for a larger device so handles extended addresses in the hex files so you can remove that for your smaller device to reduce the code size but it won't do any harm to leave in place. You'll also need to add your own UART receive / init code and determine what to do if things time out, I used a watchdog timer in this case.

enum response_t process_line()
{
    char c, line_buffer[128], data_buffer[64];
    uint8_t line_len = 0, data_len = 0, data_count, line_type, line_pos, data;
    uint8_t addrh, addrl, checksum, recv_checksum;
    uint16_t addr, extended_addr = 0, i;
    static uint32_t full_addr, last_addr = 0xFFFFFFFF;

    c = uart_getc();
    while (c != '\r')
    {
        if (c == ':')
            line_len = 0;
        else if (c == '\n')
            ;
        else if (c == '\0')
            ;
        else if (line_len < sizeof(line_buffer))
            line_buffer[line_len++] = c;
        c = uart_getc();
    }
    if (line_len < 2)
        return RSP_INVALID;
    data_count = (HEX2DEC(line_buffer[0]) << 4) + HEX2DEC(line_buffer[1]);
    if (line_len != data_count * 2 + 10)
        return RSP_INVALID;
    addrh =  (HEX2DEC(line_buffer[2]) << 4) + HEX2DEC(line_buffer[3]);
    addrl =  (HEX2DEC(line_buffer[4]) << 4) + HEX2DEC(line_buffer[5]);
    addr = (addrh << 8) + addrl;
    line_type = (HEX2DEC(line_buffer[6]) << 4) + HEX2DEC(line_buffer[7]);
    line_pos = 8;
    checksum = data_count + addrh + addrl + line_type;
    for (i=0; i < data_count; i++)
    {
        data = (HEX2DEC(line_buffer[line_pos]) << 4) + HEX2DEC(line_buffer[line_pos + 1]);
        line_pos += 2;
        data_buffer[data_len++] = data;
        checksum += data;
    }
    checksum = 0xFF - checksum + 1;
    recv_checksum = (HEX2DEC(line_buffer[line_pos]) << 4) + HEX2DEC(line_buffer[line_pos + 1]);
    if (checksum != recv_checksum)
        return RSP_CHECKSUM_FAIL;
    if (line_type == 1)
    {
        if (last_addr != 0xFFFFFFFF)
        {
            boot_page_write (last_addr & SPM_PAGEMASK);
            boot_spm_busy_wait();
        }
        return RSP_FINISHED;
    }
    else if ((line_type == 2) || (line_type == 4))
        extended_addr = (data_buffer[0] << 8) + data_buffer[1];
    else if (line_type == 0)
    {
        full_addr = ((uint32_t) extended_addr << 16) + addr;
        if ((full_addr & SPM_PAGEMASK) != (last_addr & SPM_PAGEMASK))
        {
            if (last_addr != 0xFFFFFFFF)
            {
                boot_page_write (last_addr & SPM_PAGEMASK);
                boot_spm_busy_wait();
            }
            boot_page_erase (full_addr);
            boot_spm_busy_wait ();
        }
        for (i=0; i < data_len; i+=2)
        {
            uint16_t w = data_buffer[i] + ((uint16_t) data_buffer[i + 1] << 8);
            boot_page_fill (full_addr + i, w);
        }
        last_addr = full_addr;
    }
    return RSP_OK;
}

It reads an entire hex line and verifies the checksum so check it against the Intel hex format to see how it works in detail before using. From there the general idea is that when it hits a new FLASH page boundary it does an erase but otherwise goes ahead and writes the data.

When the bootloader is entered this is the main code I was using to call it, but once again you'll need to work out what do to when a failure occurs. In my case I was using some custom PC software so sent back a few strings to indicate the status, but for a terminal upload you may just want to set an EEPROM flag depending on whether it was successful and you should boot to it or to show a human friendly message and call the bootloader again:

void enter_loader()
{
    enum response_t response = RSP_OK;

    uart_puts("+OK/\r\n");
    while (response != RSP_FINISHED)
    {
        response = process_line();
        if (response == RSP_OK)
            uart_puts("+OK/");
        else if (response != RSP_FINISHED)
            uart_puts("+FAIL/");
        wdt_reset();
    }   
    boot_rww_enable ();
    while (1) // Use watchdog to reboot
        ;
}

Anyway that should give you a good start and the above code has been pretty well tested with some fairly complex firmware. You'll just need a bit of work to write the missing UART and other initialisation code to determine when the bootloader should be called for your application. It may also need some tweaking for a ATmega32A but I think the approach should work pretty well across AVR devices.

\$\endgroup\$
3
  • \$\begingroup\$ Hi PeterJ, when some pages other than last page have short-length code as in ":02072000FFCF09 :1007220041545E4950494E49543D220000000000A8", i am getting that 2 bytes shifting in every page till the end \$\endgroup\$
    – gzix
    Commented Jun 10, 2014 at 13:12
  • \$\begingroup\$ @user2943851, I probably won't have a chance today but did you want to post the file in say pastebin and reply here with a link and I'll check it out and see what I can spot. \$\endgroup\$
    – PeterJ
    Commented Jun 11, 2014 at 3:20
  • \$\begingroup\$ Right now, I am using SRecord to convert variable-length Intel Hex to Fixed-length Intel Hex. Though, It would be nice to learn the parsing of original Intel hex format. So trying hard \$\endgroup\$
    – gzix
    Commented Jun 12, 2014 at 9:56
5
\$\begingroup\$

Regarding the HEX format:

As suggested in the comment, have a read about the Intel HEX format. http://en.wikipedia.org/wiki/Intel_HEX

What it says is basically, that each line corresponds to a block of data. The format of the line being as follows:

:XYZC

Where:

  • X is 1 byte specifying the payload length
  • Y is 1 byte specifying the payload type *
  • Z is the payload of length X bytes
  • C is 1 byte of the line's checksum

*

’00’ Data Record
’01’ End of File Record
’02’ Extended Segment Address
’03’ Start Segment Address Record
’04’ Extended Linear Address Record
’05’ Start Linear Address Record

See http://microsym.com/editor/assets/intelhex.pdf for the entire specification.


About the method of transmission:

It is also mentioned in the comment - You are much better off sending the entire file as-is. That way you don't need a specialized program at the PC-side, just a terminal that talks to your device. Then parse the text on-chip.


How to write it:

The easiest method is to start reading Atmel's application notes:

All Self-programming operations are performed using the SPM instruction

This can be found here: http://www.atmel.com/images/doc1644.pdf

The AVR109 is a famous bootloader for Mega devices. You may find lots of info here.

\$\endgroup\$
2
  • \$\begingroup\$ Ya i will parse and i will take only the data. My question now is "How to write it?", What to do with the checksum \$\endgroup\$
    – gzix
    Commented May 30, 2014 at 12:32
  • \$\begingroup\$ Hold on, Ill edit the answer. \$\endgroup\$
    – Dzarda
    Commented May 30, 2014 at 12:43
2
\$\begingroup\$

There are tools for such things. First and foremost: You don't really want to let the controller figure out where to put what, since that would require parsing the file on the controller.

Instead, convert the file to a binary file (which can be written 1:1 to flash), then let the controller flash it to the application address (you need to figure that out before). A possible program I see: Your application is linked to address 0x0 - this means that if the bootloader is at that specific address, it will not work (you will need to link it to some different address). If the bootloader resides at some other address (preferably at the end of the flash) you're good to go.

Please find a script which converts to binary and does everything else here: http://1drv.ms/1hj9eU0

It's intended for another Atmel family (AVR32) but you should get the idea. If you have questions, let me know.

\$\endgroup\$
2
  • \$\begingroup\$ I can't get what u mean by 1:1 to flash? \$\endgroup\$
    – gzix
    Commented May 31, 2014 at 6:19
  • 1
    \$\begingroup\$ When you transmit that file to your device, you will not need to write ascii chars 'C' and '9' to your device but you will need to write the byte 0xc9 to that memory location. To create such a binary from a hex file, you will need to convert the hex to a "pure" binary file (this is what srec_cat from the zip file does). \$\endgroup\$
    – Tom L.
    Commented May 31, 2014 at 8:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.