NuttX的應用記錄4

今天研究了二進制加載功能,但當我試圖把hello從romfs裏拷貝到smartfs中時,出現了IO錯誤的ERROR。關閉二進制加載後任然不能恢復正常。鬱悶。重新打開DEBUG再找找原因。但是這之後就好了。又沒BUG了。我想了想,關閉了DEBUG後再試,果然又出錯了。原因居然在DEBUG上?分析了一下,應該是LOG的問題,打印LOG需要消耗CPU時間,所以每次flash操作間隔都會比邊長。而不打印日誌間隔就會非常短。抓取一下波形。
結果顯示時間差據非常大。開啓LOG後,間隔爲2.3ms。關閉LOG後,間隔只有15us了。。。而且我還發現這個波形不是很漂亮。時鐘信號的脈寬有長有短(這是我的強迫症)。
總之,原因是找到了。但不能就這樣讓我帶着log來操作把。。。每次打印log都是很費時間的。看着桌面上的113M的日誌文件夾,我無話可說。根據經驗,finfo打開的時候不會出現BUG。那就打開它把。這意味着要被強行看一大堆日誌了。。。暫時也沒有什麼更好的辦法,原因發給作者,等他回覆。


早上就看到回覆了,作者給了幾個文件的patch。但是我這邊的文件已經不是原檔了。直接patch是不可能的,只能靠手動修改才能勉強應用這個patch這樣子。。
作者增加了可以用小於4096的扇區大小讀寫的模式。

受不了eclipse了。老是崩潰,一崩潰就內存泄漏。內存佔用經常是94%。而且還找不到是誰佔用的。活見鬼。
今天試了試MSYS2,打算把代碼移到windows上來操作。也不用經常忍受權限問題。多好。
1,安裝MSYS2,然後按照README裏的說明把缺少的東西都裝個遍。
2,下載GUN-ARM-Toolchain的壓縮包,拷貝到MSYS的根目錄下面的usr裏。
3,再下個stlink的windows版,拷貝到MSYS的根目錄下面的usr裏就能make download
4,下個picocom的源碼,編譯一下,就能開一個小窗口調試串口了,但是貌似沒有日誌功能,這點不好,但是可以使用tab了,這個好啊。
完成。試着編譯一下,看看時間,clean後加time前綴再編譯。time make -j 32
可以明顯感覺到,clean的速度完全不如在linux上的快。最後結果:

real    0m49.311s
user    0m21.357s
sys     0m49.660s

現在用什麼編輯器呢。。。。。不知道。。。先用sublime湊活一下。
試一下作者發的補丁好了。
感覺很煩躁,用這個MSYS。因爲很卡。換回Linux把。
Linux 用的也很煩躁,各種卡頓。聽說VM挺好用的,換VM試一試。去官網看了半天,下了VM station play這個免費版。
試了一下效果,簡直是,,,,太流暢了。完全沒有VBOX裏的卡頓的感覺。我想我可以在ubuntu裏裝sublime了。
果然可以。
使用起來很流暢,甚至比win上還要快。
重新git源代碼,修改

#define MX25R6435F_SECTOR_COUNT     (16384)

#define MX25R6435F_SECTOR_COUNT     (2048)

打開finfo級日誌。
編譯下載。一點問題也沒有。
重新寫接收段的邏輯。我想我可以不寫線程了,因爲實在是難控制。發送和接受同時有一個在運行就可以了。
將發送和接收封裝起來。

static void esp_8266_wchar(int fd, uint8_t *ch)
{
  uint8_t temp[2];
  temp[0] = ESP_PACK_ESC;
  switch (*ch)
  {
    case ESP_PACK_END:
    {
      temp[1] = ESP_PACK_ESC_END;
      write(fd, temp, 2);
      break;
    }
    case ESP_PACK_ESC:
    {
      temp[1] = ESP_PACK_ESC_ESC;
      write(fd, temp, 2);
      break;
    }
    default:
    {
      write(fd, ch, 1);
    }
  }
}

