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

Trong chương này, bạn sẽ sử dụng thư viện libopencm3 để build một chương trình nhấp nháy từ mã nguồn. Chương trình ví dụ này thể hiện cấu hình và sử dụng GPIO (General Purpose Input/Output: Input/Output Đa Mục đích). Chương trình được trình bày dưới đây là một phiên bản được sửa chút ít của một chương trình ví dụ libopencm3 có tên miniblink. Nó đã được sửa để định lại thời gian, để chắc rằng đoạn code mới được (đưa vào bộ nhớ) flash của bạn là cái sẽ thực thi. Sau khi build và run chương trình này, chúng ta sẽ thảo luận về GPIO API (Giao diện lập trình ứng dụng) được cung cấp bởi libopencm3.

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

Build miniblink

Chuyển đến thư mục con miniblink, như bên dưới, và gõ make:

$ cd ~/stm32f103c8t6/miniblink
$ make

gmake: Nothing to be done for ‘all’.

Nếu bạn thấy thông báo trên, có thể là do bạn đã build tất cả các dự án từ thư mục cấp cao nhất (như vậy cũng không có vấn đề gì). Tuy nhiên, nếu bạn đã thực hiện thay đổi đối với các file mã-nguồn, make phải tự động phát hiện và build lại các thành phần bị ảnh hưởng. Ở đây, chúng ta chỉ muốn build lại bắt buộc dự án miniblink. Để thực hiện, gõ make clobber trong thư mục dự án, và sau đó gõ make, như bên dưới:

$ make clobber
rm -f *.o *.d generated.* miniblink.o miniblink.d
rm -f *.elf *.bin *.hex *.srec *.list *.map
$ make
...
arm-none-eabi-size miniblink.elf
   text   data    bss    dec    hex    filename
    696      0      0    696    2b8    miniblink.elf
arm-none-eabi-objcopy -Obinary miniblink.elf miniblink.bin

Khi làm điều này, bạn sẽ thấy một vài dòng lệnh dài được thực thi để biên dịch và liên kết file thực thi của bạn được đặt tên miniblink.elf. Tuy nhiên, để flash thiết bị của bạn, chúng ta cũng cần một file ảnh. Bước cuối cùng của bản build cho thấy cách tiện ích objcopy riêng-của-ARM cụ thể được sử dụng để chuyển đổi miniblink.elf thành fie ảnh miniblink.bin.

Tuy nhiên, ngay trước bước cuối cùng, bạn có thể thấy rằng lệnh size riêng-của-ARM đã phô bày kích thước của phần dữ liệu và văn bản của chương trình. Chương trình miniblink của chúng ta chỉ bao gồm 696 byte bộ nhớ flash (phần văn bản) và không sử dụng SRAM được cấp phát (phần dữ liệu). Chừng nào mà điều này còn đúng, ta sẽ vẫn còn SRAM để dùng cho một call stack.

Flash miniblink

Sử dụng lại framework make, giờ đây chúng ta có thể flash thiết bị của bạn với file ảnh của chương trình mới. Nối bộ lập trình ST-Link V2 của bạn, kiểm tra bộ dây nối và thực hiện các thao tác sau:

$ make flash
/usr/local/bin/st-flash write miniblink.bin 0x8000000
st-flash 1.3.1-9-gc04df7f-dirty
2017-07-30T12:57:56 INFO src/common.c: Loading device parameters....
2017-07-30T12:57:56 INFO src/common.c: Device connected is: \
    F1 Medium-density device, id 0x20036410
2017-07-30T12:57:56 INFO src/common.c: SRAM size: 0x5000 bytes (20 KiB),\
    Flash: 0x20000 bytes (128 KiB) in pages of 1024 bytes
2017-07-30T12:57:56 INFO src/common.c: Attempting to write 696 (0x2b8) \
    bytes to stm32 address: 134217728 (0x8000000)
Flash page at addr: 0x08000000 erased
2017-07-30T12:57:56 INFO src/common.c: Finished erasing 1 pages of \
    1024 (0x400) bytes
...
2017-07-30T12:57:57 INFO src/common.c: Flash written and verified! \
    jolly good!

Khi quá trình này hoàn tất, thiết bị của bạn sẽ tự động reset và bắt đầu chương trình miniblink vừa flash vào. Với các hằng số thời gian đã sửa đổi được sử dụng, bạn sẽ thấy nó hiện đang nhấp nháy thường xuyên, với chu kỳ làm việc 70/30 (sáng gần hết chu kỳ). Chương trình nhấp nháy được nạp sẵn trong thiết bị của bạn dường như áp dụng chu kỳ làm việc 50/50. Nếu kiểu nhấp nháy của bạn thay đổi đôi chút so với những gì được mô tả, đừng lo lắng. Điểm quan trọng là bạn đã flash được và run thành công một chương trình khác.

