Как совместить загрузку прошивки через bootloader и программатор?

Возникла такая ситуация…
Есть устройство на микроконтроллере Atmega1280. И есть потребность организовать загрузку прошивки через программатор и через bootloader. Загрузка через bootloader будет происходить по RS485 интерфейсу на расстоянии в метров 300.

Вот возник вопрос, как совместить обе функции загрузки?
Если кто то знает и сталкивался с этим, интересно было бы узнать как такая задача решается.

Если прошивать через программатор, тогда ясно, что затрется область загрузчика и загрузчику конец. Жалко что нет такого FUSE бита, который бы запрещал затирать область загрузчика при использовании программатора. Надо написать письмо в Atmel чтоб срочно сделали. Тогда есть такой вариант, в исходный код проекта вклинить и код загрузчика. При прошивке программатором, мы зальем одновременно и загрузчик. Только мне не понятно, как совместить два проекта в одном, точнее как это сделать в СИ, чтоб код загрузчика лег в нужную область памяти микроконтроллера. На асме легко, директива .org позволяет разместить твой код в любом месте памяти микроконтроллера. Вот как это на СИ сделать, пока еще не в курсе.

Так что пока в голове вырисовывается один единственный вариант как решить вопрос. В проект загружаемый в устройство вклинить заодно и код загрузчика. Все это дело вместе скомпилировать и загружать программатором, только в таком случае загрузчик не будет погибать. Но это пока в теории, надо на практике испробовать.

Если есть какие у кого мысли по этому вопросу, прошу под кат.

Комментарии (43)

RSS свернуть / развернуть
У AVR код загрузчика валяется где-то в конце флеша. И при записи прошивки, если она не перезапишет ВЕСЬ флеш, стираться не должен. Разве нет?
0
Ну да загрузчик находится в конце флеша. Но при записи программатором прошивки сперва стирается вся флеш память, только потом сверху накатывается прошивка. Можно перед прошивкой снять галочку ERASE DEVISE. Тогда программатор накатает прошивку поверх тех старых данных которые были во флеше, тогда загрузчик не затрется. Но при этом старые данные, особенно если они выходили за границы новой прошивки удалены не будут. Я уже эксперементировал с этим раньше, если не делать стирание всего девайса, глюки начинаются в работе микроконтроллера. Перед прошивкой надо обязательно делать весь, ERASE DEVISE. Кстати случайно может кто то при использовании программатора выставить галочку ERASE DEVISE и загрузчику сразу приснится труба.
0
Я уже эксперементировал с этим раньше, если не делать стирание всего девайса, глюки начинаются в работе микроконтроллера.

Хм, честно говоря, странное поведение. По логике, если после такой перепрошивки и останется «хвост» от старой прошивки в конце памяти — то это не должно влиять на работу новой прошивки. Мусор, который остался в конце от старой прошивки, по сути, неиспользуемая часть флеш и код, который там мог остаться, никогда не должен получить управление и т. д. По логике, МК должно быть пофиг чем забита неиспользуемая часть флеш.
+2
Да согласен, но почему то без полного стирания было с прогой что то не то. Не так работает. Как только все стер и снова залил все пучком стало. Причем я не один раз пробовал. Я не знаю почему так происходит.
0
может вам уже ответили ниже — не смотрел, но всё же.
если вы «стираете» флеш-память то все биты переводятся в одно активное состояние (в логическую единицу, емнип), т.е. как-бы взводятся. как курок. это довольно длительный и энергозатратный процесс.
при записи же во флеш нужные биты переводятся в противоположное состояние, не активное. перевод в активное — только через стирание флешки.
соответственно когда вы писали поверх одной проги другую во флеше получалась белиберда — одна программа накладывалась на другую. инструкции портились. попробуйте записать ещё раз две программы, считайте и дизассемблируйте.
т.о. во флеш можно только «дописывать»
0
В общем то я и предполагал, что так и происходит. То есть при записи прошивки, надо всегда делать стирание девайса.
0
Надо выполнять стирание той области памяти, где будет находится новая программа и (опционально) используемые ей области данных.
Как по мне, если у вас без _полного_ стирания чипа глючит, а работает только после полного стирания, то вероятно у вас где-то портится указатель и он начинает указывать на флеш (это если арм или стм) или если используете РидПрогМем-не-помню-названия в авр с таким же битым указателем.
Не помешает проверить код на наличие ошибок (потеряли разадресацию, вышли за границу массива, прибавили лишнее и т.д.).
0
А если тупо склеить два .hex файла?
0
Только не забыть смещение от конца прошивки до начала загрузчика забить 0xFF.
В студии можно в Solution объединить 2 проекта. Сделать основной код зависящим от bootloader и в post build step добавить скрипт, который объединит прошивки.
0
В студии можно в Solution объединить 2 проекта
А это уже интересно. Никогда такого не делал. Можно поподробнее? Например в скриншотах или типа видео сделать и на ютуб выложить?
0
1. В студии создаете (открываете) 1й проект (MainProgram) и открываете вкладку Solution Explorer (View->Solution Explorer)
2. Правой кнопкой на Solution. Add->New Project (для нового)/Existing Project (для существующего)
3. Правой кнопкой на Solution. Project Dependencies и там ставим зависимости кто от кого зависит.
4. Правой кнопкой на проекте MainProgram ->Properties. Build Events->Post build event command line и туда пишете скрипт объединяющий прошивки.
0
Это очень даже интересно, будем попробовать.
0
Только не забыть смещение от конца прошивки до начала загрузчика забить 0xFF.
Это совсем необязательно.
0
Только не забыть смещение от конца прошивки до начала загрузчика забить 0xFF.
Зачем?
Вот маркер конца данных (:00000001FF) из первого кекса убрать надо. И если в нем есть директивы переключения сегмента — то они могут помешать второму кексу.
0
  • avatar
  • Vga
  • 13 сентября 2013, 16:48