static FAR int esp_8266_rchar(int fd, uint8_t *ch)
{
  if(read(fd, ch, 1))
  {

    /* Check wether a Escape Character */

    if (*ch == ESP_PACK_ESC)
    {
      read(fd, ch, 1);
      switch (*ch)
      {
        case ESP_PACK_ESC_END:
        {
          *ch = ESP_PACK_END;
          break;
        }
        case ESP_PACK_ESC_ESC:
        {
          *ch = ESP_PACK_ESC;
          break;
        }
        default:
        {
          *ch = NULL;
          return 0;
        }
      }
    }

    return 1;
  }
  else
  {

    /* Read nothing */

    return 0;
  }
}

假設8266死掉了,不能迴應,那麼read也不應該就卡死在這裏。需要一個延時。

借鑑《【轉】Linux串口IO模式的一些心得(阻塞、非阻塞、超時)》這篇文章,可以寫出:

1.開啓阻塞

    int flag;
    flag = fcntl(rx_fd,F_GETFL);
    flag &= ~O_NONBLOCK;
    fcntl(fd,F_SETFL,flag);

2.設置超時時間

    struct termios tio;

    ret = tcgetattr(fd, &tio);
    if (ret)
    {
      fprintf(stderr, "esp_sub_tool: OPEN_SERIAL: ERROR during tcgetattr(): %d\n", errno);
      rc = -1;
    }

    tio.c_cc[VMIN]  = 0;
    tio.c_cc[VTIME] = 1;
    ret = tcsetattr(fd, TCSANOW, &tio);
    if (ret)
    {
      fprintf(stderr, "esp_sub_tool: OPEN_SERIAL: ERROR during tcsetattr(): %d\n", errno);
      rc = -1;
    }

姑且設置爲1s把。
接下來就可以無憂無慮的寫數據轉換了。

首先,我定義了兩個結構體:

typedef struct 
{
  uint8_t   Type;
  uint8_t   Command;
  uint16_t  Data_Size;
  uint32_t  Checksum;
  uint8_t   *Body;
} TX_FRAME;

typedef struct 
{
  uint8_t   Type;
  uint8_t   Command;
  uint16_t  Data_Size;
  uint32_t  Response;
  uint8_t   *Body;
  uint8_t   Status;
  uint8_t   Error;
} RX_FRAME;

鑑於body內容長度不定,所以,這裏用指針來指。暫時性還應不上Body。先不管他。

再往上一層就是這樣的,在這一層處理轉義字符。發送一個“字符”:

static void esp_8266_wchar(int fd, uint8_t *ch)
{
  uint8_t temp[2];
  temp[0] = ESP_PACK_ESC;
  switch (*ch)
  {
    case ESP_PACK_END:
    {
      temp[1] = ESP_PACK_ESC_END;
      write(fd, temp, 2);
      break;
    }
    case ESP_PACK_ESC:
    {
      temp[1] = ESP_PACK_ESC_ESC;
      write(fd, temp, 2);
      break;
    }
    default:
    {
      write(fd, ch, 1);
    }
  }
}

接收一個“字符”:

static FAR int esp_8266_rchar(int fd, uint8_t *ch)
{
  if(read(fd, ch, 1))
  {
    if (*ch == ESP_PACK_ESC)
    {
      read(fd, ch, 1);
      switch (*ch)
      {
        case ESP_PACK_ESC_END:
        {
          *ch = ESP_PACK_END;
          break;
        }
        case ESP_PACK_ESC_ESC:
        {
          *ch = ESP_PACK_ESC;
          break;
        }
        default:
        {
          *ch = NULL;
          return 0;
        }
      }
    }

    return 1;
  }
  else
  {
    return 0;   /* Read nothing */
  }
}

再上一層。處理結構體,發送:

