STM32 - прошивка "по воздуху" через ESP





Здравствуйте.

В статье описано как через интернет прошить микроконтроллер STM32 используя WIFI-модуль ESP8266, то есть организовать удалённое обновление программного обеспечения. Прошивка осуществляется через USART_1 в режиме системного загрузчика, подробнее про это смотрите здесь.

STM32 microcontroller system memory boot mode


Подопытная плата Wemos (ESP8266), программироваться будет в IDE Arduino.


Задача такова, нужно соединить STM с ESP, передать на ESP (через интернет) прошивку, далее ESP должна подтянуть BOOT_0 к «плюсу», нажать ресет, и загрузить прошивку в STM через USART_1. Однако прежде надо научится общаться с системным загрузчиком — там не всё так просто как мне думалось по началу, но и сложного ничего нет. Вот мануал на русском языке (обязательно прочтите), описывающий протокол обмена данными между бутлоадером и внешним устройством.

В мануале говориться что максимальная скорость USART'а не должна превышать 115200 (при увеличении растет погрешность). В примере я указал 57600 так как такая скорость используется по умолчанию утилитой stm32flash. Тем не менее плата успешно прошивалась даже на скорости 921600.

Чтобы «договориться» с загрузчиком, нужно послать ему один байт (0x7F), загрузчик принимает этот байт и по нему распознаёт скорость USART'а. Если байт успешно принят (скорость определена), то загрузчик в ответ посылает байт подтверждения 0x79 — ACK-байт. После этого можно посылать загрузчику различные команды. Большая часть команд состоит из двух байт. На все команды, если они успешно выполнены, загрузчик отвечает байтом подтверждения.


Прежде чем браться за ESP, я написал программу для Arduino Mega (и для stm тоже), чтоб потренироваться «разговаривать» с загрузчиком…

код
ARDU_to_stm

#include <SPI.h>
#include <SD.h>

#define Usart_stm32 Serial1
#define WRITE_ADDR 0x08000000 // адрес для заливки прошивки
#define SIZE_WRITE 256
#define FIRMWARE "usart3.bin"

#define BOOT_PIN 5
#define RESET_PIN 6

void setup() 
{
  Serial.begin(57600);
  Usart_stm32.begin(57600, SERIAL_8E1);

  pinMode(BOOT_PIN, OUTPUT); // boot_0 
  digitalWrite(BOOT_PIN, LOW);
  pinMode(RESET_PIN, OUTPUT); // reset
  digitalWrite(RESET_PIN, LOW); 

  if(!SD.begin(53)) Serial.println(F("Card failed - ERROR"));
  else Serial.println(F("Card initialized - OK"));
}


void loop() 
{
  if(Serial.available()) 
  {
    uint8_t res = Serial.read();

    /////////////// первый запрос для установки скорости (в ответ ждёт 0x79 - ACK-байт)///////////////
    if(res == 'B') 
    {
      entr_bootloader();
    }

    ////////////////////////////////// boot_off_and_reset /////////////////////////////////////
    else if(res == 'R') 
    {
      boot_off_and_reset();
    }  
    
    ////////////////////////////////// Erase Memory /////////////////////////////////////
    else if(res == 'E') 
    {
      erase_memory();
    }   

    ///////////////////////////////////// Write_Memory ////////////////////////////////////////
    else if(res == 'W') 
    {
      write_memory();
    }
    
    ///////////////////////////////////// on_boot() ////////////////////////////////////////
    else if(res == 'h') 
    {
      on_boot();
    }

    ///////////////////////////////////// off_boot() ////////////////////////////////////////
    else if(res == 'l') 
    {
      off_boot();
    }

    ///////////////////////////////////// on_reset() ////////////////////////////////////////
    else if(res == 'r') 
    {
      on_reset();
    }     
  }
} 