Да вариант. Но в итоге загружать данные будут люди ничего в этом не соображающие. Надо сделать как можно все проще и понятнее без лишних телодвижений. Типа нажал одну кнопку, загрузка произошла. Все. Склеивание файлов для таких людей будет напряжным вопросом, требующим напряжения ума. Малейшая ошибка и труба, система навернулась.
0
Склеить можно скриптом или своей прогой. Нажали кнопку, к нужному файлу приклеился загрузчик.
А еще секции можно делать ключиком --section-start, но я не пробовал разбивать на куски.
0
А еще секции можно делать ключиком --section-star
Это типа замены директивы .ORG? А как это будет буквально в коде выглядеть?
0
Код будет такой же как и для обычной программе, но в настройках линкера нужно добавить строку наподобие такой:

-Wl,--section-start=.text=0x30000 

В случае 1280 константа на 30000, а другая.
0
Это здесь что ли? Конкретно где именно?
0
Можно здесь.
Можно тут:

Можно в makefile.
0
Так кекс же ты подготавливаешь, сам и склеишь.
Еще, в принципе, можно просто после стирания чипа прошить два кекса по отдельности.
+1
  • avatar
  • Vga
  • 13 сентября 2013, 16:45
Ну да, кекс я сам испеку. просто в будущем пользователи будут добавлять/убавлять некоторые данные в исходный код. Потом компиляции кекс измениться. Каждый раз придется склеивать заново. мне кажется надо в исходный код проекта вклинить код загрузчика, тогда при компиляции появится один кекс файл. Ничего склеивать не придется. Но это пока теория надо пробовать.
0
В нотепаде можно склеить.
0
Решение от лентяя:
склеить в контроллере :)
1. Заливаете бутлоадер,
2. Потом через него основную прогу
3. Сливаете всю прошивку из контроллера и вуаля апп+бут
+1
основная прога будет все время меняться и ее будут заливать то программатором то через бутак, эта схема не совсем подходит.
0
Тогда нужно понимать, прошивка производится скриптом или из среды разработки?
Нужно сделать два разных скрипта, один для заливки программатором, другой для заливки через загрузчик.
В скрипт для программатора нужно добавить слив двух .hex файлов (написать на любом удобном языке, который тупо отрежет конец первого .hex и сольёт со вторым. Для гарантии можно в эту прогу добавить контроль, вплоть до полной пересборки .hex файла из двух).
Тогда ошибок будет минимум, не будет попыток залить загрузчиком слитый файл.
0
ну тогда тоже просто -2 прошивки
1. прошивка под программатор — апп+бут
2. прошивка под бутлоадер апп
0
Иногда так и делал. Но лениво это. Минута-другая на это уходит.
Сейчас проще делаю: открываю hex основной прошивки и копи-пащу туда hex бута.
0
Смотрю datasheet и вижу
• Fill temporary page buffer
• Perform a Page Erase
• Perform a Page Write

