CẨM NANG STM32 CĂN BẢN CẦN BIẾT – GPIO – API

GPIO API

Đây là lúc thích hợp để bàn về các hàm libopencm3 có sẵn cho GPIO. Điều đầu tiên bạn cần làm là khai báo các file header thích hợp, như sau:

#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>

File rcc.h là cần thiết để có các định nghĩa như là kích hoạt xung GPIO. File gpio.h cần thiết cho phần còn lại:

void gpio_set(uint32_t gpioport, uint16_t gpios);
void gpio_clear(uint32_t gpioport, uint16_t gpios);
uint16_t gpio_get(uint32_t gpioport, uint16_t gpios);
void gpio_toggle(uint32_t gpioport, uint16_t gpios);
uint16_t gpio_port_read(uint32_t gpioport);
void gpio_port_write(uint32_t gpioport, uint16_t data);
void gpio_port_config_lock(uint32_t gpioport, uint16_t gpios);

Trong tất cả các hàm trước, đối số gpioport có thể là một trong các macro từ Bảng 4-1 (trên các phiên bản STM32 khác, có thể có các cổng bổ sung). Chỉ có thể chỉ định một cổng tại một thời điểm.

Bảng 4-1. Macro libopencm3 GPIO cho STM32F103C8T6

Macro CổngMô tả
GPIOAGPIO cổng A
GPIOBGPIO cổng B
GPIOCGPIO cổng C

Trong các hàm GPIO libopencm3, một hoặc nhiều bit GPIO có thể được thiết lập hoặc xóa cùng một lúc. Bảng 4-2 liệt kê các tên macro được hỗ trợ. Cũng lưu ý macro có tên GPIO_ALL.

Bảng 4-2. Macro libopencm3 chỉ định chân GPIO

Macro ChânĐịnh nghĩaMô tả
GPIO0(1 << 0)Bit 0
GPIO1(1 << 1)Bit 1
GPIO2(1 << 2)Bit 2
GPIO3(1 << 3)Bit 3
GPIO4(1 << 4)Bit 4
GPIO5(1 << 5)Bit 5
GPIO6(1 << 6)Bit 6
GPIO7(1 << 7)Bit 7
GPIO8(1 << 8)Bit 8
GPIO9(1 << 9)Bit 9
GPIO10(1 << 10)Bit 10
GPIO11(1 << 11)Bit 11
GPIO12(1 << 12)Bit 12
GPIO13(1 << 13)Bit 13
GPIO14(1 << 14)Bit 14
GPIO15(1 << 15)Bit 15
GPIO_ALL0xffffMọi bit từ 0 đến 15

Ví dụ về GPIO_ALL có thể như sau:

gpio_clear(PORTB,GPIO_ALL); // clear all PORTB pins

Một tính năng đặc biệt của serie STM32, được libopencm3 hỗ trợ, là khả năng khóa một định nghĩa GPIO I/O, như sau:

void gpio_port_config_lock(uint32_t gpioport, uint16_t gpios);

Sau khi gọi gpio_port_config_lock() trên một cổng cho các chân GPIO đã chọn, cấu hình I/O sẽ bị đóng băng cho đến khi reset hệ thống. Điều này có thể hữu ích trong các hệ thống chú trọng tính an toàn, nơi bạn không muốn chương trình không thích hợp thay đổi chúng. Khi một GPIO đã chọn (lock lại) bị áp một input hoặc output, nó đảm bảo sẽ không bị thay đổi.

=> Bài viết được trích từ sách : Cẩm nang STM32 (tập 1)

Cấu hình GPIO

Bây giờ chúng ta hãy xem xét cách GPIO được thiết lập trong hàm gpio_setup(). Dòng 26 của Bảng 4-1 có hàm sau đây được gọi:

rcc_periph_clock_enable(RCC_GPIOC);

Bạn sẽ khám phá ra trong cuốn sách này rằng serie STM32 cấu hình rất dễ dàng. Điều này bao gồm các xung cơ bản cần thiết cho các cổng GPIO và các thiết bị ngoại vi khác nhau. Hàm libopencm3 nói đến ở trên được sử dụng để bật xung hệ thống cho cổng GPIO C. Nếu xung này không được bật, cổng GPIO C sẽ không hoạt động. Đôi khi các phần mềm bị ảnh hưởng sẽ có các phép thực thi bị bỏ qua (kết quả có thể nhìn thấy), trong khi trong các tình huống khác hệ thống có thể bị kẹt. Do đó, đây là một trong những “công đoạn” quan trọng cần phải khéo léo sắp xếp.

Lý do xung bị vô hiệu hóa là tiết kiệm điện năng tiêu thụ. Điều này rất quan trọng để tiết kiệm pin.