static void ESP_8266_Sed(int fd, TX_FRAME * tx_buff)
{
  uint8_t   *tx_ptr;
  uint16_t  i;

  tx_ptr = (uint8_t *) tx_buff;

  // printf("ESP_8266_Sed: Command: 0x%02X\n", tx_buff -> Command);

  if ((tx_buff -> Command == ESP_COMMAND_SYNC) ||
      (tx_buff -> Command == ESP_COMMAND_RREG) || (false))
  {

    Send_HEAD_END:
    {
      // printf("ESP_8266_Sed: Send_HEAD_END\n");
      uint8_t temp;
      temp = ESP_PACK_END;
      write(fd, &temp, 1);
    }

    Send_Type_Command_Data_Size_Checksum:
    {
      // printf("ESP_8266_Sed: Send_Type_Command_Data_Size_Checksum\n");
      for (i = 0; i < 8; ++i)
      {
        esp_8266_wchar(fd, tx_ptr);
        tx_ptr++;
      }
    }

    Send_Body:
    {
      // printf("ESP_8266_Sed: Send_Body\n");
      tx_ptr = (*tx_buff).Body;
      for (i = 0; i < (*tx_buff).Data_Size; ++i)
      {
        esp_8266_wchar(fd, tx_ptr);
        tx_ptr++;
      }
    }

    Send_END_END:
    {
      // printf("ESP_8266_Sed: Send_END_END\n");
      uint8_t temp;
      temp = ESP_PACK_END;
      write(fd, &temp, 1);
    }
  }
}

接收:

static FAR int ESP_8266_Rec(int fd, RX_FRAME * rx_buff)
{
  uint8_t   *rx_ptr;
  uint16_t  i;

  uint8_t   err;

  rx_ptr = (uint8_t *) rx_buff;

  /* Read for HEAD */

  if(!esp_8266_rchar(fd, rx_ptr))
  {
    err = ENODATA;
    goto ESP_8266_Rec_err;
  }
  if (*rx_ptr == ESP_PACK_END)
  {
    
    Read_Type_Command_Data_Size_Response:
    {
      for (i = 0; i < 8; ++i)
      {
        if(esp_8266_rchar(fd, rx_ptr))
        {
          rx_ptr++;
        }
        else
        {
          err = ENODATA;
          goto ESP_8266_Rec_err;
        }
      }
    }

    Read_Body:
    {
      rx_ptr = (*rx_buff).Body;
      switch((*rx_buff).Command)
      {
        default:
        {
          goto Read_Status_Error;
        }
      }
    }

    Read_Status_Error:
    {
      if(!esp_8266_rchar(fd, &((*rx_buff).Status)))
      {
        err = ENODATA;
        goto ESP_8266_Rec_err;
      }
      if(!esp_8266_rchar(fd, &((*rx_buff).Error)))
      {
        err = ENODATA;
        goto ESP_8266_Rec_err;
      }
    }

    Read_END_END:
    {
      uint8_t temp;
      esp_8266_rchar(fd, &temp);
      if(temp != ESP_PACK_END)
      {
        err = EPROTO;
      }
    }

    return OK;
  }
  else
  {
    err = EPROTO;
  }

  ESP_8266_Rec_err:
  {
    (*rx_buff).Error = err;
    return ERROR;
  }
}

這裏爲了方便我看,我就將整段代碼分成了很多段。每一段附上標號。

所以,再上層就是這樣的了:
同步:

  SYNC:
  {
    uint8_t sync_count = 0;
    /* waiting for 8266 startup */

    usleep(60 * 1000);

    /* The 8266 NOW should started and printfed "ets .....""
     * those data under baudrate 74880 and useless. So,
     * droped it.
     */

    tcflush(rx_fd, TCIFLUSH);

    /* Send two Sync data */

    tx_fram.Command   = ESP_COMMAND_SYNC;
    tx_fram.Data_Size = sizeof(SYNC_PACK);
    tx_fram.Body      = (uint8_t *)SYNC_PACK;

    ESP_8266_Sed(tx_fd, &tx_fram);
    usleep(1000);

    sync_count = 0;

    int i;
    ESP_8266_Sed(tx_fd, &tx_fram);

    for (i = 0; i < 8; ++i)
    {
      ESP_8266_Rec(rx_fd, &rx_fram);
      if (rx_fram.Type == 0x01)
      {
        if (rx_fram.Command == ESP_COMMAND_SYNC)
        {
          if (rx_fram.Response == SYNC_ACK)
          {
            rx_fram.Response = (uint32_t) 0;
            sync_count++;
          }
        }
      }
    }

    if (sync_count == 8)
    {
      printf("esp_sub_tool: SYNC: Sync Successful\n");
    }
    else
    {
      printf("esp_sub_tool: SYNC: Sync Failed\n");
      goto SYNC;
    }
    
  }

