Skip to content

Công nghệ 9 giây

“Website hướng dẫn, thử nghiệm và giải thích công nghệ theo cách dễ hiểu cho mọi người.”

Menu
  • Home
  • Công nghệ
  • STM32
  • ESP32
  • Altium Desinger
  • Kiến thức điện máy
Menu

Bài 40 – OUTPUT COMPARE chuyên sâu → Bài 41 – PWM từ TIM → Bài 42 – Servo → Bài 43 – HC-SR04 bằng Input Capture.

Posted on August 23, 2026August 23, 2026 by congnghe9s

Được. Chúng ta sẽ đi đúng chuỗi này. Trước hết là BÀI 40 – OUTPUT COMPARE CHUYÊN SÂU, sau đó lần lượt 41 → 42 → 43.

BÀI 40 – OUTPUT COMPARE CHUYÊN SÂU TRÊN STM32F103

Timer tự tạo sự kiện theo mốc thời gian mà không cần CPU polling

Ở Bài 39, chúng ta đã làm điều ngược lại:

Tín hiệu bên ngoài
        ↓
Input Capture
        ↓
CCR
        ↓
Biết thời điểm tín hiệu xảy ra

Còn hôm nay:

CNT
 ↓
so sánh với CCR
 ↓
Compare Match
 ↓
Tạo sự kiện

Đây là Output Compare.


1. Output Compare thực sự là gì?

Câu định nghĩa quan trọng nhất:

Output Compare là chế độ trong đó Timer liên tục so sánh giá trị CNT với CCR. Khi hai giá trị đạt điều kiện so sánh, phần cứng tạo ra một Compare Event.

Ví dụ:

CNT:

0
1
2
3
...
99
100  ← CCR1
101
...
999

Nếu:

TIM2->CCR1 = 100;

thì khi:

CNT == 100

Timer tạo:

CC1 Event

2. Output Compare khác Input Capture thế nào?

Đây là hai khái niệm bạn phải phân biệt thật rõ.

Input Capture

GPIO event
    ↓
chụp CNT
    ↓
CCR

Nói cách khác:

Bên ngoài → Timer


Output Compare

CNT
 ↓
so sánh CCR
 ↓
Compare Event
 ↓
Output

Nói cách khác:

Timer → sự kiện/output


3. Một cách nhớ rất dễ

Chế độ Hướng
Input Capture Ngoài → Timer
Output Compare Timer → Event
PWM Timer → xung

4. Output Compare không nhất thiết phải điều khiển GPIO

Đây là một điểm rất quan trọng.

Khi:

CNT == CCR1

Timer có thể:

Compare Event

mà không cần xuất tín hiệu ra chân GPIO.

Ta có thể dùng Event đó để:

→ Interrupt
→ Trigger
→ thay đổi trạng thái output
→ điều khiển PWM

5. CCR1 chính là “mốc thời gian”

Ví dụ:

Timer tick = 1 µs

và:

CCR1 = 5000;

thì Compare xảy ra khi:

CNT = 5000

tức:

5 ms

Bạn có thể coi:

CCR1 = thời điểm cần thực hiện một sự kiện

Đây là tư duy cực kỳ hữu ích.


6. Output Compare giống một “bộ hẹn giờ phần cứng”

Ví dụ bạn muốn:

sau 10 ms → làm việc A
sau 25 ms → làm việc B
sau 50 ms → làm việc C

Ta có thể hình dung:

CNT
│
├── 10 ms  → CCR1
│
├── 25 ms  → CCR2
│
└── 50 ms  → CCR3

Một Timer có nhiều Channel nên có thể tạo nhiều mốc.


7. Đây là ưu điểm rất lớn

Thay vì:

while (1)
{
    if (millis() >= 10)
        taskA();

    if (millis() >= 25)
        taskB();

    if (millis() >= 50)
        taskC();
}

Timer phần cứng có thể tạo:

CC1 → Task A
CC2 → Task B
CC3 → Task C

CPU chỉ xử lý khi có Event.


8. Các thanh ghi quan trọng

Trong Output Compare, chúng ta đặc biệt quan tâm:

TIMx->PSC
TIMx->CNT
TIMx->ARR
TIMx->CCR1
TIMx->CCMR1
TIMx->CCER
TIMx->DIER
TIMx->SR