Chương trình này không sử dụng xung nhịp CPU điều khiển bằng thạch anh. Nó sử dụng xung RC bên trong (xung điện trở/tụ điện). Vì lý do này, thiết bị của bạn có thể flash nhanh hơn hoặc chậm hơn một chút so với thiết bị khác.

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

Mã nguồn của miniblink.c

Bây giờ, hãy xem mã nguồn cho chương trình miniblink mà bạn vừa chạy. Nếu không còn trong thư mục con miniblink, giờ hãy chuyển sang đó:

$ cd ~/stm32f103c8t6/miniblink

Trong thư mục con này, bạn nên tìm file chương trình nguồn miniblink.c. Bản kê 4-1 minh họa chương trình mà không có phần chú thích:

Bản kê 4-1. Bản kê các dòng lệnh của miniblink.c

0019: #include <libopencm3/stm32/rcc.h>
0020: #include <libopencm3/stm32/gpio.h>
0021:
0022: static void
0023: gpio_setup(void) {
0024:
0025:   /* Enable GPIOC clock. */
0026:   rcc_periph_clock_enable(RCC_GPIOC);
0027:
0028:   /* Set GPIO8 (in GPIO port C) to 'output push-pull'. */
0029:   gpio_set_mode(GPIOC,GPIO_MODE_OUTPUT_2_MHZ,
0030:                 GPIO_CNF_OUTPUT_PUSHPULL,GPIO13);
0031: }
0032:
0033: int
0034: main(void) {
0035:   int i;
0036:
0037:   gpio_setup();
0038:
0039:   for (;;) {
0040:       gpio_clear(GPIOC,GPIO13);      /* LED on */
0041:       for (i = 0; i < 1500000; i++)  /* Wait a bit. */
0042:           __asm__("nop");
0043:
0044:       gpio_set(GPIOC,GPIO13);        /* LED off */
0045:       for (i = 0; i < 500000; i++)   /* Wait a bit. */
0046:           __asm__("nop");
0047:   }
0048:
0049:   return 0;
0050: }

Lưu ý Số dòng xuất hiện ở bên trái trong bản kê không phải là một phần của file nguồn, chỉ được sử dụng để tiện tham khảo.

Cấu trúc của chương trình khá đơn giản. Nó bao gồm những thứ sau đây:

  1. Một hàm chương trình chính được khai báo trong các dòng 33-50. Lưu ý rằng không như một chương trình POSIX, không có đối số argc hoặc argv nào thực hiện hàm main.
  2. Trong chương trình main, hàm gpio_setup() được gọi để thực hiện một số khởi tạo.
  3. Các dòng 39–47 tạo thành một vòng lặp vô hạn, mà một LED được bật và tắt. Lưu ý rằng câu lệnh return trong dòng 49 không bao giờ được thực hiện và chỉ được cung cấp để theo đúng cấu trúc.

Ngay cả trong chương trình đơn giản này cũng có nhiều thứ để bàn. Như chúng ta sẽ thấy ở phần sau, chương trình ví dụ này chạy ở tần số CPU mặc định vì không có giá trị nào khác được định. Điều này sẽ được tìm hiểu sau.

Hãy xem chi tiết những điều đơn giản trước tiên. Hình 4-1 minh họa cách LED mà chúng ta sẽ flash được nối vào MCU trên PCB Blue Pill. Trong sơ đồ này, chúng ta thấy rằng dòng điện đi vào LED từ chân cấp nguồn +3.3V qua điện trở hạn dòng R1. Để hoàn thành mạch, GPIO PC13 phải kết nối LED với đất để cho phép dòng điện chạy qua. Đây là lý do tại sao chú thích trên dòng 40 nói rằng LED đang được bật, mặc dù hàm được gọi là gpio_clear(). Dòng 44 sử dụng gpio_set() để tắt LED. (Tín hiệu) logic ngược này được sử dụng đơn giản chỉ vì cách nối LED.

Hình 4-1. LED kết nối với PC13 trên Blue Pill PCB

Nhìn lại cách gọi hàm này:

gpio_clear(GPIOC,GPIO13);    /* LED on */
...
gpio_set(GPIOC,GPIO13);      /* LED off */

Lưu ý rằng hai hàm gọi này yêu cầu hai đối số, như sau:

  1. Tên cổng GPIO
  2. Số chân GPIO

Nếu bạn quen với môi trường Arduino, nó tương tự với:

int ledPin = 13;  // LED on digital pin 13
digitalWrite(ledPin,HIGH);
...
digitalWrite(ledPin,LOW);

Ngoài Arduino, bạn thường làm việc trực tiếp với cổng và chân. Trong thư viện libopencm3, bạn xác định xem bạn đang xóa hay thiết lập một bit dựa trên tên hàm (gpio_clear() hoặc gpio_set()). Bạn cũng có thể chuyển đổi một bit với cách sử dụng gpio_toggle(). Cuối cùng, bạn có thể đọc và ghi cho trọn một bộ các chân bằng một cổng, sử dụng gpio_port_read() và gpio_port_write() tương ứng.

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