Bài tập stm32 làm cho đèn sáng lần lượt năm 2024

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

int i,y;

while(1)

{

//làm led sáng dần lên

for(i\=0;i<=10;i++) //vòng lặp for này sẽ làm thay đổi độ rộng xung

{

for(y\=0;y<100;y++) //vòng lặp for này tạo ra 1 độ trễ nhất định, chính là tốc độ mà đèn sáng dần lên đó

{

P0_0 \= 1;

delay_ms(i);

P0_0 \= 0;

delay_ms(10-i);

}

}

//làm led tối dần đi

for(i\=10;i\>=0;i--) //vòng lặp for này sẽ làm thay đổi độ rộng xung

{

for(y\=0;y<100;y++) //vòng lặp for này tạo ra 1 độ trễ nhất định, chính là tốc độ mà đèn tối dần đó

{

P0_0 \= 1;

delay_ms(i);

P0_0 \= 0;

delay_ms(10-i);

}

}

}

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

int mucsangmax \= 10; //mức sáng tối đa mà ta muốn

int dem; // 1 biến con chạy

int dosangLED \= 5; // LED sẽ sáng ở mức số 5 ( khoảng 50%)

void T1_ISR() interrupt 3 // Dung timer 1 để tạo PWM ( 1ms tự gọi 1 lần)

{

TH1\=0xFC; // Nap gia tri cho TH1

TL1\=0x17; // Nap gia tri cho TL1 (đếm 1000 xung sẽ tràn, thạch anh 12Mhz)

if(dosangLED > dem) P0_0 \= 1; //xuất mức 1 nếu con chạy vẫn nhỏ hơn mức sáng cài đặt

else P0_0 \= 0;

dem++;if(dem>mucsangmax)dem\=0;

}

Đ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

Bài tập stm32 làm cho đèn sáng lần lượt năm 2024
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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

include <REGX52.H>

define led1 P2_0

void delay_ms(unsigned int t) //hàm delay

{

unsigned int x,y;

for(x\=0;x<t;x++)

{

for(y\=0;y<123;y++);

}

}

int mucsangmax \= 10; //mức sáng tối đa mà ta muốn

int dem; // 1 biến con chạy

int dosangLED \= 5; // LED sáng sáng mức số 5 ( khỏang 50%)

void T1_ISR() interrupt 3 // Dung timer 1 ngắt tạo pwm)

{

TH1\=0xFC; // Nap gia tri cho TH1

TL1\=0x17; // Nap gia tri cho TL1 (d?m 1000 xung s? tràn, th?ch anh 12Mhz)

if(dosangLED \> dem) led1 \= 1; //xuất mức 1 nếu độ sáng lớn hơn biến con chạy

else led1 \= 0; //ngược lại xuất mức 0

dem++;if(dem\>mucsangmax)dem\=0; //nếu dem vượt quá độ sáng tối đa thì hết 1 chu kì quét, reset lại

}

void main()

{

int i;

TMOD\=0x10; // khoi tao ngat T1, 16bit

ET1\=1; // cho phep ngat T1

TF1\=0; // xoa co ngat T1

TR1\=1; // khoi dong T1

EA \= 1; // cho phep ngat toan cuc

while(1)

{

for(i\=0;i<=10;i++) //vòng lặp sáng dần

{

dosangLED\=i;

delay_ms(100);

}

for(i\=10;i\>=0;i--) //vòng lặp tối dần

{

dosangLED\=i;

delay_ms(100);

}

}

}

Đó 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 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

unsigned char led[8]\={1,3,6,9,13,17,25,30}; //do sang cua 8 LED

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

define led1 P2_0

define led2 P2_1

define led3 P2_2

define led4 P2_3

define led5 P2_4

define led6 P2_5

define led7 P2_6

define led8 P2_7

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

void T1_ISR() interrupt 3 // Dung timer 1 tao PWM 0.5ms goi 1 lan)