//////////////////////////// entr_bootloader /////////////////////////////////////
void entr_bootloader()
{
  on_off_boot(HIGH); // подтягиваем BOOT_0 к плюсу
  delay(500);
  on_reset(); // нажимаем ресет
  delay(200);

  while(Usart_stm32.available()) { Usart_stm32.read(); } // в приёмном буфере может быть мусор

  Usart_stm32.write(0x7F); // первый запрос (для определения скорости) 
  if(ack_byte() == 0) Serial.println(F("Bootloader - OK"));  // Log
  else Serial.println(F("Bootloader - ERROR"));  // Log  
}

//////////////////////////// write_memory /////////////////////////////////////
void write_memory()
{
  if(erase_memory() == 0)
  {
     File df = SD.open(FIRMWARE);
     
     if(df) 
     {
       uint32_t size_file = df.size();
      
       Serial.print(F("Size file "));
       Serial.println(size_file);
     
       uint8_t cmd_array[2] = {0x31, 0xCE}; // код Write Memory 
       uint32_t count_addr = 0;
       uint16_t len = 0;
       uint32_t seek_len = 0;

       while(true)
       {      
         if(send_cmd(cmd_array) == 0)
         {
           uint8_t ret_adr = send_adress(WRITE_ADDR + count_addr);
           count_addr = count_addr + SIZE_WRITE;
           
           if(ret_adr == 0)
           {       
             uint8_t write_buff[SIZE_WRITE] = {0,};
             len = df.read(write_buff, SIZE_WRITE);
             seek_len++;
             df.seek(SIZE_WRITE * seek_len);  
             //write_memory(write_buff, len);file.position()

             //Serial.print(F("seek_len "));
             //Serial.println(seek_len);
             //Serial.println(df.position());
              
             uint8_t cs, buf[SIZE_WRITE + 2];
             uint16_t i, aligned_len;
              
             aligned_len = (len + 3) & ~3;
             cs = aligned_len - 1;
             buf[0] = aligned_len - 1;
              
             for(i = 0; i < len; i++) 
             {
               cs ^= write_buff[i];
               buf[i + 1] = write_buff[i];
             }
              
             for(i = len; i < aligned_len; i++) 
             {
               cs ^= 0xFF;
               buf[i + 1] = 0xFF;
             }
              
             buf[aligned_len + 1] = cs;
             Usart_stm32.write(buf, aligned_len + 2);
             uint8_t ab = ack_byte();
          
             if(ab != 0) 
             {
               Serial.println(F("Block not Write Memory - ERROR"));
               break;
             }
             
             if(size_file == df.position())
             {
               Serial.println(F("End Write Memory2 - OK"));
               boot_off_and_reset();
               break;
             }                 
           }
           else 
           {
             Serial.println(F("Address Write Memory - ERROR")); // Log
             break;
           }
         }
         else 
         {
           Serial.println(F("Cmd cod Write Memory - ERROR")); // Log
           break;
         }             
       } // end while
       
       df.close();
     }
     else Serial.println(F("Not file - ERROR"));          
  } 
  else Serial.println(F("Not erase Write Memory - ERROR"));    
}

////////////////////////////// erase_memory ////////////////////////////////////
uint8_t erase_memory()
{
  uint8_t cmd_array[2] = {0x43, 0xBC}; // команда на стирание 

  if(send_cmd(cmd_array) == 0) 
  {
    uint8_t cmd_array[2] = {0xFF, 0x00}; // код стирания (полное) 

    if(send_cmd(cmd_array) == 0)
    {
      Serial.println(F("Erase Memory - OK")); // Log
      return 0;
    }
    else Serial.println(F("Cmd cod Erase Memory - ERROR")); // Log             
  }
  else Serial.println(F("Cmd start Erase Memory - ERROR")); // Log

  return 1;
}

////////////////////////////// send_cmd ////////////////////////////////////
uint8_t send_cmd(uint8_t *cmd_array)
{
  Usart_stm32.write(cmd_array, 2); 
  if(ack_byte() == 0) return 0;
  else return 1;
}

