- GPIO là gì?
Với tất cả những ai đã từng lập trình vi điều khiển như 8051, AVR hay ARM, chắc không xa lạ gì với GPIO. Vậy GPIO là gì? GPIO- General Purpose In Out, tạm dịch là chân vào ra đa mục đích. Được sử dụng để đọc hoặc ghi tín hiệu số. Ví dụ: Để điều khiển bật/tắt LED- Light Emitting Diode. Một chân của LED sẽ được gắn vào GND, một chân được nối vào chân GPIO thông qua điện trở hạn dòng. Bằng việc điều khiển chân GPIO lên 1, tức là ta cấp cho chân LED nguồn dương Vcc (lúc này LED sẽ sáng) , ngược lại nếu điều khiển GPIO xuống 0, tức là ta cấp cho chân LED nguồn GND (lúc này LED sẽ tắt).
Thông thường, bất kì chân GPIO đều được sử dụng bởi các thanh ghi- Registers để đọc giá trị vào của chân, hoặc ghi ra giá trị của chân. Trong rất nhiều các hệ thống nhúng, chân GPIO có thể được sử dụng mới một hoặc nhiều chức năng đặc biệt, và vì vậy việc ghi vào thanh ghi điều khiển, sẽ quyết định chân GPIO đó thực hiện chức năng chung in/out hay thực hiện một chức năng đặc biệt nào đó. Hơn nữa, bộ vi xử lý có thể tạo ra những tín hiệu ngắt- interupts khi một sự kiện như cạnh lên, hoặc cạnh xuống của tín hiệu được phát hiện trên GPIO, và việc ghi vào thanh ghi điều khiển sẽ sử dụng được những điều kiện nói trên cho ngắt.
Cũng giống như các hệ thống nhúng khác, điều khiển GPIO trên Jetson Tx1 chính là việc chúng ta đọc/ghi các giá trị trên các thanh ghi điều khiển của Tx1. Tuy nhiên, khác với vi điều khiển, Tx1 là một hệ thống nhúng chạy trên nền tảng hệ điều hành Linux, vì vậy việc thực hiện đọc/ghi vào các thanh ghi điều khiển không đơn giản như trên vi điều khiển. Việc đọc/ghi vào một thanh ghi trên nhúng Linux không được được thực hiện trực tiếp, mà người dùng cần thông qua các hàm về thao tác file như open, close, ioctl ( các system calls) trên các file thiết bị, gọi là Device Driver.
Từ đây, chúng ta thấy rằng, muốn giao tiếp điều khiển với GPIO có nghĩa chúng ra cần phải viết được divice driver, sau đó trên lớp ứng dụng, chúng ta viết các app sử dụng các driver này. Tuy nhiên trong phạm vi bài viết này, tôi sẽ sử dụng một cách khác để điều khiển GPIO, các viết Device driver sẽ được viết trong một bài khác. Cách mà tôi đang nhắc đến đó là chúng ta sử dụng GPIO sysfs, Linux cung cấp giao diện GPIO sysfs cho phép chúng ta thao tác, điều khiển bất kì chân GPIO nào từ không gian người dùng.
Tất cả các giao diện điều khiển GPIO thông qua sysfs nằm trong thư mục /sys/class/gpio
Để kiểm tra xem hệ thống hỗ trợ cho việc điều khiển những GPIO nào, ta thực hiện câu lệnh sau:
ls /sys/class/gpio
Với board mạch Jetson Tx1, trong gpio sysfs gồm các file sau:
+ export
+ unexport
+ gpiochip0, gpiochip…
Việc lập trình sẽ trở thành việc chúng ta thao tác đọc/ghi file tương ứng với các chân tương ứng. Với lập trình shell linux thì chúng ta sẽ dùng các lệnh (echo, cat,…) với lập trình C/C++ , chúng ta sẽ dùng hàm read/write.
Trong phạm vi bài viết này, tôi sẽ giới thiệu cả 2 cách viết trên shell và viết trên C++.
Sơ đồ chân GPIO của JetsonTx1- Sử dụng cho các ví dụ dưới đây
- Thực hiện điều khiển GPIO với các lệnh shell
– Với Tx1, hệ điều hành đã cho phép chúng ta sử dụng giao diện gpio sysfs.
– Bước 1: đăng kí sử dụng chân từ không gian người dùng
Để export chân gpio, chúng ta sẽ dùng số hiệu của nó vào file ../export. Ví dụ, export chân GPIO219, ghi giá trị 219 vào file export:
echo 219 > /sys/class/gpio/export
Sau khi export chân, chúng ta sẽ kiểm tra lại bằng lệnh ls, ta sẽ thấy trong các file liệt kê, có gpio129
– Bước 2: Cấu hình cho GPIO là input hoặc output
Sử dụng lệnh sau với in/out tương ứng:
echo “out” > /sys/class/gpio/gpio219/direction
hoặc
echo “in” > /sys/class/gpio/gpio219/direction
– Bước 3: Ghi giá trị 0 hoặc 1 vào chân gpio219
echo 1 > /sys/class/gpio/gpio219/value
echo 0 > /sys/class/gpio/gpio219/value
- Viết chương trình C giao tiếp GPIO từ user space
gpio.h
1 2 3 4 5 6 7 8 9 10 11 |
/* * Control GPIO from user space on Jetson Tx1 */ #ifndef GPIO_H #define GPIO_H int gpio_export(unsigned gpio); //Hàm export GPIO ra user space int gpio_unexport(unsigned gpio); //Hàm giải phóng GPIO khi không còn sử dụng int gpio_dir_out(unsigned gpio); //Cấu hình GPIO là output int gpio_dir_in(unsigned gpio); //Cấu hình GPIO là input int gpio_value(unsigned gpio, unsigned value); //Đọc/ghi giá trị của pin #endif |
1 |
gpio.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #define GPIO_DIR_IN 0 // định nghĩa cho GPIO là input #define GPIO_DIR_OUT 1 // định nghĩa cho GPIO là input int gpio_export(unsigned gpio) { int fd, len; // Biến fd này sẽ được gán giá trị sau khi thực hiện open file char buf[11]; fd = open("/sys/class/gpio/export", O_WRONLY); // giống với trên shell, mở file export if (fd < 0) { perror("gpio/export"); return fd; } len = snprintf(buf, sizeof(buf), "%d", gpio); write(fd, buf, len); //Ghi số hiệu (ID) pin muốn sử dụng vào file /sys/class/gpio/export khi đăng ký sử dụng, tương đương câu lệnh echo. close(fd); return 0; } int gpio_unexport(unsigned gpio) { int fd, len; char buf[11]; fd = open("/sys/class/gpio/unexport", O_WRONLY); if (fd < 0) { perror("gpio/export"); return fd; } len = snprintf(buf, sizeof(buf), "%d", gpio); write(fd, buf, len); //Ghi số hiệu (ID) pin muốn sử dụng vào file /sys/class/gpio/unexport khi giải phóng close(fd); return 0; } int gpio_dir(unsigned gpio, unsigned dir) { int fd, len; char buf[60]; len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio); fd = open(buf, O_WRONLY); if (fd < 0) { perror("gpio/direction"); return fd; } //Cấu hình pin là input/output bằng cách ghi giá trị (ASCII) in, out vào file /sys/class/gpio/gpio[ID]/diriection if (dir == GPIO_DIR_OUT) write(fd, "out", 4); else write(fd, "in", 3); close(fd); return 0; } int gpio_dir_out(unsigned gpio) { return gpio_dir(gpio, GPIO_DIR_OUT); //trường hợp là output } int gpio_dir_in(unsigned gpio) { return gpio_dir(gpio, GPIO_DIR_IN); //trường hợp là input } int gpio_value(unsigned gpio, unsigned value) { int fd, len; char buf[60]; len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio); fd = open(buf, O_WRONLY); if (fd < 0) { perror("gpio/value"); return fd; } //Xuất giá trị 1, 0 bằng cách ghi ra file value tương ứng với pin đã cấu hình if (value) write(fd, "1", 2); else write(fd, "0", 2); close(fd); return 0; } #if 0 int main(int argc, char *argv[]) { int i = 20; int pin_no = 219 //Sử dụng chân 219 trên Jetson Tx1 gpio_export( pin_no ); gpio_dir_out( pin_no ); while(i--) { gpio_value( pin_no , i & 1); //ghi giá trị 0/1 chu kì 1s lên GPIO219 sleep(1); } gpio_unexport( pin_no ); } #endif |
Sau khi xong chúng ta thực hiện biên dịch mã nguồn vừa viết với gcc.
gcc gpio.c -o gpio219
Câu lệnh trên sẽ thực hiện biên dịch, gcc là tên trình biên dịch, gpio.c là file mã nguồn, gpio219 là file thực thi được tạo ra.
Để thực thi file gpio219 thực hiện câu lệnh sau: sudo ./gpio219
Bài viết tham khảo từ các nguồn sau:
https://sites.google.com/site/embedded247/ddcourse/giao-tiep-gpio-tu-userspace-1