Bài 4 - Điểu chỉnh độ sáng của đèn LED bằng phương pháp PWM
Chương trình của chúng ta sẽ
hoạt động như sau: độ sáng đèn LED sẽ tăng dần đến max, sau đó độ sáng từ max sẽ
giảm dần về 0, cứ thế lặp lại
Sơ đồ mạch như sau:
Để chiều chỉnh độ sáng của đèn LED ta thay đổi điện áp đặt vào nó. Các chân GPIO trên ESP8266 chỉ có thể đặt điện áp ở đầu ra là 0V hoặc 3,3V, không thể cài đặt điện áp ở đầu ra nằm ở giá trị giữa 0V và 3,3V (ví dụ 1V, 1,4V, 2V .. để thay đổi độ sáng). Tuy nhiên, có thể tạo ra điện áp "giả" ở giá trị giữa 0V và 3,3V bằng cách sử dụng điều chế độ rộng xung (PWM). Nếu bật(đặt điện áp 3,3V vào LED) và tắt(đặt điện áp 0V vào LED) đèn LED rất nhanh (tần số bật tắt khoảng 42Hz), mắt của chúng ta không thể theo kịp tốc độ bật và tắt của đèn LED, chúng ta sẽ chỉ thấy sự thay về độ sáng (nếu bật tắt chậm dưới 42Hz, chúng ta sẽ chỉ thấy LED đang nhấp nháy).
Duty cycle là khoảng thời gian LED được bật, hình minh họa cách hoạt động
của PWM
Duty
cycle 50% cho kết quả độ sáng của LED là 50%, duty cycle 0% cho độ sáng LED 0%,
duty cycle 100% cho độ sáng LED là 100%. Thay đổi duty cycle sẽ điều chỉnh được độ sáng của LED.
Để khởi tạo PWM trên ESP8266 chúng
ta dùng hàm sau
trong đó:
- uint32 period: là chu kỳ PWM tính bằng μs
-
uint8 *duty: duty cycle của từng channel
-
uint32 pwm_channel_num: số channel PWM muốn sử dụng
- uint32
(*pin_info_list)[3]: tham số GPIO cho các channel, đây là n *
3 array (n là số channel muốn sử dụng), array[3] gồm 3 tham số {GPIO register,
GPIO function, GPIO number} xem hình ở bài
trước.
ví dụ: chúng ta dùng GPIO2 làm 1 channel PWM như
sau:
uint32
io_info [1][3] = {PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2, 2};.
Còn muốn dùng
GPIO2, GPIO5, GPIO12 làm 3 channel PWM thì:
uint32
io_info [3][3] = {{PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2, 2}, {PERIPHS_IO_MUX_GPIO5_U,FUNC_GPIO5,5},
{PERIPHS_IO_MUX_MTDI_U,FUNC_GPIO12,12} };
Sau khi cài đặt
các thông số ta gọi pwm_start();
Để set duty cylce ta dùng hàm sau:
trong đó:
- uint32 duty: giá trị
duty muốn xuất ra, dãi giá trị duty phụ thuộc vào chu kỳ (period), max duty = Period
* 1000 /45.
- uint8 channel: channel PWM
muốn xuất ra, nếu dung 3 channel PWM thì các giá trị channel có thể là 0, 1, 2.
Copy
thư mục 3-blinkLED ,
đổi tên thành 4-PWM-LED sau đó mở Eclipse import vào (xem hướng dẫn import ở bài trước).
Mở file Makefile trong thư mục 4-PWM-LED/app, thêm dòng -lpwm \ như hình bên dưới
Chỉnh sửa file user_main.c trong thư mục 4-PWM-LED/app/user có nội
dung toàn bộ như sau:
#include "osapi.h"
#include "user_interface.h"
#include "gpio.h"
#include "ets_sys.h"
#include "pwm.h"
static os_timer_t pwm_timer; //khởi tạo soft timer cho pwm
uint32
io_info [1][3] = {PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12, 12};//GPIO12 cho channel 1
uint32
period = 1000; // chu
kỳ 1000us
int32
duty[1] = {0};//khai
báo duty cho 1 channel
int32
max_duty = 1000*1000/45; //công
thức tính max_duty = period*1000/45
uint32
pwm_channel_num = 1; //chỉ
dùng 1 channel pwm
int32
percent = 10; //phần
trăm tỷ lệ tăng hoặc giảm độ sáng
int incre_or_decre = 1; // 1 tăng, -1 giảm
void pwm_cb(void)
{
pwm_set_duty(duty[0],
0); // cài đặt giá
trị duty cho channel 0
pwm_start();
duty[0] =
duty[0] + (max_duty*percent/100)* incre_or_decre;
if (duty[0] > max_duty) {
duty[0]
= max_duty;
incre_or_decre
= -1; // giảm duty xuống
}
if (duty[0] < 0) {
duty[0]
= 0;
incre_or_decre
= 1; // tăng duty lên
}
}
void ICACHE_FLASH_ATTR
user_init(void)
{
uart_init(115200,115200);
os_printf("\n=======================================\n");
os_printf("\t\t SDK version: %s \n", system_get_sdk_version());
os_printf("\n=======================================\n");
pwm_init(period, duty, pwm_channel_num,
io_info); //khởi tạo PWM
pwm_start();
os_timer_disarm(&pwm_timer); //Stop pwm_timer
os_timer_setfn(&pwm_timer, (os_timer_func_t *)pwm_cb, NULL);//cài đặt hàm được
gọi khi pwm_timer đếm xong
os_timer_arm(&pwm_timer, 250, true);//kích hoạt led_timer với 500ms,
true có nghĩa là lặp đi lặp lại
timer 500ms này
}
/******************************************************************************
* FunctionName
: user_rf_cal_sector_set
* Description : SDK just reversed 4 sectors, used for rf
init data and paramters.
* We add this function to force
users to set rf cal sector, since
* we don't know which sector is
free in user's application.
* sector map for last several
sectors : ABCCC
* A : rf cal
* B : rf init data
* C : sdk parameters
*
Parameters : none
* Returns : rf cal sector
*******************************************************************************/
uint32
ICACHE_FLASH_ATTR
user_rf_cal_sector_set(void)
{
enum flash_size_map size_map =
system_get_flash_size_map();
uint32 rf_cal_sec = 0;
switch (size_map) {
case FLASH_SIZE_4M_MAP_256_256:
rf_cal_sec = 128 - 5;
break;
case FLASH_SIZE_8M_MAP_512_512:
rf_cal_sec = 256 - 5;
break;
case FLASH_SIZE_16M_MAP_512_512:
case FLASH_SIZE_16M_MAP_1024_1024:
rf_cal_sec = 512 - 5;
break;
case FLASH_SIZE_32M_MAP_512_512:
case FLASH_SIZE_32M_MAP_1024_1024:
rf_cal_sec = 1024 - 5;
break;
case FLASH_SIZE_64M_MAP_1024_1024:
rf_cal_sec = 2048 - 5;
break;
case FLASH_SIZE_128M_MAP_1024_1024:
rf_cal_sec = 4096 - 5;
break;
default:
rf_cal_sec = 0;
break;
}
return rf_cal_sec;
}
void ICACHE_FLASH_ATTR
user_rf_pre_init(void)
{
}
Biên dịch vào nạp vào ESP8266, xem bài trước có hướng dẫn nạp.
Nhận xét
Đăng nhận xét