{

if(led[0] \> dem) led1 \= bat; //xuat du lieu ra chan LED 1

else led1 \= tat;

if(led[1] \> dem) led2 \= bat; //xuat du lieu ra chan LED 2

else led2 \= tat;

if(led[2] \> dem) led3 \= bat; //xuat du lieu ra chan LED 3

else led3 \= tat;

if(led[3] \> dem) led4 \= bat; //xuat du lieu ra chan LED 4

else led4 \= tat;

if(led[4] \> dem) led5 \= bat; //xuat du lieu ra chan LED 5

else led5 \= tat;

if(led[5] \> dem) led6 \= bat; //xuat du lieu ra chan LED 6

else led6 \= tat;

if(led[6] \> dem) led7 \= bat; //xuat du lieu ra chan LED 7

else led7 \= tat;

if(led[7] \> dem) led8 \= bat; //xuat du lieu ra chan LED 8

else led8 \= tat;

dem++;if(dem\>mucsangmax)dem\=0;

TH1\=0xFE; // Nap gia tri cho TH1

TL1\=0x0B; // Nap gia tri cho TL1 (dem 500 xung tràn, thach anh 12Mhz)

}

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:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

include <REGX52.H>

define led1 P2_7

define led2 P2_6

define led3 P2_5

define led4 P2_4

define led5 P2_3

define led6 P2_2

define led7 P2_1

define led8 P2_0

define bat 0

define tat 1

void delay_ms(unsigned int t) //hàm delay

{

unsigned int x,y;

for(x\=0;x<t;x++)

{

for(y\=0;y<123;y++);

}

}

int mucsangmax \= 30; //muc sáng toi da mà ta muon = 30

int dem; // 1 bien con chay

unsigned char led[8]\={1,3,6,9,13,17,25,30}; //do sang cua 8 LED

void T1_ISR() interrupt 3 // Dung timer 1 d? t?o PWM 1ms g?i 1 l?n)

{

if(led[0] \> dem) led1 \= bat; //xuat du lieu ra chan LED 1

else led1 \= tat;

if(led[1] \> dem) led2 \= bat; //xuat du lieu ra chan LED 2

else led2 \= tat;

if(led[2] \> dem) led3 \= bat; //xuat du lieu ra chan LED 3

else led3 \= tat;

if(led[3] \> dem) led4 \= bat; //xuat du lieu ra chan LED 4

else led4 \= tat;

if(led[4] \> dem) led5 \= bat; //xuat du lieu ra chan LED 5

else led5 \= tat;

if(led[5] \> dem) led6 \= bat; //xuat du lieu ra chan LED 6

else led6 \= tat;

if(led[6] \> dem) led7 \= bat; //xuat du lieu ra chan LED 7

else led7 \= tat;

if(led[7] \> dem) led8 \= bat; //xuat du lieu ra chan LED 8

else led8 \= tat;

dem++;if(dem\>mucsangmax)dem\=0;

TH1\=0xFE; // Nap gia tri cho TH1

TL1\=0x0B; // Nap gia tri cho TL1 (dem 500 xung tràn, thach anh 12Mhz)

}

void main()

{

int i;

TMOD\=0x10; // khoi tao ngat T1, 16bit

ET1\=1; // cho phep ngat T1

TF1\=0; // xoa co ngat T1

TR1\=1; // khoi dong T1

EA \= 1; // cho phep ngat toan cuc

while(1)

{

}

}

Bài tập stm32 làm cho đèn sáng lần lượt năm 2024
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.

const unsigned char data_led[8]\={1,3,6,9,13,17,25,30}; //do sang luu tru

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

for(k\=7;k\>-12;k--) //-12 la khoang cach giua cac tia sao bang, cang lon khoang cach cang xa

{

for(i\=7;i\>0;i--)led[i]\=led[i-1]; //dich array led sang phai >> 1 lan

if(k\>=0)led[0] \= data_led[k]; // lay do sang trong data_led ghi vao led[0]

else led[0]\=0; //tat led 0

delay_ms(50);

}

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