STM32 Bit Band được sử dụng rất nhiều khi muốn điều khiển ngoại vi một cách nhanh chóng, nhưng có thể do mất thời gian định nghĩa ra các địa chỉ nên không được nhiều người viết hướng đẫn.
Trong bài này chúng ta sẽ học cách điều khiển GPIO bằng 4 cách khác nhau. Và cách sử dụng bit band trong STM32 nhé
Bài 20 trong Serie Học STM32 từ A tới Z
Các cách điều khiển GPIO
Thông thường có 3 cách điều khiển GPIO đó là:
- Sử dụng hàm điều khiển GPIO như: HAL_GPIO_Write, Read ….
- Sử dụng thanh ghi ODR: Khi thay đổi giá trị của thanh ghi ODR chúng sẽ ánh xạ tới đầu ra (các PORT).
- Sử dụng bit field: Tạo ra struct trong đó có các phần tử, đọc ghi giá trị từ struct đó.
- Sử dụng bit band: Sử dụng một khoảng bộ nhớ dành riêng cho việc truy cập theo từng bit.
Điều khiển bằng hàm trong thư viện
Đây là cách làm đơn giản nhất, nhưng tương tự cũng sẽ mất thời gian xử lý nhất.
Ví dụ trong HAL chúng ta sử dụng các hàm:
- HAL_GPIO_Write
- HAL_GPIO_Read
- HAL_GPIO_Toggle
Điều khiển bằng thanh ghi
Lập trình bằng thanh ghi khiến tốc độ của chương trình sẽ tăng lên, do không phải chạy qua các lệnh điều hướng (do thư viện sẽ phải tương thích với nhiều dòng vdk khác nhau).
Thao tác với thanh ghi chúng ta có 3 tác vụ chinh đó là đọc/ghi/xóa.
Ví dụ: mình sẽ ghi và xóa vào bit 13 thanh ghi ODR của GPIO C. Lệnh này tương ứng với việc bạn bật và tắt led trên chân PC13
- Đề ghi bit: GPIOC->ODR |= 1<<13; Giải thích: chúng ta sẽ dịch bit 1 sang trái 13 đơn vị, tương ứng với bit 13 trên ODR GPIOC, sau đó sử dụng phép từ OR để thay đổi giá trị của bit đó.
- Để xóa bit: GPIOC->ODR &= ~(1<<13); Tương tự dịch bit như trên nhưng chúng ta sẽ sử dụng toán tử AND và đổi bit 1 thành 0. Lúc này sẽ là xóa bit 13
- Để đọc bit: PC13_IN = (GPIOC->IDR) & (0x0001<<13). Bạn phải tạo 1 số 16bit có giá trị là 1, sau đó dịch trái 13 để tới bit 13 trên thanh ghi. Khi sử dụng phép toán & với giá trị thanh ghi đó. Tất cả những giá trị khác sẽ về 0, chỉ còn giá trị bit 13 nếu là 0 thì kết quả trả về là 0, nếu là 1 thì trả về 1
Điều khiển bằng bit field
Đầu tiên, bạn phải tạo một cấu trúc trường bit theo yêu cầu của bạn.
/* define structure of Port Pin*/ typedef struct { volatile unsigned int Bit0:1; volatile unsigned int Bit1:1; volatile unsigned int Bit2:1; volatile unsigned int Bit3:1; . . volatile unsigned int Bit31:1; }SPortPin;
Tạo một con trỏ đến trường bit mô tả ở trên và gán địa chỉ của thanh ghi cho con trỏ mà bạn muốn truy cập.
volatile SPortPin *psGpioPort = (volatile SPortPin *)(GPIO_BASE + GPIO_OFFSET);
Với GPIO_BASE là địa chỉ bắt đầu của GPIO, GPIO_OFFSET là địa chỉ cộng thêm. (VD: ODR là 0x0C)
Bây giờ cấu trúc trường bit của bạn đã được ánh xạ với thanh ghi phần cứng mà bạn muốn truy cập. Với GPIO là thanh ghi ODR và IDR.
Để đọc giá trị từ thanh ghi chúng ta sử dụng lệnh.
Value = psGpioPort-> Bit1;
Để ghi giá trị vào thanh ghi ta sử dụng
psGpioPort-> Bit1 = 1; or psGpioPort-> Bit1 = 0;
Điều khiển bằng bit band
Bit Band là gì?
Bit-banding là khả năng điều khiển theo từng bit của một vùng nhớ tới một word (một số 32 bit) trong vùng bí danh (aliased region). Vùng nhớ bí danh là vùng nhớ chỉ để dùng trong việc lưu trữ dữ liệu cho các vùng nhớ.
Nghĩa là khi bạn ghi dữ liệu vào cùng Bit Band Alias region một thao tác đọc/sửa/ghi sẽ được tạo ra để thay đổi giá trị bit trong vùng Bit Band Region. Tất cả các thao tác này được thực hiện bởi hệ thống vậy nên tốc độ xử lý rất nhanh.
Bit band thường được sử dụng để truy cập nhanh các thanh ghi, đặc biệt các ứng dụng cần điều khiển GPIO một cách nhanh chóng.
Cách này sẽ giúp việc lập trình GPIO trên STM32 đơn giản hơn rất nhiều, không cần nhiều câu lệnh lằng nhằng.
Bit Band Region trên STM32
ARM định nghĩa 2 vùng nhớ bit-band cho Cortex-M, mỗi vùng là 1MB và được map tới vùng bí danh 32Mbit. Mỗi một số 32bit trong vùng bí danh sẽ tương đương với một bit trong vùng bit-band.
Vùng bit-band đầu tiên là từ 0x2000 0000 đến 0x200F FFFF của SRAM và vùng bí danh tương ứng của nó là 0x2200 0000 tới 0x23FF FFFF.
Vùng bit-band còn lại là từ 0x4000 0000 và kết thúc ở 0x400F FFFF của các ngoại vi và vùng bí danh tương ứng là từ 0x4200 0000 đến 0x43FF FFFF.
Chi tiết các bạn đọc lại bài Bản đồ bộ nhớ trên STM32
Cách truy cập vào vùng bit band trên STM32
Để truy cập được vào vùng bit-band bí danh, ta dùng công thức sau:
bit_band_address = alias_region_base + (region_base_offset x 32) + (bit_number x 4)
Ví dụ: Chúng ta sẽ điều khiển led trên chân PC13
- Vùng alias_region_base = 0x4200 0000 là địa chỉ bắt đầu vùng bí danh của ngoại vi
- Vùng regione_base_offset chính là địa chỉ thanh ghi muốn truy cập của Port C là 0x4001100C
- bit number = 13 chính là bit số bao nhiêu trên thanh ghi đó
Vậy theo công thức chúng ta tính được địa chỉ bitband của PC13 là: 0x422201B4
Nếu cứ tính bằng tay thì cũng rất mất thời gian ngồi tra cứu phải không, ai mà nhớ hết được. Vậy nên để dễ dàng trong tính toán mình sẽ sử dụng thư viện HAL và một vài dòng code để tính toán nhanh hơn.
Lập trình Bit Band với STM32 HAL
Trong bài này mình sẽ sử dụng PA0 làm nút nhấn và PC13 điều khiển LED. Giống bài 3 STM32 GPIO, và xem code đã được thay đổi như thế nào nhé!
Tạo một project trên cube MX. SYS – Debug – Serial Write, nhớ làm bước này để chúng ta debug nhé
GPIO cho PC13 là output, PA0 là input pull up
Gen code và mở trên Keil C
Keil C mình sẽ khai báo các biến chứa địa chỉ bit band của PC13 và PA0
Sử dụng công thức bên trên để tính toán, với:
- PERIPH_BB_BASE: địa chỉ bit band
- (uint32_t)(&GPIOC->ODR): địa chỉ của thanh ghi ODR thuộc port C
- pin_num: chân hay bit cần can thiệp
Nhấn F7 để build và Ctr_F5 để vào Debug. Trong debug add các biến vào Watch 1.
Nhấn Run để lấy giá trị các biến, sau đó define PC13_OUT và PA0_IN sử dụng các giá trị đọc được.
Tiếp tới trong while chúng ta đọc giá trị của PA0 sau đó ghi vào PC13. (Bạn có thể xóa các dòng tính toán bên trên đi đc rồi nhé).
Chúng ta sẽ được kết quả tương tự bài GPIO nhưng code được thu gọn hơn rất nhiều đúng không nào.
Để tiếp tục với các chân GPIO khác, các bạn cũng làm tương tự nhé
Kết
Chúng ta đã học qua các cách điều khiển GPIO trong STM32 HAL, các cách này có thể ứng dụng với nhiều dòng vi điều khiển khác nhau chứ không riêng gì STM32. Vì bản chất của vi điều khiển là giống nhau mà.
Để sử dụng bit band, quan trọng là bạn cần nắm rõ bản đồ bộ nhớ (memory map) của dòng đấy, nếu không hãy sử dụng các thư viện đã có khai báo sẵn như HAL, SPL, LL …. bạn chỉ cần tìm tới nơi define chúng là sẽ rõ ràng mình đang truy cập tới địa chỉ nào nhé.
Nếu cảm thấy bài viết này có ích hãy chia sẻ với bạn bè nhé. Đừng quên ra nhập hội những anh em Nghiện lập trình nhé!!