讀取MAC段:

  Read_MAC:
  {
    uint32_t ESP_OTP_MAC[4] = 
    {
      0x3FF00050, 0x3FF00054, 0x3FF00058, 0x3FF0005C
    };
    uint32_t REG_OTP_MAC[4];

    int i;
    uint8_t mac[8];

    // printf("esp_sub_tool: Read_MAC: Entry\n");

    tx_fram.Command   = ESP_COMMAND_RREG;
    tx_fram.Data_Size = (uint16_t) sizeof(uint32_t);

    for (i = 0; i < 4; ++i)
    {
      // printf("esp_sub_tool: Read_MAC: Required REG 0x%08X\n", ESP_OTP_MAC[i]);
      tx_fram.Body = (uint8_t *)(&(ESP_OTP_MAC[i]));
      ESP_8266_Sed(tx_fd, &tx_fram);
      ESP_8266_Rec(rx_fd, &rx_fram);
      if (rx_fram.Type == 0x01)
      {
        if (rx_fram.Command == ESP_COMMAND_RREG)
        {
          REG_OTP_MAC[i] = rx_fram.Response;
          rx_fram.Response = (uint32_t) 0;
          printf("esp_sub_tool: Read_MAC: 0x%08X: 0x%08X\n", ESP_OTP_MAC[i], REG_OTP_MAC[i]);
        }
      }
    }

    if (REG_OTP_MAC[3] != 0)
    {
      current_MAC[0] = (REG_OTP_MAC[3] >> 16) & 0xFF;
      current_MAC[1] = (REG_OTP_MAC[3] >>  8) & 0xFF;
      current_MAC[2] = (REG_OTP_MAC[3] >>  0) & 0xFF;
    }
    else if (((REG_OTP_MAC[1] >> 16) & 0xFF) == 0)
    {
      current_MAC[0] = 0x18;
      current_MAC[1] = 0xFE;
      current_MAC[2] = 0x34;
    }
    else if (((REG_OTP_MAC[1] >> 16) & 0xFF) == 1)
    {
      current_MAC[0] = 0xAC;
      current_MAC[1] = 0xD0;
      current_MAC[2] = 0x74;
    }
    current_MAC[3] = (REG_OTP_MAC[1] >>  8) & 0xFF;
    current_MAC[4] = (REG_OTP_MAC[1] >>  0) & 0xFF;
    current_MAC[5] = (REG_OTP_MAC[0] >> 24) & 0xFF;

    printf("esp_sub_tool: Read_MAC: MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", 
      current_MAC[0],
      current_MAC[1],
      current_MAC[2],
      current_MAC[3],
      current_MAC[4],
      current_MAC[5]);
  }

結果:

nsh> esptool
esptool v1.0
esp_sub_tool: SYNC: Sync Successful
esp_sub_tool: Read_MAC: 0x3FF00050: 0xFF6A0000
esp_sub_tool: Read_MAC: 0x3FF00054: 0x02001C1A
esp_sub_tool: Read_MAC: 0x3FF00058: 0x4900B000
esp_sub_tool: Read_MAC: 0x3FF0005C: 0x00A020A6
esp_sub_tool: Read_MAC: MAC: A0:20:A6:1C:1A:FF
esptool_main: com0 exit success

最近有點忙,沒時間做。明天開始研究如何寫整個FLASH。


根據python的內容和下載手冊。燒錄流程分爲三個階段:
1,擦除目標段數據
2,發送數據
3,結束

先從擦除段開始。根據手冊內容可以寫出:

erase_page:
    {
      uint32_t temp[4];
      temp[0] = 256;    // Erase sector size: 256 x 4096 = 1Mb
      temp[1] = 1024;   // Package size
      temp[2] = 1024;   // Package size
      temp[3] = 0;      // Address offset
      tx_fram.Command   = ESP_FLASH_BEGIN;
      tx_fram.Data_Size = (uint16_t) sizeof(uint32_t) * 4;
      tx_fram.Body      = temp;

      ESP_8266_Sed(tx_fd, &tx_fram);
      ESP_8266_Rec(rx_fd, &rx_fram);
      if((rx_fram.Type == 0x01) &&(rx_fram.Command == ESP_FLASH_BEGIN))
      {
        printf("esp_sub_tool: WRITE_FLASH: erase_page: Response = 0x%04X\n", rx_fram.Response);
      }
      else
      {
        printf("esp_sub_tool: WRITE_FLASH: erase_page: ERROR package\n");
        goto ESPTOOL_END;
      }
    }

這段也可以從邏輯分析儀上得出。
接下來寫入數據。先寫個1kb吧。
發送文件的時候,會有一個開頭,共16 byts。那麼可以先開闢1024+16的內存單元,這樣文件頭就能發送出去了,所以:

    flash_block:
    {
      int BIN_fd;
      BIN_fd = open(argv[4], O_RDONLY);
      if (BIN_fd < 0)
      {
        printf("esp_sub_tool: flash_block: open file %s failed: %d", argv[4], errno);
        return ERROR;
      }

      FAR uint8_t *buffer;
      buffer = (FAR uint8_t *)malloc(sizeof(uint8_t) * 1024 + sizeof(uint32_t) * 4);
      if(buffer == NULL)
      {
        printf("esp_sub_tool: flash_block: ERROR: can not alloc mem: %d", errno);
        return ERROR;
      }
      
      FAR uint8_t *buffer_ptr;
      buffer_ptr = buffer;
      ((uint32_t *) buffer_ptr)[0] = (uint32_t) 1024;
      ((uint32_t *) buffer_ptr)[1] = (uint32_t) 1;
      ((uint32_t *) buffer_ptr)[2] = (uint32_t) 0;
      ((uint32_t *) buffer_ptr)[3] = (uint32_t) 0;

      int nbytesread = read(BIN_fd, &(((uint32_t *) buffer_ptr)[4]), 1024);
      printf("esp_sub_tool: flash_block: Read %d bytes\n", nbytesread);

      tx_fram.Command   = ESP_FLASH_DATA;
      tx_fram.Data_Size = 1024 + 16;
      tx_fram.Body      = buffer_ptr;

      ESP_8266_Sed(tx_fd, &tx_fram);
      ESP_8266_Rec(rx_fd, &rx_fram);
      if((rx_fram.Type == 0x01) &&
        (rx_fram.Command == ESP_FLASH_DATA) &&
        (rx_fram.Response == ((current_MAC[0] << 16) |
                              (current_MAC[1] <<  8) |
                              (current_MAC[2]      ))))
      {
        printf("esp_sub_tool: WRITE_FLASH: flash_block: Response = 0x%04X\n", rx_fram.Response);
        free(buffer);
      }
      else
      {
        printf("esp_sub_tool: WRITE_FLASH: flash_block: ERROR package\n");
        goto ESPTOOL_END;
      }

      free(buffer_ptr);
    }

同時我還發現一個有趣的地方。燒錄操作的返回值就是MAC的前幾位。至於是不是就是這樣的,還有待進一步考證。

運行是正常的,改成寫1M。

