Интересует тема, как можно залить прошивку в ардуино не через ISP, а через UART, по типу как это делается с помощью программатора USB-TTL, но используя не Arduino IDE, а другую Ардуино.
Если хватит "скилла" - изобрети собственный бутлоадер или "подсмотри" у других. Reset тебе не нужен, сделай сеть из ардуин и адресное обращение к ним и Boot то-же адресный. AVR БЕЗ Reset-а запросто "бутлоадится" , впринципе в датащах есть примеры и на С и на Asm. Так-шо, как грицца, фпуть)))))
// в виду наличия в своей схеме GPRS SIM800c , подумывал так : по типу web-сервера , можно перекинуть hex файл прошивки через GPRS на microSD главной ардуинкой. Вторая ардуинка промини только для программирования, считывает этот файл программирует главную ардуину. В инете вроде находил , как можно зашить ардуину с помощью другой ардуины и microSD .
Как-то было здесь обсуждение как ресетить ардуину , и вроде как самый простой и рабочий вариант - это чтоб она сама дергала своим пином, так не геморно и без софтовых ограничений, требуется только пин.
Зашиваете в еепром в каждой ардуинке разный ID . Посылаете команду по rs485 нужной ардуинке заресетиться , и команду помолчать всем одну минутку, дальше шьете по rs485.
все было сделано 7 лет назад, посмотрите здесь , как одна ардуинка шьет другую через UART:
Где же ты раньше был, мил человек))))
Посмотрел код, практически то же самое, что я уже сам почти допилил. Но это не готовое решение, так как для работы rs485 нужно дергать ногу на трансмитторе во время передачи, поэтому придется в любом случае шаманить с загрузчиком.
Одного не понял в проекте BootDrive - там стандартный загрузчик используется?
Назрел еще один вопрос, при экспорте скетчей в hex ардуино ide генерит два файла, один например blink.ino.hex, а второй blink.ino.with_bootloader.hex
Логично предполагаю, что во втором варианте находится еще код загрузчика. Но если загрузчик уже залит в МК, а я буду прошивать его первым файлом начиная с нулевого адреса загрузчик же затирается, правильно? Как в таком случае поступить, с какого адреса начинать заливку пользовательской программы? Или брать файл с загрузчиком и не париться?
Но это не готовое решение, так как для работы rs485 нужно дергать ногу на трансмитторе во время передачи, поэтому придется в любом случае шаманить с загрузчиком.
а вы используйте трансиверы CAN вместо RS485. Будет всё тоже самое как на RS485, только не нужно ногу дёргать и при коллизиях аппаратного вреда не будет. только лучше mcp2551
МАксим выпускает (с 2012-го) драйверы MAX13487 со встроенной логикой, про ногодрыг уже можно забыть. С одной стороны rs485 (A-B) с другой RX-TX ttl. К rx-tx подключал пролифик2303 и без дерганий за всякие dtr-ы и rts-ы опрашивал эл.счетчики. Единственный недостаток – цена.
МАксим выпускает (с 2012-го) драйверы MAX13487 со встроенной логикой, про ногодрыг уже можно забыть. С одной стороны rs485 (A-B) с другой RX-TX ttl. К rx-tx подключал пролифик2303 и без дерганий за всякие dtr-ы и rts-ы опрашивал эл.счетчики. Единственный недостаток – цена.
Да, интересно. Посмотрел схему подключния по даташиту, там установлена гальваническая развязка на tx и rx. Я так понимаю трансивер питается от 5 вольт и RO DI у него на 5В, и развязки для использования последовательного порта с напряжением -12 - +12В. то есть в варианте с ардуино это все не нужно, подаем RX на RO, TX на DI и 5В на RE и SHDN, так?
Но это не готовое решение, так как для работы rs485 нужно дергать ногу на трансмитторе во время передачи, поэтому придется в любом случае шаманить с загрузчиком.
а вы используйте трансиверы CAN вместо RS485. Будет всё тоже самое как на RS485, только не нужно ногу дёргать и при коллизиях аппаратного вреда не будет. только лучше mcp2551
Так к нему же еще can контроллер надо типа mcp2515
Именно так и делал только не к ардуине а к пролифику юсб-ттл, и поддтяжки делал для линий A-B и терминатор на них повесил. Но дорого, в чип-дипе по 60₽ в розницу.
Так к нему же еще can контроллер надо типа mcp2515
не обязательно. MCP2515 нужен для полноценного CANa. В вашем случае можно просто цеплять на rx tx . И будет эдакий недо CAN не до RS485. Т.е. по сути физическая линия будет CAN, а пользоваться будете как RS485, только без ноги прием передача.
Вот первая рабочая версия. Программа читает файл на SD и пишет его по обычному UART в пациента. Для понимания, что прошивка перезаписывается создал на SD в корне 3 файла и прошиваю каждым из них в зависимости от отправленного символа в Serial порт (a,s,d).
#define SS_SD_PIN BUILTIN_SDCARD - это константа для teensy, для обчной ардуино обчно 4.
В программе нет проверки загруженной программы на корректность и проверок ответов optiboot, лишь OK неОК.
Serial.begin(500000); - скорость поставил побольше, чтобы не тормозила при выводе инфы в Serial
#include <SD.h>
#include <SPI.h>
#define STK_OK 0x10
#define STK_FAILED 0x11 // Not used
#define STK_UNKNOWN 0x12 // Not used
#define STK_NODEVICE 0x13 // Not used
#define STK_INSYNC 0x14 // ' '
#define STK_NOSYNC 0x15 // Not used
#define ADC_CHANNEL_ERROR 0x16 // Not used
#define ADC_MEASURE_OK 0x17 // Not used
#define PWM_CHANNEL_ERROR 0x18 // Not used
#define PWM_ADJUST_OK 0x19 // Not used
#define STK_CRC_EOP 0x20 // 'SPACE'
#define STK_GET_SYNC 0x30 // '0'
#define STK_GET_SIGN_ON 0x31 // '1'
#define STK_SET_PARAMETER 0x40 // '@'
#define STK_GET_PARAMETER 0x41 // 'A'
#define STK_SET_DEVICE 0x42 // 'B'
#define STK_SET_DEVICE_EXT 0x45 // 'E'
#define STK_ENTER_PROGMODE 0x50 // 'P'
#define STK_LEAVE_PROGMODE 0x51 // 'Q'
#define STK_CHIP_ERASE 0x52 // 'R'
#define STK_CHECK_AUTOINC 0x53 // 'S'
#define STK_LOAD_ADDRESS 0x55 // 'U'
#define STK_UNIVERSAL 0x56 // 'V'
#define STK_PROG_FLASH 0x60 // '`'
#define STK_PROG_DATA 0x61 // 'a'
#define STK_PROG_FUSE 0x62 // 'b'
#define STK_PROG_LOCK 0x63 // 'c'
#define STK_PROG_PAGE 0x64 // 'd'
#define STK_PROG_FUSE_EXT 0x65 // 'e'
#define STK_READ_FLASH 0x70 // 'p'
#define STK_READ_DATA 0x71 // 'q'
#define STK_READ_FUSE 0x72 // 'r'
#define STK_READ_LOCK 0x73 // 's'
#define STK_READ_PAGE 0x74 // 't'
#define STK_READ_SIGN 0x75 // 'u'
#define STK_READ_OSCCAL 0x76 // 'v'
#define STK_READ_FUSE_EXT 0x77 // 'w'
#define STK_READ_OSCCAL_EXT 0x78 // 'x'
#define STK_HW_VER 0x80
#define STK_SW_MAJOR 0x81
#define STK_SW_MINOR 0x82
#define STK_VTARGET 0x84
#define STK_VADJUST 0x85
#define STK_OSC_PSCALE 0x86
#define STK_OSC_CMATCH 0x87
#define STK_SCK_DURATION 0x89
#define STK_TOPCARD_DETECT 0x98
#define SS_SD_PIN BUILTIN_SDCARD //Пин SD-карты
#define SWITCH_TO_SD digitalWrite(SS_SD_PIN,LOW); delay(2)
#define RESET_PIN 2
byte downloadstate = 0;
char inBuf[200];
byte tic = 0;
String Filename = "";
void setup() {
Serial.begin(500000);
while (!Serial) {
}
if (!SD.begin(SS_SD_PIN)) {
Serial.println("SD failed!");
return;
}
Serial.println("SD OK");
Serial1.begin(115200);
Serial.println("READY");
Serial.println("Press a, s or d");
}
void loop() {
if (downloadstate == 0) { //ждем нажатия а, s, d
if (Serial.available()) {
char inChar = (char)Serial.read();
if (inChar == 'a') {
Serial.println("WORKING");
downloadstate = 1;
Filename = "Blink13.hex";
}
else if (inChar == 's') {
Serial.println("WORKING");
downloadstate = 1;
Filename = "Blink11.hex";
}
else if (inChar == 'd') {
Serial.println("WORKING");
downloadstate = 1;
Filename = "Blink51.hex";
}
}
}
else if (downloadstate == 1) { //команда на перезагрузку
pinMode(RESET_PIN, OUTPUT);
digitalWrite(RESET_PIN, HIGH);
delay(20);
digitalWrite(RESET_PIN, LOW);
Serial.println("REBOOT PROMINI");
downloadstate = 2;
}
else if (downloadstate == 2) { //работа с загрузчиком
if (OptibootWork()) {
downloadstate = 0;
Serial.println("FLASH OK");
}
else {
Fail();
}
digitalWrite(RESET_PIN, HIGH);
}
}
byte OptibootWork() {
Serial.println("START FLASH");
if (!WriteSTK_Sync()) {
delay(200);
if (!WriteSTK_Sync()) {
delay(200);
if (!WriteSTK_Sync()) {
return 0;
}
}
}
Serial.println("STK_HW_VER");
if (!WriteSTK_GetParamert(STK_HW_VER)) return 0;
Serial.println("STK_SW_MAJOR");
if (!WriteSTK_GetParamert(STK_SW_MAJOR)) return 0;
Serial.println("STK_SW_MINOR");
if (!WriteSTK_GetParamert(STK_SW_MINOR)) return 0;
Serial.println("STK_TOPCARD_DETECT");
if (!WriteSTK_GetParamert(STK_TOPCARD_DETECT)) return 0;
Serial.println("STK_VTARGET");
if (!WriteSTK_GetParamert(STK_VTARGET)) return 0;
Serial.println("STK_VADJUST");
if (!WriteSTK_GetParamert(STK_VADJUST)) return 0;
Serial.println("STK_OSC_PSCALE");
if (!WriteSTK_GetParamert(STK_OSC_PSCALE)) return 0;
Serial.println("STK_OSC_CMATCH");
if (!WriteSTK_GetParamert(STK_OSC_CMATCH)) return 0;
Serial.println("STK_SCK_DURATION");
if (!WriteSTK_GetParamert(STK_SCK_DURATION)) return 0;
Serial.println("STK_SW_MAJOR");
if (!WriteSTK_GetParamert(STK_SW_MAJOR)) return 0;
Serial.println("STK_SW_MINOR");
if (!WriteSTK_GetParamert(STK_SW_MINOR)) return 0;
Serial.println("ENTER PROG MODE");
if (!WriteSTK_EnterProgMode()) return 0;
Serial.println("READ SIGN");
if (!WriteSTK_ReadSign()) return 0;
Serial.println("UPLOAD");
if (!UploadFileFromSD(Filename)) return 0;
Serial.println("LEAVE PROG MODE");
if (!WriteSTK_LeaveProgMode()) return 0;
return 1;
}
void Fail() {
downloadstate = 0;
Serial.println("FAIL TO FLASH");
}
byte WriteSTK_Sync() {
Serial1.write(STK_GET_SYNC);
Serial1.write(STK_CRC_EOP);
if (!optibootRcv()) return 0;
return 1;
}
byte WriteSTK_GetParamert(byte Paramert) {
Serial1.write(STK_GET_PARAMETER);
Serial1.write(Paramert);
Serial1.write(STK_CRC_EOP);
if (!optibootRcv()) return 0;
return 1;
}
byte WriteSTK_SetDevice() {
Serial1.write(STK_SET_DEVICE);
Serial1.write(0x86); //device code
Serial1.write(0x00); //revision
Serial1.write(0x00); //progtype: “0” – Both Parallel/High-voltage and Serial mode
Serial1.write(0x01); //parmode: “1” – Full parallel interface
Serial1.write(0x01); //polling: “1” – Polling may be used
Serial1.write(0x01); //selftimed: “1” – Self timed
Serial1.write(0x01); //lockbytes: Number of Lock bytes.
Serial1.write(0x03); //fusebytes: Number of Fuse bytes
Serial1.write(0xff); //flashpollval1
Serial1.write(0xff); //flashpollval2
Serial1.write(0xff); //eeprompollval1
Serial1.write(0xff); //eeprompollval2
Serial1.write(0x00); //pagesizehigh
Serial1.write(0x80); //pagesizelow
Serial1.write(0x04); //eepromsizehigh
Serial1.write(0x00); //eepromsizelow
Serial1.write(0x00); //flashsize4
Serial1.write(0x00); //flashsize3
Serial1.write(0x80); //flashsize2
Serial1.write(0x00); //flashsize1
Serial1.write(STK_CRC_EOP);
if (!optibootRcv()) return 0;
return 1;
}
byte WriteSTK_SetDeviceExt() {
Serial1.write(STK_SET_DEVICE_EXT);
Serial1.write(0x05); //commandsize: how many bytes follow
Serial1.write(0x04); //eeprompagesize: EEPROM page size in bytes.
Serial1.write(0xd7); //signalpagel:
Serial1.write(0xc2); //signalbs2:
Serial1.write(0x00); //ResetDisable: Defines whether a part has RSTDSBL Fuse
Serial1.write(STK_CRC_EOP);
if (!optibootRcv()) return 0;
return 1;
}
byte WriteSTK_EnterProgMode() {
Serial1.write(STK_ENTER_PROGMODE);
Serial1.write(STK_CRC_EOP);
if (!optibootRcv()) return 0;
return 1;
}
byte WriteSTK_ReadSign() {
Serial1.write(STK_READ_SIGN);
Serial1.write(STK_CRC_EOP);
if (!optibootRcv()) return 0;
return 1;
}
byte WriteSTK_LoadAddress(unsigned int addr) {
byte highbyte = addr & 0xff;
byte lowbyte = (addr >> 8) & 0xff;
Serial1.write(STK_LOAD_ADDRESS);
Serial1.write(highbyte);
Serial1.write(lowbyte);
Serial1.write(STK_CRC_EOP);
if (!optibootRcv()) return 0;
return 1;
}
byte WriteSTK_LeaveProgMode() {
Serial1.write(STK_LEAVE_PROGMODE);
Serial1.write(STK_CRC_EOP);
if (!optibootRcv()) return 0;
return 1;
}
byte optibootRcv() {
tic = 0;
byte inCount;
byte inByte;
while (Serial1.available() < 2) {
delay(1);
tic++;
if (tic > 100) {
return 0;
}
}
delay(50);
inCount = 0;
inBuf[inCount] = 0;
while (Serial1.available()) {
inByte = Serial1.read();
//Serial.print(inByte, HEX);
//Serial.print(" ");
if (inCount < 199)
{
inBuf[inCount] = inByte;
inCount++;
}
}
//Serial.println("");
if (inBuf[0] == STK_INSYNC && inBuf[inCount - 1] == STK_OK) {
Serial.println("OK");
return 1;
}
else return 0;
}
unsigned int HexToInt ( char buf[], byte n, byte m) {
unsigned int y = 0;
for (int i = 0; i < m; i++)
{
if (buf[n + i] - '0' > 16) {
y = y + ((buf[n + i] - '0' - 7) << 4 * (m - 1 - i)) ;
}
if (buf[n + i] - '0' < 16) {
y = y + ((buf[n + i] - '0') << 4 * (m - 1 - i));
}
}
return y;
}
byte UploadFileFromSD (String filename) {
const char *fn = filename.c_str();
char header[9] = "00000000";
char charbyte[3] = "00";
//SWITCH_TO_SD;
byte linecount = 0;
byte blockcount = 0;
if (!SD.exists(fn)) return 0;
Serial.print("START TO FLASH FILE ");
Serial.println(filename);
File myFile = SD.open(fn);
if (!myFile) return 0;
if (!WriteSTK_LoadAddress(0)) return 0;
Serial.println("LOAD ADDRESS");
Serial1.write(STK_PROG_PAGE);
Serial1.write(0x00);
Serial1.write(0x80);
Serial1.write(0x46);
while (myFile.available()) {
char c = myFile.read();
if (c == ':') {
linecount++;
for (int i = 0; i < 8; i++) {
header[i] = myFile.read();
}
unsigned int bytecount = HexToInt(header, 0, 2);
unsigned int address = HexToInt(header, 2, 4);
unsigned int comm = HexToInt(header, 6, 2);
if (comm == 0) { //данные для записи
for (int i = 0; i < bytecount; i++) {
for (int j = 0; j < 2; j++) {
charbyte[j] = myFile.read();
}
//Serial.print(charbyte);
byte bytetowrite = HexToInt(charbyte, 0, 2);
Serial1.write(bytetowrite);
}
if (bytecount < 16) {
for (int i = bytecount; i < 16; i++) {
//Serial.print("FF");
Serial1.write(0xFF);
}
}
}
else if (comm == 1) { // последняя строка
for (int i = linecount; i < 9; i++) {
for (int j = 0; j < 16; j++) {
//Serial.print("FF");
Serial1.write(0xFF);
}
}
Serial1.write(STK_CRC_EOP);
Serial.println("END BLOCK");
if (!optibootRcv()) return 0;
myFile.close();
Serial.println("ENF FILE");
return 1;
}
}
if (linecount == 8) {
Serial.println("END BLOCK");
Serial1.write(STK_CRC_EOP);
if (!optibootRcv()) return 0;
Serial.println("LOAD ADDRESS");
linecount = 0;
blockcount++;
if (!WriteSTK_LoadAddress(blockcount * 64)) return 0;
Serial1.write(STK_PROG_PAGE);
Serial1.write(0x00);
Serial1.write(0x80);
Serial1.write(0x46);
}
}
}
Дальше самое интересное, прикрутить это все к рабочей системе.
Хмм, возникла проблема с резетом путем посылки команды и прижатия линии резета к нулю.
Подключил следующим образом, Reset притянут к +5 через 10кОм, пин 2 подключен к Reset через кондер 0,1мФ.
Логика следующая, кидаю команду с доктора на пациента, пациент отвечает доктору и перегружается (устанавливает на пине 2 - LOW), доктор получает ответ и пытается установить синхронизацию, но пациент не отвечает на синхронизацию, такое ощущение что на нем уже начинает работать основная программа. Для проверки в setup выставил
WORKING
REBOOT PROMINI
START FLASH
no sync
no sync
no sync
7
no sync
no sync
no sync
no sync
no sync
no sync
no sync
no sync
no sync
no sync
no sync
no sync
Видно, в какой момент пациент перегружается. Но вот почему он не заходит в бутлоадер не понятно.
Вот программа пациента. Посмотрите пожалуйста, может я чего-то упускаю
#define RS485SPEED 115200 //скорость RS-485
#define LED1 13
#define RESET_PIN 2
#define DIR A5
#define REBOOT 0x8
#define REBOOT_OK 0x9
unsigned long CurrentMillis;
byte flag = 0;
void setup() {
// put your setup code here, to run once:
pinMode(DIR, OUTPUT);
digitalWrite(DIR, HIGH);
Serial.begin(RS485SPEED);
Serial.write(7);
Serial.flush();
pinMode(LED1, OUTPUT);
digitalWrite(LED1, HIGH);
delay(7000);
digitalWrite(LED1, LOW);
}
void loop() {
if (Serial.available()) {
byte inChar = Serial.read();
if (inChar == REBOOT) {
Serial.write(REBOOT_OK);
Serial.flush();
delay(100);
pinMode(RESET_PIN, OUTPUT);
digitalWrite(RESET_PIN, HIGH);
delay(20);
digitalWrite(RESET_PIN, LOW);
}
}
if (flag == 0) {
digitalWrite(LED1, HIGH);
CurrentMillis = millis();
flag = 1;
}
else if (flag == 1) {
if (millis() - CurrentMillis > 1000) {
digitalWrite(LED1, LOW);
CurrentMillis = millis();
flag = 2;
}
}
else if (flag == 2) {
if (millis() - CurrentMillis > 500) {
flag = 0;
}
}
}
Проблема нашлась, но я пока не понимаю как ее решить. При прошивке программатором Usbasp походу затирается загрузчик, потому что после такой прошивке пациент уже не реагирует на команды доктора. Если же просто загрузить загрузчик, то все работает, но затирается программа. Отсюда вопрос, можно ли загрузить программу через Usbasp вместе с загрузчиком. Конечно есть обходной маневр, загрузчик записывать через ubsasp, а первичную прошивку уже лить через какой-нибудь USB-TTL программатор, но хотелось бы обойтись одним устройством.
то есть в середине блок записывается не полностью.
Вот исправленная функция
byte UploadFileFromSD (String filename) {
const char *fn = filename.c_str();
char header[9] = "00000000";
char charbyte[3] = "00";
//SWITCH_TO_SD;
byte linecount = 0;
unsigned int globaladress = 0;
unsigned int bytecount = 0;
unsigned int address = 0;
unsigned int comm = 0;
if (!SD.exists(fn)) return 0;
Serial.print("START TO FLASH FILE ");
Serial.println(filename);
File myFile = SD.open(fn);
if (!myFile) return 0;
if (!WriteSTK_LoadAddress(0)) return 0;
Serial.println("LOAD ADDRESS 0");
RS485.write(STK_PROG_PAGE);
RS485.write(0x00);
RS485.write(0x80);
RS485.write(0x46);
RS485.flush();
while (myFile.available()) {
char c = myFile.read();
if (c == ':') {
linecount++;
for (int i = 0; i < 8; i++) {
header[i] = myFile.read();
}
bytecount = HexToInt(header, 0, 2);
address = HexToInt(header, 2, 4);
comm = HexToInt(header, 6, 2);
if (linecount == 9) {
linecount = 1;
Serial.println("END BLOCK");
RS485.write(STK_CRC_EOP);
RS485.flush();
if (!optibootRcv()) return 0;
if (!WriteSTK_Sync()) return 0;
if (comm == 0) {
Serial.print("LOAD ADDRESS ");
float f = globaladress / 2;
Serial.println(int(f));
if (!WriteSTK_LoadAddress(int(f))) return 0;
RS485.write(STK_PROG_PAGE);
RS485.write(0x00);
RS485.write(0x80);
RS485.write(0x46);
}
}
globaladress = address + bytecount;
if (comm == 0) { //данные для записи
for (int i = 0; i < bytecount; i++) {
for (int j = 0; j < 2; j++) {
charbyte[j] = myFile.read();
}
Serial.print(charbyte);
byte bytetowrite = HexToInt(charbyte, 0, 2);
RS485.write(bytetowrite);
RS485.flush();
}
if (bytecount < 16) {
for (int i = bytecount; i < 16; i++) {
Serial.print("FF");
RS485.write(0xFF);
RS485.flush();
}
}
Serial.println("");
}
else if (comm == 1) { // последняя строка
Serial.print("LAST LINE ");
Serial.println(linecount);
if (linecount != 1) {
for (int i = linecount; i < 9; i++) {
for (int j = 0; j < 16; j++) {
Serial.print("FF");
RS485.write(0xFF);
}
}
Serial.println("");
RS485.write(STK_CRC_EOP);
RS485.flush();
if (!optibootRcv()) return 0;
}
Serial.println("LAST BLOCK");
myFile.close();
Serial.println("ENF FILE");
return 1;
}
}
}
}
Но есть и проблемы в целом. Мой скетч с самой программой занимает почти всю память МК, то есть достаточно большой. И я не могу заставить его прошиваться по RS-485. Да, я поставил трансиверы о которых шла речь выше (MAX13487) чтобы бутлоадер не переписывать. Маленькие скетчи шьются отлично до какого-то размера, а дальше как повезет. Причем через обычный UART (без RS485) все прошивается без проблем, то есть явно проблема из-за RS-485. Я пробывал собрать свой бутлоадер с пониженной скоростью прошивки (57600 и 9600), но становилось еще хуже, как будто на не родной скорости оптибуту плохо работается.
Пока теряюсь, что можно предпринять.... Может кто чего подскажет
Вот скетч доктора. В нем оптимизирована логика работы, добавлена возможность повторить команду в случае неправильного ответа, это случается на больших скетчах.
#include <SD.h>
#include <SPI.h>
#define STK_OK 0x10
#define STK_FAILED 0x11 // Not used
#define STK_UNKNOWN 0x12 // Not used
#define STK_NODEVICE 0x13 // Not used
#define STK_INSYNC 0x14 // ' '
#define STK_NOSYNC 0x15 // Not used
#define ADC_CHANNEL_ERROR 0x16 // Not used
#define ADC_MEASURE_OK 0x17 // Not used
#define PWM_CHANNEL_ERROR 0x18 // Not used
#define PWM_ADJUST_OK 0x19 // Not used
#define STK_CRC_EOP 0x20 // 'SPACE'
#define STK_GET_SYNC 0x30 // '0'
#define STK_GET_SIGN_ON 0x31 // '1'
#define STK_SET_PARAMETER 0x40 // '@'
#define STK_GET_PARAMETER 0x41 // 'A'
#define STK_SET_DEVICE 0x42 // 'B'
#define STK_SET_DEVICE_EXT 0x45 // 'E'
#define STK_ENTER_PROGMODE 0x50 // 'P'
#define STK_LEAVE_PROGMODE 0x51 // 'Q'
#define STK_CHIP_ERASE 0x52 // 'R'
#define STK_CHECK_AUTOINC 0x53 // 'S'
#define STK_LOAD_ADDRESS 0x55 // 'U'
#define STK_UNIVERSAL 0x56 // 'V'
#define STK_PROG_FLASH 0x60 // '`'
#define STK_PROG_DATA 0x61 // 'a'
#define STK_PROG_FUSE 0x62 // 'b'
#define STK_PROG_LOCK 0x63 // 'c'
#define STK_PROG_PAGE 0x64 // 'd'
#define STK_PROG_FUSE_EXT 0x65 // 'e'
#define STK_READ_FLASH 0x70 // 'p'
#define STK_READ_DATA 0x71 // 'q'
#define STK_READ_FUSE 0x72 // 'r'
#define STK_READ_LOCK 0x73 // 's'
#define STK_READ_PAGE 0x74 // 't'
#define STK_READ_SIGN 0x75 // 'u'
#define STK_READ_OSCCAL 0x76 // 'v'
#define STK_READ_FUSE_EXT 0x77 // 'w'
#define STK_READ_OSCCAL_EXT 0x78 // 'x'
#define STK_HW_VER 0x80
#define STK_SW_MAJOR 0x81
#define STK_SW_MINOR 0x82
#define STK_VTARGET 0x84
#define STK_VADJUST 0x85
#define STK_OSC_PSCALE 0x86
#define STK_OSC_CMATCH 0x87
#define STK_SCK_DURATION 0x89
#define STK_TOPCARD_DETECT 0x98
#define PAGE_SIZE 128
#define MAX_TRIES 3
#define REBOOT 0x8
#define REBOOT_OK 0x9
#define DIR 3
#define SS_SD_PIN BUILTIN_SDCARD //Пин SD-карты
#define SWITCH_TO_SD digitalWrite(SS_SD_PIN,LOW); delay(2) //Переключение на использование SD карты в случае нескольких устройств на SPI
HardwareSerial & RS485 = Serial3; //Изменить на нужный интерфейс
byte downloadstate = 0;
char inBuf[PAGE_SIZE];
byte buf[PAGE_SIZE];
byte tic = 0;
String Filename = "";
byte trycount = 0;
void setup() {
Serial.begin(500000);
while (!Serial) {
}
if (!SD.begin(SS_SD_PIN)) {
Serial.println("SD failed!");
return;
}
Serial.println("SD OK");
RS485.begin(38400);
pinMode(DIR, OUTPUT);
digitalWrite(DIR, LOW);
Serial.println("READY");
Serial.println("Press a, s or d");
}
void loop() {
if (downloadstate == 0) { //ждем нажатия а, s, d
if (Serial.available()) {
char inChar = (char)Serial.read();
if (inChar == 'a') {
Serial.println("WORKING");
downloadstate = 1;
Filename = "test1.hex";
}
else if (inChar == 's') {
Serial.println("WORKING");
downloadstate = 1;
Filename = "test2.hex";
}
else if (inChar == 'd') {
Serial.println("WORKING");
downloadstate = 1;
Filename = "Blink51.hex";
}
}
}
else if (downloadstate == 1) { //команда на перезагрузку
digitalWrite(DIR, HIGH);
delay(1);
RS485.write(REBOOT);
RS485.flush();
digitalWrite(DIR, LOW);
delay(1);
downloadstate = 2;
}
else if (downloadstate == 2) { //работа с загрузчиком
if (OptibootWork()) {
downloadstate = 0;
Serial.println("FLASH OK");
}
else {
Fail();
}
}
}
byte OptibootWork() {
while (RS485.available()) {
RS485.read();
}
Serial.println("START FLASH");
if (!WriteSTK_Sync()) return 0;
Serial.println("STK_HW_VER");
if (!WriteSTK_GetParamert(STK_HW_VER)) return 0;
Serial.println("STK_SW_MAJOR");
if (!WriteSTK_GetParamert(STK_SW_MAJOR)) return 0;
Serial.println("STK_SW_MINOR");
if (!WriteSTK_GetParamert(STK_SW_MINOR)) return 0;
Serial.println("STK_TOPCARD_DETECT");
if (!WriteSTK_GetParamert(STK_TOPCARD_DETECT)) return 0;
Serial.println("STK_VTARGET");
if (!WriteSTK_GetParamert(STK_VTARGET)) return 0;
Serial.println("STK_VADJUST");
if (!WriteSTK_GetParamert(STK_VADJUST)) return 0;
Serial.println("STK_OSC_PSCALE");
if (!WriteSTK_GetParamert(STK_OSC_PSCALE)) return 0;
Serial.println("STK_OSC_CMATCH");
if (!WriteSTK_GetParamert(STK_OSC_CMATCH)) return 0;
Serial.println("STK_SCK_DURATION");
if (!WriteSTK_GetParamert(STK_SCK_DURATION)) return 0;
Serial.println("STK_SW_MAJOR");
if (!WriteSTK_GetParamert(STK_SW_MAJOR)) return 0;
Serial.println("STK_SW_MINOR");
if (!WriteSTK_GetParamert(STK_SW_MINOR)) return 0;
if (!WriteSTK_EnterProgMode()) return 0;
if (!WriteSTK_ReadSign()) return 0;
if (!UploadFileFromSD(Filename)) return 0;
if (!WriteSTK_LeaveProgMode()) return 0;
return 1;
}
void Fail() {
downloadstate = 0;
Serial.println("FAIL TO FLASH");
}
byte WriteSTK_Sync() {
trycount = 0;
digitalWrite(DIR, HIGH);
delay(1);
RS485.write(STK_GET_SYNC);
RS485.write(STK_CRC_EOP);
RS485.flush();
digitalWrite(DIR, LOW);
delay(1);
if (!optibootRcv()) {
delay(100);
trycount++;
Serial.println("ERROR SYNC");
WriteSTK_Sync();
if (trycount > MAX_TRIES) {
return 0;
}
}
return 1;
}
byte WriteSTK_GetParamert(byte Paramert) {
Serial.println("GET PARAMETR");
trycount = 0;
digitalWrite(DIR, HIGH);
delay(2);
RS485.write(STK_GET_PARAMETER);
RS485.write(Paramert);
RS485.write(STK_CRC_EOP);
RS485.flush();
digitalWrite(DIR, LOW);
delay(2);
if (!optibootRcv()) {
delay(100);
trycount++;
Serial.println("GET PARAMETR");
WriteSTK_GetParamert(Paramert);
if (trycount > MAX_TRIES) {
return 0;
}
}
return 1;
}
byte WriteSTK_SetDevice() {
Serial.println("SET DEVICE");
trycount = 0;
digitalWrite(DIR, HIGH);
delay(2);
RS485.write(STK_SET_DEVICE);
RS485.write(0x86); //device code
RS485.write(0x00); //revision
RS485.write(0x00); //progtype: “0” – Both Parallel/High-voltage and Serial mode
RS485.write(0x01); //parmode: “1” – Full parallel interface
RS485.write(0x01); //polling: “1” – Polling may be used
RS485.write(0x01); //selftimed: “1” – Self timed
RS485.write(0x01); //lockbytes: Number of Lock bytes.
RS485.write(0x03); //fusebytes: Number of Fuse bytes
RS485.write(0xff); //flashpollval1
RS485.write(0xff); //flashpollval2
RS485.write(0xff); //eeprompollval1
RS485.write(0xff); //eeprompollval2
RS485.write(0x00); //pagesizehigh
RS485.write(0x80); //pagesizelow
RS485.write(0x04); //eepromsizehigh
RS485.write(0x00); //eepromsizelow
RS485.write(0x00); //flashsize4
RS485.write(0x00); //flashsize3
RS485.write(0x80); //flashsize2
RS485.write(0x00); //flashsize1
RS485.write(STK_CRC_EOP);
RS485.flush();
digitalWrite(DIR, LOW);
delay(2);
if (!optibootRcv()) {
delay(100);
trycount++;
Serial.println("SET DEVICE");
WriteSTK_SetDevice();
if (trycount > MAX_TRIES) {
return 0;
}
}
return 1;
}
byte WriteSTK_SetDeviceExt() {
Serial.println("SET DEVICE EXT");
trycount = 0;
digitalWrite(DIR, HIGH);
delay(2);
RS485.write(STK_SET_DEVICE_EXT);
RS485.write(0x05); //commandsize: how many bytes follow
RS485.write(0x04); //eeprompagesize: EEPROM page size in bytes.
RS485.write(0xd7); //signalpagel:
RS485.write(0xc2); //signalbs2:
RS485.write(0x00); //ResetDisable: Defines whether a part has RSTDSBL Fuse
RS485.write(STK_CRC_EOP);
RS485.flush();
digitalWrite(DIR, LOW);
delay(2);
if (!optibootRcv()) {
delay(100);
trycount++;
Serial.println("SET DEVICE EXT");
WriteSTK_SetDeviceExt();
if (trycount > MAX_TRIES) {
return 0;
}
}
return 1;
}
byte WriteSTK_EnterProgMode() {
Serial.println("ENTER PROG MODE");
trycount = 0;
digitalWrite(DIR, HIGH);
delay(2);
RS485.write(STK_ENTER_PROGMODE);
RS485.write(STK_CRC_EOP);
RS485.flush();
digitalWrite(DIR, LOW);
delay(2);
if (!optibootRcv()) {
delay(100);
trycount++;
Serial.println("ENTER PROG MODE");
WriteSTK_EnterProgMode();
if (trycount > MAX_TRIES) {
return 0;
}
}
return 1;
}
byte WriteSTK_ReadSign() {
Serial.println("READ SIGN");
trycount = 0;
digitalWrite(DIR, HIGH);
delay(2);
RS485.write(STK_READ_SIGN);
RS485.write(STK_CRC_EOP);
RS485.flush();
digitalWrite(DIR, LOW);
delay(2);
if (!optibootRcv()) {
delay(100);
trycount++;
Serial.println("READ SIGN");
WriteSTK_ReadSign();
if (trycount > MAX_TRIES) {
return 0;
}
}
return 1;
}
byte WriteSTK_WritePage() {
Serial.println("WRITE PAGE");
trycount = 0;
digitalWrite(DIR, HIGH);
delay(2);
RS485.write(STK_PROG_PAGE);
RS485.write(0x00);
RS485.write(0x80);
RS485.write(0x46);
for (int i = 0; i < PAGE_SIZE; i++) {
RS485.write(buf[i]);
}
RS485.write(STK_CRC_EOP);
RS485.flush();
digitalWrite(DIR, LOW);
delay(2);
if (!optibootRcv()) {
delay(100);
trycount++;
Serial.println("ERROR WRITE PAGE");
WriteSTK_WritePage();
if (trycount > MAX_TRIES) {
return 0;
}
}
return 1;
}
byte WriteSTK_LoadAddress(unsigned int addr) {
Serial.print("LOAD ADDRESS ");
Serial.println(addr);
byte highbyte = addr & 0xff;
byte lowbyte = (addr >> 8) & 0xff;
trycount = 0;
digitalWrite(DIR, HIGH);
delay(2);
RS485.write(STK_LOAD_ADDRESS);
RS485.write(highbyte);
RS485.write(lowbyte);
RS485.write(STK_CRC_EOP);
RS485.flush();
digitalWrite(DIR, LOW);
delay(2);
if (!optibootRcv()) {
delay(100);
trycount++;
Serial.println("ERROR LOAD ADDRESS");
WriteSTK_LoadAddress(addr);
if (trycount > MAX_TRIES) {
return 0;
}
}
return 1;
}
byte WriteSTK_LeaveProgMode() {
Serial.print("LEAVE PROG MODE");
trycount = 0;
digitalWrite(DIR, HIGH);
delay(2);
RS485.write(STK_LEAVE_PROGMODE);
RS485.write(STK_CRC_EOP);
RS485.flush();
digitalWrite(DIR, LOW);
delay(2);
if (!optibootRcv()) {
delay(100);
trycount++;
Serial.println("ERROR LEAVE PROG MODE");
WriteSTK_LeaveProgMode();
if (trycount > MAX_TRIES) {
return 0;
}
}
return 1;
}
byte optibootRcv() {
tic = 0;
byte inCount;
byte inByte;
while (RS485.available() < 2) {
delay(1);
tic++;
if (tic > 200) {
return 0;
}
}
inCount = 0;
inBuf[inCount] = 0;
while (RS485.available()) {
inByte = RS485.read();
//Serial.print(inByte, HEX);
//Serial.print(" ");
if (inCount < 199)
{
inBuf[inCount] = inByte;
inCount++;
}
}
//Serial.println("");
if (inBuf[0] == STK_INSYNC && inBuf[inCount - 1] == STK_OK) {
Serial.println("OK");
return 1;
}
else return 0;
}
unsigned int HexToInt ( char buf[], byte n, byte m) {
unsigned int y = 0;
for (int i = 0; i < m; i++)
{
if (buf[n + i] - '0' > 16) {
y = y + ((buf[n + i] - '0' - 7) << 4 * (m - 1 - i)) ;
}
if (buf[n + i] - '0' < 16) {
y = y + ((buf[n + i] - '0') << 4 * (m - 1 - i));
}
}
return y;
}
byte UploadFileFromSD (String filename) {
const char *fn = filename.c_str();
char header[9] = "00000000";
char charbyte[3] = "00";
//SWITCH_TO_SD;
unsigned int bytecount = 0;
unsigned int address = 0;
unsigned int comm = 0;
byte n = 0;
unsigned int blockcount = 0;
if (!SD.exists(fn)) return 0;
Serial.print("START TO FLASH FILE ");
Serial.println(filename);
File myFile = SD.open(fn);
if (!myFile) return 0;
while (myFile.available()) {
char c = myFile.read();
if (c == ':') { //начальный символ
for (int i = 0; i < 8; i++) {
header[i] = myFile.read();
}
bytecount = HexToInt(header, 0, 2); //кол-во байт в строке
address = HexToInt(header, 2, 4); //адрес
comm = HexToInt(header, 6, 2); //команда
if (comm == 0) { //данные для записи
for (int i = 0; i < bytecount; i++) {
for (int j = 0; j < 2; j++) {
charbyte[j] = myFile.read();
}
Serial.print(charbyte);
byte bytetowrite = HexToInt(charbyte, 0, 2);
buf[n] = bytetowrite;
n++;
if (n == PAGE_SIZE) { //блок заполнен
if (!WriteSTK_LoadAddress(blockcount * 64)) return 0;
if (!WriteSTK_WritePage()) return 0;
blockcount++;
n = 0;
}
}
Serial.println("");
}
else if (comm == 1) { // последняя строка
Serial.print("LAST LINE ");
if (n != 0) {
for (int j = n; j < PAGE_SIZE; j++) {
Serial.print("FF");
buf[j] = 0xFF;
}
if (!WriteSTK_LoadAddress(blockcount * 64)) return 0;
if (!WriteSTK_WritePage()) return 0;
}
myFile.close();
Serial.println("ENF FILE");
return 1;
}
}
}
}
И вот скетч пациента для теста
#define RS485SPEED 38400 //скорость RS-485
#define LED1 7
#define RESET_PIN 2
#define DIR A5
#define REBOOT 0x8
#define REBOOT_OK 0x9
unsigned long CurrentMillis;
byte flag = 0;
void setup() {
// put your setup code here, to run once:
pinMode(DIR, OUTPUT);
digitalWrite(DIR, LOW);
Serial.begin(RS485SPEED);
digitalWrite(DIR, HIGH);
delay(1);
Serial.write(7);
Serial.write(7);
Serial.flush();
digitalWrite(DIR, LOW);
delay(1);
pinMode(LED1, OUTPUT);
digitalWrite(LED1, HIGH);
delay(4000);
digitalWrite(LED1, LOW);
}
void loop() {
if (Serial.available()) {
byte inChar = Serial.read();
if (inChar == REBOOT) {
delay(200);
pinMode(RESET_PIN, OUTPUT);
digitalWrite(RESET_PIN, HIGH);
delay(1);
digitalWrite(RESET_PIN, LOW);
delay(1);
digitalWrite(RESET_PIN, HIGH);
}
}
if (flag == 0) {
digitalWrite(LED1, HIGH);
CurrentMillis = millis();
flag = 1;
}
else if (flag == 1) {
if (millis() - CurrentMillis > 1000) {
digitalWrite(LED1, LOW);
CurrentMillis = millis();
flag = 2;
}
}
else if (flag == 2) {
if (millis() - CurrentMillis > 500) {
flag = 0;
}
}
}
Теперь что касается загрузчика, пришлось повозиться, чтобы переделать загрузчик для использования стандартных 485 трансиверов. В результате удалось адаптировать последний optiboot под нужды RS485.
3. Открыть архив и вытащить оттуда папку Utils (arduino-1.5.6-r2-windows.zip\arduino-1.5.6-r2\hardware\tools\avr\utils)
4. Положить ее в папку с ардуино C:\Program Files (x86)\Arduino\hardware\tools\avr\
5. Перейти в папку с загрузчиком C:\Program Files (x86)\Arduino\hardware\arduino\avr\bootloaders\optiboot
6. Запустить командную строку от имени админа. Для генерации загрузчика использовать команду
my atmega328
Создастся файл optiboot_atmega328.hex - это и есть загрузчик
Возможные команды BAUD_RATE=скорость, SOFT_UART - используем софтовый uart, LED=D7 - какой пин использовать для светодиода, LED_START_FLASHES=2 - кол-во миганий светодиода при старте, LED_DATA_FLASH - мигание светодиода при загрузке программы, UART=0 - номер uart, если их несколько, RS485=C5 - пин для ногодрыга RS485. Например команда my atmega328 BAUD_RATE=38400 LED=D6 RS485=C5 создаст загрузчик, который будет работать на скорости 38400, при этом светодиод будет находиться на 6-ом пине, и нога для работы RS485 на пине А5.
7. Поправить board.txt (C:\Program Files (x86)\Arduino\hardware\arduino\avr) если необходимо, например выставить фьюзы и скорость прошивки
Вот моя секция
##############################################################
## Optiboot on 32pin (SMT) CPUs (Nano, Pro Micro, etc.)
##############################################################
optiboot32.name=Optiboot on 32-pin cpus
optiboot32.upload.tool=arduino:avrdude
optiboot32.upload.protocol=arduino
optiboot32.upload.speed=38400
optiboot32.bootloader.tool=arduino:avrdude
//optiboot32.bootloader.low_fuses=0xF7
optiboot32.bootloader.low_fuses=0xFF
optiboot32.bootloader.unlock_bits=0x3F
optiboot32.bootloader.lock_bits=0x2F
optiboot32.build.f_cpu=16000000L
#
# Other Clock speeds.
# For 8MHz using the internal RC Oscillator, we adjust fuses, use the same
# bootloader binary, and halve the upload rate.
#
optiboot32.menu.mhz.16MHz=16MHz
optiboot32.menu.mhz.16MHz.upload.speed=38400
optiboot32.menu.mhz.8MHz=8MHz (int)
optiboot32.menu.mhz.8MHz.build.f_cpu=8000000L
optiboot32.menu.mhz.8MHz.bootloader.low_fuses=0xE2
optiboot32.menu.mhz.8MHz.upload.speed=57600
optiboot32.menu.mhz.1MHz=1MHz (int)
optiboot32.menu.mhz.1MHz.build.f_cpu=1000000L
optiboot32.menu.mhz.1MHz.bootloader.low_fuses=0x62
optiboot32.menu.mhz.1MHz.upload.speed=9600
# optiboot platforms should be UNO-like more than anything else.
optiboot32.build.board=AVR_UNO
optiboot32.build.core=arduino:arduino
optiboot32.build.variant=arduino:eightanaloginputs
## Optiboot for ATmega328p
## ---------------------------------------------
optiboot32.menu.cpu.atmega328p=ATmega328p
optiboot32.menu.cpu.atmega328p.upload.maximum_size=32256
optiboot32.menu.cpu.atmega328p.upload.maximum_data_size=2048
optiboot32.menu.cpu.atmega328p.bootloader.high_fuses=0xD6
optiboot32.menu.cpu.atmega328p.bootloader.extended_fuses=0x05
optiboot32.menu.cpu.atmega328p.bootloader.file=optiboot/optiboot_atmega328.hex
optiboot32.menu.cpu.atmega328p.build.mcu=atmega328p
## Optiboot for ATmega328
## ---------------------------------------------
optiboot32.menu.cpu.atmega328=ATmega328
optiboot32.menu.cpu.atmega328.upload.maximum_size=32256
optiboot32.menu.cpu.atmega328.upload.maximum_data_size=2048
optiboot32.menu.cpu.atmega328.bootloader.high_fuses=0xDE
optiboot32.menu.cpu.atmega328.bootloader.extended_fuses=0x05
optiboot32.menu.cpu.atmega328.bootloader.file=optiboot/optiboot_atmega328.hex
# lie! Arduino wise, these are compatible
optiboot32.menu.cpu.atmega328.build.mcu=atmega328p
## Optiboot ATmega168
## ---------------------------------------------
optiboot32.menu.cpu.atmega168=ATmega168
optiboot32.menu.cpu.atmega168.upload.maximum_size=15872
optiboot32.menu.cpu.atmega168.upload.maximum_data_size=1024
optiboot32.menu.cpu.atmega168.bootloader.high_fuses=0xDD
optiboot32.menu.cpu.atmega168.bootloader.extended_fuses=0xFC
optiboot32.menu.cpu.atmega168.bootloader.file=optiboot/optiboot_atmega168.hex
optiboot32.menu.cpu.atmega168.build.mcu=atmega168
## ---------------------------------------------
optiboot32.menu.cpu.atmega168p=ATmega168p
optiboot32.menu.cpu.atmega168p.upload.maximum_size=15872
optiboot32.menu.cpu.atmega168p.upload.maximum_data_size=1024
optiboot32.menu.cpu.atmega168p.bootloader.high_fuses=0xDD
optiboot32.menu.cpu.atmega168p.bootloader.extended_fuses=0xFC
optiboot32.menu.cpu.atmega168p.bootloader.file=optiboot/optiboot_atmega168.hex
optiboot32.menu.cpu.atmega168p.build.mcu=atmega168
ТС, а зачем ты делал загрузку удалённого МК с помощью другой ардуины да ещё и SD карты. Цель была такая? Или потому что не получилось просто с arduino IDE и конвертера usb_rs485 реализовать загрузку скетчей?
Напишу все таки в этой теме. т.к. она тесно касается моей хотелки. Надеюсь кто-нибудь ответит.
Тоже заморочился с удаленной прошивкой ардуин, соединённых в сеть. Но у меня задача другая, не как у ТС. Хотелось, удаленно прошивать нужную ардуину с компа по UART, используя штатную arduino IDE + конвертер USB_RS485. Сразу скажу, что и загрузчик очень не хотелось бы менять, и тем более править.
Планируется несколько промини соединить в сеть CAN, но мозгов у меня не хватит по CAN прошивать. Собираюсь кидать сетевой провод витую пару STP 5e, одна пара на сеть CAN, и получается ещё три пары свободных. Поэтому решил одну пару выделить под ещё одну сеть чисто для удалённой прошивки. Возникают те же самые проблемы , что у ТС, а именно:
1. как дёргать ресет на нужной ардуине,
2. кто будет дергать пином приём/передача, если использовать обычные трансиверы RS485.
3. проблема, вытекает при попытке решить проблему 2. при использовании трансиверов, не требующих переключения приём/передача, как избавится от данных, появляющихся в приёмнике от передачи.
Первая проблема легко решается в моём случае, т.к. по CAN я могу выбрать какой МК входит в режим прошивки - он начинает слушать прошивальную сеть. как только от ардуино IDE прилетает 0x30 0x20, МК дергает сам свой ресет (например прделоженным Евгением вариантом дёрганием любой ногой за ресет ), и далее по штатной схеме все должно прошиваться, если бы не вторая проблема. В середине темы я сам предлагал типо решение :
MaksVV пишет:
а вы используйте трансиверы CAN вместо RS485. Будет всё тоже самое как на RS485, только не нужно ногу дёргать и при коллизиях аппаратного вреда не будет. только лучше mcp2551
но это решение действует просто при обмене между МК, а не при прошивке (в контексте прошивки через стандартную ардуино IDE). Т.к. когда мы дергаем DE RE на обычном трансивере RS485 при включенной передаче мы выключаем приемник (потому что пины DE RE объединяем и подключаем к дергающей ноге со стороны МК), поэтому мы не видим на шине того, что передаем. В случае же с CAN трансивером, всё что передается в шину - попадает в приёмник, это мешает штатному обмену МК и программатора при прошивке и ничего не работает, это третья проблема.
Так же не понятно, как у ТС заработал, (хотя может и не заработал??) вариант с предложенным более современным трансивером сети RS485 MAX13487, который не требует (вроде бы как оказалось) переключения приём передача.
gonzales пишет:
Да, я поставил трансиверы о которых шла речь выше (MAX13487) чтобы бутлоадер не переписывать. Маленькие скетчи шьются отлично до какого-то размера, а дальше как повезет. Причем через обычный UART (без RS485) все прошивается без проблем, то есть явно проблема из-за RS-485.
Для исключения эффекта (3-я проблема), описанного выше (убрать появление в приёмнике, того, что только что передали на шину) нужно всё равно дергать линию RE (управление приёмником) этого трансивера.
Поэтому либо со стороны МК править бутлоадер для дергания ногой чтоб управлять приёмом (приемом/передачей) , а как быть на стороне компа и конвертера USB_RS485 - он сам переключает приём передача?
Либо использовать ещё витую пару для применения полнодуплекса, чтоб исключить проблемы с необходимостью дергать ногой, и появления данных в приемнике при передаче (исключить 2 и 3 проблемы).
для дергания ногой чтоб управлять приёмом (приемом/передачей) , а как быть на стороне компа и конвертера USB_RS485 - он сам переключает приём передача?
сейчас проверил, такой конвертер
да, естественно сам переключает приём/передача. и нет эффекта ЭХО (проблема 3). Таким образом, проблема №2 и №3 со стороны компа и ардуино IDE отпадает, что не может не радовать.
Значит буду использовать все таки полудуплекс и со стороны ардуин, видимо, самое адекватное решение будет применить обычные трансиверы rs485 и бутлоадер оптибут допиленный управлением приемом/передачей. Тем более ТС уже предоставил готовое решение, попробую, отпишусь...
Да, всё получилось! ТСу спасиб за инфу! Со стороны компа никаких заморочек, прошиваем из стандартной Arduino IDE при помощи конвертера USB<->RS485, как в посте выше. В прошиваемых ардуинах залить бутлоадер оптибут с настройкой пина RS485 как описано в посте #69. Вот ещё видео мне помогло понять как это сделать. Ну и в каждой ардуине в код добавлять вот это :
#define RS485DERE_PIN 17 // пин A3 управления прием/передача трансивера RS485 (такой же, который делали при настройке и компиляции бутлоадера)
#define RESET_PIN 4 // пин 4 управления ресетом ардуины (любой можно, соединять можно сразу с пином Reset)
bool firmware_update = 1; // флаг режима обновления прошивки, поставил для примера сейчас включено, вы включайте когда и где вам это надо
void setup()
{
Serial.begin (38400); // здесь ставим такую скорость, на которую настраивали бутлоадер
pinMode (RESET_PIN,INPUT_PULLUP);
pinMode (RS485DERE_PIN,OUTPUT);
digitalWrite (RS485DERE_PIN,LOW);
}
void load_rebooting ()
{
if (firmware_update && Serial.available())
{
// если получили начальную команду от программатора (ардуино ИДЕ) ребутимся:
if (Serial.read()==0x30) {pinMode (RESET_PIN,OUTPUT); digitalWrite (RESET_PIN,0); }
}
}
void loop()
{
load_rebooting(); // функция рестарта МК при обновлении прошивки
// тут остальной код
}
Кажется, где то писал. Это делается красивше. Заводится отдельный .ino, куда помещаются все эти манипуляции. А тогда вы просто копируете этот ino в рабочую папку, тогда как другой (рабочий) ino будет кристально чистым - даже с пустым setup() и loop(). И ни о чём не думай.)
может как вы говорите , так и красивее, зато когда данные в сетапе присутствуют их видишь и по запаре случайно не испортишь настройки ребутных пинов и Serial. Но реализацию, такую как вы предложили, хотелось бы увидеть. типа там все в int main () настраивается?
> невнимательно читаете документацию. Параметр STK_LOAD_ADDRESS указывается не в байтах, а в 16-битных словах:
Точно, упустил. Интересно, зачем они так сделали?
Интересует тема, как можно залить прошивку в ардуино не через ISP, а через UART, по типу как это делается с помощью программатора USB-TTL, но используя не Arduino IDE, а другую Ардуино.
Если хватит "скилла" - изобрети собственный бутлоадер или "подсмотри" у других. Reset тебе не нужен, сделай сеть из ардуин и адресное обращение к ним и Boot то-же адресный. AVR БЕЗ Reset-а запросто "бутлоадится" , впринципе в датащах есть примеры и на С и на Asm. Так-шо, как грицца, фпуть)))))
все было сделано 7 лет назад, посмотрите здесь , как одна ардуинка шьет другую через UART:
http://robocraft.ru/blog/arduino/784.html
https://github.com/osbock/Baldwisdom/tree/master/BootDrive
тож подпишусь на тему. мало-ли пригодится.
// в виду наличия в своей схеме GPRS SIM800c , подумывал так : по типу web-сервера , можно перекинуть hex файл прошивки через GPRS на microSD главной ардуинкой. Вторая ардуинка промини только для программирования, считывает этот файл программирует главную ардуину. В инете вроде находил , как можно зашить ардуину с помощью другой ардуины и microSD .
Как-то было здесь обсуждение как ресетить ардуину , и вроде как самый простой и рабочий вариант - это чтоб она сама дергала своим пином, так не геморно и без софтовых ограничений, требуется только пин.
Зашиваете в еепром в каждой ардуинке разный ID . Посылаете команду по rs485 нужной ардуинке заресетиться , и команду помолчать всем одну минутку, дальше шьете по rs485.
//// вот еще проект , чтоб ардуина вообще одна сама себя прошивала , считывая прошивку из microSD
https://toster.ru/q/149309
https://github.com/thseiler/embedded/tree/master/avr/2boots
все было сделано 7 лет назад, посмотрите здесь , как одна ардуинка шьет другую через UART:
Где же ты раньше был, мил человек))))
Посмотрел код, практически то же самое, что я уже сам почти допилил. Но это не готовое решение, так как для работы rs485 нужно дергать ногу на трансмитторе во время передачи, поэтому придется в любом случае шаманить с загрузчиком.
Одного не понял в проекте BootDrive - там стандартный загрузчик используется?
Назрел еще один вопрос, при экспорте скетчей в hex ардуино ide генерит два файла, один например blink.ino.hex, а второй blink.ino.with_bootloader.hex
Логично предполагаю, что во втором варианте находится еще код загрузчика. Но если загрузчик уже залит в МК, а я буду прошивать его первым файлом начиная с нулевого адреса загрузчик же затирается, правильно? Как в таком случае поступить, с какого адреса начинать заливку пользовательской программы? Или брать файл с загрузчиком и не париться?
а вы используйте трансиверы CAN вместо RS485. Будет всё тоже самое как на RS485, только не нужно ногу дёргать и при коллизиях аппаратного вреда не будет. только лучше mcp2551
Да, интересно. Посмотрел схему подключния по даташиту, там установлена гальваническая развязка на tx и rx. Я так понимаю трансивер питается от 5 вольт и RO DI у него на 5В, и развязки для использования последовательного порта с напряжением -12 - +12В. то есть в варианте с ардуино это все не нужно, подаем RX на RO, TX на DI и 5В на RE и SHDN, так?
а вы используйте трансиверы CAN вместо RS485. Будет всё тоже самое как на RS485, только не нужно ногу дёргать и при коллизиях аппаратного вреда не будет. только лучше mcp2551
Так к нему же еще can контроллер надо типа mcp2515
не обязательно. MCP2515 нужен для полноценного CANa. В вашем случае можно просто цеплять на rx tx . И будет эдакий недо CAN не до RS485. Т.е. по сути физическая линия будет CAN, а пользоваться будете как RS485, только без ноги прием передача.
Проверено это работает.
вы ж это уже проходили
Вот первая рабочая версия. Программа читает файл на SD и пишет его по обычному UART в пациента. Для понимания, что прошивка перезаписывается создал на SD в корне 3 файла и прошиваю каждым из них в зависимости от отправленного символа в Serial порт (a,s,d).
#define SS_SD_PIN BUILTIN_SDCARD - это константа для teensy, для обчной ардуино обчно 4.
В программе нет проверки загруженной программы на корректность и проверок ответов optiboot, лишь OK неОК.
Serial.begin(500000); - скорость поставил побольше, чтобы не тормозила при выводе инфы в Serial
Дальше самое интересное, прикрутить это все к рабочей системе.
Хмм, возникла проблема с резетом путем посылки команды и прижатия линии резета к нулю.
Подключил следующим образом, Reset притянут к +5 через 10кОм, пин 2 подключен к Reset через кондер 0,1мФ.
Логика следующая, кидаю команду с доктора на пациента, пациент отвечает доктору и перегружается (устанавливает на пине 2 - LOW), доктор получает ответ и пытается установить синхронизацию, но пациент не отвечает на синхронизацию, такое ощущение что на нем уже начинает работать основная программа. Для проверки в setup выставил
В мониторе вижу
Видно, в какой момент пациент перегружается. Но вот почему он не заходит в бутлоадер не понятно.
Вот программа пациента. Посмотрите пожалуйста, может я чего-то упускаю
Проблема нашлась, но я пока не понимаю как ее решить. При прошивке программатором Usbasp походу затирается загрузчик, потому что после такой прошивке пациент уже не реагирует на команды доктора. Если же просто загрузить загрузчик, то все работает, но затирается программа. Отсюда вопрос, можно ли загрузить программу через Usbasp вместе с загрузчиком. Конечно есть обходной маневр, загрузчик записывать через ubsasp, а первичную прошивку уже лить через какой-нибудь USB-TTL программатор, но хотелось бы обойтись одним устройством.
Отсюда вопрос, можно ли загрузить программу через Usbasp вместе с загрузчиком.
А то!
В IDE в меню выбираете
В результате в папке скетча появятся два файла, типа таких
А теперь угадайте с трёх раз в каком из них есть загрузчик? Вот его программатором и заливайте.
Ну да, походу это единственный вариант.
Нашел несколько недоработок в функции UploadFileFromSD
В hex файле могут оказывается быть вот такие истории
то есть в середине блок записывается не полностью.
Вот исправленная функция
Но есть и проблемы в целом. Мой скетч с самой программой занимает почти всю память МК, то есть достаточно большой. И я не могу заставить его прошиваться по RS-485. Да, я поставил трансиверы о которых шла речь выше (MAX13487) чтобы бутлоадер не переписывать. Маленькие скетчи шьются отлично до какого-то размера, а дальше как повезет. Причем через обычный UART (без RS485) все прошивается без проблем, то есть явно проблема из-за RS-485. Я пробывал собрать свой бутлоадер с пониженной скоростью прошивки (57600 и 9600), но становилось еще хуже, как будто на не родной скорости оптибуту плохо работается.
Пока теряюсь, что можно предпринять.... Может кто чего подскажет
Все!!! Задачу решил, все отлично прошивается.
Вот скетч доктора. В нем оптимизирована логика работы, добавлена возможность повторить команду в случае неправильного ответа, это случается на больших скетчах.
И вот скетч пациента для теста
Теперь что касается загрузчика, пришлось повозиться, чтобы переделать загрузчик для использования стандартных 485 трансиверов. В результате удалось адаптировать последний optiboot под нужды RS485.
По ссылке можно его скачать https://yadi.sk/d/hStc67QUkG-UIQ
Но для того, что пользоваться нужно сделать следующее
1. Скачать архив с загрузчиком, распаковать папку optiboot в C:\Program Files (x86)\Arduino\hardware\arduino\avr\bootloaders
2. Скачать дистрибутив Arduino IDE ver 1.5.6 r2 beta (не старше), только не установщик, а архив. Вот ссылка https://www.arduino.cc/download_handler.php?f=/arduino-1.5.6-r2-windows.zip
3. Открыть архив и вытащить оттуда папку Utils (arduino-1.5.6-r2-windows.zip\arduino-1.5.6-r2\hardware\tools\avr\utils)
4. Положить ее в папку с ардуино C:\Program Files (x86)\Arduino\hardware\tools\avr\
5. Перейти в папку с загрузчиком C:\Program Files (x86)\Arduino\hardware\arduino\avr\bootloaders\optiboot
6. Запустить командную строку от имени админа. Для генерации загрузчика использовать команду
my atmega328
Создастся файл optiboot_atmega328.hex - это и есть загрузчик
Возможные команды BAUD_RATE=скорость, SOFT_UART - используем софтовый uart, LED=D7 - какой пин использовать для светодиода, LED_START_FLASHES=2 - кол-во миганий светодиода при старте, LED_DATA_FLASH - мигание светодиода при загрузке программы, UART=0 - номер uart, если их несколько, RS485=C5 - пин для ногодрыга RS485. Например команда my atmega328 BAUD_RATE=38400 LED=D6 RS485=C5 создаст загрузчик, который будет работать на скорости 38400, при этом светодиод будет находиться на 6-ом пине, и нога для работы RS485 на пине А5.
7. Поправить board.txt (C:\Program Files (x86)\Arduino\hardware\arduino\avr) если необходимо, например выставить фьюзы и скорость прошивки
Вот моя секция
Вот собственно все.
На новой версии IDE (сейчас это 1.8.12) перестал собираться загрузчик. MakeFile пишет не очень внятный комментарий
Конкретно его не устраивает while (!(UART_SRA & _BV(TXC0)));
Еще конкретнее UART_SRA, потому как while (!(UART_SRA)); - не работает, а while (!(_BV(TXC0))); - работает.
Знатоки Си, помогите, чего ему от меня надо?
Полный файл большой, но могу выложить, если необходимо.
Да, в файле pin_defs.h есть описание этого UART_SRA
В общем плюнул на эту затею, откатился на IDE 1.8.8 - сделал новый загрузчик и вернулся обратно. Все работает
ТС, а зачем ты делал загрузку удалённого МК с помощью другой ардуины да ещё и SD карты. Цель была такая? Или потому что не получилось просто с arduino IDE и конвертера usb_rs485 реализовать загрузку скетчей?
сейчас проверил, такой конвертер
да, естественно сам переключает приём/передача. и нет эффекта ЭХО (проблема 3). Таким образом, проблема №2 и №3 со стороны компа и ардуино IDE отпадает, что не может не радовать.
Значит буду использовать все таки полудуплекс и со стороны ардуин, видимо, самое адекватное решение будет применить обычные трансиверы rs485 и бутлоадер оптибут допиленный управлением приемом/передачей. Тем более ТС уже предоставил готовое решение, попробую, отпишусь...
Да, всё получилось! ТСу спасиб за инфу! Со стороны компа никаких заморочек, прошиваем из стандартной Arduino IDE при помощи конвертера USB<->RS485, как в посте выше. В прошиваемых ардуинах залить бутлоадер оптибут с настройкой пина RS485 как описано в посте #69. Вот ещё видео мне помогло понять как это сделать. Ну и в каждой ардуине в код добавлять вот это :
Кажется, где то писал. Это делается красивше. Заводится отдельный .ino, куда помещаются все эти манипуляции. А тогда вы просто копируете этот ino в рабочую папку, тогда как другой (рабочий) ino будет кристально чистым - даже с пустым setup() и loop(). И ни о чём не думай.)
может как вы говорите , так и красивее, зато когда данные в сетапе присутствуют их видишь и по запаре случайно не испортишь настройки ребутных пинов и Serial. Но реализацию, такую как вы предложили, хотелось бы увидеть. типа там все в int main () настраивается?