Compare commits
No commits in common. "main" and "r0.0.0" have entirely different histories.
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,6 +0,0 @@
|
|||||||
venv*/
|
|
||||||
build/
|
|
||||||
build_win/
|
|
||||||
build_linux/
|
|
||||||
__pycache__/
|
|
||||||
*.xml
|
|
||||||
21
README.md
21
README.md
@ -1,21 +1,2 @@
|
|||||||
# elbear_uploader
|
# elbear_uploader
|
||||||
Скрипт программирования памяти MIK32 Амур платы ELBEAR ACE-UNO.
|
Скрипт программирования памяти MIK32
|
||||||
Загрузчик построчно отправляет указанный при вызове hex-файл прошивки в заданный com-порт. При этом парсинг команд осуществляется на стороне микроконтроллера.
|
|
||||||
|
|
||||||
Для использования загрузчика необходимо:
|
|
||||||
1. Cкачать последнюю актуальную версию загрузчика для нужной операционной системы из `https://gitflic.ru/project/elron-tech/elbear_uploader/release`
|
|
||||||

|
|
||||||
2. Распаковать скачанный архив на ПК.
|
|
||||||

|
|
||||||
3. Подключить устройство к ПК. Открыть диспетчер устройств и посмотреть номер com порта подключенного устройства.
|
|
||||||

|
|
||||||
4. В распакованной папке, содержащей файл elbear_uploader.exe, открыть терминал. Ввести команду на запуск файла с указанием полного пути до файла, который необходимо загрузить в устройство, а так же номера com порта, к которому подключено устройство.
|
|
||||||
`.\elbear_uploader.exe E:\firmware\firmware.hex --com=COM35`
|
|
||||||
При успешном подключении к устройству в терминале отобразится процесс и результат загрузки файла. В случае неудачного подключения в терминале так же отобразится информация об этом.
|
|
||||||