/////////////////////////////// ack_byte ////////////////////////////////////
uint8_t ack_byte()
{ 
  for(uint16_t i = 0; i < 500; i++)
  {
    if(Usart_stm32.available())
    {
      uint8_t res = Usart_stm32.read();
      if(res == 0x79) return 0;         
    } 
    delay(1);  
  }

  return 1;
}

///////////////////////////// send_adress ////////////////////////////////////
uint8_t send_adress(uint32_t addr)
{
  uint8_t buf[5] = {0,};  
  buf[0] = addr >> 24;
  buf[1] = (addr >> 16) & 0xFF;
  buf[2] = (addr >> 8) & 0xFF;
  buf[3] = addr & 0xFF;
  buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
  
  Usart_stm32.write(buf, 5);
  if(ack_byte() == 0) return 0;
  else return 1;  
}

////////////////////////////// on_reset //////////////////////////////////////
void on_reset()
{
  digitalWrite(RESET_PIN, HIGH); // reset
  delay(50);    
  digitalWrite(RESET_PIN, LOW); 
}

////////////////////////////// on_off_boot ///////////////////////////////////
void on_off_boot(uint8_t i)
{
  digitalWrite(BOOT_PIN, i); 
}

//////////////////////////// boot_off_and_reset //////////////////////////////
void boot_off_and_reset()
{
  on_off_boot(LOW);
  delay(500);
  on_reset(); 
  Serial.println(F("Boot off and reset")); // Log
}

////////////////////////////// on_boot ///////////////////////////////////
void on_boot()
{
  digitalWrite(BOOT_PIN, HIGH); 
}

////////////////////////////// off_boot ///////////////////////////////////
void off_boot()
{
  digitalWrite(BOOT_PIN, LOW); 
}



Загружаемая прошивка хранится на SD карте.

Serial1 — соединён с USART'ом stm32. Настраивается на работу с битом чётности (SERIAL_8E1).

Serial — смотрим отладочную инфу, и «рулим» ардуиной.

WRITE_ADDR — начальный адрес для загрузки новой прошивки.

SIZE_WRITE — прошивка загружается в stm блоками, максимальный размер блока 256 байт.

Передача каждого блока происходит так: передаётся команда (0x31, 0xCE), в ответ приходит байт подтверждения, передаётся адрес по которому будет записан блок (адрес каждого последующего блока сдвигается на длину размера блока), в ответ приходит байт подтверждения, передаётся блок, в ответ приходит байт подтверждения, и т.д. Соответственно, чем больше размер блока, тем меньше будет промежуточных операций.

Файл размером 64Кб, на скорости 57600, прошивается за ~15сек.

Я где-то встречал неподтверждённую инфу, что у некоторых МК максимальный размер блока 128 байт.


FIRMWARE — название файла (прошивки). Программа работает только с bin-файлами.

D5 — реле для управления BOOT_0.


2 — на пин BOOT_0. У платы BluePill это центральный штырёк, на который надет джампер.
3 — на «минус».
1 — на «плюс».

В отключённом состоянии будет тянуть BOOT_0 на «минус», а при включении на «плюс».

D6 — управляет ресетом. Коротит ресет на «минус» через NPN транзистор.



RX и TX нужно подтянуть к «плюсу» резисторами ~20кОм.




Подключайте все железки, прошивайте ардуину, открывайте  и отправляйте ей команды…

B — активирует загрузчик (подтягивает BOOT_0 к «плюсу»), нажимает ресет, отправляет байт для определения скорости (0x7F) и ожидает ACK-байт. Если ACK-байт пришёл, значит МК готов к приёму команд.

R — деактивирует загрузчик и нажимает ресет.

E — посылает команду очистки всей памяти.

W — загружает прошивку в stm32. Сначала вызывается функция очистки памяти — erase_memory(), и если ACK-байт вернулся, то происходит открытие файла, которое тоже проверяется. Далее в бесконечном цикле отправляется команда на запись, потом начальный адрес для записи первого блока (вместе с контрольной суммой адреса), а потом из файла читается блок, создаётся контрольная сумма блока, и это отправляется в stm. Если файл не закончился, то цикл повторяется. При каждом следующем цикле адрес для записи смещается на длину блока. Так повторяется до тех пор пока файл не закончится. Все действия постоянно проверяются на возврат ACK-байта. Если всё прошло успешно, то загрузчик отключается и нажимается ресет, то есть МК переводится в рабочее состояние.


