1. Tổng quan
Trong các thiết bị Linux, có rất nhiều ngoại vi sử dụng các chân Pin để có thể hoạt động được. Với mỗi ngoại vi khác nhau lại yêu cầu cấu hình chân pin với các chức năng (mode) tương ứng khác khau.
Một chân pin có thể hoạt động ở các chức năng khác nhau: GPIO, I2C, SPI,…Tuy nhiên, tại mỗi thời điểm thì chỉ có mộtchức năng được phép hoạt động.
Ví dụ: trên BeagleBone Black, mỗi chức năng của chân pin tương ứng với một mode hoạt động tương ứng
![](https://vinalinux.com.vn/wp-content/uploads/2023/12/image.png)
Kỹ thuật lập trình nhóm các chân của một vi điều kiển để đáp ứng các chức năng như trên được gọi là pin muxing
![](https://vinalinux.com.vn/wp-content/uploads/2023/12/image-1.png)
Ví dụ khi cần cấu hình một chân pin với chế độ GPIO, ta cần thực hiện các công việc như: request, map các thanh ghi, set các giá trị phù hợp cho thanh ghi,… Nhưng trong trường hợp chúng ta phải thiết lập 5 hay 10 chân với các chức năng khác nhau sẽ tiêu tốn rất nhiều thời gian
Chính vì vậy, Linux đã hỗ trợ giải quyết khó khăn trên thông qua hệ thống Subsystem là Pinctrl (Pin Control)
2. Kiến trúc của Pin Control trong Linux
![](https://vinalinux.com.vn/wp-content/uploads/2023/12/image-2.png)
Hình ảnh trên mô tả chi tiết ngoại vi SPI sử dụng bus 0
- SPI0 Driver: Điều khiển trực tiếp ngoại vi trên SoC, có thể cấu hình pins theo mong muốn
- Pinctrl Core: là core của framework được cung cấp bởi Linux và cung cấp các APIs cho Driver sử dụng
- Pinctrl Base: lấy cấu hình từ Device Tree để đăng kí chân pin với Pinctrl Core
- Pinctrl BeagleBone: điều khiển Pin Control trong các thiết bị beagleBone, đăng kí các hàm callback đến Pinctrl để truy cập hardware
- Debugfs: cung cấp interface để debug đến cho user space
Ưu điểm của kiến trúc:
- Phần điều khiển ngoại vi tách biệt với việc tương tác với thanh ghi của ngoại vi nên có thể sử dụng ở các SoC khác nhau
- Có các quy luật để tránh xung đột trong hệ thống: không cho phép nhiều driver điều khiển các chân pin giống nhau
- Dễ dàng debug khi xảy ra lỗi
3. Pin Control Subsystem và Device Tree
Để có thể cấu hình các chức năng và sử dụng chức năng đó cho pin tương ứng, ta cần phải khai báo 2 node trong Device Tree:
- Node khai báo cấu hình
- Node khai báo chức năng
Khai báo cấu hình
Node khai báo cấu hình là node con của node pin control quản lý nó. Ví dụ đối với kiến trúc am335 trên beaglebone black, node pincontrol có tên là “am33xx_pinmux”
Nó được gán một label để định danh
Với mỗi Soc khác nhau thì việc khai báo cấu hình sẽ khác nhau. Để biết thêm chi tiết cấu hình với từng SoC cụ thể, ta có thể tham khảo: pinctrl – Documentation/devicetree/bindings/pinctrl – Linux source code (v6.6.3) – Bootlin
Ví dụ: cấu hình chức năng uart trên bus 0 trên beaglebone black
![](https://vinalinux.com.vn/wp-content/uploads/2023/12/image-3.png)
Trong source code của kernel cung cấp macro trong file header để có thể ghi các giá trị vào các thanh ghi cụ thể để cấu hình chức năng mong muốn
1 |
#define AM33XX_PADCONF(pa, conf, mux) OMAP_IOPAD_OFFSET((pa), 0x0800) (conf) (mux) |
Với pa là offset thanh ghi cần ghi giá trị, cof là cấu hình thuộc tính của chân pin, mux là số thự tự mode của chân pin với chức năng tương ứng (tra trong bảng Header của SoC do nhà sản xuất cung cấp)
Thông thường các driver cũng sẽ define các macro đại diện cho địa chỉ của chân Pin
Khai báo chức năng
Sử dụng 2 thuộc tính pinctrl-names và pinctrl-<id>:
- pinctrl-names đại diện cho các state
- pinctrl-<id> trỏ đến node cấu hình state tương ứng
Mỗi state sẽ tương ứng với một ID với số nguyên bắt đầu từ 0 và tăng dần (tương tự như với chỉ số của mảng trong C, ta coi pinctrl-names chứa danh sách các phần tử tương ứng 1 chuỗi kí tự)
Xét ví dụ sau:
![](https://vinalinux.com.vn/wp-content/uploads/2023/12/image-4.png)
pinctrl-0 sẽ tương ứng với state đầu tiên trong pinctrl-names “default”, sau dấu phẩy sẽ là state thứ hai “sleep” tương ứng với pinctrl kế tiếp ( ở đây là pinctrl-1). Ta có thể liên tưởng tới các index của mảng trong C.
Khi SoC boot thành công, state được áp dụng tự động cho chân pin là “default”. Với vị dụ trên thì cấu hình được nằm trong node “cpsw_default”. Còn khi hệ thống muốn tiết kiệm năng lượng, cấu hình tương ứng với state “sleep” sẽ được áp dụng với chân pin.
Ngoài ra, mỗi chân pin có thể cấu hình được nhiều chức năng khác nhau ngoài default và sleep. Chúng ta có thể chuyển đổi giữa các mode đó thông qua driver
4. Cấu hình GPIO sử dụng Pin Control trên BeagleBone Black
Với beaglebone black, khi hệ thống được boot lên thành công, các chân pin sẽ sử dụng các chức năng mặc định do nhà sản xuất cung cấp:
![](https://vinalinux.com.vn/wp-content/uploads/2023/12/image-5.png)
Chúng ta cùng nhau cấu hình chân P9_17 thành chân GPIO để bật tắt LED
Tra trong bảng P9 Header của beaglebone black, ta thấy mode tương ứng với chức năng GPIO là mode 7 và địa chỉ tương ứng là 0x95c
![](https://vinalinux.com.vn/wp-content/uploads/2023/12/image-6.png)
Để cấu hình chức năng GPIO cho pin P9_17 ta cần khai báo trong devie tree các node sau:
- Node khai báo cấu hình
![](https://vinalinux.com.vn/wp-content/uploads/2023/12/image-7.png)
Với macro AM335X_PIN_SPI0_CS0 tương ứng với địa chỉ 0x95c
- Node khai báo chức năng
![](https://vinalinux.com.vn/wp-content/uploads/2023/12/image-8.png)
Sau đó, ta thực hiện reboot lại hệ thống, nếu thành công, chân P9_17 sẽ sử dụng cấu hình mặc định “default” là GPIO. Từ đó, ta có thể thực hiện việc bật tắt LED bằng cách sử dụng sysfs
Đầu tiên, ta thực hiện export GPIO pin 5 với hệ thống bằng cmd:
1 |
echo 5 >/sys/class/gpio/export |
Tiếp đó, set output cho GPIO 5:
1 |
echo out >/sys/class/gpio/gpio5/direction |
Cuối cùng, ta có thể tùy ý set mức logic cho GPIO5 để bật tắt LED
1 2 3 |
echo 0 >/sys/class/gpio/gpio5/value echo 1 >/sys/class/gpio/gpio5/value |