Điều chế độ rộng xung (PWM) là một trong những kỹ thuật phổ biến nhất trong các hệ thống nhúng. Nói một cách đơn giản, PWM điều khiển lượng điện năng cung cấp cho các thiết bị bằng cách bật và tắt tín hiệu rất nhanh. Nhờ đó, bạn có thể làm mờ đèn LED, điều khiển tốc độ động cơ, hoặc thậm chí tạo tín hiệu âm thanh chỉ bằng một chân vi điều khiển.


Trong vi điều khiển STM32, việc tạo PWM trở nên rất đơn giản vì bộ định thời được tích hợp sẵn hỗ trợ. Bạn chỉ cần cấu hình bộ định thời, chọn kênh, đặt tần số và chu kỳ làm việc. Sau đó, phần cứng sẽ tự động tạo ra tín hiệu PWM.

So với việc bật/tắt GPIO thủ công, việc sử dụng bộ định thời chính xác và hiệu quả hơn nhiều. Do đó, PWM trong STM32 được sử dụng rộng rãi trong robot, tự động hóa, trình điều khiển động cơ, điều khiển độ sáng đèn LED và nhiều dự án thực tế khác.

Trong hướng dẫn này, bạn sẽ học từng bước cách cấu hình PWM trên STM32 bằng STM32CubeIDE và thư viện HAL . Đầu tiên, chúng ta sẽ tìm hiểu khái niệm cơ bản về PWM và bộ định thời. Sau đó, chúng ta sẽ cấu hình dự án trong STM32CubeIDE, tạo mã và cuối cùng là kiểm tra đầu ra trên phần cứng thực.


PWM (Điều chế độ rộng xung) là gì?


Điều chế độ rộng xung (PWM) là một kỹ thuật điều khiển điện áp hoặc công suất trung bình cung cấp cho thiết bị bằng cách chuyển đổi tín hiệu giữa trạng thái BẬT và TẮT rất nhanh. Ý tưởng chính rất đơn giản: thời gian tín hiệu BẬT so với TẮT càng lâu thì thiết bị nhận được càng nhiều công suất. Tỷ lệ này được gọi là chu kỳ làm việc (duty cycle ), trong khi tốc độ chuyển mạch được gọi là tần số (frequency) .

Ví dụ, chu kỳ hoạt động 50% nghĩa là tín hiệu sẽ BẬT trong một nửa thời gian và TẮT trong nửa thời gian còn lại. Kết quả là, thiết bị nhận được khoảng một nửa công suất tối đa có thể. Bằng cách điều chỉnh chu kỳ hoạt động, bạn có thể làm mờ đèn LED, điều khiển tốc độ động cơ hoặc quản lý cường độ của bộ điều khiển nhiệt.

PWM hiệu quả vì quá trình chuyển mạch diễn ra nhanh và tổn thất điện năng rất thấp so với các phương pháp tương tự như điện trở hoặc bộ điều chỉnh điện áp.


























PWM trong Vi điều khiển (MCU)


Các vi điều khiển (MCU) như STM32 có bộ định thời tích hợp giúp việc tạo PWM trở nên rất dễ dàng. Thay vì phải chuyển đổi chân thủ công, bộ định thời phần cứng sẽ tự động tạo ra tín hiệu PWM chính xác ở tần số và chu kỳ làm việc bạn chọn.


Với STM32, bạn có thể cấu hình PWM bằng STM32CubeIDE và sau đó điều khiển nó bằng mã với thư viện HAL . Cách tiếp cận này đảm bảo độ chính xác và giải phóng CPU cho các tác vụ khác.

Trong các dự án thực tế, PWM trong vi điều khiển xuất hiện ở khắp mọi nơi — từ điều khiển động cơ DC trong robot đến điều chỉnh độ sáng đèn LED trong màn hình. Vì vừa mạnh mẽ vừa đơn giản, PWM thường là khái niệm đầu tiên bạn học khi bắt đầu với STM32.


Tổng quan về bộ đếm thời gian STM32 cho PWM


Chế độ hẹn giờ liên quan đến PWM

Vi điều khiển STM32 bao gồm các bộ định thời mạnh mẽ có thể tạo trực tiếp tín hiệu PWM. Các bộ định thời này hoạt động độc lập với CPU, nghĩa là bạn có thể tạo ra dạng sóng chính xác mà không cần sử dụng nhiều năng lượng xử lý.

