[Matrix LED] Bài 9: Kĩ thuật PWM – hiệu ứng led sao băng

xung PWM

Chúng ta đã quen với việc điều khiển led bằng các tín hiệu bật tắt, còn để điểu khiển độ sáng của LED thì sao ? Muốn LED thay đổi độ sáng thì phải thay đổi điện áp cấp cho LED, nhưng vi điều khiển thì chỉ có thể cấp ra tín hiệu 1 và 0 tương ứng 5V hoặc 0V. Có 1 kĩ thuật để thay đổi điện áp cấp ra cho LED đó là sử dụng PWM
Bản chất, PWM là liên tục đưa ra các mức logic 1 và 0 ở 1 tốc độ rất cao. Khoảng thời gian chênh lệch giữa thời gian tồn tại mức 1 và 0 quyết định điện áp cấp, cái này chính là điều chế độ rộng xung PWM.
Ví dụ, 1 chương trình đơn giản để tạo xung PWM ra 1 chân của vi điều khiển như sau, mình dẽ demo với 1 con LED đơn thôi đã nhé !

Chương trình trên liên tục bật tắt chân P0_0 với thời gian tồn tại mức 1 là 1ms, thời gian tồn tại mức 0 là 9ms. Do vậy xung này có chu kì 10ms <=> 100Hz. Mức cao chiếm 10% của chu kì nên LED sẽ chỉ sáng với 10% độ sáng tối đa của nó ! Để thay đổi độ sáng, ta chỉ cần điều chế lại độ rộng này, nói đơn giản là thay đổi thời gian delay(1). Tuy nhiên cái delay bên trên tăng thêm bao nhiều thì cái delay phía dưới giảm đi bây nhiêu để đảm bảo tổng 2 cái delay là 10ms nhé. Điều này sẽ đảm bảo tần số PWM của ta luôn ổn định.

Ví dụ chương trình làm đèn sáng lên dần rồi tối đi dần

Kĩ thuật trên gọi là PWM mềm. Thực chất hầu hết các bộ vi điều khiển đều trang bị các bộ PWM cứng, nó là 1 phần cứng độc lập với MCU, chúng ta chỉ cần cấu hình các thông số tần số và độ rộng cho nó là co ngay xung PWM, tuy nhiện nó chỉ gjới hạn 1 vài kênh PWM thôi. Nói chung, PWM cứng thường dùng với các mục đích như điều khiển động cơ. Còn điều khiển LED thì số lượng led của chúng ta rất nhiều, không 1 vi điều khiển nào có nhiều bộ PWM cứng như thế cả. Do vậy, để điều khiển LED bằng PWM, chúng ta sẽ sử dụng PWM mềm nhé !

PWM cần tuân thủ chính xác về tần số và độ rộng, nếu lập trình như code bên trên, chúng ta sẽ chỉ điều khiển được 1 kênh LED và không thể làm được công việc khác. Do vậy mình sẽ sử dụng ngắt timer cho bộ PWM mềm

PWM mềm bằng phương pháp ngắt timer

Đến lúc này, 1 thông số chúng ta cần đặc biệt quan tâm tới PWM là độ phân giải của xung. Trở lại code PWM mềm mình đã chia sẻ bên trên, các bạn có thể thấy độ sáng của LED được điều khiển với vòng lặp for chạy từ 0 tới 10. Có tổng cộng 11 giá trị sáng trong dải số này. Do vậy số mức sáng khác nhau mà con LED chúng ta vừa điều khiển là 11. Độ phân giải là 1 thông số rất quan trọng khi điều khiển LED. Độ phân giải càng cao đương nhiên càng tốt nhưng sẽ càng hao phí tài nguyên của vi điều khiển, hãy thử code để kiểm nghiệm nhé !

Mình sẽ sử dụng bộ timer 1 của vi điều khiển 89s52 để demo 1 chương trình tạo PWM trên chân P0_0 với mức sáng là 11 mức

Đoạn chương trình này tạo ra 1 PWM trên P0_0, tuy nhiên nó được tự động thực hiện nhờ vào ngắt timer chứ không cần bỏ vào hàm main như chương trình đã viết bên trên nữa. Do đó, ta có thể thoải mái làm cộng việc khác trong main mà không lo ảnh hướng tới tần số của xung. Mình sẽ giải thích code trên 1 chút

Do giá trị đếm nạp vào thanh ghi TH và TL là 0xFC17 = 64 535 nên bộ timer sẽ đếm 1000 xung rồi tràn. Do thạch anh là 12Mhz nên 1 xung đếm sẽ mất 1us. 1000 xung sẽ mất 1mili giây (1ms). Nên hàm ngắt này sẽ tự gọi 1ms 1 lần

Biến mucsangmax chính là độ phẩn giải mà ta mong muốn. Do mình cần 11 mức sáng khác nhau nên mình để mucsangmax = 10. Tại sao là bị trừ đi ?. Bởi biến đếm của chúng ta bắt đầu từ 0

Biến dosangLED là độ sáng mà chúng ta mong muốn trên LED và nó nằm trong khoảng từ 0 -> mucsangmax