9. CCMR1

Thanh ghi:

TIM2->CCMR1

chứa cấu hình cho:

CH1
CH2

Trong Output Compare, phần quan trọng là:

OC1M

Output Compare Mode.


10. OC1M

OC1M quyết định Timer sẽ làm gì khi xảy ra Compare Match.

Một số chế độ quan trọng:

Frozen
Set output
Clear output
Toggle output
PWM

Đây là phần cực kỳ quan trọng.


11. Chế độ Toggle

Một ứng dụng rất dễ hiểu:

CNT == CCR1
       ↓
Toggle output

Nếu chân đang:

LOW

thì:

HIGH

Nếu đang:

HIGH

thì:

LOW

12. Vì sao Toggle rất hữu ích?

Giả sử:

Compare mỗi 500 µs

Mỗi Compare:

LOW → HIGH
HIGH → LOW

thì ta tạo được:

      ┌───┐   ┌───┐
──────┘   └───┘   └──

Đó chính là một tín hiệu vuông.


13. Tần số tín hiệu

Nếu Toggle mỗi:

500 µs

thì:

HIGH  = 500 µs
LOW   = 500 µs

Chu kỳ:

T = 1000 µs

→

f = 1000 Hz

Đây là cách Timer có thể tự tạo clock/square wave.


14. Không cần delay()

Không cần:

GPIOA->ODR |= ...
delay_us(500);

GPIOA->ODR &= ...
delay_us(500);

Timer phần cứng làm:

CNT → CCR
     ↓
Toggle
     ↓
CNT → CCR mới
     ↓
Toggle

CPU gần như không phải làm gì.


15. Output Compare Interrupt

Nếu không muốn Timer trực tiếp điều khiển output mà muốn CPU xử lý:

TIM2->DIER |= TIM_DIER_CC1IE;

Khi Compare:

CC1IF = 1

và:

TIM2_IRQHandler()

được gọi.


16. Sự khác nhau giữa Hardware Output và Interrupt

Hardware

CNT == CCR
    ↓
GPIO Toggle

CPU:

không cần tham gia

Interrupt

CNT == CCR
    ↓
CC1IF
    ↓
ISR
    ↓
CPU

CPU phải xử lý.


17. Khi nào dùng Hardware?

Dùng khi cần:

  • xung chính xác;
  • PWM;
  • clock;
  • toggle chân;
  • timing rất ổn định.

18. Khi nào dùng Interrupt?

Dùng khi:

  • cần thực hiện code;
  • cần cập nhật biến;
  • cần điều khiển logic;
  • cần thay đổi CCR;
  • cần phối hợp nhiều thiết bị.

19. Ví dụ thực hành: Toggle LED bằng Output Compare

Giả sử:

TIM2 = 72 MHz

Ta muốn:

1 tick = 1 µs

Dùng:

TIM2->PSC = 71;

Sau đó:

TIM2->ARR = 9999;
TIM2->CCR1 = 5000;

Compare tại:

5 ms

20. Cấu hình CH1 Output Compare

Về nguyên lý:

TIM2->CCMR1

phải đặt CH1 ở Output Compare.

Ví dụ:

TIM2->CCMR1 &= ~(TIM_CCMR1_CC1S);

CC1S = 00:

CH1 = Output

21. Chọn Toggle

Ta đặt:

OC1M = 011

Tức:

Toggle on match

Có thể viết:

TIM2->CCMR1 &= ~(TIM_CCMR1_OC1M);
TIM2->CCMR1 |= (3 << TIM_CCMR1_OC1M_Pos);

22. Enable CH1

TIM2->CCER |= TIM_CCER_CC1E;

Sau đó Timer có thể điều khiển output của Channel 1.


23. Nhưng cần nhớ chân GPIO

TIM2_CH1 mặc định:

PA0

Vì vậy:

PA0 = TIM2_CH1

Nếu muốn nhìn tín hiệu bằng oscilloscope/logic analyzer:

PA0 → oscilloscope

24. GPIO PA0 phải là Alternate Function Output

Khác Input Capture.

Input Capture:

PA0 → Input

Output Compare:

PA0 → Alternate Function Output

Ví dụ:

