Debug (gỡ lỗi) là một kĩ năng thiết yếu của một lập trình viên. Debug không chỉ được sử dùng để phát hiện và loại bỏ lỗi (error) khỏi chương trình mà còn giúp lập trình viên hiểu rõ hơn và kiểm tra được sự thực thi của chương trình trong suốt quá trình xây dựng. Nếu bạn là một lập trình viên nhưng chưa trang bị cho bản thân kĩ năng debug thì sẽ gặp rất nhiều khó khăn trong công việc. Bài viết này cung cấp các hướng dẫn cơ bản nhất cho các bạn lập trình nhúng trên dòng vi điều khiển STM32 sử dụng công cụ STM32CubeIDE.
[HỌC ONLINE: LẬP TRÌNH VI ĐIỀU KHIỂN STM32, VI XỬ LÝ ARM CORTEX – M]
1. Biên dịch một Project
Để biên dịch một project trong STM32CubeIDE các bạn chọn project cần biên dịch trong Project Explorer[1], sau đó nhấn vào biểu tượng Build [2] và quan sát các thông báo kết quả biên dịch [3]:
2. Vào giao diện Debug một Project
Trước khi vào trình Debug của một Project, bạn cần kiểm tra thông báo lỗi của quá trình biên dịch và đảm bảo không có lỗi nào (0 errors) [1]. Sau đó, nhấn vào biểu tượng Debug và chuyển qua giao diện Debug [2]:
3. Nạp chương trình cho vi điều khiển
Từ giao diện Debug, bạn nhấn vào biểu tượng Resume để đưa code xuống vi điều khiển [1]. Sau khi kết thúc quá trình debug, bạn nhấn vào biểu tượng Terminate để thoát cửa sổ debug[2]:
4. Sử dụng Breakpoint để Debug
Breakpoint là những điểm (vị trí) trong chương trình được người lập trình chọn mà trình biên dịch sẽ dừng lại nếu gặp nó, breakpoint rất hữu ích trong việc phát triển chương trình và gỡ lỗi.
- Sử dụng breakpoint để kiểm tra chương trình của bạn có thực hiện được đến câu lệnh mà bạn đặt breakpoint hay không. Nếu chương trình bạn không dừng lại tại câu lệnh đó, thì chứng tỏ lệnh tại breakpoint không được thực hiện nhưng mong đợi. Như vậy chương trình của bạn có thể có lỗi.
- Sử dụng breakpoint để theo dõi, kiểm tra và thay đổi giá trị các biến, bộ nhớ, các thanh ghi ngoại vi, thanh ghi CPU tại thời điểm dừng câu lệnh để quan sát chương trình chạy có đúng hay không, xem xét các giá trị có theo dự kiến hay không hoặc để tác động trực tiếp vào chương trình.
- Sử dụng breakpoint tạm dừng chương trình để người sử dụng tương tác vào thông qua các ngoại vi, từ đó quan sát các bước thực thi tiếp theo (luồng) của chương trình có thực hiện đúng hay không sau khi tác động.
Tạm dừng chương trình bằng cách sử dụng Breakpoint:
Trong giao diện Debug (sau khi nhấn vào biểu tượng Debug) thì bạn có thể đặt Breakpoint tại thanh dọc màu xanh bằng cách nhấp đôi chuột vào vị trí tương ứng với câu lệnh muốn đặt breakpoint[1], có thể đặt nhiều breakpoint trong 1 chương trình. Nhấn đôi chuột vào breakpoint nếu bạn muốn loại bỏ nó. Để kiểm tra chương trình có dừng lại tại Breakpoint không, bạn nhấn vào biểu tượng Resume để chương trình bắt đầu thực thi[2]. Nếu đúng, chương trình sẽ dừng lại tại điểm Breakpoint, nếu muốn chương trình chạy tiếp thì tiếp túc nhấn vào biểu tượng Resume. Để reset MCU cho chương trình thực hiện lại từ đầu thì nhấn vào biểu tượng Reset the chip and restart the debug session[3]:
Bên cạnh đó, chúng ta thường kết hợp với các nút điều khiển cả trình debugger để:
Nhảy vào các lệnh bên trong 1 hàm hoặc thực hiện qua một lệnh
Thực hiện qua 1 hàm hoặc 1 lệnh
Thoát khỏi 1 hàm
5. Xem giá trị các biến cập nhật liên tục
Để xem giá trị các biến được cập nhật liên tục, từ giao diện Debug, các bạn chọn cửa sổ Live Expressions [1], sau đó các bạn nhấn vào chữ Add new Expression và gõ tên biến các bạn muốn quan sát giá trị [2]. Một số tùy chọn định dạng hiển thị các bạn có thể lựa chọn để dễ quan sát hơn như Hex, Dec, Oct, Binary các bạn có thể cài đặt tại [3]:
6. Xem giá trị các thanh ghi cập nhật liên tục
Để xem giá trị thanh ghi được cập nhật liên tục, bạn cần nhập đúng biểu thức đại diện cho thanh ghi vào thẻ Live Expressions. Ví dụ, để xem giá trị thanh ghi ODR của GPIOA các bạn cần nhập GPIOA -> ODR. Thường thì để xem giá trị thanh ghi chúng ta sẽ chỉnh định dạng hiển thị thành Binary cho dễ theo dõi.
7. Xem giá trị các thanh ghi cập nhật theo yêu cầu
Để xem giá trị thanh ghi mỗi khi được yêu cầu, bạn chọn thẻ SFRs[1], sau đó chọn thanh ghi mà bạn muốn xem[2]. Nhấn vào chữ RD (Force read Selected Register) trong lúc chương trình đang thực thi để cập nhật giá trị mới nhất của thanh ghi [3], có thể tùy chỉnh định dạng hiển thị giá trị ở mã Hex (x16), mã Dec (x10), mã Binary(x2):
8. Debug in dữ liệu từ chương trình lên console SWV
Để sử dụng tính năng này, bạn cần:
- Bổ sung đoạn code sau vào trước hàm int main (void) {}
- Thêm nội dung bạn muốn in lên console, có thể in dữ liệu ở nhiều kiểu khác nhau:
- Cấu hình Debug: Nhấn chuột phải vào project muốn debug [1], sau đó chọn Debug as [2] và chọn Debug Configuration[3]:
- Trên cửa sổ Debug Configurations, bạn chọn thẻ Debugger[1], sau đó tích chọn Enable Serial Wire Viewer (SWV) [2], nhấn chọn Apply[3] rồi Debug[4]:
- Mở giao diện hiển thị dữ liệu: Từ giao diện Debug, chọn Window -> Show View -> SWV -> SWV ITM Data Console:
- Trên diện SWV ITM Data Console, nhấn vào biểu tượng configure trace[1], tích chọn vào Port 0[2] và nhấn chọn Ok[3]. Sau đó chọn biểu tượng Start trace[4] và nhấn vào biểu tượng Resume [5] để bắt đầu chạy debug và quan sát kết quả ở thẻ Port 0 của SWV ITM Data Console [6]
Kết quả với ví dụ:
9. Debug vẽ biểu đồ dữ liệu với SWV
Biến được sử dụng để vẽ biểu đồ là biến toàn cục. Trong ví dụ của mình thì mình khai báo biến toàn cục test: uint8_t test = 0; và thực hiện tăng biến test lên 1 đơn vị sau mỗi 1 giây để thấy sự thay đổi trên biểu đồ:
Nhấn chuột phải vào Project muốn debug [1], sau đó chọn Debug as [2], chọn Debug Configuration [3]:
Trên cửa sổ Debug Configurations, bạn chọn thẻ Debugger[1], sau đó tích chọn Enable Serial Wire Viewer (SWV) [2], nhấn chọn Apply[3] rồi Debug[4]:
Từ cửa sổ Debug, chọn Window -> Show View -> SWV -> SWV Data Trace Timeline Graph:
Tại thẻ SWV ITM Data Console, nhấn vào biểu tượng configure trace[1], tích chọn vào Port 0[2], tích chọn Enable Comparator và điền tên biến muốn vẽ biểu đồ giá trị [3] và sau đó nhấn chọn OK[4]. Chọn biểu tượng Start trace[4], sau đó chọn biểu tượng Resume [5] để bắt đầu chạy debug và quan sát kết quả ở SWV Data Trace Timeline Graph [6]
Kết quả với ví dụ:
10. Nhận xét
Trong quá trình sử dụng thì mình số có một số ưu điểm của STM32CubeIDE so với KeilC
- STM32CubeIDE không chỉ báo các lỗi cú pháp mà còn gợi ý cách sửa lỗi
- STM32CubeIDE có thể chú thích code bằng tổ hợp phím Ctr+/
- STM32CubeIDE hỗ trợ định dạng, canh chỉnh coding style: Window -> Preferences -> Code Style -> Formatter (tổ hợp phím: Ctrl + Shift + F)
- Chức năng xem memory của STM32CubeIDE tiện hơn KeilC là CubeIDE cho phép xem dữ liệu trong memory nhiều chế độ như hex, ascii,… cùng lúc.
Lưu ý: Để tăng/ giảm kích cỡ của mã code thì dùng tổ hợp phím Ctrl + Shift + [+] / [-] thay vì Ctrl + lăn chuột như Keil C.
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