Đối với PWM, các chế độ hẹn giờ được sử dụng phổ biến nhất là:


  • Up-counting mode : Bộ đếm bắt đầu đếm từ 0 và tăng lên giá trị trong Thanh ghi Tự động Nạp lại (ARR). Sau khi đạt đến giá trị này, bộ đếm sẽ đặt lại và bắt đầu lại.
  • PWM Mode 1 : Ngõ ra vẫn ở mức CAO khi bộ đếm nhỏ hơn giá trị so sánh (CCR). Ngõ ra chuyển sang mức THẤP khi bộ đếm lớn hơn giá trị CCR.
  • PWM Mode 2 : Ngược lại với Chế độ 1. Đầu ra ở mức THẤP khi bộ đếm nhỏ hơn CCR và trở thành CAO sau đó.


Bằng cách chọn chế độ phù hợp, bạn có thể quyết định dạng sóng PWM của mình hoạt động như thế nào.

Hình ảnh trên cho thấy bộ đếm thời gian so sánh với ARR và CCR để tạo ra đầu ra PWM.


  • Đường màu xanh là bộ đếm tăng dần đến ARR.
  • Đường màu đỏ (CCR) quyết định thời điểm đầu ra chuyển từ CAO sang THẤP.
  • Dạng sóng màu xanh lá cây chính là tín hiệu PWM thực tế được tạo ra.


Cơ bản về tần số và chu kỳ hoạt động

Mỗi tín hiệu PWM đều có hai đặc tính quan trọng: tần số và chu kỳ hoạt động .

  • Tần số PWM cho biết tín hiệu hoàn thành một chu kỳ đầy đủ bao nhiêu lần trong một giây. Nó phụ thuộc vào ba thông số: xung nhịp bộ định thời (timer clock), bộ chia trước (prescaler - PSC) và ARR.
  • Công thức

PWM frequency = Timer clock / (PSC + 1) / (ARR + 1)

  • Chu kỳ hoạt động xác định tỷ lệ phần trăm thời gian BẬT trong mỗi chu kỳ. Nó phụ thuộc vào giá trị thanh ghi so sánh (CCR).
  • Công thức:

Duty cycle (%) = (CCR / (ARR + 1)) × 100


Ví dụ, nếu ARR = 999 và CCR = 500, thì chu kỳ hoạt động = 50%. Điều này có nghĩa là tín hiệu duy trì ở mức CAO trong một nửa thời gian và mức THẤP trong nửa thời gian còn lại.




Hình ảnh trên cho thấy tín hiệu PWM với chu kỳ hoạt động 25%, 50% và 75% .


Chuẩn bị phần cứng cho hướng dẫn PWM STM32

Để làm theo hướng dẫn này, bạn sẽ cần phần cứng sau:




  • Cáp USB Type C (để kết nối bo mạch STM32 với PC để lập trình và gỡ lỗi)



Cấu hình PWM bằng STM32CubeIDE

Tôi đang sử dụng STM32 UNO( STM32F103C8T6) cho dự án này. Chúng ta sẽ bắt đầu với Clock.


Cấu hình Clock STM32

Cấu hình Clock cho dự án được hiển thị trong hình ảnh bên dưới.




  • Thạch anh (8MHz) được sử dụng để cung cấp xung nhịp thông qua PLL và hệ thống chạy ở xung nhịp tối đa 72MHz.
  • Tôi sẽ sử dụng TIM1 cho PWM, được kết nối với Bus APB2.
  • Đồng hồ hẹn giờ APB2 hiện đang ở mức 72MHz và điều đó làm cho đồng hồ TIM1 = 72MHz.


Cấu hình STM32 PWM Timer 


Tôi sẽ sử dụng Timer 1 để tạo tín hiệu PWM. Cấu hình PWM của Timer 1 được hiển thị trong hình bên dưới.

Mô tả chi tiết về hình ảnh được đề cập bên dưới.



Cấu hình Timer 

  • Clock Source: Internal Clock


Điều này thiết lập bộ hẹn giờ sử dụng xung nhịp APB2 nội bộ làm nguồn. Điều này rất cần thiết để tạo ra các tín hiệu thời gian chính xác như PWM mà không cần phụ thuộc vào xung nhịp bên ngoài.


  • Channel1: PWM Generation CH1


Kênh này cho phép xuất tín hiệu PWM trên Kênh 1 của TIM1. Đây là yếu tố khiến bộ hẹn giờ xuất tín hiệu PWM thay vì chỉ đếm.


