汇顶GR5526蓝牙芯片学习记录1(页面逻辑)-CSDN博客
本文目的是记录我如何使用5526的PWM引脚来驱动RGB彩灯,以及通过LVGL的slider来调整RGB的颜色
一、PWM相关函数介绍
首先先讲解一下5526的SK开发板如何配置PWM,我们可以通过下面这个链接来了解PWM的配置
GR5xx APP驱动用户手册 | 汇顶科技 (goodix.com)
下面是几个待会会用到的函数,其它函数暂时用不到就不放了
1、app_pwm_init
函数原型 | uint16_t app_pwm_init(app_pwm_params *p_params) |
功能说明 | 根据初始化参数初始化PWM模块 |
输入参数 | p_params:初始化参数的结构体指针,把结构体里的初始化变量传入该函数 |
返回值 | APP_DRV_xxx:详见SDK_Folder\drivers\inc\app_drv_error.h宏定义 |
备注 | 无 |
2、app_pwm_start
函数原型 | uint16_t app_pwm_start(app_pwm_id_t id) |
功能说明 | PWM开始运行 |
输入参数 | id:指定需要运行的PWM模块(0或1) |
返回值 | APP_DRV_xxx:详见SDK_Folder\drivers\inc\app_drv_error.h宏定义 |
备注 | 无 |
3、app_pwm_config_channel
函数原型 | uint16_t app_pwm_config_channel(app_pwm_id_t id, app_pwm_active_channel_t channel, app_pwm_channel_init_t *p_config) |
功能说明 | 配置PWM通道参数,包括占空比、极性和停止电平 |
输入参数 | id:指定需要配置的PWM模块(0或1) channel:需要配置的通道选择 p_config:需要配置的参数,包括占空比、极性和停止电平 |
返回值 | APP_DRV_xxx:详见SDK_Folder\drivers\inc\app_drv_error.h宏定义 |
备注 | 在PWM运行和停止时均可调用该接口 |
二、PWM驱动代码编写
1、初始化PWM相关的接口
第一步肯定是初始化PWM相关的接口,首先我选择在board_sk.c这个文件编写初始化代码,下面这部分是对app_pwm_params_t这个结构体的一些配置。
app_pwm_params_t pwm_params = {
/* 1、PWM module ID选择APP_PWM0_MODULE, 因为我们使用的引脚MSIO0, MSIO1, MSIO2对应的是PWM0 */
.id = APP_PWM0_MODULE,
/* 2、配置PWM各通道IO属性 */
.pin_cfg = {
.channel_a = {
.type = APP_PWM0_GPIO_TYPE,//设置IO类型
.mux = APP_PWM0_GPIO_MUX,//设置IO映射功能
.pin = APP_PWM0_CHANNEL_A,//设置IO引脚
.pull = APP_IO_NOPULL,//设置IO无上下拉
.enable = APP_PWM_PIN_ENABLE,//开启PIN
},
.channel_b = {
.type = APP_PWM0_GPIO_TYPE,//设置IO类型
.mux = APP_PWM0_GPIO_MUX,//设置IO映射功能
.pin = APP_PWM0_CHANNEL_B,//设置IO引脚
.pull = APP_IO_NOPULL,//设置IO无上下拉
.enable = APP_PWM_PIN_ENABLE,//开启PIN
},
.channel_c = {
.type = APP_PWM0_GPIO_TYPE,//设置IO类型
.mux = APP_PWM0_GPIO_MUX,//设置IO映射功能
.pin = APP_PWM0_CHANNEL_C,//设置IO引脚
.pull = APP_IO_NOPULL,//设置IO无上下拉
.enable = APP_PWM_PIN_ENABLE,//开启PIN
},
},
/* 3、配置PWM激活的通道 */
.active_channel = APP_PWM_ACTIVE_CHANNEL_ALL,//PWM所有通道激活
/* 4、配置PWM初始化参数 */
.init = {
.mode = PWM_MODE_FLICKER,//FLICKER模式
.align = PWM_ALIGNED_EDGE,//三个通道的波形以PWM周期起始边沿对齐
.freq = 2000,//PWM频率
.bstoplvl = PWM_STOP_LVL_LOW,//PWM停止时为低电平
.channel_a = {
.duty = 0,//设置PWM占空比为0
.drive_polarity = PWM_DRIVEPOLARITY_NEGATIVE,//设置PWM输出模式下的驱动极性为负极性
.fstoplvl = PWM_STOP_LVL_LOW,//PWM在Flicker模式下,停止时的电平配置为低电平
},
.channel_b = {
.duty = 0,//设置PWM占空比为0
.drive_polarity = PWM_DRIVEPOLARITY_NEGATIVE,//设置PWM输出模式下的驱动极性为负极性
.fstoplvl = PWM_STOP_LVL_LOW,//PWM在Flicker模式下,停止时的电平配置为低电平
},
.channel_c = {
.duty = 0,//设置PWM占空比为0
.drive_polarity = PWM_DRIVEPOLARITY_NEGATIVE,//设置PWM输出模式下的驱动极性为负极性
.fstoplvl = PWM_STOP_LVL_LOW,//PWM在Flicker模式下,停止时的电平配置为低电平
},
},
};
void bsp_rgb_led_init(void)
{
/*初始化PWM*/
uint16_t ret = APP_DRV_SUCCESS;
ret = app_pwm_init(&pwm_params);
if (ret != APP_DRV_SUCCESS)
{
printf("\r\nPWM initial failed! Please check the input paraments.\r\n");
}
app_pwm_channel_init_t channel_cfg = {0};
app_pwm_start(APP_PWM0_MODULE);
channel_cfg.duty = 0;
ret = app_pwm_config_channel(APP_PWM0_MODULE, APP_PWM_ACTIVE_CHANNEL_ALL, &channel_cfg);
if (ret != APP_DRV_SUCCESS)
{
printf("\r\nPWM initial failed! Please check the input paraments.\r\n");
}
}
void board_init(void)
{
bsp_log_init();
bsp_key_init();
bsp_rgb_led_init();
// bsp_led_init(); // this causes external crystal not work for QFN68 CHIP. because LED takes up the crystal pin
}
2、rgb灯的相关逻辑设计
接下来是rgb灯的相关逻辑设计, 首先我们最终的目的是通过LVGL的slider去修改rgb灯的颜色,LVGL是一个任务,而PWM彩灯的控制我们也要新建一个彩灯任务去控制rgb灯
在main.c中可以看到lv_user_task_create()这个函数,
static void vStartTasks(void *arg)
{
lv_user_task_create();
osal_task_delete(NULL);
}
/**
*****************************************************************************************
* @brief main function
*****************************************************************************************
*/
int main(void)
{
app_graphics_adjust_dcore_policy(); /*<Should call this firstly if using graphics modules */
SetSerialClock(SERIAL_N96M_CLK);
app_periph_init(); /*<Init user periph .*/
// Initialize ble stack.
ble_stack_init(ble_evt_handler, &heaps_table);
osal_task_create("create_task", vStartTasks, 1024, 0, NULL);
osal_task_start();
for (;;); /*< Never perform here */
}
进去后我们照葫芦画瓢,也写一个rgb_led_task。
*/
void lv_user_task_create(void)
{
osal_task_create("task_indev", app_indev_read_task, TASK_SIZE_TOUCHPAD_READ, TASK_PRIO_TOUCHPAD_READ, &g_task_handle.indev_handle);
osal_task_create("task_gui", app_gui_render_task, TASK_SIZE_GUI_RENDER, TASK_PRIO_GUI_RENDER, &g_task_handle.gui_handle);
extern void lv_gui_evt_task_startup(void);
lv_gui_evt_task_startup();
/*create rgb task*/
extern void lv_rgb_led_evt_task_startup(void);
lv_rgb_led_evt_task_startup();
}
接下来是pwm_color_led.c的设计,总而言之就是创建一个任务和一个消息队列,在这个任务里面等待LVGL里面slider传过来的数据,从而改变RGB灯的颜色。
#include "pwm_color_led.h"
#include <stdio.h>
#include <string.h>
#include "app_log.h"
#include "app_pwm.h"
#include "board_SK.h"
#include "app_sys_manager.h"
#define APP_PWM_LED_R APP_PWM_ACTIVE_CHANNEL_A //红色所使用的PWM CHANNEL
#define APP_PWM_LED_G APP_PWM_ACTIVE_CHANNEL_B //绿色所使用的PWM CHANNEL
#define APP_PWM_LED_B APP_PWM_ACTIVE_CHANNEL_C //蓝色所使用的PWM CHANNEL
#define TASK_SIZE_RGB_LED 512u //rgb_led的任务栈大小
#define TASK_PRIO_RGB_LED 7 //rgb_led的任务优先级
// 定义一个全局的消息队列句柄
osal_queue_handle_t g_rgb_queue;
/*定义rgb结构体全局变量*/
rgb_message_t g_rgb_msg;
void app_rgb_led_task(void * args)
{
rgb_message_t rgb_data;
osal_base_type_t xRet = OSAL_SUCCESS;
while (1) {
// 从消息队列中读取 RGB 数据(阻塞模式)
xRet = osal_queue_receive(g_rgb_queue, &rgb_data, OSAL_MAX_DELAY);
if (OSAL_SUCCESS == xRet)
{
// 更新 PWM 占空比
update_pwm_rgb(rgb_data.red, rgb_data.green, rgb_data.blue);
}
}
}
void lv_rgb_led_evt_task_startup(void) {
/* 创建一个10个rgb_message_t大小的消息队列*/
osal_queue_create(&g_rgb_queue, 10, sizeof(rgb_message_t));
/* 创建app_rgb_led_task*/
osal_task_create("task_RGB_LED", app_rgb_led_task, TASK_SIZE_RGB_LED, TASK_PRIO_RGB_LED, &g_task_handle.rgb_led_handle);
return;
}
/*更新灯的颜色*/
void update_pwm_rgb(uint8_t red, uint8_t green, uint8_t blue)
{
app_pwm_channel_init_t channel_cfg = {0};
uint16_t ret = APP_DRV_SUCCESS;
// 配置红色通道
channel_cfg.duty = (red * 100) / 255; // 将 0-255 转换为 0-100%
ret = app_pwm_config_channel(APP_PWM0_MODULE, APP_PWM_LED_R, &channel_cfg);
if (ret != APP_DRV_SUCCESS) {
printf("\r\nPWM config_channel failed for RED! %d\r\n", __LINE__);
}
// 配置绿色通道
channel_cfg.duty = (green * 100) / 255; // 将 0-255 转换为 0-100%
ret = app_pwm_config_channel(APP_PWM0_MODULE, APP_PWM_LED_G, &channel_cfg);
if (ret != APP_DRV_SUCCESS) {
printf("\r\nPWM config_channel failed for GREEN! %d\r\n", __LINE__);
}
// 配置蓝色通道
channel_cfg.duty = (blue * 100) / 255; // 将 0-255 转换为 0-100%
ret = app_pwm_config_channel(APP_PWM0_MODULE, APP_PWM_LED_B, &channel_cfg);
if (ret != APP_DRV_SUCCESS) {
printf("\r\nPWM config_channel failed for BLUE! %d\r\n", __LINE__);
}
}
pwm_color_led.h就更简单了,放了一个结构体和一些外部引用的变量
#ifndef __PWM_COLOR_LED_H
#define __PWM_COLOR_LED_H
#include "stdint.h"
#include "osal.h"
/* struct */
typedef struct {
uint8_t red;
uint8_t green;
uint8_t blue;
} rgb_message_t;
/* extern variable */
extern osal_queue_handle_t g_rgb_queue;
extern rgb_message_t g_rgb_msg;
/*function*/
void update_pwm_rgb(uint8_t red, uint8_t green, uint8_t blue);
#endif
三、LVGL相关UI界面编写
1、首先我们需要在这个结构体数组里面加入自己的页面
const static lv_wms_wid_map_t g_win_id_map[] = {
{WMS_WID_NONE, lv_default_gesture_checker, lv_layout_none},
{WMS_WID_CARD_WATCHFACE, lv_default_gesture_checker, lv_card_watchface_vivid_layout_create},
{WMS_WID_CARD_STATUS_BAR, lv_default_gesture_checker, lv_card_status_layout_create},
{WMS_WID_CARD_APP_ENTRY, lv_default_gesture_checker, lv_card_list_entry_layout_create},
{WMS_WID_CARD_NOTIFICATION, lv_default_gesture_checker, lv_card_notification_layout_create},
{WMS_WID_CARD_DAY_ACTIVITY, lv_default_gesture_checker, lv_card_day_activity_layout_create},
{WMS_WID_CARD_HR, lv_default_gesture_checker, lv_card_layout_heartrate_create},
{WMS_WID_CARD_STRESS, lv_default_gesture_checker, lv_card_layout_stress_create},
{WMS_WID_CARD_SLEEP, lv_default_gesture_checker, lv_card_layout_sleep_create},
{WMS_WID_CARD_WEATHER, lv_default_gesture_checker, lv_card_layout_weather_create},
{WMS_WID_APP_LIST, lv_applist_gesture_checker, lv_layout_app_menu_create},
{WMS_WID_SETTING_ENTRY, lv_default_gesture_checker, lv_layout_setting_create},
{WMS_WID_SETTING_LIST_STYLE, lv_default_gesture_checker, lv_layout_setting_list_style_create},
{WMS_WID_SETTING_SWITCH_EFFECT, lv_default_gesture_checker, lv_layout_settings_switch_effect_create},
{WMS_WID_SETTING_CARD_STYLE, lv_default_gesture_checker, lv_layout_setting_cardflow_style_create},
{WMS_WID_POWER_OFF, lv_default_gesture_checker, lv_layout_pwr_off_layout_create},
/*add rgb_led view*/
{WMS_WID_SET_RGB_LED, lv_default_gesture_checker, lv_card_layout_rgb_led_create},
};
2、接下来是页面设计,并不复杂所以直接贴代码
#include <stdio.h>
#include "lvgl.h"
#include "lv_layout_router.h"
#include "lv_font.h"
#include "lv_user_font.h"
#include "lv_img_dsc_list.h"
#include "app_log.h"
#include "Freertos.h"
#include "queue.h"
#include "pwm_color_led.h"
#define RGB_LED_TITLE_X (108)
#define RGB_LED_TITLE_Y (20)
/*
* STATIC VARS DEFINITIONS
*****************************************************************************************
*/
static lv_obj_t* slider_r = NULL;
static lv_obj_t* slider_g = NULL;
static lv_obj_t* slider_b = NULL;
static lv_obj_t *slier_red_val = NULL;
static lv_obj_t *slier_green_val = NULL;
static lv_obj_t *slier_blue_val = NULL;
/*
* STATIC METHODS DECLARATION
*****************************************************************************************
*/
/*
* PUBLIC VARS DEFINITIONS
*****************************************************************************************
*/
/*
* STATIC METHODS IMPLEMENT
*****************************************************************************************
*/
static void slider_event_cb(lv_event_t * e)
{
lv_obj_t * slider = lv_event_get_target(e);
char buf[8];
if(slider == slider_r)
{
lv_snprintf(buf, sizeof(buf), "%d", (int)lv_slider_get_value(slider));
lv_label_set_text(slier_red_val, buf);
/*get red val from slider_r*/
g_rgb_msg.red = (uint8_t)lv_slider_get_value(slider);
}
else if(slider == slider_g)
{
lv_snprintf(buf, sizeof(buf), "%d", (int)lv_slider_get_value(slider));
lv_label_set_text(slier_green_val, buf);
/*get green val from slider_g*/
g_rgb_msg.green = (uint8_t)lv_slider_get_value(slider);
}
else if(slider == slider_b)
{
lv_snprintf(buf, sizeof(buf), "%d", (int)lv_slider_get_value(slider));
lv_label_set_text(slier_blue_val, buf);
/*get blue val from slider_b*/
g_rgb_msg.blue = (uint8_t)lv_slider_get_value(slider);
}
/* use osal_queue send color val */
if (g_rgb_queue != NULL) {
osal_base_type_t xRet = osal_queue_send(g_rgb_queue, &g_rgb_msg, OSAL_MAX_DELAY);
}
}
/*
* GLOBAL METHODS IMPLEMENT
*****************************************************************************************
*/
lv_obj_t *lv_card_layout_rgb_led_create(lv_obj_t *parent_tv_obj)
{
lv_obj_t *p_window = lv_obj_create(parent_tv_obj);
lv_obj_set_size(p_window, DISP_HOR_RES, DISP_VER_RES);
// rgb_led_title
lv_obj_t *rgb_led_title = lv_label_create(p_window);
lv_obj_set_style_text_font(rgb_led_title, &lv_font_montserrat_26, LV_STATE_DEFAULT); // 30
lv_label_set_text(rgb_led_title, "RGB_LED");
lv_obj_align(rgb_led_title, LV_ALIGN_TOP_MID, 0, 20);
/*********************************** slider_r ***********************************/
slider_r = lv_slider_create(p_window);
/* set the bg_color */
lv_obj_set_style_bg_color(slider_r, lv_color_make(255, 179, 179), LV_PART_MAIN);
lv_obj_set_style_bg_color(slider_r, lv_color_make(255, 0, 0), LV_PART_INDICATOR);
lv_obj_set_style_bg_color(slider_r, lv_color_white(), LV_PART_KNOB);
/* set the opa */
lv_obj_set_style_bg_opa(slider_r, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_bg_opa(slider_r, LV_OPA_COVER, LV_PART_INDICATOR);
lv_obj_set_style_bg_opa(slider_r, LV_OPA_COVER, LV_PART_KNOB);
/* set the radius */
lv_obj_set_style_radius(slider_r, LV_RADIUS_CIRCLE, LV_PART_MAIN);
lv_obj_set_style_radius(slider_r, LV_RADIUS_CIRCLE, LV_PART_INDICATOR);
lv_obj_set_style_radius(slider_r, LV_RADIUS_CIRCLE, LV_PART_KNOB);
lv_obj_set_style_pad_all(slider_r, 8, LV_PART_KNOB);
lv_obj_set_size(slider_r, 20, 150);
lv_obj_align(slider_r, LV_ALIGN_CENTER, -80, 0);
lv_slider_set_range(slider_r, 0, 255);
lv_slider_set_value(slider_r, g_rgb_msg.red, LV_ANIM_OFF);
lv_obj_add_event_cb(slider_r, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
/*********************************** slier_red_val ***********************************/
slier_red_val = lv_label_create(p_window);
lv_obj_set_style_text_font(slier_red_val, &lv_font_montserrat_26, LV_STATE_DEFAULT); // 30
lv_obj_set_style_text_color(slier_red_val, lv_color_make(255, 0, 0), LV_PART_MAIN);
lv_obj_set_style_text_align(slier_red_val, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
lv_label_set_text_fmt(slier_red_val, "%d", g_rgb_msg.red);
lv_obj_align(slier_red_val, LV_ALIGN_CENTER, -80, 110);
/*********************************** slider_g ***********************************/
slider_g = lv_slider_create(p_window);
/* set the bg_color */
lv_obj_set_style_bg_color(slider_g, lv_color_make(179, 255, 179), LV_PART_MAIN);
lv_obj_set_style_bg_color(slider_g, lv_color_make(0, 255, 0), LV_PART_INDICATOR);
lv_obj_set_style_bg_color(slider_g, lv_color_white(), LV_PART_KNOB);
/* set the opa */
lv_obj_set_style_bg_opa(slider_g, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_bg_opa(slider_g, LV_OPA_COVER, LV_PART_INDICATOR);
lv_obj_set_style_bg_opa(slider_g, LV_OPA_COVER, LV_PART_KNOB);
/* set the radius */
lv_obj_set_style_radius(slider_g, LV_RADIUS_CIRCLE, LV_PART_MAIN);
lv_obj_set_style_radius(slider_g, LV_RADIUS_CIRCLE, LV_PART_INDICATOR);
lv_obj_set_style_radius(slider_g, LV_RADIUS_CIRCLE, LV_PART_KNOB);
lv_obj_set_style_pad_all(slider_g, 8, LV_PART_KNOB);
/*set the size*/
lv_obj_set_size(slider_g, 20, 150);
lv_obj_align(slider_g, LV_ALIGN_CENTER, 0, 0);
lv_slider_set_range(slider_g, 0, 255);
lv_slider_set_value(slider_g, g_rgb_msg.green, LV_ANIM_OFF);
/*********************************** slier_green_val ***********************************/
slier_green_val = lv_label_create(p_window);
lv_obj_set_style_text_font(slier_green_val, &lv_font_montserrat_26, LV_STATE_DEFAULT); // 30
lv_label_set_text_fmt(slier_green_val, "%d", g_rgb_msg.green);
lv_obj_set_style_text_color(slier_green_val, lv_color_make(0, 255, 0), LV_PART_MAIN);
lv_obj_set_style_text_align(slier_green_val, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
lv_obj_align(slier_green_val, LV_ALIGN_CENTER, 0, 110);
lv_obj_add_event_cb(slider_g, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
/*********************************** slider_b ***********************************/
slider_b = lv_slider_create(p_window);
/* set the bg_color */
lv_obj_set_style_bg_color(slider_b, lv_color_make(179, 179, 255), LV_PART_MAIN);
lv_obj_set_style_bg_color(slider_b, lv_color_make(0, 0, 255), LV_PART_INDICATOR);
lv_obj_set_style_bg_color(slider_b, lv_color_white(), LV_PART_KNOB);
/*set opa*/
lv_obj_set_style_bg_opa(slider_b, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_bg_opa(slider_b, LV_OPA_COVER, LV_PART_INDICATOR);
lv_obj_set_style_bg_opa(slider_b, LV_OPA_COVER, LV_PART_KNOB);
/* set the radius */
lv_obj_set_style_radius(slider_b, LV_RADIUS_CIRCLE, LV_PART_MAIN);
lv_obj_set_style_radius(slider_b, LV_RADIUS_CIRCLE, LV_PART_INDICATOR);
lv_obj_set_style_radius(slider_b, LV_RADIUS_CIRCLE, LV_PART_KNOB);
lv_obj_set_style_pad_all(slider_b, 8, LV_PART_KNOB);
/*set the size*/
lv_obj_set_size(slider_b, 20, 150);
lv_obj_align(slider_b, LV_ALIGN_CENTER, 80, 0);
lv_slider_set_range(slider_b, 0, 255);
lv_slider_set_value(slider_b, g_rgb_msg.blue, LV_ANIM_OFF);
/*********************************** slier_blue_val ***********************************/
slier_blue_val = lv_label_create(p_window);
lv_obj_set_style_text_font(slier_blue_val, &lv_font_montserrat_26, LV_STATE_DEFAULT); // 30
lv_label_set_text_fmt(slier_blue_val, "%d", g_rgb_msg.blue);
lv_obj_set_style_text_color(slier_blue_val, lv_color_make(0, 0, 255), LV_PART_MAIN);
lv_obj_set_style_text_align(slier_blue_val, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
lv_obj_align(slier_blue_val, LV_ALIGN_CENTER, 80, 110);
lv_obj_add_event_cb(slider_b, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
return p_window;
}
为了方便演示,我在lv_card_list_entry_layout.c里面修改了这个结构体变量,可以进入这个RGB_LED灯的UI界面。
static icon_item_t g_icon_items[ICON_COUNT] = {
{NULL, &wd_img_APPLIST_03_HEARTRATE, WMS_WID_CARD_HR},
{NULL, &wd_img_APPLIST_04_SPO2, WMS_WID_NONE},
{NULL, &wd_img_APPLIST_07_BREATH_TRAINING, WMS_WID_NONE},
{NULL, &wd_img_APPLIST_00_MENU, WMS_WID_APP_LIST},
{NULL, &wd_img_APPLIST_10_NFC, WMS_WID_SET_RGB_LED},/* 添加RGB_LED页面ID, 原来是WMS_WID_NONE*/
{NULL, &wd_img_APPLIST_14_FIND_PHONE, WMS_WID_NONE},
{NULL, &wd_img_APPLIST_09_MUSIC, WMS_WID_NONE},
};