LVGL 对接 EC11
EC11旋转编码器驱动
本驱动核心:
滚动检测:利用检测滚动一次整个周期的 AB 电平变化来判断滚动一次
读取滚动方向函数:用于对接 LVGL。
#include "ec11.h"
#include "driver/gpio.h"
int8_t enc = 0;
int FLAG = 0; //一次变化标志位
bool CW_1 = 0; //电平 1
bool CW_2 = 0; //电平 2
static void IRAM_ATTR intrHandler_ENCA(void *arg) {
int ALV = gpio_get_level(ENCA);
int BLV = gpio_get_level(ENCB);
//第一次中断,如果是上升沿,记录 B 的电平
if (FLAG == 0 && ALV == 1) {
FLAG = 1;
CW_1 = BLV;
}
//第二次中断,如果是下降沿,记录 B 的电平
if (FLAG == 1 && ALV == 0) {
FLAG = 0;
CW_2 = BLV;
if (CW_1 == 0 && CW_2 == 1) {
enc++;
} else if (CW_1 == 1 && CW_2 == 0) {
enc--;
}
}
}
void ec11Init() {
gpio_config_t enc_config = {
.pin_bit_mask = 1UL << ENCA,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_ANYEDGE
};
gpio_config(&enc_config);
gpio_install_isr_service(0);
gpio_isr_handler_add(ENCA, intrHandler_ENCA, (void *) ENCA);
enc_config.pin_bit_mask = 1UL << ENCB,
enc_config.intr_type = GPIO_INTR_DISABLE;
gpio_config(&enc_config);
gpio_config_t btn_config = {
.pin_bit_mask = (1ULL << BTN_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&btn_config);
}
int16_t ec11Read() {
if (enc > 4) {
enc = 1;
} else if (enc < -4) {
enc = -1;
}
return enc;
}
void ec11Reset() {
enc = 0;
}
int ec11State() {
return !gpio_get_level(BTN_PIN);
}
#ifndef ROTARY_ENCODER_H
#define ROTARY_ENCODER_H
#include <stdint-gcc.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ENCA 10
#define ENCB 6
#define BTN_PIN 9
//外部中断定义
void ec11Init();
int16_t ec11Read();
void ec11Reset();
int ec11State();
#ifdef __cplusplus
}
#endif
#endif
驱动对接
整体流程:
初始化编码器驱动,其中注册了读取编码器方向回调
编码器回调定时自动调用检测编码器方向(-1,0,1)。当然检测完需要把编码器归零,否则一直 检测在滚动。
void lv_port_indev_init(void) {
static lv_indev_drv_t indev_drv;
encoder_init();
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_ENCODER;
indev_drv.read_cb = encoder_read;
indev_encoder = lv_indev_drv_register(&indev_drv);
}
static void encoder_init(void) {
ec11Init();
}
//本函数自动定时调用检测,利用enc_diff的正负来选择上一个下一个对象
static void encoder_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) {
data->enc_diff = ec11Read();
data->state = ec11State();
ec11Reset();//清除实时编码器数值
}
使用
创建组
创建 3 个按钮加入组
void ui_Screen1_screen_init(void){
//创建屏幕对象
ui_Screen1 = lv_obj_create(NULL);
lv_obj_clear_flag( ui_Screen1, LV_OBJ_FLAG_SCROLLABLE );
//创建一个组链接实体按键
lv_group_t * group = lv_group_create();
lv_indev_set_group(indev_encoder, group);
//创建按钮1
lv_obj_t * button1 = lv_btn_create(ui_Screen1);
lv_obj_set_size(button1, 60, 35);
lv_obj_set_pos(button1, 120, 20);
lv_obj_add_event_cb(button1, btn_event_cb, LV_EVENT_ALL, NULL);
//创建按钮2
lv_obj_t * button2 = lv_btn_create(ui_Screen1);
lv_obj_set_size(button2, 60, 35);
lv_obj_set_pos(button2, 120, 80);
lv_obj_add_event_cb(button2, btn_event_cb, LV_EVENT_ALL, NULL);
//创建按钮3
lv_obj_t * button3 = lv_btn_create(ui_Screen1);
lv_obj_set_size(button3, 60, 35);
lv_obj_set_pos(button3, 120, 160);
lv_obj_add_event_cb(button3, btn_event_cb, LV_EVENT_ALL, NULL);
//链接实体按键
lv_group_add_obj(group, button2);
lv_group_add_obj(group, button1);
lv_group_add_obj(group, button3);
}
现象
滚动选择 3 个按键,单机确认
延伸拓展
任何包含按钮的例子都可以用本方法对接实体
static void scroll_event_cb(lv_event_t * e)
{
lv_obj_t * cont = lv_event_get_target(e);
lv_area_t cont_a;
lv_obj_get_coords(cont, &cont_a);
lv_coord_t cont_y_center = cont_a.y1 + lv_area_get_height(&cont_a) / 2;
lv_coord_t r = lv_obj_get_height(cont) * 7 / 10;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(cont);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(cont, i);
lv_area_t child_a;
lv_obj_get_coords(child, &child_a);
lv_coord_t child_y_center = child_a.y1 + lv_area_get_height(&child_a) / 2;
lv_coord_t diff_y = child_y_center - cont_y_center;
diff_y = LV_ABS(diff_y);
/*Get the x of diff_y on a circle.*/
lv_coord_t x;
/*If diff_y is out of the circle use the last point of the circle (the radius)*/
if(diff_y >= r) {
x = r;
}
else {
/*Use Pythagoras theorem to get x from radius and y*/
uint32_t x_sqr = r * r - diff_y * diff_y;
lv_sqrt_res_t res;
lv_sqrt(x_sqr, &res, 0x8000); /*Use lvgl's built in sqrt root function*/
x = r - res.i;
}
/*Translate the item by the calculated X coordinate*/
lv_obj_set_style_translate_x(child, x, 0);
/*Use some opacity with larger translations*/
lv_opa_t opa = lv_map(x, 0, r, LV_OPA_TRANSP, LV_OPA_COVER);
lv_obj_set_style_opa(child, LV_OPA_COVER - opa, 0);
}
}
/**
* Translate the object as they scroll
*/
extern lv_indev_t * indev_encoder;
void lv_example_scroll_6(void){
lv_group_t * group = lv_group_create();
lv_indev_set_group(indev_encoder, group);
lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 200, 200);
lv_obj_center(cont);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
lv_obj_add_event_cb(cont, scroll_event_cb, LV_EVENT_SCROLL, NULL);
lv_obj_set_style_radius(cont, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_clip_corner(cont, true, 0);
lv_obj_set_scroll_dir(cont, LV_DIR_VER);
lv_obj_set_scroll_snap_y(cont, LV_SCROLL_SNAP_CENTER);
lv_obj_set_scrollbar_mode(cont, LV_SCROLLBAR_MODE_OFF);
uint32_t i;
for(i = 0; i < 20; i++) {
lv_obj_t * btn = lv_btn_create(cont);
lv_obj_set_width(btn, lv_pct(100));
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text_fmt(label, "Button %"LV_PRIu32, i);
lv_group_add_obj(group, btn);
}
/*Update the buttons position manually for first*/
lv_event_send(cont, LV_EVENT_SCROLL, NULL);
/*Be sure the fist button is in the middle*/
lv_obj_scroll_to_view(lv_obj_get_child(cont, 0), LV_ANIM_OFF);
}
void LCD(void *arg) {
lv_example_scroll_6();
while (vTaskDelay(1), true) {
lv_task_handler();
GC9A01_Update();
}
}
scroll_event_cb()为了在滚动时候让聚焦的按钮在中央,并且按钮有滚动的效果,只是界面显示回调,不包含实体按钮对接过程,对接实体按钮还是一样的流程,注册组,加入组。
License:
CC BY 4.0