Porting LWIP cho vi điều khiển STM32 (P1)

LWIP – Light weight IP là một bộ thư viện mã nguồn mở được thiết kế dành cho những hệ thống có tài nguyên tương đối hạn chế, phù hợp với các hệ thống nhúng. Hỗ trợ tương đối đầy đủ các giao thức mạng trên nền TCP/IP. Có thể hỗ trợ giao tiếp với vi điều khiển thông qua Serial hoặc Ethernet. TAPIT chia sẻ đến các bạn chuỗi bài viết hướng dẫn về lý thuyết thư viện, porting các function, kết nối MQTT, kết nối HTTP trên nền tảng LWIP và tối ưu chương trình trên vi điều khiển STM32.

Tại phần 1, mình sẽ giới thiệu đến các bạn bộ thư viện LWIP, hướng dẫn cách add thư viện, cách để port các file và port các function khi sử dụng LWIP. 

Phần 1. Porting LWIP cho vi điều khiển STM32

1. Giới thiệu về LWIP

1.1 Mô hình kết nối giữa vi điều khiển và server

LWIP hỗ trợ các cách kết nối vật lí chính:

  • Serial
  • Ethernet
  • Các chuẩn kết nối khác (WiFi…)

a. Sử dụng serial UART

Ở mô hình kết nối này, vi điều khiển sẽ kết nối với modem mạng qua chuẩn giao tiếp nối tiếp UART, phù hợp với nhiều dòng VDK khác nhau, tuy nhiên tốc độ kết nối phụ thuộc nhiều vào tốc độ baudrate của VDK.

b. Sử dụng Ethernet

Với mô hình kết nối này, LWIP sẽ sử dụng Ethernet để kết nối lên server, tốc độ cao hơn Serial. Tuy nhiên VDK cần phải hỗ trợ Phy Ethernet hoặc module chuyển đổi SPI-Ethernet. Bài viết sẽ tập trung vào cách porting LWIP qua chuẩn giao tiếp serial UART.

1.2 So sánh cách giao tiếp AT command và LWIP stack

Về cơ bản có 2 cách kết nối chính từ vi điều khiển lên cloud qua serial với module 4G như sau:

  • Ở cách thứ 1 : vi điều khiển dựa hoàn toàn vào tập lệnh của nhà sản xuất module 4G để thực hiện kết nối internet.
  • Ở cách thứ 2 : vi điều khiển dựa vào tập lênh của nhà sản xuất module 4G để khởi tạo, sau đó kết nối internet qua LWIP.
  • So sánh ưu – nhược điểm của 2 phương pháp
Phương pháp Network stack Ưu điểm Nhược điểm
AT command (cách 1)

 

· Phần xử lí các application như TCP, UDP, HTTP… đều nằm trên module 4G.

· Chip vi điều khiển chỉ nhận lấy các kết quả cuối cùng của các giao thức bằng các tập lệnh AT-Command

· Tiết kiệm tài nguyên cho vi điều khiển

· Các ứng dụng mạng phụ thuộc vào firmware của nhà sản xuất module

· Khi thay đổi module thì cần phải thay đổi tập lệnh của các ứng dụng.

· Tiêu tốn ít tài nguyên của vi điều khiển.· Độ ổn định của firmware module 4G cần test lại với mỗi module, mỗi nhà sản xuất khác nhau.

· Nhiều giao thức mạng bị hạn chế bởi module của nhà sản xuất, và phụ thuộc và tính năng firmware module của nhà sản xuất.

LWIP stack (cách 2)

 

· Phần xử lí tầng application các giao thức mạng đều nằm hết trên vi điều khiển, module 4G không xử lí các giao thức mạng.

 

· Khả năng port code giữa các nền tảng tương đối dễ dàng.· Kế thừa được nhiều framework base trên LWIP.

· Khả năng “portable, reuseable” cao· Dễ dàng chuyển đổi module 4G giữa các nhà cung cấp khác nhau.

· Tiêu tốn tài nguyên của vi điều khiển nhiều hơn so với dùng AT command và network stack của nhà sản xuất.

· Một số ứng dụng cần nhiều tài nguyên phần cứng (SSL..) thì đòi hỏi cấu hình RAM của vi điều khiển lớn.

· Không chạy được đồng thời data mode và command mode. Phải chuyển qua lại giữa data mode và command mode.

2. Porting

Trong bài viết này, mình sử dụng kit có sẵn cho thuận tiện, với tài nguyên như sau:

  • Vi điều khiển STM32L083RZ, cấu hình CPU 32Mhz, 20KB RAM, 192KB Flash. Lưu ý các bạn có thể dùng MCU có flash size nhỏ hơn, nhưng theo khuyến nghị của mình là 64KB Flash.
  • IDE : KeilC (MDK-ARM), optimize code level 0.
  • Tool gen code STM32CubeMX.