Чтоб загрузить прошивку, нужно положить её на SD-карту, указать имя в дефайне (имя не должно быть длиннее 8 символов), отправить B, дождаться ответа Bootloader — OK, и отправить W



Обязательно укажите — «Нет конца строки».

Если SD-карты нет, то просто поклацайте релюшкой и ресетом с помощью последних трёх команд, или добавьте в код команды для запуска существующей в МК прогрограммы и вывода ID…

команды
/////////////////////////////// Go Запуск программы //////////////////////////////////    
    else if(res == 'g') // Go Запуск программы
    {
      Serial.println(F("Start Programm")); // Log
      uint8_t cmd_array[2] = {0x21, 0xDE}; // код запуска программы
      
      if(send_cmd(cmd_array) == 0) 
      {
        Serial.println(F("Cmd start programm - OK"));  // Log
        
        uint8_t ret_adr = send_adress(WRITE_ADDR);

        if(ret_adr == 0)
        {
          Serial.println(F("Start programm - OK")); // Log
        }
        else Serial.println(F("Address start programm - ERROR")); // Log
              
      }
      else Serial.println(F("Cmd start programm - ERROR")); // Log
    }

    //////////////////////////////////////// Get ID //////////////////////////////////////////
    else if(res == 'i') 
    {
       Serial.println(F("Start Get ID")); // Log
       uint8_t cmd_array[2] = {0x02, 0xFD}; // код Get ID

       if(send_cmd(cmd_array) == 0)
       {
         Serial.println(F("Cmd Get ID - OK")); // Log

         for(uint16_t i = 0; i < 500; i++)
         {
           delay(1);

           if(Usart_stm32.available()) 
           {
             uint8_t id[4] = {0,};
           
             for(uint8_t i = 0; i < 4; i++) 
             {
               id[i] = Usart_stm32.read();
             }

             if(id[3] == 0x79)
             {
               uint16_t stm_id = 0;
               stm_id = id[1]; 
               stm_id = (stm_id << 8) | id[2];
               Serial.print(F("stm_id 0x"));
               Serial.println(stm_id, HEX); // Log
             }
             else Serial.println(F("Not ID - ERROR")); // Log
           }
         }
       }
       else Serial.println(F("Cmd Get ID - ERROR")); // Log
    }


Войдите в режим бутлоадера и пошлите символ g или i.


Если же и ардуины нет, то вот то же самое для stm32. В архиве два примера, один с SD-картой, другой без — просто отправка команд. Общение с целевой платой сделано через USART_1, логи и управление через USB. Карта подключается к SPI2. Весь механизм прописан в файле to_stm.c




ESP подключается так же как и ардуина…




С протами вывода здесь всё наоборот, Serial работает с stm, а Serial1 (в ESP у Serial1 есть только TX) печатает логи (они закомментированы).

Код для ESP
ESP_on_air_stm

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <FS.h>

#define WRITE_ADDR 0x08000000 // адрес для заливки прошивки
#define SIZE_WRITE 256
#define FIRMWARE "/esptostm.bin" // перед именем должен стоять слеш

#define BOOT_PIN D5
#define RESET_PIN D6

#define Usart_stm32 Serial
//#define DBG_OUTPUT_PORT Serial1

const char* ssid = "istarik";
const char* password = "SssfsfsfsfDf1";
const char* host = "esptostm32"; // адрес - http://esptostm32.local/

ESP8266WebServer server(80);
File fsUploadFile;

String getContentType(String filename) 
{
  if (server.hasArg("download")) 
  {
    return "application/octet-stream";
  } 
  else if (filename.endsWith(".htm")) 
  {
    return "text/html";
  } 
  else if (filename.endsWith(".html")) 
  {
    return "text/html";
  } 
  else if (filename.endsWith(".css")) 
  {
    return "text/css";
  } 
  else if (filename.endsWith(".js")) 
  {
    return "application/javascript";
  } 

  return "text/plain";
}