Сюда по тому же datasheet bootloader может занимать от 4 до 32 страниц.
А теперь такая ситуация: в прошивке поменялся бутлоадер. Кусок кода отвечающий за прошивку страницы находился (в предыдущем лоадере) на границе страниц (пусть 3-4). Находясь на четвертой странице мы прошиваем третью и какой-нибудь ret возвращает нас на 3ю страницу, где уже совсем другой код.

Если ошибаюсь, то прошу не пинать.
0
Пару лет назад решал такую же задачу. Сначала был написан и отлажен бутлодер, затем кекс бутлодера был переведен самописной утилиткой HEX2ASM.exe в boot.asm файл с примерно таким содержанием

// Ver 2.0 2MGz
#asm
.cseg
RetAddress_BootLdr_asm:
.org 0x7000
  .db  0x0c,0x94,0x67,0x70,0x18,0x95,0x18,0x95,0x18,0x95,0x18,0x95,0x18,0x95,0x18,0x95
  .db  0x18,0x95,0x18,0x95,0x18,0x95,0x18,0x95,0x18,0x95,0x18,0x95,0x18,0x95,0x18,0x95

. . .

  .db  0xe8,0x17,0x19,0xf0,0xed,0xbb,0xe2,0x9a,0xe1,0x9a,0x9f,0xbf,0x08,0x95,0x5a,0x93
  .db  0x4a,0x93,0x3a,0x93,0x2a,0x93,0x1a,0x93,0x0a,0x93,0x08,0x95,0x5d,0x81,0x4c,0x81
  .db  0x3b,0x81,0x2a,0x81,0x19,0x81,0x08,0x81,0x08,0x95,0xac,0x0f,0xbd,0x1f,0x05,0x90
  .db  0x0d,0x92,0x8a,0x95,0xe1,0xf7,0x08,0x95

.org RetAddress_BootLdr_asm
#endasm

Затем полученный файл был подключен в основном проекте.

#include "boot.asm"

