Tôi đã triển khai macro vòng lặp trễ đơn giản cho Cortex-M4 trong chương trình C:
#define DELAY_CYCLES (F_CPU / 3000000) //F_CPU là 72000000
#define delayUS(n) __asm__ dễ bay hơi( \
"1: phụ %0, #1 \n" \
"bne 1b \n" \
: /* không có kết quả đầu ra */ \
: "r" (n * DELAY_CYCLES) /* đầu vào */ \
: "0" /* ghi đè */ \
)
điều này sẽ trì hoãn n
Micro giây (giả sử các ngắt bị vô hiệu hóa). Hầu hết thời gian nó hoạt động tốt. Tuy nhiên, tôi thấy rằng nó không hoạt động bình thường trong một chức năng sử dụng nó hai lần:
kiểm tra khoảng trống tĩnh (uint8_t num) {
digitalWrite(12, 1);
độ trễUS(10);
digitalWrite(13, 1);
độ trễUS(10);
digitalWrite(12, 0);
digitalWrite(13, 0);
}
(Đây là một ứng dụng thực tế số
nhưng đã bị loại bỏ thành chức năng này khi gỡ lỗi vấn đề này. Nó cũng được nội tuyến vào chủ yếu
, do đó nhãn được tháo rời. )
Những gì đang xảy ra ở đây là đúng độ trễUS()
Cuộc gọi thứ hai không bao giờ hoàn thành. Việc kiểm tra tổ hợp được tạo đã phát hiện ra vấn đề:
528: 2701 lượt r7, #1
52a: 6037 str r7, [r6, #0] ;digitalWrite(12, 1)
52c: 23f0 mov r3, #240 ;delayUS(10); 10 * DELAY_CYCLES = 240
52e: 3b01 sub r3, #1
530: d1fd bne.n 52e
532: 4c0d ldr r4, [pc, #52]
534: 6027 str r7, [r4, #0] ;digitalWrite(13, 1)
536: 3b01 subs r3, #1 ;delayUS(10), nhưng r3 vẫn bằng 0
538: d1fd bne.n 536
53a: 2300 lượt r3, #0
53c: 6033 str r3, [r6, #0] ;digitalWrite(12, 0)
Vì lý do nào đó, gcc không khởi tạo lại trước khi sử dụng nó trong vòng lặp trễ thứ hai r3
, do đó, nó không bị trễ 240 lần lặp (10µs), mà là 2^32 (khoảng 3 phút).
Với biến thể này, vấn đề sẽ biến mất:
__attribute__((used)) int giả;
#define delayUS(n) __asm__ dễ bay hơi( \
"1: phụ %0, #1 \n" \
"bne 1b \n" \
: "=r" (giả) /* không có kết quả đầu ra */ \
: "0" (n * DELAY_CYCLES) /* đầu vào */ \
: "0" /* ghi đè */ \
)
Tạo mã chính xác hơn:
528: 2701 lượt r7, #1
52a: 23f0 mov r3, #240 ;r3 = 10 * DELAY_CYCLES
52c: 6037 str r7, [r6, #0] ;digitalWrite(12, 1)
52e: 461a mov r2, r3 ;r2 = r3
530: 3a01 sub r2, #1 ;delayUS(r2)
532: d1fd bne.n 530
534: 4c0d ldr r4, [pc, #52]
536: 6027 str r7, [r4, #0] ;digitalWrite(13, 1)
538: 3b01 sub r3, #1 ;delayUS(r3)
53a: d1fd bne.n 538
53c: 4a0c ldr r2, [pc, #48]
53e: 6013 str r3, [r2, #0] ;digitalWrite(12, 0)
Ở đây, nó nhận ra chính xác rằng vòng lặp trễ làm hỏng thanh ghi đầu vào của nó và do đó không sử dụng lại nó mà không khởi tạo nó r3
(Nó sử dụng r2
như một trong số chúng chứ không phải là một vòng lặp. )
Vậy tại sao gcc không nhận ra rằng phiên bản trước cũng bị hỏng đầu vào khi nó được liệt kê trong danh sách bị hỏng?
Tôi là một lập trình viên xuất sắc, rất giỏi!