GPIOA->CRL &= ~(0xF << 0);
GPIOA->CRL |=  (0xB << 0);

0xB:

MODE = 11
CNF  = 10

tức:

Output 50 MHz
Alternate Function Push-Pull

25. Code minh họa

#include "stm32f10x.h"

void GPIO_Init(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

    /*
        PA0 = TIM2_CH1
        Alternate Function Push-Pull
        50 MHz
    */

    GPIOA->CRL &= ~(0xF << 0);
    GPIOA->CRL |=  (0xB << 0);
}


void TIM2_OC_Init(void)
{
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;

    /*
        72 MHz / 72
        = 1 MHz

        1 tick = 1 us
    */

    TIM2->PSC = 71;

    /*
        Timer period = 10 ms
    */

    TIM2->ARR = 9999;

    /*
        Compare every 5 ms
    */

    TIM2->CCR1 = 5000;

    /*
        CH1 = Output Compare
    */

    TIM2->CCMR1 &= ~TIM_CCMR1_CC1S;

    /*
        Toggle on Compare
        OC1M = 011
    */

    TIM2->CCMR1 &= ~TIM_CCMR1_OC1M;
    TIM2->CCMR1 |= (3 << TIM_CCMR1_OC1M_Pos);

    /*
        Enable CH1
    */

    TIM2->CCER |= TIM_CCER_CC1E;

    /*
        Start Timer
    */

    TIM2->CR1 |= TIM_CR1_CEN;
}


int main(void)
{
    GPIO_Init();

    TIM2_OC_Init();

    while (1)
    {
    }
}

26. Nhưng code trên có một điểm cần hiểu

Với:

ARR = 9999
CCR1 = 5000

Timer sẽ Compare tại:

CNT = 5000

sau đó đến:

CNT = 9999

Timer Update.

Sau khi quay về:

CNT = 0

nó lại đi tới:

CNT = 5000

và Toggle lần nữa.

Do đó output sẽ có dạng:

      5ms       5ms
HIGH ─────┐     ┌────
          │     │
LOW       └─────┘

Chu kỳ:

10 ms

Tần số:

100 Hz

27. Đây là chỗ rất dễ nhầm

Bạn có thể nghĩ:

Compare xảy ra mỗi 5 ms nên tần số là 200 Hz.

Không.

Vì mỗi Compare chỉ đảo trạng thái một lần.

Compare 1 → HIGH
Compare 2 → LOW
Compare 3 → HIGH

Do đó:

1 chu kỳ = 2 lần Toggle

Nếu Toggle mỗi 5 ms:

T = 10 ms
f = 100 Hz

28. Làm thế nào để tạo 1 kHz?

Muốn:

f = 1 kHz

thì:

T = 1 ms

Mỗi Toggle:

0.5 ms

Với:

1 tick = 1 µs

thì:

CCR1

phải được cập nhật mỗi:

500 ticks

29. Đây là lúc kỹ thuật CCR += delta xuất hiện

Thay vì cố định:

CCR1 = 500;

ta làm:

TIM2->CCR1 += 500;

Mỗi Compare:

CCR1
500
↓
1000
↓
1500
↓
2000
↓
2500
...

Như vậy Timer tạo sự kiện đều đặn.


30. Đây thực chất là một Hardware Scheduler

Bạn có:

CCR1 = thời điểm kế tiếp

Mỗi khi sự kiện xảy ra:

CCR1 = CCR1 + khoảng_thời_gian

Đây chính là một dạng:

Deadline scheduling bằng phần cứng.


31. Tại sao CCR += delta tốt hơn CCR = CNT + delta?

Giả sử:

TIM2->CCR1 = TIM2->CNT + 500;

Nếu ISR/hệ thống bị trễ:

CNT đã chạy thêm

thời điểm có thể bị trôi.

Trong khi:

TIM2->CCR1 += 500;

giữ lịch theo mốc Timer ban đầu.

Đây là một kỹ thuật rất hay để tránh timing drift.


32. Ví dụ

Lịch lý tưởng:

1000
1500
2000
2500
3000

Nếu CPU xử lý trễ ở mốc:

1500

thì:

CCR += 500

vẫn hướng tới:

2000

chứ không phải:

CNT hiện tại + 500

33. Output Compare có thể điều khiển nhiều Channel