Mình sẽ hướng dẫn các bạn khởi một Project và cách thêm thư viện LWIP vào Project.

2.1 Khởi tạo project

  • Trong bài viết này mình sẽ sử dụng bộ giao động nội 32Mhz clock, USART với DMA. Tuy nhiên mình sẽ không đi quá chi tiết vào cách thức khởi tạo và code driver.
  • Pinout cơ bản.

  • Clock cơ bản.

2.2 Thêm thư viện LWIP vào project

  • Cấu trúc folder cần import như sau.

  • Folder “porting” là phần lập trình viên cần code cho nền tảng Vi điều khiển STM32.
  • Folder “app” chứa các thư viện liên quan đến tầng ứng dụng của LWIP như HTTP, MQTT, MDNS…
  • Folder “core” và “ipv4” chứa thư viện lõi của LWIP.
  • Đường dẫn đến header file cần include vào project.

  • Chi tiết các file cần import vào project.

2.3 Port các file cần thiết cho nền tảng STM32

a. lwipopts.h

 lwipopts.h chứa toàn bộ các cấu hình của LWIP stack.

b. sys_timer.c

Có nhiệm vụ cung cấp các timer để định thời, timeout cho internal timer của LWIP. Trong đó bắt buộc cần có 2 hàm trả về thời gian hiện tại của hệ thống là “sys_now” và “sys_jiffies” với đơn vị millisecond, các bạn có thể thay thế hàm “sys_get_tick_ms” bằng hàm “HAL_GetTick” của STM32.

c. cc.h và cpu.h

Chứa các định nghĩa về kiến trúc MCU đang sử dụng.

2.4 Porting các function cần thiết

a. sys_rand

Dùng để ramdom 1 số uint32_t, để cho đơn giản mình sẽ lấy systick value của vi điều khiển lỗi ARM

b. Các hàm liên quan đến việc log dữ liệu debug

Để phục vụ in dữ liệu qua màn hình, các bạn có thể retarget printf qua UART, tuy nhiên ở ví dụ này, mình sử dụng bộ thư việc “Segger_RTT”, ưu điểm của bộ thư viện này là chỉ cần 2 dây debug “SWD” để in dữ liệu. Các bạn có thể tìm hiểu thêm về bộ thư viện này ở đây “https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/”, tuy nhiên ở bài viết này mình sẽ không đề cập chi tiết.

c. Hàm gửi dữ liệu qua cổng UART từ LWIP đến module 4G

 “ppp_output_callback” gửi data qua cổng “USART1”, với 2 tham số cơ bản là “data” và “len”, lần lượt là dữ liệu và độ dài dữ liệu LWIP muốn đẩy tới module 4G.

d. gsm_hw_pppos_polling và sio_read

  • Định kì nhận dữ liệu từ cổng UART của module sim, truyền vào LWIP stack. Đồng thời polling các timer của LWIP, hàm này cần gọi liên tục vô hạn trong vòng lặp while(1) của hệ thống

  • Khi chuyển sang chế độ PPP mode, mọi dữ liệu từ cổng UART sẽ chuyển vào PPP buffer, chúng ta cần phân biệt uart rx data đó dành cho AT command hay PPP stack, ý tưởng của mình đơn giản như sau:

e. Hàm ngắt nhận dữ liệu UART

Hàm nhận dữ lịêu ngắt uart rồi lưu vào PPP buffer, hoặc AT command buffer. Đây là 1 cách implement đơn giản của mình.

Trong đó “uint32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)” trả về số byte UART-RX mà vi điều khiển nhận về từ module 4G, cần truyền lượng data này vào lwip stack, với tham số truyền vào là buffer data và độ dài dữ liệu mong muốn của LWIP. Dữ liệu được đưa tiếp vào LWIP stack bằng hàm “pppos_input”.

Vậy là tại phần này, mình đã giới thiệu đến các bạn về LWIP và các hàm driver cần thiết cho LWIP. Các nội dung tiếp theo của chuỗi bài viết:

Phần 2: Thiết kế chương trình và porting ứng dụng cho LWIP

Phần 3: Kết nối MQTT trên nền tảng LWIP

Phần 4: Kết nối HTTP trên nền tảng LWIP

Phần 5: Tối ưu các tham số cho vi điều khiển – Optimize memory (ram, flash, speed)

Cố vấn tại Cộng đồng Kỹ thuật TAPIT

Trần Văn Huy

Tìm hiểu thêm:
Fanpage Cộng đồng Kỹ thuật TAPIT: TAPIT – Learning, Research and Sharing Community