bool handleFileRead(String path) 
{
  //DBG_OUTPUT_PORT.println("handleFileRead: " + path);
  
  if (path.endsWith("/")) 
  {
    path += "edit.htm";
  }
  
  String contentType = getContentType(path);
  
  if(SPIFFS.exists(path)) 
  {   
    File file = SPIFFS.open(path, "r");
    server.streamFile(file, contentType);
    file.close();
    return true;
  }
  return false;
}

void handleFileUpload() 
{
  if(server.uri() != "/edit") return;

  HTTPUpload& upload = server.upload();

  if(upload.status == UPLOAD_FILE_START) 
  {
    String filename = upload.filename;
    
    if(!filename.startsWith("/")) filename = "/" + filename;
    
    //DBG_OUTPUT_PORT.print("handleFileUpload Name: "); 
    //DBG_OUTPUT_PORT.println(filename);
    
    fsUploadFile = SPIFFS.open(filename, "w");
    filename = String();
    
  } 
  else if(upload.status == UPLOAD_FILE_WRITE) 
  {
    if(fsUploadFile) fsUploadFile.write(upload.buf, upload.currentSize);
  } 
  else if(upload.status == UPLOAD_FILE_END) 
  {
    if(fsUploadFile) fsUploadFile.close();
    
    //DBG_OUTPUT_PORT.print("handleFileUpload Size: "); 
    //DBG_OUTPUT_PORT.println(upload.totalSize);
  }
}

void handleFileDelete() 
{
  if(server.args() == 0) return server.send(500, "text/plain", "BAD ARGS");
  
  String path = server.arg(0);
  
  //DBG_OUTPUT_PORT.println("handleFileDelete: " + path);
  
  if(path == "/") return server.send(500, "text/plain", "BAD PATH");
  
  if(!SPIFFS.exists(path)) return server.send(404, "text/plain", "FileNotFound");
  
  SPIFFS.remove(path);
  server.send(200, "text/plain", "");
  path = String();
}

void handleFileList() 
{
  if (!server.hasArg("dir")) 
  {
    server.send(500, "text/plain", "BAD ARGS");
    return;
  }

  String path = server.arg("dir");
  //DBG_OUTPUT_PORT.println("handleFileList: " + path);
  Dir dir = SPIFFS.openDir(path);
  path = String();

  String output = "[";
  while (dir.next()) 
  {
    File entry = dir.openFile("r");
    
    if(output != "[") output += ',';
    
    bool isDir = false;
    output += "{\"type\":\"";
    output += (isDir) ? "dir" : "file";
    output += "\",\"name\":\"";
    output += String(entry.name()).substring(1);
    output += "\"}";
    entry.close();
  }

  output += "]";
  server.send(200, "text/json", output);
}

///////////////////////////////////// Entr_bootloader //////////////////////////////////////////
void handleEntr_bootloader() 
{
  //DBG_OUTPUT_PORT.printf("Entr_bootloader\n");
  entr_bootloader();
}

///////////////////////////////////// Write //////////////////////////////////////////
void handleWrite() 
{
  //DBG_OUTPUT_PORT.printf("Write\n");
  write_memory();
}

///////////////////////////////////// Reset //////////////////////////////////////////
void handleReset() 
{
  //DBG_OUTPUT_PORT.printf("Reset\n");
  on_reset();
  server.send(200, "text/plain", "Reset - OK");  
}

///////////////////////////////////// On_boot //////////////////////////////////////////
void handleOn_boot() 
{
  //DBG_OUTPUT_PORT.printf("On_boot\n");
  on_boot();
  server.send(200, "text/plain", "On boot - OK");
}

///////////////////////////////////// Off_boot //////////////////////////////////////////
void handleOff_boot() 
{
  //DBG_OUTPUT_PORT.printf("Off_boot\n");
  off_boot();
  server.send(200, "text/plain", "Off boot - OK");
}

