Theo tài liệu Reference manual cho dòng STM32F103xx của hãng STM thì khi 1 chân được cấu hình INPUT thì Output Buffer sẽ bị disable, điều này đồng nghĩa với việc hoạt động ghi giá trị Logic ra 1 chân thông qua thanh ghi ODR sẽ không được thực hiện.
Hình ảnh sơ đồ cấu trúc 1 chân khi được cấu hình là Input
Vấn đề: Tối nay, mình đã vô tình ghi giá trị vào 1 chân được cấu hình GPIO_EXTI (Input External Interrupt) và phát hiện ra việc ghi giá trị đến 1 chân INPUT hoặc 1 chân EXTI đều làm thay đổi giá trị logic của chân đó. Mình rút gọn bài toán của mình 1 cách đơn giản như sau để mọi người dễ hình dung:
- Chân PA0 đến chân PA7 Được cấu hình GPIO_OUTPUT để điều khiển LED7 đoạn.
- Chân PA10 được cấu hình GPIO_EXTI, sử dụng trở Pull-up, Falling Edge để phát hiện người dùng nhấn nút và hiển thị số lần nhấn nút lên LED7 đoạn.
- Trong hàm Callback (phục vụ ngắt ngoài), mình tác động trực tiếp đến thanh ghi ODR của PORTA để xuất các mã LED đã được lưu sẵn (Ví dụ muốn sáng số 0 thì thực hiện lệnh: GPIOA -> ODR = 0xC0).
Suy nghĩ của mình dựa vào reference manual:
- Câu lệnh GPIOA -> ODR = 0xC0 thực hiện được việc ghi giá trị ra các chân từ PA0 đến PA7 như mình mong muốn, tuy nhiên nó cũng sẽ tác động đến các bit còn lại của thanh ghi ORD, chính là tác động đến các chân còn lại của PortA nếu các chân đó được khai báo là OUTPUT. Chấp nhận được vì mình không xài những chân còn lại.
Thực tế khi chạy chương trình:
- Lần đầu tiên nhấn nút, chương trình ngắt được thực hiện và LED7 đoạn hiển thị giá trị là 1. (đúng)
- Các lần nhấn nút tiếp theo LED7 đoạn không tăng giá trị hiển thị (không hiểu vì sao).
Tiến hành Debug:
- Mình đặt breakpoint tại 1 câu lệnh trong hàm callback (chương trình phục vụ ngắt) thì thấy chương trình chỉ nhảy đến đây duy nhất 1 lần đầu tiên khi mình nhấn nút. ==> có thể bị treo.
- Mình đặt breakpoint tại 1 điểm trong hàm while (1) thì thấy chương trình có thực hiện lặp lại mỗi lần bấm run. => chứng tỏ chương trình không treo.
- Tiếp tục xem giá trị của bộ thanh ghi GPIO và mình phát hiện giá trị BIT10 của thanh ghi IDR – Input Data Register (tương ứng với mức Logic trên chân PA10) có giá trị là 0 mặc dù đã khai báo là Input Pull-up (sử dụng trở kéo lên bên trong để định mức logic). ==> nghi ngờ 1 chân Input External Interrupt có thể bị tác động bởi việc thực hiện lệnh ghi.
Thử nghiệm: Đo giá trị logic trên chân PA10 trong 4 trường hợp, quan sát debugger và đo đạc bằng đồng hồ như sau:
-
- Khai báo PA10 là INPUT, sử dụng trở Pull up: Kết quả Logic 1, điện áp 3.3V
- Khai báo PA10 là INPUT, sử dụng trở Pull up và tác động ghi mức 0: Kết quả Logic 0, điện áp 0V
- Khai báo PA10 là EXTI, sử dụng trở Pull up: Kết quả Logic 1, điện áp 3,3V
- Khai báo PA10 là EXTI, sử dụng trở Pull up và tác động ghi mức 0: Kết quả Logic điện áp 0, 0V
Kết luận: Vì lý do việc thực hiện ghi giá trị cho 1 chân đã được cấu hình INPUT hoặc EXTI là có tác dụng (không giống như mình hiểu theo reference manual), điều này dẫn đến trong lần đầu tiên nhấn nút, hàm phục vụ ngắt được thực hiện vì chân PA10 được khai báo trở kéo lên, có sườn xuống. Tuy nhiên, sau khi thực hiện xong chương trình phục vụ ngắt, tác động ghi mức 0 đến chân PA10 thì các lần tiếp theo không vào ngắt được nữa vì phần cứng không phát hiện thêm sự thay đổi mức logic từ mức cao xuống mức thấp (falling edge) nào nữa cả.
Mình chia sẻ cùng mọi người trường hợp trên để tránh sau này có thể gặp phải khi vô tình thực hiện việc ghi dữ liệu đến 1 chân được cấu hình Input hoặc External Interrupt.
Các bạn cũng có thể xem thêm “Tổng hợp các bài hướng dẫn Lập trình vi điều khiển STM32” tại đây.
[HỌC ONLINE: LẬP TRÌNH VI ĐIỀU KHIỂN STM32, VI XỬ LÝ ARM CORTEX – M]
Nhóm TAPIT – ARM R&D