Ví dụ:

TIM2

CH1 → CCR1 = 100
CH2 → CCR2 = 300
CH3 → CCR3 = 500
CH4 → CCR4 = 800

Một chu kỳ:

0
│
100 → Event 1
│
300 → Event 2
│
500 → Event 3
│
800 → Event 4
│
999 → Update

Đây là một trong những lý do Timer STM32 rất mạnh.


34. Output Compare và PWM

Đến đây bạn có thể bắt đầu thấy:

Output Compare
      ↓
CNT so với CCR
      ↓
Output thay đổi

PWM thực chất cũng dựa trên ý tưởng này.

Khác biệt là PWM được Timer tổ chức thành một chế độ tự động tạo xung tuần hoàn.


35. Một ví dụ PWM đơn giản

Giả sử:

ARR = 999
CCR1 = 250

thì:

CNT:
0 ───────── 250 ───────── 999
            ↑
          Compare

Tùy PWM mode:

Output
████████
██
██

ta thu được duty tương ứng.


36. Từ Output Compare sang PWM

Hãy nhớ:

Output Compare:
    CNT == CCR
         ↓
      Event

PWM:

CNT + ARR + CCR
       ↓
tự động tạo xung tuần hoàn

Do đó:

Hiểu Output Compare thật chắc sẽ khiến PWM dễ hiểu hơn rất nhiều.


37. Output Compare có 4 Channel

Với TIM2:

TIM2_CH1
TIM2_CH2
TIM2_CH3
TIM2_CH4

Các thanh ghi:

CCR1
CCR2
CCR3
CCR4

Mỗi Channel có thể có Compare riêng.


38. Nhưng không phải chân nào cũng tùy ý

Đây là điều cần nhớ khi làm phần cứng.

Timer Channel được nối với những chân GPIO cụ thể theo Alternate Function Mapping.

Ví dụ TIM2 mặc định:

CH1 → PA0
CH2 → PA1
CH3 → PA2
CH4 → PA3

Nếu remap TIM2 thì chân có thể thay đổi.

Vì vậy khi thiết kế PCB STM32:

Không nên chọn chân Timer/PWM tùy ý trước khi kiểm tra Alternate Function.


39. Output Compare Interrupt và CC1IF

Nếu dùng interrupt:

if (TIM2->SR & TIM_SR_CC1IF)
{
    ...
}

sau đó phải xử lý flag.

Ví dụ:

TIM2->SR &= ~TIM_SR_CC1IF;

Nếu quên xử lý đúng:

ISR có thể bị gọi liên tục

Đây là lỗi rất thường gặp khi học Timer.


40. Một ứng dụng rất hay: tạo nhiều nhịp thời gian

Ví dụ Timer tick:

1 µs

Bạn có thể lập lịch:

CCR1 → 1000 µs
CCR2 → 2000 µs
CCR3 → 5000 µs
CCR4 → 10000 µs

Như vậy một Timer có thể tạo nhiều mốc.


41. So sánh với millis()

Software Scheduler

TIM interrupt
      ↓
millis++
      ↓
CPU kiểm tra
      ↓
Task

Output Compare

Timer CNT
    ↓
CCR
    ↓
Compare Hardware
    ↓
Event

Output Compare chính xác và ít phụ thuộc vào thời gian CPU chạy code.


42. Nhưng Output Compare không thay thế Scheduler

Đừng hiểu nhầm rằng:

“Từ giờ không cần millis nữa.”

Không.

Mỗi thứ có mục đích:

millis()
→ quản lý task mức phần mềm

Output Compare
→ timing chính xác mức phần cứng

Ví dụ:

Robot
 ├── millis → đọc cảm biến mỗi 50 ms
 ├── millis → cập nhật LCD mỗi 200 ms
 └── Timer PWM → điều khiển motor

43. Đây là kiến trúc rất giống robot của bạn

Robot tránh vật cản của bạn có thể phân chia:

               STM32
                 │
      ┌──────────┼──────────┐
      │          │          │
    Timer      GPIO       UART
      │
      ├── PWM → Motor
      │
      └── Input Capture → HC-SR04

Đây chính là cách tư duy phần cứng mà chúng ta đang xây dựng từ Bài 36 đến Bài 43.


44. Bài tập 1