///////////////////////////////////// Event //////////////////////////////////////////
void handleEvent() 
{
  // что-то делаем
  server.send(200, "text/plain", "Event - OK");
}

//////////////////////////////////////////////////////////////////////////////////////////


void setup(void) 
{
  pinMode(BOOT_PIN, OUTPUT); // boot_0 
  digitalWrite(BOOT_PIN, LOW);
  pinMode(RESET_PIN, OUTPUT); // reset
  digitalWrite(RESET_PIN, LOW);

  Usart_stm32.begin(57600, SERIAL_8E1);
  
  //DBG_OUTPUT_PORT.begin(57600);
  //DBG_OUTPUT_PORT.print("\n");
  //DBG_OUTPUT_PORT.setDebugOutput(true);
  
  SPIFFS.begin();
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  while(WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    //DBG_OUTPUT_PORT.print(".");
  }
  
  //DBG_OUTPUT_PORT.println("");
  //DBG_OUTPUT_PORT.print("Connected! IP address: ");
  //DBG_OUTPUT_PORT.println(WiFi.localIP());

  MDNS.begin(host);
  //DBG_OUTPUT_PORT.print("Open http://");
  //DBG_OUTPUT_PORT.print(host);
  //DBG_OUTPUT_PORT.println(".local/edit to see the file browser");

  //SERVER INIT
  server.on("/list", HTTP_GET, handleFileList);
  server.on("/enter_boot", HTTP_GET, handleEntr_bootloader);
  server.on("/write", HTTP_GET, handleWrite);
  server.on("/reset", HTTP_GET, handleReset);
  server.on("/on_boot", HTTP_GET, handleOn_boot);
  server.on("/off_boot", HTTP_GET, handleOff_boot);
  server.on("/event", HTTP_GET, handleEvent);
  server.on("/edit", HTTP_DELETE, handleFileDelete);
  
  server.on("/edit", HTTP_GET, []() 
  {
    if(!handleFileRead("/edit.htm")) server.send(404, "text/plain", "FileNotFound");
  });
  
  server.on("/edit", HTTP_POST, []() 
  {
    server.send(200, "text/plain", "");
  }, handleFileUpload);

  server.onNotFound([]() 
  {
    if(!handleFileRead(server.uri())) server.send(404, "text/plain", "FileNotFound");
  });

  server.begin();
  //DBG_OUTPUT_PORT.println("HTTP server started");
}

void loop(void) 
{
  server.handleClient();
}

//////////////////////////// entr_bootloader /////////////////////////////////////
void entr_bootloader()
{
  on_off_boot(HIGH); // подтягиваем BOOT_0 к плюсу
  delay(500);
  on_reset(); // нажимаем ресет
  delay(200);

  while(Usart_stm32.available()) { Usart_stm32.read(); } // в приёмном буфере может быть мусор

  Usart_stm32.write(0x7F); // первый запрос (для определения скорости) 
  if(ack_byte() == 0) 
  {
    //DBG_OUTPUT_PORT.println(F("Bootloader - OK"));  // Log
    server.send(200, "text/plain", "Bootloader - OK");
  }
  else 
  {
    //DBG_OUTPUT_PORT.println(F("Bootloader - ERROR"));  // Log  
    server.send(200, "text/plain", "Bootloader - ERROR");
  }
}

