Khi đến với lập trình nhúng, ai cũng sẽ có lần gặp không ít khó khăn với việc không nắm được bản chất và cách hoạt động của GPIO, một ngoại vi thông dụng mà bất kỳ vi xử lý nào cũng có. Do đó, trong bài này, chúng ta sẽ cùng tìm hiểu một cách rõ ràng về GPIO, cách hoạt động của chúng và ví dụ tham khảo.
1. Định nghĩa về GPIO
- GPIO hay General Purpose Input/Output là một chức năng ngoại vi cơ bản của các vi điều khiển, bao gồm các chân đầu vào và đầu ra có thể được điều khiển bởi người dùng tùy vào mục đích.
- Trong đó, cần phân biệt giữa nhánh con là Pin và Port:
- Port là nhóm các chân GPIO trên cùng 1 cổng hoặc nhóm chức năng. Mỗi Port sẽ chứa 1 số chân GPIO và cung cấp giao tiếp với các thiết bị ngoại vi hoặc môi trường bên ngoài. Vi điều khiển sẽ cung cấp các thanh ghi và vùng nhớ để điều khiển trạng thái của các chân GPIO trên mỗi Port.
- Pin là viết tắt của Port Input, chính là những chân đầu vào/ra của vi điều khiển. Đồng thời, ta có thể điều khiển/đọc trạng thái logic. Mỗi chân GPIO sẽ có số thứ tự hoặc label để phân biệt với các chân GPIO khác trên cùng 1 Port.
2. Sơ đồ chuẩn của GPIO
- Một GPIO gồm có 2 phần mạch cho chiều dữ liệu vào và ra, trong đó:
- Ở chiều vào, bộ vi điều khiển được thiết kế gồm một khối giúp thực hiện điện trở kéo lên/ xuống (pull up/down) và một khối kích hoạt TTL Schmit (tức TTL Schmit trigger).
- Ở chiều ra, bộ vi điều khiển được thiết kế gồm 1 cổng Mux nối với bộ điều khiển đầu ra (output control) và bộ N-MOS/P-MOS giúp kiểm soát trạng thái open-drain, push-pull hoặc bị vô hiệu hóa.
- Các cấu hình cơ bản của 1 chân GPIO:
- Floating: trạng thái lơ lửng mà tại đó điện áp không cố định, làm rò rỉ điện và tiêu tốn năng lượng.
- Pull-up: mặc định là trạng thái điện áp thấp, khi có sự kiện đầu vào như nhấn nút, … thì sẽ kích trạng thái chân lên điện áp cao.
- Pull-down: mặc định là trạng thái điện áp cao, khi có sự kiện đầu vào như nhấn nút, … thì sẽ kéo trạng thái chân xuống điện áp thấp.
- Cách vận hành khi được cài đặt là chân đầu vào:
- Phần mạch xử lý đầu ra (tức Output Buffer) bị vô hiệu.
- Bộ kích thích TTL Schmit được kích hoạt.
- Hai khối điện trở được tùy ý kích hoạt dựa trên cấu hình đầu vào (tức pull-up, pull-down, floating). Trong mọi trường hợp, bộ cấu hình luôn chọn floating làm mặc định.
- Dữ liệu nằm được truyền vào chân I/O và trạng thái của chân sẽ được đưa vào thanh ghi dữ liệu đầu vào (tức là Input Data Register) sau mỗi chu kì clock của đường dẫn APB2.
- Truy cập đọc vào Input Data Register sẽ trả về trạng thái chân I/O.
- Cách vận hành khi được cài đặt là chân đầu ra:
- Phần mạch xử lý đầu ra (Output Buffer) được kích hoạt với một trong hai chế độ:
- Open Drain: Mức “0” trong thanh ghi đầu ra (Output register) được kéo vào để kích hoạt N-MOS trong khi mức “1” sẽ làm cổng ở chế độ HI-Z (tức High Impedance State) không cho P-MOS được kích hoạt.
- Push-Pull: Mức “0” trong thanh ghi đầu ra (Output register) được kéo vào để kích hoạt N-MOS và mức 1 sẽ kích hoạt P-MOS.
- Bộ kích thích TTL Schmit được kích hoạt.
- Hai khối điện trở pull-up và pull-down bị vô hiệu.
- Dữ liệu nằm trên chân I/O và trạng thái của chân sẽ được đọc và đưa vào thanh ghi dữ liệu đầu vào (tức là Input Data Register) sau mỗi chu kì clock của đường dẫn APB2.
- Dữ liệu nằm được truyền vào chân I/O và trạng thái của chân sẽ được đưa vào thanh ghi dữ liệu đầu vào (tức là Input Data Register) sau mỗi chu kì clock của đường dẫn APB2.
- Truy cập đọc vào Input Data Register sẽ trả về trạng thái chân I/O ở chế độ Open Drain.
- Truy cập đọc vào thanh ghi dữ liệu đầu ra (tức là Ouput Data Register) sẽ trả về giá trị được ghi gần nhất trong chế độ Push-Pull.
- Phần mạch xử lý đầu ra (Output Buffer) được kích hoạt với một trong hai chế độ:
3. Thực hành lập trình GPIO với STM32: GPIO với LED và BUTTON
Trong nội dung thực hành này, chúng ta sẽ làm quen với việc lập trình GPIO với LED và BUTTON sử dụng vi điều khiển STM32 với IDE STM32Cubes – một IDE khá phổ biến. Trước khi bắt đầu, bạn có thể xem lại một số nội dung lý thuyết đã được đề cập ở phần trước để hiểu thêm về nguyên lý lập trình nhé.
3.1. Chuẩn bị linh kiện
3.2. Lập trình với STM32 Cube
- Bước 1: Khởi tạo project CubeMX, sau đó chọn cổng nạp dữ liệu Serial Wire, setup các thông số như những Part 0 đã trình bày
- Bước 2: Cài đặt chân PA6 là chân GPIO_Input để giao tiếp với ngoại vi input là BUTTON, chân PC13 là chân GPIO_Output để giao tiếp với ngoại vi LED, chúng mình chọn chân PA6 và PC13 sử dụng để lập trình trong ví dụ này, các bạn có thể chọn các chân khác hoặc nhiều chân khác nếu có nhu cầu nhé (nhớ kiểm tra spec đặc tính của chân trước khi sử dụng)
- Bước 3: Đổi lại tên các port để thuận tiện cho việc lập trình bằng cách chuột phải vào port và chọn “Enter User Label”
- Bước 4: Với GPIO BUTTON ta sẽ lập trình ở chế độ pull-up (Các chế độ của GPIO trình bày ở mục 2). Thực hiện bằng cách vào “GPIO” đổi chức năng của PA6 thành “pull-up”
- Bước 5: Genarate code và code một vài chức năng cơ bản. Sau bước này, IDE đã setup và tạo cho ta một chương trình cơ bản có đầy đủ các thư viện và khai báo ban đầu, việc còn lại của chúng ta tiến hành lập trình điều khiển với các GPIO đã cấu hình. Thông thường, chương trình thực thi trên STM32 sẽ được lập trình trong vòng lặp while để chương trình được thực hiện liên tục. Trong ví dụ này, chúng ta sẽ lập trình đọc tín hiệu BUTTON bằng lệnh HAL_GPIO_ReadPin và xuất giá trị ra LED với lệnh HAL_GPIO_WritePin. Cùng theo dõi đoạn code mẫu bên dưới và phần giải thích để hiểu thêm cú pháp và cách lệnh vận hành.
- Giải thích:
- Trước hết ghi LED ở chế độ high để tắt LED.
- Trong vòng loop vô hạn, ta sẽ dùng 1 biến tạm lưu lại trạng thái nút bấm
- Thêm điều kiện để LED sáng và LED tắt khi nhấn nút.
3.3. Video demo
Lời kết
Phần hướng dẫn này đã trình bày một cách cơ bản về GPIO và cách lập trình GPIO qua một ví dụ cơ bản. Từ ví dụ này, các bạn có thể vận dụng thêm các hàm delay, cấu trúc điều kiện hoặc các ngoại vi khác để tạo ra một sản phẩm thú vị. Chúc các bạn thành công ^^
Thực hiện bài viết: Huỳnh Thanh Sang, Nguyễn Hoàng Quốc Cường