Mẹo   Nếu thiết bị ngoại vi hoặc GPIO của bạn không hoạt động, hãy kiểm tra xem bạn đã bật (các) xung cần thiết chưa.

Hàm tiếp theo được gọi là gpio_set_mode() ở dòng 29:

gpio_set_mode(
    GPIOC,                      // Table 4-1
    GPIO_MODE_OUTPUT_2_MHZ,     // Table 4-3
    GPIO_CNF_OUTPUT_PUSHPULL,   // Table 4-4
    GPIO13                      // Table 4-2
);

Hàm này yêu cầu bốn đối số. Đối số đầu tiên xác định cổng GPIO chịu ảnh hưởng (Bảng 4-1). Đối số thứ tư chỉ định các chân GPIO chịu ảnh hưởng (Bảng 4-2). Giá trị macro của đối số thứ ba được liệt kê trong Bảng 4-3 và xác định chế độ chung của cổng GPIO.

Bảng 4-3. Định nghĩa chế độ GPIO

Tên Chế độ MacroGiá trịMô tả
GPIO_MODE_INPUT0x00Chế độ input
GPIO_MODE_OUTPUT_2_MHZ0x02Chế độ output, ở 2 MHz
GPIO_MODE_OUTPUT_10_MHZ0x01Chế độ output, ở 10 MHz
GPIO_MODE_OUTPUT_50_MHZ0x03Chế độ output, ở 50 MHz

Macro GPIO_MODE_INPUT xác định chân GPIO làm input, như bạn muốn. Nhưng có ba chế độ output macros được liệt kê.

Mỗi lựa chọn output ảnh hưởng đến tốc độ mỗi chân output đáp ứng thay đổi. Trong chương trình ví dụ của chúng ta, tùy chọn 2 MHz được chọn, vì rằng mắt người không thể nhận biết tốc độ thay đổi tín hiệu LED. Bằng cách chọn 2 MHz, điện năng sẽ tiêu tốn ít hơn và EMI (nhiễu điện từ) sẽ giảm.

Đối số thứ ba tiếp tục thiên về cách mà cổng (port) được cấu hình. Bảng 4-4 ​​liệt kê các tên macro được cung cấp.

Bảng 4-4. Macro Chuyên biệt hóa Cấu hình I/O

Tên Macro Chuyên biệt hóaGiá trịMô tả
GPIO_CNF_INPUT_ANALOG0x00Chế độ input analog
GPIO_CNF_INPUT_FLOAT0x01Input digital, thả nổi (mặc định)
GPIO_CNF_INPUT_PULL_UPDOWN0x02Input digital, kéo lên và xuống
GPIO_CNF_OUTPUT_PUSHPULL0x00Output digital, kéo/đẩy
GPIO_CNF_OUTPUT_OPENDRAIN0x01Output digital, cực máng mở
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL0x02Output chức năng thay thế, kéo/đẩy
GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN0x03Output chức năng thay thế, cực máng hở

=> xem thêm : Lập trình Arm STM32

Cổng Input

Các macro trong tên có chứa INPUT chỉ áp dụng khi đối số thứ hai hàm ý một cổng input. Chúng ta thấy từ Bảng 4-4 ​​rằng input có thể được chuyên biệt hóa theo ba cách khác nhau:

  • Analog
  • Digital, đầu vào nổi (floating)
  • Digital, kéo lên và xuống (pull up/down)

Để rõ hơn về input GPIO và cấu hình của nó, hãy xem xét giản đồ trong Hình 4-2.

Hình 4-2. Cấu trúc cơ bản của input GPIO

Phía MCU tiếp nhận ở bên trái, trong khi input bên ngoài đi vào từ bên phải. Có hai điốt bảo vệ đi kèm, thường chỉ đi vào hoạt động nếu điện áp tĩnh có giá trị âm hoặc vượt quá nguồn cung cấp.

Khi cổng input được cấu hình như input analog, các công tắc được nối với điện trở R1 và R2 đều tắt. Điều này là để tránh kéo tín hiệu analog lên hoặc xuống. Với các điện trở không được nối mạch, tín hiệu input analog được dẫn hướng đến đường có nhãn “Analog Input” mà không có chút dòng tín hiệu nào sang phía ngoại vi ADC (bộ biến đổi analog-to-digital). Bộ khởi động Schmitt (Schmitt trigger) cũng bị vô hiệu để tiết kiệm điện năng tiêu thụ.