//////////////////////////// write_memory /////////////////////////////////////
void write_memory()
{
  if(erase_memory() == 0)
  {
     File df = SPIFFS.open(FIRMWARE, "r");
     
     if(df) 
     {
       uint32_t size_file = df.size();
      
       //DBG_OUTPUT_PORT.print(F("Size file "));
       //DBG_OUTPUT_PORT.println(size_file);
     
       uint8_t cmd_array[2] = {0x31, 0xCE}; // код Write Memory 
       uint32_t count_addr = 0;
       uint16_t len = 0;
       uint32_t seek_len = 0;

       while(true)
       {      
         if(send_cmd(cmd_array) == 0)
         {
           uint8_t ret_adr = send_adress(WRITE_ADDR + count_addr);
           count_addr = count_addr + SIZE_WRITE;
           
           if(ret_adr == 0)
           {       
             uint8_t write_buff[SIZE_WRITE] = {0,};
             len = df.read(write_buff, SIZE_WRITE);
             seek_len++;
             df.seek(SIZE_WRITE * seek_len);  
                     
             uint8_t cs, buf[SIZE_WRITE + 2];
             uint16_t i, aligned_len;
              
             aligned_len = (len + 3) & ~3;
             cs = aligned_len - 1;
             buf[0] = aligned_len - 1;
              
             for(i = 0; i < len; i++) 
             {
               cs ^= write_buff[i];
               buf[i + 1] = write_buff[i];
             }
              
             for(i = len; i < aligned_len; i++) 
             {
               cs ^= 0xFF;
               buf[i + 1] = 0xFF;
             }
              
             buf[aligned_len + 1] = cs;
             Usart_stm32.write(buf, aligned_len + 2);
             uint8_t ab = ack_byte();
          
             if(ab != 0) 
             {
               //DBG_OUTPUT_PORT.println(F("Block not Write Memory - ERROR"));
               server.send(200, "text/plain", "Block not Write Memory - ERROR");
               break;
             }
             
             if(size_file == df.position())
             {
               Usart_stm32.begin(57600, SERIAL_8N1);
               while(Usart_stm32.available())
               {
                 Usart_stm32.read();
                 delay(1);
               } 

               boot_off_and_reset();
               String reciv = "<br />";
             
               for(uint16_t i = 0; i < 2000; i++)
               {
                 if(Usart_stm32.available())
                 {
                   while(Usart_stm32.available())
                   {
                     char c = Usart_stm32.read();
                     reciv += c;
                     delay(1);
                   } 
                   break;      
                 } 
                 delay(1);  
               }

               server.send(200, "text/plain", "Done Write Memory - OK" + reciv); //DBG_OUTPUT_PORT.println(F("Done Write Memory - OK"));
               Usart_stm32.begin(57600, SERIAL_8E1);
               break;
             }                 
           }
           else 
           {
             server.send(200, "text/plain", "Address Write Memory - ERROR"); //DBG_OUTPUT_PORT.println(F("Address Write Memory - ERROR")); // Log
             break;
           }
         }
         else 
         {
           server.send(200, "text/plain", "Cmd cod Write Memory - ERROR"); //DBG_OUTPUT_PORT.println(F("Cmd cod Write Memory - ERROR")); // Log
           break;
         }             
       } // end while
       
       df.close();
     }
     else server.send(200, "text/plain", "Not file - ERROR"); //DBG_OUTPUT_PORT.println(F("Not file - ERROR"));          
  } 
  else server.send(200, "text/plain", "Not erase Write Memory - ERROR"); //DBG_OUTPUT_PORT.println(F("Not erase Write Memory - ERROR"));    
}

////////////////////////////// erase_memory ////////////////////////////////////
uint8_t erase_memory()
{
  uint8_t cmd_array[2] = {0x43, 0xBC}; // команда на стирание 

  if(send_cmd(cmd_array) == 0) 
  {
    uint8_t cmd_array[2] = {0xFF, 0x00}; // код стирания (полное) 

    if(send_cmd(cmd_array) == 0)
    {
      //DBG_OUTPUT_PORT.println(F("Erase Memory - OK")); // Log
      return 0;
    }
    //else DBG_OUTPUT_PORT.println(F("Cmd cod Erase Memory - ERROR")); // Log             
  }
  //else DBG_OUTPUT_PORT.println(F("Cmd start Erase Memory - ERROR")); // Log

  return 1;
}

////////////////////////////// send_cmd ////////////////////////////////////
uint8_t send_cmd(uint8_t *cmd_array)
{
  Usart_stm32.write(cmd_array, 2); 
  if(ack_byte() == 0) return 0;
  else return 1;
}