flash_block:
    {
      int BIN_fd;
      BIN_fd = open(argv[4], O_RDONLY);
      if (BIN_fd < 0)
      {
        printf("esp_sub_tool: flash_block: open file %s failed: %d", argv[4], errno);
        return ERROR;
      }

      FAR uint8_t *buffer;
      buffer = (FAR uint8_t *)malloc(sizeof(uint8_t) * 1024 + sizeof(uint32_t) * 4);
      if(buffer == NULL)
      {
        printf("esp_sub_tool: flash_block: ERROR: can not alloc mem: %d", errno);
        return ERROR;
      }
      
      FAR uint8_t *buffer_ptr;
      buffer_ptr = buffer;
      ((uint32_t *) buffer_ptr)[0] = (uint32_t) 1024;
      ((uint32_t *) buffer_ptr)[2] = (uint32_t) 0;
      ((uint32_t *) buffer_ptr)[3] = (uint32_t) 0;

      tx_fram.Command   = ESP_FLASH_DATA;
      tx_fram.Data_Size = 1024 + 16;
      tx_fram.Body      = buffer_ptr;

      int i;
      for (i = 0; i < 1024; ++i)
      {
        ((uint32_t *) buffer_ptr)[1] = (uint32_t)(i + 1);
        int nbytesread = read(BIN_fd, &(((uint32_t *) buffer_ptr)[4]), 1024);
        if (nbytesread > 0)
        {
          // printf("esp_sub_tool: flash_block: Read %d bytes %d times\n", nbytesread, i + 1);
        }
        else
        {
          printf("esp_sub_tool: flash_block: ERROR: Read nothing\n");
          free(buffer);
          goto ESPTOOL_END;
        }
        ESP_8266_Sed(tx_fd, &tx_fram);
        ESP_8266_Rec(rx_fd, &rx_fram);
        if((rx_fram.Type == 0x01) &&
          (rx_fram.Command == ESP_FLASH_DATA) &&
          (rx_fram.Response == ((current_MAC[0] << 16) |
                                (current_MAC[1] <<  8) |
                                (current_MAC[2]      ))))
        {
          printf("esp_sub_tool: flash_block: write %d bytes %d times\n", nbytesread, i + 1);
        }
        else
        {
          printf("esp_sub_tool: WRITE_FLASH: flash_block: ERROR package\n");
          free(buffer);
          goto ESPTOOL_END;
        }
      }

      free(buffer);
    }

改改細節,運行:

nsh> esptool
esptool v1.0
esp_sub_tool: SYNC: Sync Successful
esp_sub_tool: Read_MAC: 0x3FF00050: 0xFF6A0000
esp_sub_tool: Read_MAC: 0x3FF00054: 0x02001C1A
esp_sub_tool: Read_MAC: 0x3FF00058: 0x4900B000
esp_sub_tool: Read_MAC: 0x3FF0005C: 0x00A020A6
esp_sub_tool: Read_MAC: MAC: A0:20:A6:1C:1A:FF
esp_sub_tool: WRITE_FLASH: erase_page: Response = 0x00A020A6
esp_sub_tool: flash_block: write 1024 bytes 1 times
esp_sub_tool: flash_block: write 1024 bytes 2 times
esp_sub_tool: flash_block: write 1024 bytes 3 times
esp_sub_tool: flash_block: write 1024 bytes 4 times
esp_sub_tool: flash_block: write 1024 bytes 5 times
esp_sub_tool: flash_block: write 1024 bytes 6 times
esp_sub_tool: flash_block: write 1024 bytes 7 times
esp_sub_tool: flash_block: write 1024 bytes 8 times
esp_sub_tool: flash_block: write 1024 bytes 9 times
esp_sub_tool: flash_block: write 1024 bytes 10 times
...
esp_sub_tool: flash_block: write 1024 bytes 1018 times
esp_sub_tool: flash_block: write 1024 bytes 1019 times
esp_sub_tool: flash_block: write 1024 bytes 1020 times
esp_sub_tool: flash_block: write 1024 bytes 1021 times
esp_sub_tool: flash_block: write 1024 bytes 1022 times
esp_sub_tool: flash_block: write 1024 bytes 1023 times
esp_sub_tool: flash_block: write 1024 bytes 1024 times
esp_sub_tool: WRITE_FLASH: Write successful
esptool_main: com0 exit success

邏輯分析儀的結果與hexdump的結果一致。下來就是校驗和高速下載了。
OK。新開一篇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章