Cập nhật chương trình từ xa trên vi điều khiển STM32 – Firmware Over The Air (FOTA) – P5

Trong chuỗi bài viết hướng dẫn cập nhật chương trình từ xa (FOTA) cho dòng vi điều khiển STM32F103, mình đã trình bày tổng quan về mô hình FOTA ở phần 1, cách viết chương trình Bootloader ở phần 2, và giải thích về cấu trúc của một file chương trình Intel HEX ở phần 3. Ở phần 4, mình đã hướng dẫn lập trình chương trình FOTA với file định dạng Intel HEX. Bài viết này mình sẽ tiếp tục giới thiệu cấu trúc của file firmware ở định dạng BIN và hướng dẫn lập trình chương trình FOTA với file đó. Những hướng dẫn này cũng có thể tham khảo và áp dụng cho các dòng vi điều khiển khác.

[HỌC ONLINE: LẬP TRÌNH VI ĐIỀU KHIỂN STM32, VI XỬ LÝ ARM CORTEX – M]

Phần 5: Lập trình chương trình FOTA với file định dạng BIN

1. Định nghĩa

File định dạng BIN (Binary file) có đuôi .bin. Đây là một dạng tệp tin phi văn bản, được lưu trữ dưới định dạng mã nhị phân. Máy tính chúng ta hoạt động dựa theo mã nhị phân nên chúng ta thường dùng loại tệp tin này để lưu tất cả thông tin dữ liệu của bộ nhớ chương trình trên vi điều khiển.

Cấu trúc của một file định dạng BIN là:

  • Các giá trị trong file là mã nhị phân 1 và 0.
  • Dữ liệu được lưu một cách liên tục, không chia ra thành từ dòng.
  • Không chứa địa chỉ bắt đầu nạp.
  • Chỉ chứa dữ liệu, thông tin của chương trình chứ không chứa các trường không liên quan như Start code, Byte count, Address, Record type, Checksum (xem lại cấu trúc file Intel HEX ở phần 3 của chuỗi bài viết này )
  • Không chứa các ký tự kết thúc câu và xuống dòng.

Chúng ta khó có thể đọc hiểu được mã nhị phân nên mình dùng phần mềm SublimeText để chuyển đổi kiểu đọc từ Binary sang Hexadecimal thì sẽ thấy được nội dung của tệp tin này như hình bên dưới:

File định dạng BIN được đọc dưới dạng mã hexadecimal.

2. Cách tạo file định dạng BIN với phần mềm Keil C

Để tạo file định dạng BIN với phần mềm Keil C ta lần lượt làm các bước sau:

Giả sử tên file định dạng BIN của mình là VectorTableRelocation.bin, được lưu vào thư mục MDK-ARM/ VectorTableRelocation

Bước 1: Ấn vào Option, chọn User.

Bước 2: Kích chọn Run#1 tại mục After Build/Rebuild và Copy chuỗi sau vào trong

C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe –bin VectorTableRelocation\VectorTableRelocation.axf –output VectorTableRelocation\VectorTableRelocation.bin

Với

C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe là đường dẫn file đến file fromelf.exe
VectorTableRelocation\VectorTableRelocation.axf là file .axf
VectorTableRelocation\VectorTableRelocation.bin là file .bin ta cần

Bước 3: Sau khi Build chương trình thì các bạn sẽ được như dưới

3. So sánh khi tải dữ liệu kiểu file Intel Hex và file Binary từ Server xuống vi điều khiển:

– File Intel Hex:

Dữ liệu tải xuống vi điều khiển qua giao tiếp UART được lưu dưới dạng các kí tự bảng mã ASCII.

Ví dụ mình tải một giá trị dữ liệu 32 bit là 0x200013E0. Trong Intel HEX file thì giá trị trên sẽ được chuyển sang kiểu chuỗi và tách ra thành 8 byte là ‘E’ ‘0’ ‘1’ ‘3’ ‘0’ ‘0’ ‘2’ ‘0’, khi thực hiện quá trình GET thì thông qua giao thức UART, ta sẽ nhận 8 lần kí tự như dưới.

Giá trị 0x200013E0 trong file định dạng Intel HEX khi tải xuống vi điều khiển

– File Binary:

Dữ liệu tải xuống vi điều khiển qua giao thức UART sẽ lưu 8 bit 1 lần mà không bị thay đổi thành kí tự.

Cũng giá trị 0x200013E0 như ví dụ trên thì trong file định dạng BIN thì nó sẽ được chuyển thành 4 byte 0xE0, 0x13, 0x00, 0x20. Khi thực hiện quá trình GET thì thông qua giao thức UART sẽ nhận 4 lần dữ liệu như hình dưới.

Giá trị 0x200013E0 trong Binary File khi tải xuống vi điều khiển

3. Ghi Firmware vào bộ nhớ chương trình (FLASH) và kiểm tra lỗi:

Trong bài viết này mình sẽ thực hiện quá trình lưu Firmware vào vi điều khiển trước rồi sẽ kiểm tra tính toàn ven của dữ liệu. Do vậy chương trình FOTA sẽ có một chút thay đổi như sau:

 

– Lưu Firmware vào bộ nhớ FLASH:

Để lưu Firmware vào vi điều khiển, mình sẽ thực hiện các bước như sau:

Thuật toán ghi Firmware vào bộ nhớ chương trình

Bước 1: Thực hiện GET dữ liệu về MCU, sau đó mình sẽ chia lần lượt 4 byte thành một cặp.
Ví dụ ta lấy được 0xE0, 0x13, 0x00, 0x20 là bốn byte đầu tiên như hình dưới.

Bước 2: Kết hợp phép tính OR và dịch để tạo thành một biến 32bit.

Lưu ý: Khi sử dụng phép dịch trái thì các bạn nên sử dụng ép kiểu uint32_t nhé.

Bước 3: Dùng kết quả vừa tính được ở bước 2 ghi vào địa chỉ trong bộ nhớ FLASH
Với ví dụ biến valTemp tính trên thì sẽ lưu vào địa chỉ đầu tiên trong Current Firmware Address 0x08013800 (xem lại tổ chức bộ nhớ mô hình FOTA của mình tại phần 1)

Bước 4: Tiếp tục cộng 4 cho giá trị địa chỉ (vì một lần lưu 4 byte dữ liệu) và làm lại những bước trên cho đến khi nạp hết dữ liệu trong tệp tin Binary đã tải về vào bộ nhớ FLASH.
Tiếp tục theo ví dụ trên thì giá trị 0x0800E945 sẽ lưu vào địa chỉ 0x08013804, 0801253F sẽ lưu vào 0x08013808,…

Sau khi đã lưu firmware vào bộ nhớ FLASH ta sẽ kiểm tra dữ liệu:

– Kiểm tra dữ liệu

File định dạng BIN thì chỉ chứa dữ liệu để lưu vào Firmware của MCU và không có trường kiểm tra như trường checksum trong file Intel HEX, vì vậy ta sẽ không thể kiểm tra dữ liệu đã tải về bị lỗi hay không. Ta có thể khắc phục nhược điểm đó bằng cách thêm các byte kiểm tra tính toàn vẹn của dữ liệu vào file trước khi tải lên Server.

Có nhiều phương pháp để kiểm tra lỗi trong quá trình truyền nhận như: Kiểm tra chẵn lẻ (parity check), Checksum hay CRC (Cyclic redundancy check),… Bạn có thể đọc lại thuật toán Checksum file nạp chương trình Intel HEX ở phần 3 của chuỗi bài viết này.

Còn trong phần hướng dẫn làm việc với file binary này, mình sẽ giới thiệu phương pháp CRC-32 với những lý do sau:

  • CRC là 1 phương pháp tổng kiểm tra có độ chính xác cao.
  • Một số MCU STM32 đã tích hợp sẵn phần cứng để thực hiện CRC 32 bit (tham khảo tài liệu Reference Manual của dòng chip các bạn sử dụng).
  • Thư viện HAL cũng hỗ trợ những hàm tính CRC cho người lập trình.

Lưu ý: Sau khi sử dụng tính toán CRC-32 thì các bạn sẽ có 4 byte kiểm tra và các byte này mình sẽ được gắn vào cuối Binary File. Trong quá trình lưu firmware thì ta cũng có thể lưu cả 4 byte này vào bộ nhớ Flash, điều đó không ảnh hưởng đến đến quá trình hoạt động của vi điều khiển.

Tìm hiểu thêm về CRC tại đâyCRC được STM32 hỗ trợ là CRC-32/MPEG-2

Bạn có thể sử dụng các cách sau:

Cách 1: Các bạn tự tìm hiểu và viết 1 chương trình C hoặc C# hoặc một ngôn ngữ nào khác theo các bước sau:

Bước 1: Tổng quan và phân loại về CRC32.
Bước 2: Thuật toán CRC32/MPEG-2.
Bước 3: Tính toán thủ công và kiểm tra lại bằng 1 số tool online như: https://crccalc.com/.
Bước 4: Viết chương trình đọc file bin, tính toán CRC, in giá trị lên màn hình hoặc lưu giá trị vào cuối file.

Cách 2: Sử dụng trực tiếp từ chức năng CRC của STM32.