/////////////////////////////// ack_byte ////////////////////////////////////
uint8_t ack_byte()
{ 
  for(uint16_t i = 0; i < 500; i++)
  {
    if(Usart_stm32.available())
    {
      uint8_t res = Usart_stm32.read();
      if(res == 0x79) return 0;         
    } 
    delay(1);  
  }

  return 1;
}

///////////////////////////// send_adress ////////////////////////////////////
uint8_t send_adress(uint32_t addr)
{
  uint8_t buf[5] = {0,};  
  buf[0] = addr >> 24;
  buf[1] = (addr >> 16) & 0xFF;
  buf[2] = (addr >> 8) & 0xFF;
  buf[3] = addr & 0xFF;
  buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
  
  Usart_stm32.write(buf, 5);
  if(ack_byte() == 0) return 0;
  else return 1;  
}


////////////////////////////// on_boot ///////////////////////////////////
void on_boot()
{
  digitalWrite(BOOT_PIN, HIGH); 
}

////////////////////////////// off_boot ///////////////////////////////////
void off_boot()
{
  digitalWrite(BOOT_PIN, LOW); 
  on_reset();
}  

////////////////////////////// on_reset //////////////////////////////////////
void on_reset()
{
  digitalWrite(RESET_PIN, HIGH); // reset
  delay(50);    
  digitalWrite(RESET_PIN, LOW); 
}

////////////////////////////// on_off_boot ///////////////////////////////////
void on_off_boot(uint8_t i)
{
  digitalWrite(BOOT_PIN, i); 
}

//////////////////////////// boot_off_and_reset //////////////////////////////
void boot_off_and_reset()
{
  on_off_boot(LOW);
  delay(500);
  on_reset(); 
  //DBG_OUTPUT_PORT.println(F("Boot off and reset")); // Log
}



Всё что касается работы с сетью, взято из стандартного примера FSBrowser, и немного переработано. Код из ардуины полностью перенесён в ESP с той лишь разницей, что управление и загрузка прошивки осуществляется через веб-интерфейс, а вместо SD-карты используется SPIFFS. Про работу с SPIFFS в частности, и про ESP в целом, я подробно писал здесь.


Скачиваем архив и загружаем папку data в ESP как это описано по ссылке выше. В программе вписываем название прошивки, имя своей точки доступа и пароль. Прошиваем ESP и заходим в браузер по аресу esptostm32.local/




Загружаем файл прошивки — Обзор ⇨ Upload, ждём когда он появится в левой колонке, жмём Enter boot и при положительном ответе жмём Write. МК прошьётся и перейдёт в обычный режим работы. При последующем обновлении нужно удалить прежний файл правой кнопкой мышки и загрузить новый.

Если в прошивке добавить, перед бесконечным циклом, вывод какой-либо инфы в USART, например так…

/* USER CODE BEGIN 2 */
HAL_UART_Transmit(&huart1, (uint8_t*)"Ver 1.3", 12, 100);
/* USER CODE END 2 */


… тогда после прошивки это будет выводится в веб-интерфейс, сообщая об успешном обновлении. При дальнейших обновлениях будете изменять версию.

Как вы помните, USART системного загрузчика работает с битом чётности, однако в прошивке учитывать это не нужно, настраивайте как обычно…



… а в функции write_memory() специально для этого сделана переинициализация Serial'а.


С помощью кнопок Reset, On boot и Off boot можно обресетить МК или включить/отключить бутлоадер, но по большому счёту они и не нужны, так, на всякий случай. Кнопка Event вообще ничего не делает (только возвращает «ОК»), мало ли кто-то захочет какую-то функцию прикрутить.


На этом всё.


Всем спасибо


Исходники на GitHub


Телеграм-чат istarik

Телеграм-чат STM32


  • 0
  • 26970
Поддержать автора


Telegram-чат istarik

Задать вопрос по статье
Telegram-канал istarik

Известит Вас о новых публикациях






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

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.