Все писалось в CodeVision.
0
Оригинальная идея.
1.Получается что твоя основная прошивка была написана на АСМЕ или на си?
2.Слушай братуха можешь с этой программкой HEX2ASM.exe поделиться?
3. А можешь этот проект скинуть?
0
1.Получается что твоя основная прошивка была написана на АСМЕ или на си?
На С, по видимому.
Интересно, а что это за директивы такие #asm #endasm? Прокатит все это в AtmelStudio6?
Сомневаюсь. Но это и не нужно. Создаешь файл аналогичного вида (без специфичных директив вроде #asm), даешь ему расширение .s и включаешь в проект.
Можно также сконвертировать в сишный массив (прогой вроде hex2h/bin2h) и включить в программу его. Массиву нужно назначить адрес директивой, аналогичной .org (какая именно директива для этого используется надо смотреть в доках на компилятор), либо назначить ему соответствующий сегмент и задать линкеру адрес этого сегмента, как писали выше.
2.Слушай братуха можешь с этой программкой HEX2ASM.exe поделиться?
Ее элементарно нагуглить (прямо по названию hex2asm) и не менее элементарно написать.
0
  • avatar
  • Vga
  • 14 сентября 2013, 09:47
Гуглил все ссылки нерабочие. Так и не нашел.
Кажется это решение предлагаемое janulan оптимальное в моем случае.
0
Ее свою написать — задача на час, и то из-за необходимости парсить кекс.
hex2asm.lua:
function HexToBin(Line)
  local Result = ""
  
  local function HexToInt(HexChar)
    if (HexChar >= 65) and (HexChar <= 70) then -- 'A' to 'F'
      return HexChar - 55
    elseif (HexChar >= 48) and (HexChar <= 57) then -- '0' to '9'
      return HexChar - 48
    end
  end
  
  for Byte in Line:gmatch("%x%x") do
    Result = Result .. string.char(HexToInt(Byte:upper():byte(1)) * 16 + HexToInt(Byte:upper():byte(2)))
  end
  
  return Result
end

function ParseHexLine(Line)
  local Data = HexToBin(Line:match(":(%x+)"))
  if Data:len() < 5 then
    print("Too short line:")
    return false
  end
  local Count, Addr, Cmd = Data:byte(1), Data:byte(2) * 256 + Data:byte(3), Data:byte(4)
  if (Data:len() < Count + 5) or ((Cmd ~= 0) and (Cmd ~= 1)) then
    print("Too short line or unknown command:")
    return false
  end
  local Checksum = 0
  for i = 1, Count + 5 do
    Checksum = Checksum + Data:byte(i)
  end
  if math.fmod(Checksum, 256) ~= 0 then
    print("Checksum error:")
    return false
  end
  if Cmd == 1 then
    return -1
  else
    return Addr, Data:sub(5, Count+4)
  end
end

print("iHEX to Assembler converter")
print("Usage: lua "..arg[0].." <hex file> [output file]")

HexFile = io.open(arg[1] or "")
if not HexFile then
  print("Can't open input file")
  os.exit()
end

AsmFile = io.open(arg[2] or (arg[1] .. ".s"), "w+")
if not HexFile then
  print("Can't open output file")
  HexFile:close()
  os.exit()
end

OutBuf = ""

function DumpBuf()
  if OutBuf:len() > 0 then
    AsmFile:write("  .db " .. OutBuf .. "\n")
    OutBuf = ""
  end
end

function EmitOrg(Addr)
  DumpBuf()
  AsmFile:write(string.format(".org 0x%04x\n", Addr))
end

function EmitBytes(Data)
  for i = 1, Data:len() do
    if OutBuf ~= "" then
      OutBuf = OutBuf .. ", "
    end
    OutBuf = OutBuf .. string.format("0x%02x", Data:byte(i))
    if OutBuf:len() >= 69 then
      DumpBuf()
    end
  end
end

CurAddr = 0

for Line in HexFile:lines() do
  local Addr, Data = ParseHexLine(Line)
  if not Addr then
    print(Line)
  elseif Addr == -1 then
    break
  else
    if Addr ~= CurAddr then
      EmitOrg(Addr) 
    end
    EmitBytes(Data)
    CurAddr = Addr + Data:len()
  end
end

DumpBuf()
print("Done")

Usage: lua hex2asm.lua <hex file> [output file]
+1
  • avatar
  • Vga
  • 14 сентября 2013, 11:32
Упс, небольшая ошибка. Последние две строчки надо заменить на
DumpBuf()

AsmFile:close()
HexFile:close()

print("Done")
0
  • avatar
  • Vga
  • 14 сентября 2013, 12:15
О, вижу знакомый синтаксис.
Рекомендовал бы подправить код:
AsmFile = io.open(arg[2] or (arg[1] .. ".s"), "w+")
if not AsmFile then
  print("Can't open output file")
  HexFile:close()
  os.exit()
end
Так работать будет лучше, без attempt to call a null value'ев :)
Еще бы имена файлов подготовить зарание, что бы была возможность добавить их в вывод сообщений об ошибках. Полезно.
+1
А, идиотская ошибка) Копипаст зло.
Вывести-то и так можно было, но я забил. Там вообще половина сообщений об ошибках появилась только в процессе отладки.
0
  • avatar
  • Vga
  • 16 сентября 2013, 18:02
Только на процесс отладки используется
assert( arg[1], "Specify HEX-file name")
AsmFile = assert(  io.open(arg[2] or (arg[1] .. ".s"), "w+" ), "Failed to open output file" )
for Line in io.lines(arg[1]) do
 -- Do Magic
end
Но смотреть на дампы стека для непосвященного… При том что допустить ошибку в имени файла — плевое дело и сам (не то что юзер) порой на столько тупанешь, что не замечаешь этого: «ведь все же правильно, что оно падает?»
Вот, а у тебя уже «более культурные заготовки для юзерфрендли интерфейса». И обработка ошибок лишней не бывает. Но копипаста — зло :)
0
Но смотреть на дампы стека для непосвященного…
Честно сказать, я и сам на них не смотрю)

Хм, а про ассерт не знал.
0
  • avatar
  • Vga
  • 16 сентября 2013, 22:48
1. Основная прошивка написана на с.
2. Hex2asm.exe yadi.sk/d/vrw0XoCc9Leen
3. Сам конечный рабочий проект отдать не могу, но проектик, иллюстрирующий идею, пожалуйста. yadi.sk/d/x2Cy0HYo9Ln2D
0
Спасибо тебе, настоящий мужик.
0
Интересно, а что это за директивы такие #asm #endasm? Прокатит все это в AtmelStudio6?
0
два проекта (бутлодырь и собственно аппликуха), живущих своей жизнью. и только перед продакшеном для прошивки программатором собираются в единый кекс.
рекомендую srecord.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.