Các chế độ hoạt động của QSPI Flash và thư viện quadspi

Bộ nhớ QSPI Flash mở rộng có thể được giao tiếp một cách gián tiếp thông qua các HAL API hoặc vi xử lý có thể xem QSPI Flash là một phần của bộ nhớ và truy cập sử dụng trực tiếp được bộ nhớ này tương tự bộ nhớ Flash tích hợp. Bài viết này sẽ cung cấp thông tin về các chế độ hoạt động của QSPI Flash và hướng dẫn hiệu chỉnh thư viện quadspi để có được những hàm khởi tạo chế độ hoạt động phù hợp và các hàm đọc ghi giúp làm việc với vùng nhớ mới này.

Nội dung cần đọc qua trước:
Các bài viết liên quan nên đọc trước:
IC nhớ FLASH,SDRAM và mô hình bộ nhớ của vi điều khiển khi sử dụng bộ nhớ ngoài
Sử dụng Linker Script trong khai báo, cấu hình sử dụng bộ nhớ ngoài -VĐK STM32 
Mở rộng bộ nhớ STM32 với QSPI Flash MT25QL128: Ghép nối phần cứng, cấu hình ngoại vi

1. Các chế độ hoạt động của QSPI Flash

Trước khi bộ nhớ QSPI Flash được sử dụng với các tao tác đọc ghi, người sử dụng cần hiểu về hai cách sử dụng chủ yếu của QSPI Flash. 

1.1. Giao tiếp với QSPI Flash thông qua các HAL API (INDIRECT MODE – CHẾ ĐỘ GIÁN TIẾP)

  • Ở chế độ này, vi điều khiển không nhận thức được không gian nhớ của QSPI Flash. Nói cách khác để đọc hoặc ghi vào QSPI Flash, ta phải thông qua các HAL API và thư viện hỗ trợ như:
    • uint8_t CSP_QSPI_WriteMemory(uint8_t* buffer, uint32_t address, uint32_t buffer_size)
    • uint8_t CSP_QSPI_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size)
    • uint8_t CSP_QSPI_Erase_Chip (void)
  • Ví dụ bạn muốn 8 bytes tại địa chỉ 0 trở đi, ta có thể làm như sau:
    • char buffer[10] = {0};
    • uint8_t CSP_QSPI_Read(buffer, 0, 8);
  • Nếu chúng ta có một số dữ liệu cố định và không cần truy cập thường xuyên và mỗi lần truy cập chỉ cần đọc một đoạn nhỏ thì ta có thể dùng cách này.
  • Quy tắc làm việc của chế độ này có thể được tóm tắt như hình 1 dưới:

Hình 1: Đọc QSPI Flash bằng các HAL API

1.2. Chế độ Memory mapped

  • Ở chế độ này, vi điều xử lý biết được không gian nhớ của bộ nhớ QSPI Flash. Việc đọc đối với bộ nhớ Flash ngoài được thực thi bằng các assembly instruction tương tự như đối với flash nội (LDR).
  • Ngoại vi QSPI sẽ là phần cứng trung gian nhận biết được vùng địa chỉ thuộc về QSPI Flash, sau đó thực hiện việc biến đổi LDR instructions trên thành các thủ tục (command, địa chỉ, tín hiệu chip select,…) của giao thức QSPI. Sau khi đọc được dữ liệu, ngoại vi sẽ đưa dữ liệu lên hệ thống bus AHB cho vi điều khiển.
  • Phương pháp này có ưu điểm là người lập trình không cần quan tâm đến việc phải gọi hàm HAL để đọc. Lập trình viên chỉ cần khai báo các biến ở bộ nhớ ngoài để vi điều khiển nạp địa chỉ tương ứng với bộ nhớ ngoài còn ngoại vi QSPI sẽ lo phần còn lại.
  • Quy tắc làm việc được mô tả như ở hình 2 dưới:

Hình 2: Quy tắc làm việc của chế độ Memory mapped

