Compare commits

...

17 Commits
r0.1.0 ... main

Author SHA1 Message Date
67759c6439 Обновление статуса загрузки прошивки в ArduinoIDE
Co-authored-by: KLASSENTS <klassen@elron.tech>
Co-committed-by: KLASSENTS <klassen@elron.tech>
2025-01-14 11:51:30 +03:00
b88ba38dcd сборка под ubuntu 18.04 2024-10-17 13:14:48 +07:00
2ded141cc5 Обновлен README.md 2024-10-14 12:23:28 +03:00
3173cde3e4 Обновить README.md 2024-10-14 11:53:55 +03:00
b39e3d3e0c Обновить README.md 2024-10-14 11:53:13 +03:00
4d6eb5d004 Обновить README.md 2024-10-14 11:52:23 +03:00
96827e82f4 Обновить README.md 2024-10-14 11:51:57 +03:00
220b388e55 Обновлен README.md 2024-10-14 11:51:10 +03:00
1ab68859ff картинки для описания 2024-10-14 15:48:47 +07:00
14e1089512 исправила формирование контрольной суммы для новых строк 2024-10-07 15:58:01 +07:00
bfecb4bd2e изменена функция отправки команд - обработка ситуации, когда ответ не совпадает с ack/nack. исправлен баг при загрузке с опцией no-cache-section. добавлена вставка строк с нулевыми данными в случае пропуска адресов в hex файле 2024-10-07 12:27:20 +07:00
29d4fa93b4 папки для сборки 2024-09-10 16:19:22 +07:00
d6afe948cf need different venvs for win and linux 2024-09-10 16:03:07 +07:00
e250512ab1 no venv in build scripts 2024-09-10 16:01:08 +07:00
b2c1557107 ARD-84 (#1)
- изменилась команда full erase
- на время очистки чипа отключается таймаут в serial
2024-09-10 11:52:50 +03:00
b8553c40a1 win chars remove 2024-09-10 10:09:02 +07:00
3542c33889 скрипт для сборки под linux 2024-08-20 19:50:14 +07:00
9 changed files with 125 additions and 49 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
venv*/
build/
build_win/
build_linux/
__pycache__/
*.xml

View File

@ -1,3 +1,21 @@
# elbear_uploader
Скрипт программирования памяти MIK32 Амур платы ELBEAR ACE-UNO.
Загрузчик построчно отправляет указанный при вызове hex-файл прошивки в заданный com-порт. При этом парсинг команд осуществляется на стороне микроконтроллера. Для ускорения процесса прошивки скрипт позволяет при помощи map-файла вырезать из отправляемого hex-файла команды записи данных в неиспользуемую область памяти перед некэшируемой областью. Полный набор опций доступен в справке при запуске скрипта с опцией -help.
Загрузчик построчно отправляет указанный при вызове hex-файл прошивки в заданный com-порт. При этом парсинг команд осуществляется на стороне микроконтроллера.
Для использования загрузчика необходимо:
1. Cкачать последнюю актуальную версию загрузчика для нужной операционной системы из `https://gitflic.ru/project/elron-tech/elbear_uploader/release`
![Releases](images/Releases.PNG)
2. Распаковать скачанный архив на ПК.
![Unpacked](images/Unpacked.PNG)
3. Подключить устройство к ПК. Открыть диспетчер устройств и посмотреть номер com порта подключенного устройства.
![comPort](images/comPort.PNG)
4. В распакованной папке, содержащей файл elbear_uploader.exe, открыть терминал. Ввести команду на запуск файла с указанием полного пути до файла, который необходимо загрузить в устройство, а так же номера com порта, к которому подключено устройство.
`.\elbear_uploader.exe E:\firmware\firmware.hex --com=COM35`
При успешном подключении к устройству в терминале отобразится процесс и результат загрузки файла. В случае неудачного подключения в терминале так же отобразится информация об этом.
![command](images/command.PNG)
Для ускорения процесса прошивки скрипт позволяет при помощи map-файла вырезать из отправляемого hex-файла команды записи данных в неиспользуемую область памяти перед некэшируемой областью. Полный набор опций доступен в справке при запуске скрипта с опцией -help.
# необходимые для сборки пакеты
ubuntu18.04: zlib1g-dev patchelf scons

View File

@ -1,6 +1,5 @@
call .\venv\Scripts\activate.bat
python -m nuitka .\elbear_uploader.py ^
--output-dir=build ^
--output-dir=build_win ^
--output-filename="elbear_uploader.exe" ^
--standalone

10
build_linux.sh Executable file
View File

@ -0,0 +1,10 @@
#!/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

View File

@ -3,48 +3,67 @@ import time
import argparse
from sys import exit
ACK = 0x0F # МК подтвердил 0b00001111
NACK = 0xF0 # МК отверг 0b11110000
COMMAND_PACKAGE_SIZE = b'\x30' # Команда размера пакета
COMMAND_SEND_PACKAGE = b'\x60' # Команда отправить пакет
COMMAND_FULL_ERASE = b'\xFE'
TIMEOUT_DEFAULT = 0.1 # sec
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():
ser.write(bytes(COMMAND_FULL_ERASE))
read_byte = ser.read(1) # Прочесть байт ACK/NACK от контроллера
# отправить команду очистки чипа
send_parsel(COMMAND_FULL_ERASE.to_bytes(4, "big"))
# Если дошли сюда, значит контроллер ответил на команду
ser.timeout = None # Выключить таймаут, чтобы дождаться завершения процесса стирания чипа
read_byte = ser.read(1) # Прочесть байт ACK/NACK от контроллера
ser.timeout = TIMEOUT_DEFAULT # Включить таймаут обратно
if int.from_bytes(read_byte, "big") == NACK:
print("NACK. COMMAND_FULL_ERASE")
print("NACK. FULL_ERASE FAILED")
exit()
# Задать размер пакета
def cmd_package_size(package_size):
ser.write(COMMAND_PACKAGE_SIZE) # Отправить команду размера передаваемого пакета
read_byte = ser.read(1) # Прочесть байт ACK/NACK от контроллера
if int.from_bytes(read_byte, "big") == NACK:
print("NACK. COMMAND_PACKAGE_SIZE")
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()
return read_byte == b'\x0f'
# Отправить команду размера передаваемого пакета
send_parsel(COMMAND_PACKAGE_SIZE)
# Если дошли сюда, значит контроллер ответил на команду. Послать количество передаваемых байт
send_parsel((package_size - 1).to_bytes(1, "big"))
return True
# Отправить пакет
def cmd_send_package(data_package):
# Команда загрузить пакет
ser.write(COMMAND_SEND_PACKAGE)
read_byte = ser.read(1) # Прочесть байт ACK/NACK
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()
send_parsel(COMMAND_SEND_PACKAGE)
# Если дошли сюда, значит контроллер ответил на команду. Отправить пакет
send_parsel(bytes(data_package))
def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '_', printEnd = "\r"):
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
@ -58,7 +77,7 @@ DEFAULT_BAUDRATE = 230400
def createParser():
parser = argparse.ArgumentParser(
prog='bootloader.py',
prog='elbear_uploader.py',
description='''Script for writing to external flash on SPIFI interface'''
)
@ -104,9 +123,6 @@ def createParser():
parser = createParser()
namespace = parser.parse_args()
REC_TYPE_DATA = 0
REC_TYPE_EXT_LIN_ADDR = 4
if namespace.hexpath:
# читаем хекс
with open(f"{namespace.hexpath}", "r", encoding='utf-8') as f:
@ -125,18 +141,51 @@ if namespace.hexpath:
data.append(int(line[i:i+2], 16))
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*
if namespace.mappath and namespace.ncsn:
with open(f"{namespace.mappath}", "r", encoding='utf-8') as f:
map_lines = f.readlines()
# находим строку, где впервые упоминается некешируемая область
no_cache_line_idx = 0
for i in range(0, len(map_lines)):
if namespace.ncsn in map_lines[i]:
no_cache_line_idx = i
break
# от нее поднимаемся выше, пока не наткнемся на *fill*
filter_start_addr = filter_stop_addr = 0
for i in range(no_cache_line_idx, 0, -1):
if '*fill*' in map_lines[i]:
fill_cmd_str = map_lines[i]
@ -161,19 +210,13 @@ if namespace.hexpath:
i -= 1 # текущая строчка удалилась, следующая будет с тем же индексом
i += 1
ser = serial.Serial(port = namespace.com, baudrate = namespace.baudrate, timeout = 0.1)
ser = serial.Serial(port = namespace.com, baudrate = namespace.baudrate, timeout = TIMEOUT_DEFAULT)
# проверка подключения устройства
ping = False
for i in range(10): # вместо задержки забрасываем запросами
ping = cmd_package_size(15)
if ping:
break
ping = cmd_package_size(15) # если устройство не отвечает, дальше этой функции не пройдем
if ping:
print("Device connected")
else:
print("Device not responding")
exit()
if namespace.fullerase:
print('Erasing memory')
@ -192,7 +235,7 @@ if namespace.hexpath:
# printProgressBar(progress, 100, prefix = 'Upload:', suffix = 'Complete', length = 50)
if time.time() > timestart + resolution:
timestart += resolution
print(f'Uploaded {int(progress)}%')
print(f'Uploaded {int(progress)}%', flush=True)
if progress == 100:
all_showed = True

BIN
images/Releases.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
images/Unpacked.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
images/comPort.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
images/command.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB