Có thể hiểu state machine là một hệ thống mà kết quả đầu ra không những phụ thuộc và đầu vào mà còn phụ thuộc vào trạng thái của hệ thống. Một hệ thống thường có những trạng thái hữu hạn, để chuyển từ trạng thái này sang trạng thái khác cần những sự kiện chuyển trạng thái. Ví dụ
Hình trên mô tả một State machine đơn giản. Hệ thống sẽ kết thúc khi nhập 2 ký tự // liên tiếp. Ở ký tự đầu tiên, hệ thống chưa cho output nhưng sẽ chuyển qua state 2. Nếu đang ở state 2 mà nhập thêm “/” thì output ngược lại sẽ quay về trạng thái 1. Một state machine có thể được mô tả bởi các thành phần sau:
- Một tập các trạng thái của hệ thống.
- Trạng thái khởi tạo.
- Tập hợp các sự kiện Input.
- Tập hợp các sự kiện Output.
- Tập hợp các hành động hay các sự kiện output ứng với mỗi trạng thái của hệ thống (gọi là state event handler).
- Các sự kiện chuyển trạng thái (state transtion).
Thông thường, state machine cho phép người dùng mô tả các trạng thái phức tạp thông qua nhiều trạng thái của các hệ thống phân cấp (composit state). Một hệ thống state machine phân cấp dựa trên quan hệ cha con ( parent – chid):
Tương tự như một sub-state nằm trong một nhóm state mô tả trạng thái hiện tại của hệ thống.
Composite states cũng có chuyển trạng thái, entry, exit …
Sub-state kế thừa từ composit state.
Trạng thái hiện tại (active state) thể hiện một đường từ trạng thái ban đầu cho đến vị trí hiện tại.
Để cài đặt state Machine, thông thường người ta định nghĩa ra các trạng thái của hệ thống. Trong mỗi vòng lặp sẽ có sử dụng switch() để check trạng thái hiện tại của hệ thống và các điều kiện chuyển tiếp. Nếu điều kiện chuyển tiếp thỏa mãn thì gọi hàm xử lý và update sang trạng thái mới. Có thể tham khảo code mẫu sau
Ứng dụng State Machine để cài đặt thư viện Button Ý tưởng của bộ thư viện Button là có thể cài đặt để xử lý được 3 loại tác động cơ bản bao gồm: Click(), DoubleClick() và Press() (giữ phím). Biểu diễn state machine của nút bấm như hình dưới
Ở trạng thái khởi tạo (IDLE), khi buttun được nhấn, hệ thống chuyển sang trạng thái 1. Trạng thái này sẽ chờ trong một khoảng thời gian nhất định (timeout), trong khoảng thời gian này, nếu button được thả ra, hệ thống chuyển qua trạng thái 2. Nếu hết thời gian timeout mà button chưa được thả thì sẽ kết luận là sự kiện giữ phím – Press(). Tương tự đối với các trạng thái khác. Ý tưởng để cài đặt hệ thống trên như sau: Để thực hiện việc detect ra các sự kiện nút bấm ta cần biết trạng thái hiện thời của hệ thống. Trạng thái này gồm 2 thành phần là
- Giá trị logic của chân GPIO nối với nút bấm để xác định Button đang ” up” hay “down”.
- Thời gian kể từ lúc chuyển trạng thái để xác định đã hết thời gian time out hay chưa.
Giá trị logic hoàn toàn có thể dựa vào các hàm hỗ trợ đọc giá trị của chân GPIO. Vấn đề là xác định thời gian. Trong nhiều hệ thống nhúng, và cả trong các bộ thư viện của Andruno, để xác định thời gian trong hệ thống, người ta dùng một biến toàn cục để đếm số lượng “tick”. “Tick” ở đây có thể hiểu là một đơn vị thời gian của hệ thống. Sau mỗi khoảng thời gian nhất định thì sẽ tăng lên một “tick”, thông thường thời gian tăng một “tick” vào khoảng 1ms. Có thể cài đặt tăng “tick” bằng cách sử dụng ngắt timer để sau mỗi 1ms thì tăng tick lên một đơn vị.
Cài đặt thư viện Button Bước đầu tiên là định nghĩa các trạng thái của hệ thống. Dựa vào hình trên, ta định nghĩa ra các trạng thái tương ứng như sau:
Đối với Button, ta tiếp cận theo hướng hướng đối tượng. Nếu coi Button như một đối tượng thì nó bao gồm các thuộc tính và phương thức như sau:
Lưu ý, do ngôn ngữ C không hỗ trợ hướng đối tượng nên các phương thức sẽ được khai báo bằng các con trỏ hàm. Như ở ví dụ trên, SYSTEMCALLBACK là một macro định nghĩa con trỏ hàm sau:
Các tham số về thời gian timeout được định nghĩa như sau:
Để xử lý được nhiều button, ta sẽ định nghĩa ra một mảng chứa các con trỏ hàm, mỗi phần tử trong hàm này sẽ trỏ đến một đối tượng Button.
Khi muốn sử dụng button, ta chỉ việc định nghĩa Button và gán vào một vị trí trong mảng trên. Mỗi vòng lặp, hệ thống sẽ thực hiện quét các phần tử trong mảng và lần lượt xác định trạng thái các button và gọi hàm callback tương ứng. Đối với thư viện Button, ta cần xây dựng các hàm sau.
Cài đặt cụ thể các hàm, mọi người có thể tham khảo theo link github. Trong bài viết này, mình sẽ tập trung vào cài đặt xử lý các trạng thái Button theo state machine. Ở mỗi vòng lặp, hệ thống sẽ quét mảng g_pButtons để xác định các phần tử mảng đã được gán với Button chưa, đồng thời, các button này có được enable hay không. Nếu các điều kiện này thỏa mãn sẽ thực hiện switch trạng thái Button.
Ở trạng thái IDE, nếu Button được ấn thì sẽ chuyển sang trạng thái WAIT_BUTTON_UP ( Tương ứng trạng thái 1 trên sơ đồ) và bắt đầu đếm thời gian để xác định timeout.
Ở trạng thái WAIT_BUTTON_UP, nếu Button được thả ra và chưa hết thời gian time out, hệ thống sẽ chuyển sang trạng thái WAIT_CLICK_TIMEOUT (Tương ứng trạng thái 2). Nếu timeout, hệ thống sẽ nhận ra đây là sự kiện Hold phím và gọi hàm xử lý tương ứng.
Phân tích tương tự đối với các trạng thái 3 và 6 trên sơ đồ, ta có các cài đặt tương ứng là:
Để sử dụng thư viện trên, ta khai báo các biến button sau đó gán hàm xử lý sự kiện tương ứng và đưa vào mảng button.
Ví dụ:
Code đầy đủ của thư viện, các bạn tham khảo ở link github.