Cài đặt bộ đếm

  • Bộ chia trước (PSC = 72-1) 

Bộ chia này chia tần số xung nhịp đầu vào của bộ hẹn giờ cho 72.

Ví dụ, nếu xung nhịp APB là 72 MHz, bộ Timer sẽ đếm ở 1 MHz sau khi chia trước.


  • Counter Mode: Up


Bộ đếm đếm từ 0 đến giá trị trong Thanh ghi Tự động Nạp lại (ARR), sau đó đặt lại và bắt đầu lại. Đây là chế độ điển hình của PWM.


  • Counter Period (ARR = 100-1)


Đặt giá trị đếm tối đa (100 – 1 = 99), xác định tần số PWM kết hợp với bộ chia trước.

PWM Frequency = Timer Clock / (PSC + 1) / (ARR + 1)

→ Với APB Clock: 72 MHz:

PWM Frequency = 72 MHz / 72 / 100 = 10 kHz


Cài đặt Kênh 1 Tạo PWM

  • Mode: PWM mode 1


Điều này đặt PWM Output thành PWM Mode 1 , nghĩa là:

  • Đầu ra ở mức CAO khi bộ đếm nhỏ hơn giá trị xung.
  • Đầu ra ở mức THẤP khi bộ đếm lớn hơn hoặc bằng giá trị xung.
  • Pulse (CCR1) = 0
  • Giá trị này xác định chu kỳ hoạt động ban đầu — trong trường hợp này là 0, nghĩa là không có thời gian BẬT.
  • Ngõ ra sẽ duy trì ở mức THẤP cho đến khi giá trị này được thay đổi trong mã.


Ánh xạ chân (PA8 – TIM1_CH1)

  • PA8 (TIM1_CH1)
  • Đây là chân GPIO nơi tín hiệu PWM sẽ xuất hiện.
  • Chân này được tự động cấu hình làm đầu ra chức năng thay thế cho TIM1 Channel 1. Bạn có thể kết nối đèn LED, trình điều khiển động cơ hoặc thiết bị khác vào chân này để điều khiển bằng PWM.


Cấu hình PWM DMA

Hình ảnh bên dưới hiển thị Cấu hình Stm32CubeIDE cho Chế độ DMA PWM STM32.



Bạn có thể chọn bất kỳ luồng DMA nào khả dụng cho kênh Timer cụ thể mà bạn đang sử dụng. Điểm quan trọng ở đây là cấu hình DMA direction là Memory to Peripheral , vì chu kỳ làm việc PWM (giá trị độ rộng xung) sẽ được lấy từ bộ nhớ và ghi vào thanh ghi Capture/Compare (CCR) của Timer.

Đối với độ rộng dữ liệu , tôi đã chọn Half Word (16-bit, uint16_t) . Điều này là do các thanh ghi Timer (chẳng hạn như CCR1, CCR2, v.v.) có độ rộng 16-bit trên hầu hết các thiết bị STM32. Nếu giá trị độ rộng xung của bạn nằm trong phạm vi 16 bit, sử dụng Half Word là lựa chọn hiệu quả nhất.


Chế độ PWM DMA

Bây giờ, chúng ta hãy nói về chế độ Normal và Chế độ Circular :


  • Chế độ Normal :
  • Ở chế độ này, DMA chỉ truyền dữ liệu từ bộ nhớ đến Timer một lần. Ví dụ, nếu bạn có một mảng độ rộng xung được lưu trữ trong bộ nhớ, DMA sẽ lần lượt chuyển chúng đến Timer CCR, và sau khi giá trị cuối cùng được gửi đi, quá trình truyền dữ liệu sẽ dừng lại.
  • Phù hợp nhất để tạo dạng sóng một lần , như gửi một loạt xung được xác định trước.
  • Sau khi hoàn tất, nếu bạn muốn tạo thêm dạng sóng, bạn cần phải khởi động lại DMA theo cách thủ công.
  • Chế độ Circular :
  • Ở chế độ này, sau khi DMA hoàn tất việc truyền dữ liệu từ bộ đệm bộ nhớ, nó sẽ tự động lặp lại và bắt đầu lại từ đầu. Quá trình này tiếp tục vô tận cho đến khi bạn dừng nó.
  • Lý tưởng để tạo dạng sóng liên tục , chẳng hạn như tạo các mẫu PWM lặp lại (sóng sin, sóng tam giác, v.v.).
  • Vì CPU không cần phải khởi động lại quá trình truyền dữ liệu nên giảm được gánh nặng cho bộ xử lý và đảm bảo đầu ra PWM mượt mà, không bị gián đoạn.