Cho:

TIM2 = 72 MHz
PSC = 71

Hỏi:

Một Timer tick bao nhiêu?

Đáp án:

72 MHz / 72
= 1 MHz

→

1 tick = 1 µs

45. Bài tập 2

Cho:

CCR1 = 2500

Compare xảy ra sau:

2500 µs

hay:

2.5 ms

46. Bài tập 3

Cho:

ARR = 9999
CCR1 = 5000

Timer tick:

1 µs

Hỏi chu kỳ Timer?

10000 µs
= 10 ms

Hỏi tần số nếu Toggle một lần mỗi Compare?

T = 10 ms
f = 100 Hz

47. Bài tập 4

Muốn tạo square wave:

1 kHz

với:

1 µs/tick

hãy tìm khoảng thời gian giữa hai lần Toggle.

Ta có:

T = 1 ms

Một chu kỳ có 2 lần Toggle:

500 µs

Do đó:

CCR_next = CCR_current + 500;

48. Bài tập 5

Hãy trả lời:

Input Capture và Output Compare khác nhau ở đâu?

Câu trả lời chuẩn:

Input Capture:

External Event
      ↓
capture CNT
      ↓
CCR

Còn:

Output Compare:

CNT
 ↓
compare CCR
 ↓
Event / Output

Nếu bạn hiểu được hai sơ đồ này thì bạn đã nắm được cốt lõi của bài.


49. Tóm tắt Bài 40

Bạn cần nhớ:

PSC
 ↓
tạo tốc độ CNT

CNT
 ↓
bộ đếm

CCR
 ↓
mốc so sánh

CNT == CCR
 ↓
Compare Event

Compare Event có thể:

→ Interrupt
→ Toggle
→ Set
→ Clear
→ PWM

Và kỹ thuật quan trọng:

CCR1 += interval;

cho phép Timer tự tạo các mốc thời gian định kỳ.


50. Chuỗi kiến thức tiếp theo

Bây giờ chúng ta đã có:

Bài 38
Timer Architecture
       ↓
Bài 39
Input Capture
       ↓
Bài 40
Output Compare
       ↓

Bước tiếp theo là:

BÀI 41 – PWM TỪ TIMER TRÊN STM32F103

Ở bài đó chúng ta sẽ giải quyết một câu hỏi rất quan trọng:

Làm thế nào chỉ bằng PSC, ARR, CCR, Timer có thể tự tạo ra một xung PWM mà CPU không phải liên tục bật/tắt GPIO?

Sau đó chúng ta sẽ dùng chính PWM đó để học:

Bài 41
PWM
 ↓
Bài 42
Servo SG90 / MG90S
 ↓
Bài 43
HC-SR04 + Input Capture

Và khi hoàn thành Bài 43, bạn sẽ có đủ nền tảng Timer để hiểu khá sâu motor PWM + servo + ultrasonic + encoder trên STM32F103.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • Bạn có thể hướng dẫn tôi quy trình sửa chữa một mainboard laptop cũng như mainboard của máy tính để bàn không?
  • Có những mức nguồn DC nào trên một bo mạch mainboard laptop?Kỹ thuật đốt dòng để kiểm tra nguồn laptop được thực hiện như thế nào?
  • bài tiếp theo hợp lý là: BÀI 63 – SENSOR FUSION THỰC CHIẾN: ENCODER + IMU MPU6050 + EKF + ODOMETRY X,Y,θ TRÊN STM32F103
  • BÀI 62 – EKF (EXTENDED KALMAN FILTER) CHO ROBOT 2 BÁNH: FUSION ENCODER + IMU + ƯỚC LƯỢNG X, Y, θ, V, ω
  • BÀI 61 – KALMAN FILTER CHO ENCODER + IMU: ƯỚC LƯỢNG TỐC ĐỘ, GÓC θ VÀ SENSOR FUSION

Recent Comments

No comments to show.

Archives

  • September 2026
  • August 2026
  • July 2026
  • June 2026
  • May 2026
  • February 2026

Categories

  • Altium Desinger
  • Công nghệ
  • ESP32
  • Khác
  • Kiến thức điện máy
  • STM32
Live 3D Globes Visitor
©2026 Công nghệ 9 giây | Design: Newspaperly WordPress Theme