Khi cổng input được cấu hình cho input là digital, điện trở R1 hoặc R2 ở vào trạng thái hoạt động trừ khi bạn chọn tùy chọn “nổi” GPIO_CNF_INPUT_FLOAT. Đối với cả hai chế độ đầu vào digital, Schmitt trigger được kích hoạt để cung cấp một tín hiệu gọn hơn với độ trễ. Output của bộ Schmitt trigger sau đó đi đến “Input chức năng thay thế” và đến thanh ghi dữ liệu input (GPIO). Ta sẽ bàn thêm sau về chức năng thay thế, nhưng một cách hiểu đơn giản là một input có thể làm nhiệm vụ như một input GPIO hoặc như một input ngoại vi.

Các input chịu được áp 5V giống hệt với sơ đồ trình bày ở hình 4-2, ngoại trừ diode bảo vệ cao cho phép điện áp tăng từ 3,3V đến ít nhất là +5V.

Lưu ý Khi định cấu hình output ngoại vi, hãy đảm bảo sử dụng một trong các macro chức năng thay thế. Nếu không, chỉ tín hiệu GPIO mới được định cấu hình.

Cổng Output

Khi cổng GPIO được cấu hình cho output, bạn có bốn cách để lựa chọn:

  • GPIO đẩy/kéo (push/pull)
  • GPIO open-drain (cực máng hở)
  • Chức năng đẩy/kéo thay thế
  • Chức năng open-drain thay thế

Đối với hoạt động GPIO, bạn luôn chọn các chế độ chức năng không-thay-thế. Để sử dụng ngoại vi như USART, bạn chọn từ các chế độ chức năng thay thế. Một sai lầm phổ biến là cấu hình để sử dụng GPIO, như GPIO_CNF_OUTPUT_PUSHPULL cho ouput TX của USART. Macro chính xác cho thiết bị ngoại vi là GPIO_CNF_OUTPUT_ALTFN_PUSHPULL. Nếu bạn không thấy output ngoại vi, hãy tự hỏi liệu bạn có chọn từ một trong các giá trị chức năng thay thế hay không.

Hình 4-3 minh họa sơ đồ khối cho các output GPIO. Đối với các output chịu được áp 5V (như input), sự thay đổi duy nhất đối với mạch điện là diode bảo vệ bên cao có khả năng chịu điện áp cao tới +5V. Đối với các cổng không chịu được áp 5 V, các diode bảo vệ bên cao chỉ có thể tăng lên +3.3 V (thực tế, nó có thể tăng mức sụt áp trên diode lên quá 3.3 V).

Mạch “điều khiển output” là xác định nếu nó đang điều khiển các transistor P-MOS và N-MOS (ở chế độ đẩy/kéo) hay chỉ là N-MOS (ở chế độ open-drain). Trong chế độ open-drain (cực máng hở), transistor P-MOS luôn tắt (off). Chỉ khi bạn ghi một giá trị zéro vào output thì transistor N-MOS sẽ bật và kéo chân output xuống thấp. Ghi bit “1” cho một cổng open-drain sẽ ngắt mạch cổng vì cả hai transistor được đưa vào trạng thái “tắt”.

Các điện trở input nhỏ thể hiện trong hình 4-2 bị vô hiệu ở chế độ ouptput. Vì lý do này, chúng được bỏ qua trong Hình 4-3.

Các bit dữ liệu output được chọn từ hoặc thanh ghi dữ liệu output (GPIO) hoặc nguồn chức năng thay thế. Output GPIO đi đến thanh ghi dữ liệu output, có thể được ghi dưới dạng toàn bộ từ hoặc dưới dạng các bit riêng lẻ. Bit set/reset (đặt/đặt lại) cho phép các bit GPIO riêng biệt được thay thế như thể chúng là một hoạt động nguyên tử (atomic operation). Nói cách khác, một ngắt không thể xảy ra vào giữa của một phép toán “and/or” trên một bit.

Vì dữ liệu output GPIO được ghi lại trong thanh ghi dữ liệu output, nó có thể đọc lại các thiết lập output hiện tại là gì. Tuy nhiên, tính năng này không hoạt động đối với các cấu hình chức năng thay thế.

Hình 4-3. Mạch điều khiển GPIO đầu ra Khi đầu ra được cấu hình cho một thiết bị ngoại vi như USART, dữ liệu đến từ thiết bị ngoại vi thông qua đường output chức năng thay thế. Cách dữ liệu được lái hướng đi mà ta thấy trong Hình 4-3 nhấn mạnh một điều là bạn phải cấu hình cổng cho GPIO hoặc các hàm thay thế. Tôi sẽ còn nhắc đi nhắc lại việc này nữa, để bạn không phải tốn thời gian debug những lỗi kiểu này.

=> Sách Arduino, ESP8266, STM32 : Sách tự động hóa