Các bạn cần cấu hình bên STM32CubeMX như sau: chọn CRC trong Computing, sau đó bật Activated nhé.
Lưu ý: Một số dòng vi điều khiển sẽ không có phần Configuration CRC nhưng các bạn vẫn sử dụng được bình thường nhé.

Dưới đây là một chương trình ví dụ để tính giá trị CRC của một mảng: Firmware Input {0x200013E0, 0x0800E945, 0x0801253F, 0x080118D5}.

Ta sử dụng hàm HAL_CRC_Calculate trong thư viện stm32f1xx_hal_crc.c

Kết quả tính ra sẽ bằng 0xB0EDFEE4

Như vậy, để tính toán CRC-32 cho cả một file Binary làm như sau:

Bước 1: Nạp file chương trình mới vào vi điều khiển.
Bước 2: Sử dụng hàm tính CRC-32 của HAL để tính kiểm tra trực tiếp vào bộ nhớ FLASH.
Bước 3: Lấy kết quả vừa tính được thêm vào cuối file Binary.

Trong chương trình FOTA, để kiểm tra CRC-32 sau khi tải file về thì ta sẽ có một chút thay đổi ở bước 3, ở đây sẽ có 2 cách để kiểm tra:

Cách 1: So sánh kết quả tính CRC vừa tính được với 4 byte kiểm tra trong Binary File ta đã thêm trước đó. Nếu kết quả trả về bằng nhau thì chứng tỏ dữ liệu tải xuống không bị lỗi.
Cách 2: Tính CRC luôn cả 4 byte kiểm tra cuối cùng trong file .Nếu kết quả bằng 0 thì chứng tỏ dữ liệu đã được toàn vẹn trong lúc truyền tải từ Sever về đến MCU.

Ví dụ chúng ta chỉ cần truyền vào địa chỉ bắt đầu nơi muốn thực hiện CRC trong bộ nhớ FLASH và độ dài của Firmware.  

4. Kết luận

Mình đã thực hiện quá trình FOTA với 2 loại tệp tin chứa Firmware định dạng Intel HEX và định dạng BIN như ở trên đã rút ra một số kết luận như sau:

  • Về kích thước:
    Hiện tại mình có một Project, từ đó tạo ra 2 file có kích thước là (66.6 kByte) đối với định dạng Intel HEX và (23.6 kByte) với định dạng BIN.

    => Có thể thấy kích thước của file định dạng BIN nhỏ hơn nhiều so với file định dạng Intel HEX. Kích thước của file ảnh hưởng rất lớn đến quá trình FOTA, file nhẹ thì thời gian tải xuống vi điều khiển nhanh hơn. Cùng với đó, khi truyền tải dữ liệu ít hơn thì nguy cơ xảy ra lỗi cũng ít hơn.
  • Về quá trình xử lý file: cấu trúc file định dạng BIN đơn giản hơn, không cần phải xử lý các thông tin của các trường khác như file định dạng Intel HEX.
    => Tối giản được quá trình xử lý file FOTA, từ đó có thể tiết kiệm được thời gian xử lý trong quá trình cập nhật chương trình.  
  • Kiểm tra dữ liệu: File định dạng BIN không có các byte kiểm tra như trong file định dạng Intel HEX nên khả năng kiểm tra lỗi trong quá trình truyền nhận dữ liệu là không có.
    Tuy nhiên, ta có thể khắc phục được nhược điểm này bằng cách tự thêm các byte kiểm tra vào như ví dụ trong bài viết. Một phương pháp kiểm tra khá hiệu quả là CRC-32, tuy phức tạp nhưng khả năng phát hiện lỗi cao. Đồng thời, STM32 có hỗ trợ hàm tính toán CRC nên có thể áp dụng dễ dàng.

Có thể kết luận rằng thực hiện quá trình FOTA với Binary File sẽ tối ưu hơn cho nền tảng cập nhập chương trình từ xa.

 

Vậy qua bài viết này mình đã cung cấp 2 cách để cập nhật chương trình từ xa, cách bạn có thể cập nhật bằng file định dạng Intel HEX hoặc file định dạng BIN. Hi vọng sau các bài viết này, các bạn có thể hiểu thêm về Firmware Over The Air, sau đó có thể tối ưu và phát triển tiếp các mô hình khác phù hợp với các ứng dụng của các bạn.

Theo dõi thêm:
– Cơ bản về cấu trúc và tính năng Vi xử lý ARM Cortex – Mx. 
– Phân biệt các khái niệm Processor Core, Processor và Microcontroller trong Hệ thống nhúng
– Tổng hợp các bài hướng dẫn Lập trình vi điều khiển STM32F1
– Tổng hợp hướng dẫn Lập trình vi điều khiển STM32F4

 

Chúc các bạn thành công!

Nhóm TAPIT ARM R&D