Chúng ta sẽ xem xét cả hai chế độ, nhưng trước tiên tôi sẽ bắt đầu với Chế độ Normal.


Tại sao chúng ta cần sử dụng -1?

Trong bộ định thời STM32, Bộ chia trước (PSC) và Thanh ghi Tự động Nạp lại (ARR) hoạt động theo cách tự động cộng +1 vào giá trị bạn nhập. Điều này có nghĩa là nếu bạn nhập giá trị PSC = 71, bộ định thời sẽ chia xung nhịp cho 72. Tương tự, nếu bạn đặt ARR = 999, bộ đếm sẽ đếm đến 1000.



Đó là lý do tại sao khi tính toán giá trị cho tần suất hoặc chu kỳ, chúng ta trừ 1 trong công thức để phù hợp với hành vi của phần cứng.


Lập trình PWM với HAL (Không có DMA)

Thư viện STM32 HAL giúp cấu hình và điều khiển tín hiệu PWM dễ dàng hơn mà không cần thao tác trực tiếp với các thanh ghi cấp thấp. Hãy cùng phân tích quy trình từng bước.


Khởi tạo bộ đếm thời gian

Đầu tiên, hãy khởi tạo bộ hẹn giờ ở chế độ PWM. Khi bạn tạo mã từ STM32CubeIDE , nó sẽ tự động tạo các hàm khởi tạo như MX_TIMx_Init(). Hàm này sẽ thiết lập bộ chia trước (PSC), thanh ghi tự động tải lại (ARR) và cấu hình bộ hẹn giờ ở chế độ PWM .

Ngoài ra, Stm32CubeIDE cấu hình chân GPIO được liên kết với kênh Timer của bạn ở chế độ Alternate Function mode có thể xuất hiện trên chân vật lý.


Bắt đầu đầu ra PWM

Sau khi khởi tạo, hãy bắt đầu đầu ra PWM bằng hàm HAL:

HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);

Giải thích:


  • htim1 là bộ xử lý hẹn giờ được tạo bởi Stm32CubeIDE.
  • TIM_CHANNEL_1 chọn kênh PWM (Kênh 1 trong trường hợp này).

Bạn có thể lặp lại chức năng tương tự với TIM_CHANNEL_2TIM_CHANNEL_3, hoặc TIM_CHANNEL_4 nếu bộ hẹn giờ của bạn hỗ trợ nhiều kênh.


Thay đổi Duty Cycle trong Code


Để thay đổi duty cycle khi chạy, hãy cập nhật thanh ghi Capture/Compare Register (CCR). Ví dụ:

TIM1->CCR1 = 30; // Set duty cycle value <= ARR


  • Giá trị được ghi vào để CCR1 điều khiển thời gian BẬT của tín hiệu.
  • Giá trị CCR cao hơn có nghĩa là xung dài hơn (chu kỳ hoạt động cao hơn).
  • Giá trị CCR nhỏ hơn có nghĩa là xung ngắn hơn (chu kỳ hoạt động thấp hơn).

Chu kỳ hoạt động được tính theo công thức:

Phương pháp này cho phép bạn điều chỉnh độ sáng của đèn LED, tốc độ của động cơ hoặc bất kỳ tải nào khác được kết nối với chân PWM một cách linh hoạt.


Code PWM hoàn chỉnh

Bạn có thể tham khảo chức năng chính bên dưới để thay đổi chu kỳ hoạt động tại Runtime.



Đoạn mã trên dần dần thay đổi chu kỳ hoạt động của PWM từ 0% đến 100% . Nó tăng chu kỳ hoạt động theo từng bước 10% và mỗi bước diễn ra sau mỗi 500 mili giây .


Kết quả tạo PWM STM32


Hình ảnh bên dưới hiển thị xung PWM được ghi lại trên Logic Analyser .

Dạng sóng PWM ở trên có những đặc điểm sau:


  • Tần số: 10 kHz — mỗi chu kỳ hoàn chỉnh kéo dài 100 micro giây.
  • Chu kỳ hoạt động: 30% — tín hiệu duy trì ở mức CAO trong 30 micro giây và mức THẤP trong 70 micro giây trong mỗi chu kỳ.
  • Dạng sóng hiển thị rõ ràng khoảng cách và độ rộng xung nhất quán, xác nhận đầu ra PWM ổn định.