1.3. Nâng cao: Execute In Place (XIP)

  • Đây là chế độ cuối cùng được giới thiệu và cũng là chế độ phức tạp hơn. Ở chế độ này, toàn bộ code phần ứng dụng (application) mà người dùng viết sẽ được nạp ở bộ nhớ ngoài. Còn bộ nhớ nội sẽ chứa một firmware nhỏ còn được gọi là bootloader. 
  • Khi khởi động lên, bootloader sẽ đảm nhiệm việc cấu hình clock, debug, ngoại vi QSPI với chế độ memory mapped và có thể có cả các ngoại vi khác như ADC, TIMER,… nếu cần; Sau đó, chương trình sẽ nạp lại bảng vector table vào địa chỉ mới và sau đó nhảy đến vị trí mới nằm trong bộ nhớ QSPI Flash. Khi này, QSPI đã hoạt động bình thường nên vi điều khiển sẽ truy cập lệnh từ QSPI Flash như một bộ nhớ nội.
  • Có nhiều phương án để và các thiết lập khác nhau giữa phần bootloader với application code. Một ví dụ tham khảo nằm trong video [1]

2. Hiệu chỉnh thư viện “quadspi” cho bộ nhớ QSPI Flash

Dù ở bất kì mode nào, thì ta đều phải thêm các hàm vào 2 file “quadspi.h” và “quadspi.c”. Mặc định khi mới sinh code, các file này chỉ chứa các hàm khởi tạo cơ bản như:

  • void MX_QUADSPI_Init(void)
  • void HAL_QSPI_MspInit(QSPI_HandleTypeDef* qspiHandle)
  • void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef* qspiHandle)

Các hàm trên chỉ có tác dụng khởi tạo ngoại vi qspi trong vi điều khiển. Giờ ta phải thêm vào các hàm để khởi tạo các chế độ phù hợp cho chip QSPI Flash cũng như các hàm để đọc/ghi đối với QSPI Flash. Một số các hàm thêm vào như sau:

  • uint8_t CSP_QUADSPI_Init(void) : Khởi tạo cho QSPI Flash
  • uint8_t CSP_QSPI_Era+raseSector(uint32_t EraseStartAddress, uint32_t EraseEndAddress) : Xóa một sector trên chip
  • uint8_t CSP_QSPI_WriteMemory(uint8_t* buffer, uint32_t address, uint32_t buffer_size) : Ghi một số byte lên Flash
  • uint8_t CSP_QSPI_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size) : Đọc một số bytes trong Flash

Việc trình bày chi tiết định nghĩa hàm trong bài viết này sẽ quá dài. Chính vì thế, tác giả xin gửi đến người đọc 2 nguồn tham khảo để tải các file được viết sẵn:

  • File thư viện cho 3 loại chip QSPI Flash của STMicroelectronics: [2]
  • File thư viện của kênh “ControllersTech”[3]. Tác giả thêm vào một hàm 

Lưu ý: Nếu bạn đọc cũng đang dùng kit STM32F746 DISCO với chip nhớ MT25QL128 thì cần phải sửa một số vị trí để đọc được dữ liệu chính xác:

  • Trong file “quadspi.c”, hàm “uint8_t CSP_QSPI_WriteMemory(uint8_t* buffer, uint32_t address, uint32_t buffer_size)”, sửa như sau:
    sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
  • Trong file “quadspi.c”, hàm “uint8_t CSP_QSPI_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size)”, sửa như sau:
    s_command.AddressSize = QSPI_ADDRESS_24_BITS;
  • Trong file “quadspi.h”, sửa dòng “#define DUMMY_CLOCK_CYCLES_READ_QUAD 8” lại như sau:
    #define DUMMY_CLOCK_CYCLES_READ_QUAD 10
  • Trong file “quadspi.c”, hàm “uint8_t CSP_QSPI_Erase_Chip(void)”, sửa như sau:
    sCommand.AddressSize = QSPI_ADDRESS_24_BITS;

Phần tiếp theo của bài viết này sẽ cung cấp các thông tin về File external loader cho QSPI Flash và các thực nghiệm đọc dữ liệu từ bộ nhớ QSPI Flash. Chúc các bạn thành công!

Tài liệu tham khảo:
[1]https://www.youtube.com/watch?v=gAyuF20ok8c

[2]https://github.com/controllerstech/STM32 
[3]https://www.elprotronic.com/products/flashpro-m-x2s

Nhóm tác giả
Ng.Q.Phương
Ng.H.Phúc
Ng.H.N.Thương