|
|
||||||
|
|
||||||
Для ускорения процесса прошивки скрипт позволяет при помощи map-файла вырезать из отправляемого hex-файла команды записи данных в неиспользуемую область памяти перед некэшируемой областью. Полный набор опций доступен в справке при запуске скрипта с опцией –-help.
|
|
||||||
|
|
||||||
# необходимые для сборки пакеты
|
|
||||||
|
|
||||||
ubuntu18.04: zlib1g-dev patchelf scons
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
call .\venv\Scripts\activate.bat
|
||||||
python -m nuitka .\elbear_uploader.py ^
|
python -m nuitka .\elbear_uploader.py ^
|
||||||
--output-dir=build_win ^
|
--output-dir=build ^
|
||||||
--output-filename="elbear_uploader.exe" ^
|
--output-filename="elbear_uploader.exe" ^
|
||||||
--standalone
|
--standalone
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
rm -rf build_linux
|
|
||||||
python -m nuitka ./elbear_uploader.py \
|
|
||||||
--output-dir=build_linux \
|
|
||||||
--output-filename="elbear_uploader" \
|
|
||||||
--standalone
|
|
||||||
|
|
||||||
cd build_linux
|
|
||||||
mv elbear_uploader.dist elbear_uploader
|
|
||||||
tar -czvf elbear_uploader_linux.tar.gz elbear_uploader
|
|
||||||
@ -1,69 +1,48 @@
|
|||||||
import serial
|
import serial
|
||||||
import time
|
import time
|
||||||
import argparse
|
import argparse
|
||||||
from sys import exit
|
|
||||||
|
|
||||||
TIMEOUT_DEFAULT = 0.1 # sec
|
ACK = 0x0F # МК подтвердил 0b00001111
|
||||||
|
NACK = 0xF0 # МК отверг 0b11110000
|
||||||
|
COMMAND_PACKAGE_SIZE = b'\x30' # Команда размера пакета
|
||||||
|
COMMAND_SEND_PACKAGE = b'\x60' # Команда отправить пакет
|
||||||
|
COMMAND_FULL_ERASE = b'\xFE'
|
||||||
|
|
||||||
BYTE_COUNT_POS = 0 # индекс счетчика байт данных
|
|
||||||
ADDRESS_POS = 1 # индекс адреса в строке hex файла
|
|
||||||
RECORD_TYPE_POS = 3 # индекс типа записи
|
|
||||||
REC_TYPE_DATA = 0 # тип записи - данные
|
|
||||||
REC_TYPE_EXT_LIN_ADDR = 4 # тип записи - расширенный адрес
|
|
||||||
|
|
||||||
COMMAND_PACKAGE_SIZE = b'\x30' # Команда размера пакета
|
|
||||||
COMMAND_SEND_PACKAGE = b'\x60' # Команда отправить пакет
|
|
||||||
COMMAND_FULL_ERASE = 0xBADC0FEE # Команда очистить чип
|
|
||||||
|
|
||||||
ACK = 0x0F # МК подтвердил 0b00001111
|
|
||||||
NACK = 0xF0 # МК отверг 0b11110000
|
|
||||||
|
|
||||||
|
|
||||||
def send_parsel(data):
|
|
||||||
for attempt in range(10):
|
|
||||||
ser.write(data) # Отправка данных
|
|
||||||
read_byte = ser.read(1) # Чтение ACK/NACK
|
|
||||||
response = int.from_bytes(read_byte, "big")
|
|
||||||
if response == ACK:
|
|
||||||
return True
|
|
||||||
elif response == NACK:
|
|
||||||
print("Get NACK. Exit")
|
|
||||||
exit()
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
# print(f"Invalid answer: {response}. Try again... (Attempt {attempt + 1})")
|
|
||||||
|
|
||||||
# за 10 раз не получили внятного ответа - устройство не отвечает, выходим
|
|
||||||
print("Device not responding")
|
|
||||||
exit()
|
|
||||||
|
|
||||||
def cmd_full_erase():
|
def cmd_full_erase():
|
||||||
# отправить команду очистки чипа
|
ser.write(bytes(COMMAND_FULL_ERASE))
|
||||||
send_parsel(COMMAND_FULL_ERASE.to_bytes(4, "big"))
|
read_byte = ser.read(1) # Прочесть байт ACK/NACK от контроллера
|
||||||
|
|
||||||
# Если дошли сюда, значит контроллер ответил на команду
|
|
||||||
ser.timeout = None # Выключить таймаут, чтобы дождаться завершения процесса стирания чипа
|
|
||||||
read_byte = ser.read(1) # Прочесть байт ACK/NACK от контроллера
|
|
||||||
ser.timeout = TIMEOUT_DEFAULT # Включить таймаут обратно
|
|
||||||
if int.from_bytes(read_byte, "big") == NACK:
|
if int.from_bytes(read_byte, "big") == NACK:
|
||||||
print("NACK. FULL_ERASE FAILED")
|
print("NACK. COMMAND_FULL_ERASE")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
# Задать размер пакета
|
# Задать размер пакета
|
||||||
def cmd_package_size(package_size):
|
def cmd_package_size(package_size):
|
||||||
# Отправить команду размера передаваемого пакета
|
ser.write(COMMAND_PACKAGE_SIZE) # Отправить команду размера передаваемого пакета
|
||||||
send_parsel(COMMAND_PACKAGE_SIZE)
|
read_byte = ser.read(1) # Прочесть байт ACK/NACK от контроллера
|
||||||
# Если дошли сюда, значит контроллер ответил на команду. Послать количество передаваемых байт
|
if int.from_bytes(read_byte, "big") == NACK:
|
||||||
send_parsel((package_size - 1).to_bytes(1, "big"))
|
print("NACK. COMMAND_PACKAGE_SIZE")
|
||||||
return True
|
exit()
|
||||||
|
ser.write((package_size - 1).to_bytes(1, "big")) # Если контроллер ответил на команду, послать количество передаваемых байт
|
||||||
|
read_byte = ser.read(1) # Прочесть байт ACK/NACK
|
||||||
|
if int.from_bytes(read_byte, "big") == NACK:
|
||||||
|
print("NACK. COMMAND_PACKAGE_SIZE")
|
||||||
|
exit()
|
||||||
|
|
||||||
# Отправить пакет
|
# Отправить пакет
|
||||||
def cmd_send_package(data_package):
|
def cmd_send_package(data_package):
|
||||||
# Команда загрузить пакет
|
# Команда загрузить пакет
|
||||||
send_parsel(COMMAND_SEND_PACKAGE)
|
ser.write(COMMAND_SEND_PACKAGE)
|
||||||
# Если дошли сюда, значит контроллер ответил на команду. Отправить пакет
|
read_byte = ser.read(1) # Прочесть байт ACK/NACK
|
||||||
send_parsel(bytes(data_package))
|
if int.from_bytes(read_byte, "big") == NACK:
|
||||||
|
print("NACK. COMMAND_SEND_PACKAGE")
|
||||||
|
exit()
|
||||||
|
# Отправка пакета
|
||||||
|
ser.write(bytes(data_package)) # Если контроллер ответил на команду, послать пакет
|
||||||
|
read_byte = ser.read(1) # Прочесть байт ACK/NACK
|
||||||
|
if int.from_bytes(read_byte, "big") == NACK:
|
||||||
|
print("NACK. SEND_PACKAGE")
|
||||||
|
exit()
|
||||||
|
|
||||||
def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '_', printEnd = "\r"):
|
def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '_', printEnd = "\r"):
|
||||||
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
|
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
|
||||||
@ -77,7 +56,7 @@ DEFAULT_BAUDRATE = 230400
|
|||||||
|
|
||||||
def createParser():
|
def createParser():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
prog='elbear_uploader.py',
|
prog='bootloader.py',
|
||||||
description='''Script for writing to external flash on SPIFI interface'''
|
description='''Script for writing to external flash on SPIFI interface'''
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -123,6 +102,9 @@ def createParser():
|
|||||||
parser = createParser()
|
parser = createParser()
|
||||||
namespace = parser.parse_args()
|
namespace = parser.parse_args()
|
||||||
|
|
||||||
|
REC_TYPE_DATA = 0
|
||||||
|
REC_TYPE_EXT_LIN_ADDR = 4
|
||||||
|
|
||||||
if namespace.hexpath:
|
if namespace.hexpath:
|
||||||
# читаем хекс
|
# читаем хекс
|
||||||
with open(f"{namespace.hexpath}", "r", encoding='utf-8') as f:
|
with open(f"{namespace.hexpath}", "r", encoding='utf-8') as f:
|
||||||
@ -141,51 +123,18 @@ if namespace.hexpath:
|
|||||||
data.append(int(line[i:i+2], 16))
|
data.append(int(line[i:i+2], 16))
|
||||||
data_lines.append(data)
|
data_lines.append(data)
|
||||||
|
|
||||||
# проверить, что в адресах строк с данными нет пропусков, иначе elbear_bootloader_fw может сработать некорректно
|
|
||||||
while i < (len(data_lines) - 1):
|
|
||||||
# если обе строки содержат данные
|
|
||||||
if (data_lines[i][RECORD_TYPE_POS] == REC_TYPE_DATA) and (data_lines[i+1][RECORD_TYPE_POS] == REC_TYPE_DATA):
|
|
||||||
# если разница адресов текущей и следующей строк больше, чем кол-во байт в текущей строке,
|
|
||||||
# нужно забить пропуск строками с нулевыми данными
|
|
||||||
address_cur = (data_lines[i][ADDRESS_POS]<<8) | data_lines[i][ADDRESS_POS+1]
|
|
||||||
bytesQty_cur = data_lines[i][BYTE_COUNT_POS]
|
|
||||||
address_next = (data_lines[i+1][ADDRESS_POS]<<8) | data_lines[i+1][ADDRESS_POS+1]
|
|
||||||
missedBytesQty = (address_next - address_cur) - bytesQty_cur
|
|
||||||
while missedBytesQty > 0:
|
|
||||||
# адрес новой строки данных
|
|
||||||
address_cur += bytesQty_cur
|
|
||||||
# кол-во байт в новой строке
|
|
||||||
# буферы в elbear_bootloader_fw рассчитаны на строки, в которых не более 16 байт данных, поэтому
|
|
||||||
# бьем пропущенные данные по 16 байт
|
|
||||||
if missedBytesQty > 16:
|
|
||||||
bytesQty_cur = 16
|
|
||||||
# или пишем все, что осталось
|
|
||||||
else:
|
|
||||||
bytesQty_cur = missedBytesQty
|
|
||||||
# сформировать новый элемент списка, добавить его контрольную сумму и вставить в общий список
|
|
||||||
# кол-во байт данных 2 байта адреса тип записи нужное кол-во нулей CRC
|
|
||||||
new_line = [bytesQty_cur, (address_cur >> 8)&0xFF, address_cur&0xFF, REC_TYPE_DATA] + [0] * bytesQty_cur
|
|
||||||
new_line.append((256 - sum(new_line) % 256) % 256)
|
|
||||||
data_lines.insert(i + 1, new_line)
|
|
||||||
# обновить оставшееся количество байт и перейти к следующей строке
|
|
||||||
missedBytesQty -= bytesQty_cur
|
|
||||||
i += 1
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
# убираем из отправки неиспользуемое место, которое сделал *fill*
|
# убираем из отправки неиспользуемое место, которое сделал *fill*
|
||||||
if namespace.mappath and namespace.ncsn:
|
if namespace.mappath and namespace.ncsn:
|
||||||
with open(f"{namespace.mappath}", "r", encoding='utf-8') as f:
|
with open(f"{namespace.mappath}", "r", encoding='utf-8') as f:
|
||||||
map_lines = f.readlines()
|
map_lines = f.readlines()
|
||||||
|
|
||||||
# находим строку, где впервые упоминается некешируемая область
|
# находим строку, где впервые упоминается некешируемая область
|
||||||
no_cache_line_idx = 0
|
|
||||||
for i in range(0, len(map_lines)):
|
for i in range(0, len(map_lines)):
|
||||||
if namespace.ncsn in map_lines[i]:
|
if namespace.ncsn in map_lines[i]:
|
||||||
no_cache_line_idx = i
|
no_cache_line_idx = i
|
||||||
break
|
break
|
||||||
|
|
||||||
# от нее поднимаемся выше, пока не наткнемся на *fill*
|
# от нее поднимаемся выше, пока не наткнемся на *fill*
|
||||||
filter_start_addr = filter_stop_addr = 0
|
|
||||||
for i in range(no_cache_line_idx, 0, -1):
|
for i in range(no_cache_line_idx, 0, -1):
|
||||||
if '*fill*' in map_lines[i]:
|
if '*fill*' in map_lines[i]:
|
||||||
fill_cmd_str = map_lines[i]
|
fill_cmd_str = map_lines[i]
|
||||||
@ -210,13 +159,8 @@ if namespace.hexpath:
|
|||||||
i -= 1 # текущая строчка удалилась, следующая будет с тем же индексом
|
i -= 1 # текущая строчка удалилась, следующая будет с тем же индексом
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
ser = serial.Serial(port = namespace.com, baudrate = namespace.baudrate, timeout = TIMEOUT_DEFAULT)
|
ser = serial.Serial(port = namespace.com, baudrate = namespace.baudrate)
|
||||||
|
time.sleep(0.5) # Задержка чтобы успел выставиться RESET
|
||||||
# проверка подключения устройства
|
|
||||||
ping = False
|
|
||||||
ping = cmd_package_size(15) # если устройство не отвечает, дальше этой функции не пройдем
|
|
||||||
if ping:
|
|
||||||
print("Device connected")
|
|
||||||
|
|
||||||
if namespace.fullerase:
|
if namespace.fullerase:
|
||||||
print('Erasing memory')
|
print('Erasing memory')
|
||||||
@ -235,7 +179,7 @@ if namespace.hexpath:
|
|||||||
# printProgressBar(progress, 100, prefix = 'Upload:', suffix = 'Complete', length = 50)
|
# printProgressBar(progress, 100, prefix = 'Upload:', suffix = 'Complete', length = 50)
|
||||||
if time.time() > timestart + resolution:
|
if time.time() > timestart + resolution:
|
||||||
timestart += resolution
|
timestart += resolution
|
||||||
print(f'Uploaded {int(progress)}%', flush=True)
|
print(f'Uploaded {int(progress)}%')
|
||||||
if progress == 100:
|
if progress == 100:
|
||||||
all_showed = True
|
all_showed = True
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.6 KiB |
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Loading…
Reference in New Issue
Block a user