[Thảo luận]PWM với GPIO - Updated
Chả là cái này mình nghe được từ anh Hải hồi training RYA vòng 1, dạo gần đây thấy nhiều anh em G-Force hỏi quá nên lên đây lập cái thread mời các bô lão vào thảo luận.)
Về định nghĩa PWM là gì, cũng như cách tạo PWM bằng chân PWM của PIC(hoặc các dòng microcontroller khác) xin không nhắc lại.
500đ hình cho nó bớt chán:
1) Mục đích: cách code cho các chân GPIO để tạo xung PWM, giúp có thể tận dụng nhiều chân vi điều khiển nói chung trong việc làm hiệu ứng đẹp cho led....
Dùng khi cần nhiều chân GPIO, nếu chỉ 1 2 chân thì ráng làm chân PWM đi.
2) Cách thực hiện: nói ví dụ cho dễ hiểu, không chơi lý thuyết nữa
a/Cách 1:
_cho 1 timer A vào ngắt sau 1 khoảng thời gian nhỏ (lấy 0,1ms đi)
_chọn 1 khoảng cycle =timer A x độ phân giải, cycle phải đủ nhỏ để tạo thời gian lưu ảnh trong mắt (ví dụ pwm 32 bit thì lấy cycle là 3,2ms nhé.)
_cần xuất ra 2 port 1.0 giá trị 12/32, 1.1 giá trị 25/32 trong 10s.
_bắt đầu: khi vào ngắt timer, ta thực hiện tuần tự như sau
__dem++; //tăng dem lên 1
__if (dem==32) dem = 0; //khi hết 3,2ms thì reset.
__if (dem<12) P1.0=1, else P1.0=0;
__if (dem<25) P1.1=1, else P1.1=0;
__thoát ngắt.
_nên dùng 1 timer B để sau 10s dừng timer A lại và tắt cả 2 port đi, không nên dùng chỉ 1 timer(sẽ giải thích sau).
Nhận xét:
_không phức tạp
_tạo được xung PWM y hết như chân PWM tạo ra(giống cái hình 500đ ở trên kia)
_tuy vậy,độ phân giải càng cao thì phải vào ngắt càng nhiều, mỗi lần vào ngắt phải check tất cả các chân -> càng nhiều chân càng nhiều lệnh if, càng tăng số chu kỳ máy tiêu tốn cho 1 lần vào ngắt -> thời gian thực tế vào ngắt 1 lần > thời gian mong muốn.
_code như thế này người ta copy của mình dễ ợt
Thế nên phương pháp anh Hải hướng dẫn sẽ giải quyết được những vấn đề ở trên theo 1 cách rất pro:
b/Cách 2: cho là đề bài cũng như trên, 2 bước đầu thực hiện hoàn toàn tương tự
_Thực hiện trong 1 ngắt của timer khác việc huyển 2 giá trị pwm 12 và 25 (lưu trong 2 biến pwm1,pwm2) về nhị phân bằng cách của anh Hoàng
__lưu giá trị nhị phân vào mảng -> chỉ cần xuất giá trị của 1 phần tử trong mảng khi vào ngắt cần kiểm tra -> ít tốn chu kỳ máy.
__thực hiện and với mask rồi xuất luôn -> tốn chu kỳ máy hơn do phải thực hiện lệnh and và sau đó mới xuất.
mà chu kỳ máy rất quan trọng do timer A có giá trị nhỏ -> có câu trả lời cho anh Hoàng rồi nhé. kết quả là 12-> 01100; 25-> 11001,lưu vào mảng pwm1[0:4] và pwm2[0:4].
_bắt đầu tạo xung PWM: khi vào ngắt ta thực hiện tuần tự
__dem++; //tăng dem lên 1
__xài switch case biến dem tại các mốc có giá trị là 2^n(với n là số bit của xung PWM)
xét ví dụ PWM có giá trị max là 32-> 5bit:
___case 1: xuất bit pwm1[0] ra P1.0, pwm2[0] ra P1.1 -> P1.0 tắt, P1.1 sáng
___case 2: xuất bit pwm1[1] ra P1.0, pwm2[1] ra P1.1 -> P1.0 tắt, P1.1 tắt
___case 4: xuất bit pwm1[2] ra P1.0, pwm2[2] ra P1.1 -> P1.0 sáng, P1.1 tắt
___case 8: xuất bit pwm1[3] ra P1.0, pwm2[3] ra P1.1 -> P1.0 sáng, P1.1 sáng
___case 16:xuất bit pwm1[4] ra P1.0, pwm2[4] ra P1.1 -> P1.0 tắt, P1.1 sáng
___case 31:reset dem về 0.(vì còn 1 khoảng 0.1ms khi đếm từ 0->1 nên ta chọn giá trị 31)
__thoát ngắt
_nên dùng timer B để định sau 10s tắt các chân P1.0 ,P1.1 và dừng timer A vì: vào ngắt phải chạy 1 số lệnh tại các mốc như trên, đặc biệt càng nhiều port thì càng sai lệch về thời gian -> nếu dùng để định 1 khoảng thời gian bằng vài chục ~ vài trăm ngàn lần timer A thì sai số sẽ rất đáng kể.
Nhận xét:
_Số lần kiểm tra, xuất giá trị, vào ngắt,...nói chung làm những việc vô công rỗi nghề ít -> ít sai số về thời gian
_điều khiển được nhiều chân hơn giải thuật kia
_code nhìn vào là ngại đọc ngay'+_+
3)Kết: nhìn chung các giải thuật trên đầu bị hạn chế về độ phân giải, đòi hỏi VXL phải mạnh để hiển thị được độ phân giải cao, sự chính xác trong phát xung PWM không thể nào so sánh được với chân PWM gốc của vi điều khiển nên mình khuyến khích sử dụng các IC PWM driver trong các ứng dụng cần sự chính xác.
Mời các bô lão vào ném gạch Còn thằng G-Force nào nữa mà hỏi mình không giải thích nữa đâu )
Chả là cái này mình nghe được từ anh Hải hồi training RYA vòng 1, dạo gần đây thấy nhiều anh em G-Force hỏi quá nên lên đây lập cái thread mời các bô lão vào thảo luận.)
Về định nghĩa PWM là gì, cũng như cách tạo PWM bằng chân PWM của PIC(hoặc các dòng microcontroller khác) xin không nhắc lại.
500đ hình cho nó bớt chán:
1) Mục đích: cách code cho các chân GPIO để tạo xung PWM, giúp có thể tận dụng nhiều chân vi điều khiển nói chung trong việc làm hiệu ứng đẹp cho led....
Dùng khi cần nhiều chân GPIO, nếu chỉ 1 2 chân thì ráng làm chân PWM đi.
2) Cách thực hiện: nói ví dụ cho dễ hiểu, không chơi lý thuyết nữa
a/Cách 1:
_cho 1 timer A vào ngắt sau 1 khoảng thời gian nhỏ (lấy 0,1ms đi)
_chọn 1 khoảng cycle =timer A x độ phân giải, cycle phải đủ nhỏ để tạo thời gian lưu ảnh trong mắt (ví dụ pwm 32 bit thì lấy cycle là 3,2ms nhé.)
_cần xuất ra 2 port 1.0 giá trị 12/32, 1.1 giá trị 25/32 trong 10s.
_bắt đầu: khi vào ngắt timer, ta thực hiện tuần tự như sau
__dem++; //tăng dem lên 1
__if (dem==32) dem = 0; //khi hết 3,2ms thì reset.
__if (dem<12) P1.0=1, else P1.0=0;
__if (dem<25) P1.1=1, else P1.1=0;
__thoát ngắt.
_nên dùng 1 timer B để sau 10s dừng timer A lại và tắt cả 2 port đi, không nên dùng chỉ 1 timer(sẽ giải thích sau).
Nhận xét:
_không phức tạp
_tạo được xung PWM y hết như chân PWM tạo ra(giống cái hình 500đ ở trên kia)
_tuy vậy,độ phân giải càng cao thì phải vào ngắt càng nhiều, mỗi lần vào ngắt phải check tất cả các chân -> càng nhiều chân càng nhiều lệnh if, càng tăng số chu kỳ máy tiêu tốn cho 1 lần vào ngắt -> thời gian thực tế vào ngắt 1 lần > thời gian mong muốn.
_code như thế này người ta copy của mình dễ ợt
Thế nên phương pháp anh Hải hướng dẫn sẽ giải quyết được những vấn đề ở trên theo 1 cách rất pro:
b/Cách 2: cho là đề bài cũng như trên, 2 bước đầu thực hiện hoàn toàn tương tự
_Thực hiện trong 1 ngắt của timer khác việc huyển 2 giá trị pwm 12 và 25 (lưu trong 2 biến pwm1,pwm2) về nhị phân bằng cách của anh Hoàng
có điều ở đây nếu chúng ta:Ví dụ khi i=2^(4) thì em sẽ kiểm tra bit thứ 4 của biến dutycycle bằng lệnh and với 1 mặt nạ (1<<4) xem kết quả là 0 hay 1 -> bit đó là 0 hay 1.
__lưu giá trị nhị phân vào mảng -> chỉ cần xuất giá trị của 1 phần tử trong mảng khi vào ngắt cần kiểm tra -> ít tốn chu kỳ máy.
__thực hiện and với mask rồi xuất luôn -> tốn chu kỳ máy hơn do phải thực hiện lệnh and và sau đó mới xuất.
mà chu kỳ máy rất quan trọng do timer A có giá trị nhỏ -> có câu trả lời cho anh Hoàng rồi nhé. kết quả là 12-> 01100; 25-> 11001,lưu vào mảng pwm1[0:4] và pwm2[0:4].
_bắt đầu tạo xung PWM: khi vào ngắt ta thực hiện tuần tự
__dem++; //tăng dem lên 1
__xài switch case biến dem tại các mốc có giá trị là 2^n(với n là số bit của xung PWM)
xét ví dụ PWM có giá trị max là 32-> 5bit:
___case 1: xuất bit pwm1[0] ra P1.0, pwm2[0] ra P1.1 -> P1.0 tắt, P1.1 sáng
___case 2: xuất bit pwm1[1] ra P1.0, pwm2[1] ra P1.1 -> P1.0 tắt, P1.1 tắt
___case 4: xuất bit pwm1[2] ra P1.0, pwm2[2] ra P1.1 -> P1.0 sáng, P1.1 tắt
___case 8: xuất bit pwm1[3] ra P1.0, pwm2[3] ra P1.1 -> P1.0 sáng, P1.1 sáng
___case 16:xuất bit pwm1[4] ra P1.0, pwm2[4] ra P1.1 -> P1.0 tắt, P1.1 sáng
___case 31:reset dem về 0.(vì còn 1 khoảng 0.1ms khi đếm từ 0->1 nên ta chọn giá trị 31)
__thoát ngắt
_nên dùng timer B để định sau 10s tắt các chân P1.0 ,P1.1 và dừng timer A vì: vào ngắt phải chạy 1 số lệnh tại các mốc như trên, đặc biệt càng nhiều port thì càng sai lệch về thời gian -> nếu dùng để định 1 khoảng thời gian bằng vài chục ~ vài trăm ngàn lần timer A thì sai số sẽ rất đáng kể.
Nhận xét:
_Số lần kiểm tra, xuất giá trị, vào ngắt,...nói chung làm những việc vô công rỗi nghề ít -> ít sai số về thời gian
_điều khiển được nhiều chân hơn giải thuật kia
_code nhìn vào là ngại đọc ngay'+_+
3)Kết: nhìn chung các giải thuật trên đầu bị hạn chế về độ phân giải, đòi hỏi VXL phải mạnh để hiển thị được độ phân giải cao, sự chính xác trong phát xung PWM không thể nào so sánh được với chân PWM gốc của vi điều khiển nên mình khuyến khích sử dụng các IC PWM driver trong các ứng dụng cần sự chính xác.
Mời các bô lão vào ném gạch Còn thằng G-Force nào nữa mà hỏi mình không giải thích nữa đâu )