Công việc bây giờ là xuất các giá trị 1 và 0 ra GPIO. Bằng việc so sánh xem giá trị sáng của LED có lơn biến dem không, nếu giá trị sáng vẫn lớn hơn thì out ra 1. Ngược lại thì out ra 0. Biến dem chạy tới mucsangmax là kết thúc 1 chu kì quét rồi đó, nên sẽ quay lại làm lại từ đầu ( dem=0 )

Các bạn có thể nhìn hình ảnh minh họa cho dễ hiểu

OK. bây giờ trong hàm main ta chỉ cần thay đổi dosangLED là được

Vi dụ chương trình đầy đủ sáng dần tắt dần 1 LED trên chân P0_0 dùng ngắt timer

Đó là 11 mức sáng, vậy muốn 100 mức sáng khác nhau thì sao ? Đơn giản chỉ cần tăng biến dosangmax lên 99 là xong :D, lúc này biến dem sẽ chạy tới 100 mới kết thúc 1 chu kì, do vậy 1 chu kì sẽ có tới 100 lần ngắt lận, tương tự cho 1000 mức sáng. Khi tăng số mức sáng sẽ kéo theo số lần ngắt trong 1 giây tăng lên, hãy nhớ rằng các lệnh con con tròng hàm ngắt cũng chiếm 1 chút thời gian xử lí và nếu ngắt tới 1000 lần cho 1 chu kì thì thời gian thực hiện các lệnh đó sẽ đáng kể hẳn lên. Dẵn tới việc vi điều khiển sẽ hao tốn thời gian cho các lệnh này làm tốc độ thực hiện chương trình ở hàm main bị chậm lại đáng kể.

Demo trên KIT 8051 V2

Hiệu ứng LED sao băng

Hiệu ứng LED sao băng là 1 trong những hiệu ứng đẹp và độc đáo ! Với pwm mềm bằng timer, chúng ta sẽ dễ dàng tạo ra hiệu ứng này. Mình sẽ tạo 1 sao băng rượt đuổi trên 8 con LED. Do vậy cần 8 biến lưu trữ độ sáng của chúng, nhưng thay vì tạo 8 biến thì mình sẽ tạo 1 mảng dữ liệu gồm 8 phần tử cho dễ thao tác, và mình sẽ gán sẵn 8 mức sáng khác nhau vào mảng

Bây giờ định nghĩa các chân LED tương ứng

Biến độ sáng max mình sẽ tăng lênh thành 30 để có nhiều mức sáng khác nhau hơn, do đó chu kì lúc này sẽ dài hơn -> tần số quét giảm đi. Để tăng tần số lên mình sẽ nạp vào 2 thanh ghi TH1 TL1 giá trị là 0xFE0B (0.5ms ngắt 1 lần – nhanh gấp đổi code bên trên)

OK. Giờ chỉnh lại chương trình ngắt thành như sau

Chương trình ngắt trên giống như ngắt với 1 con LED. Chỉ là có nhiều LED hơn nên mình sẽ phải kiểm tra và xuất tín hiệu ra ra chân nhiều hơn thôi !

Toàn bộ CODE như sau:

Kết quả 8 led sáng tướng ứng với mức sáng đà đặt trong mảng dữ liệu

Giờ mình sẽ code hàm main cho 8 led này chạy rượt đuổi để tạo ra hiệu ứng sao băng. Mình sẽ tạo thêm 1 mảng là hằng số để lưu giữ liệu sáng của led.

OK. Trong hàm main mình sẽ viết vòng lặp để lấy 8 độ sáng đã lưu trong mản data_led ghi vào mảng led

Demo hiệu ứng led sao băng

Các bạn thử thay đổi biến -12 để thấy tốc độ xuất hiện của sao băng nhé. hàm delay chính là tốc độ chạy của sao băng

Vòng lặp ngoài cùng bắt đâu từ 7 tương ứng thao tác lên con led ngoài cùng đầu tiên ( led[7] ). Tiếp đến vòng lặp con 7 lần mình sẽ dịch phải toàn bộ mảng led ( tức là con led bên phải sẽ lấy độ sáng của con led bên trái) Cứ thế tới con led[1] sẽ lấy độ sáng của con led[0]. Tới led[0] do không con con led nào bên trái nó nữa nên nó sẽ chạy vào dữ liệu được lưu trong data_led để lấy, tương ứng với vị trí của biến k . nếu k giảm xuống âm thì sẽ cho led[0] = 0 luôn !

Bạn càng giảm k xuống nhỏ thì thời gian con led[0] bị tắt càng lâu hơn ! Dẫn tơi sao băng xuất hiện chậm hơn

FULL CODE đây nhé

 

Từ tác giả:

Nếu có bất kì thắc mắc nào trong bài viết, vui lòng để lại comment dưới mỗi bài ! Mình sẽ không trả lời thắc mắc của các bạn ở facebook hay email !

Giới thiệu Đào Nguyện 60 bài viết
DIY,chế cháo, viết blog chia sẽ kiến thức về lập trình,điện tử - IoT. Rất mong được giao lưu, kết bạn với các bạn cùng đam mê. Địa chỉ Facebook: https://www.facebook.com/nguyendao207

1 bình luận

  1. Hiya, I am really glad I’ve found this info. Today bloggers publish just about gossip and net stuff and this is really irritating. A good blog with exciting content, that is what I need. Thanks for making this web-site, and I will be visiting again. Do you do newsletters by email?

Để lại bình luận