通过PWM控制液晶屏幕的亮度
一、工程准备
在上一个工程:RT-Thread开发之路(8)— 通过TFTLCD液晶屏幕显示数据,我们已经完成了LCD的基本显示,可是我们会发现,LCD屏幕非常的亮,那么可以降低他的亮度吗,接下来我们就通过PWM来控制他的亮度。实际上只要控制他的背光引脚LCD_POWER即可。
二、开启PWM设备
打开board.h文件,找到PWM的配置处,按照其提示配置: 首先,打开【RT-Thread Settings】,找到PWM设备驱动程序,将其选中,然后保存使之生效 通过查看STM32L431的数据手册,可以看到,LCD_POWER即PB15引脚,可以由TIM1和TIM15两个定时器控制,但是TIM1是互补输出引脚,所以我们选择TIM15,在board.h的添加TIM15宏定义
#define BSP_USING_PWM15
接下来使用CubeMx生成HAL_TIM_Base_MspInit()和HAL_TIM_MspPostInit()函数,将SPI以及LCD需要的控制引脚设置为如图所示: 将HAL_TIM_Base_MspInit()修改为HAL_TIM_PWM_MspInit()后,和HAL_TIM_MspPostInit()函数复制到board.c里,这里后其其给的注释不一样,要注意,(可能是还没来得及修改注释)
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM15)
{
/* USER CODE BEGIN TIM15_MspInit 0 */
/* USER CODE END TIM15_MspInit 0 */
/* TIM15 clock enable */
__HAL_RCC_TIM15_CLK_ENABLE();
/* USER CODE BEGIN TIM15_MspInit 1 */
/* USER CODE END TIM15_MspInit 1 */
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(timHandle->Instance==TIM15)
{
/* USER CODE BEGIN TIM15_MspPostInit 0 */
/* USER CODE END TIM15_MspPostInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/**TIM15 GPIO Configuration
PB15 ------> TIM15_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF14_TIM15;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN TIM15_MspPostInit 1 */
/* USER CODE END TIM15_MspPostInit 1 */
}
}
然后打开stm32xxxx_hal_config.h文件,添加#define HAL_TIM_MODULE_ENABLED的注释去掉: 因为配置文件没有定义PWM15输出通道,所以我们需要添加一些配置,打开drives/include/config/目录下的pwm_config文件,添加如下代码,其中.channel可以采用相或的方式设置多个通道的初始化:channel1->0x01,channel2->0x02,channel3->0x04,channel1->0x08
#ifdef BSP_USING_PWM15
#ifndef PWM15_CONFIG
#define PWM15_CONFIG \
{ \
.tim_handle.Instance = TIM15, \
.name = "pwm15", \
.channel = 0x02 \
}
#endif /* PWM15_CONFIG */
#endif /* BSP_USING_PWM15 */
然后编译,很好,报错了,提示找不到TIM17,可是STM32L431RCT6本就没有TIM17, 这可能是一个bug我们直接将其注释掉: 下载运行,在msh下输入list_device,可以看到,pwm15已经注册上去了
三、编写代码,控制LCD背光
在main.c中,包含头文件,
#include "drv_lcd.h"
然后设置PWM15 的参数:
#define PWM_DEV_NAME "pwm15" /* PWM设备名称 */
#define PWM_DEV_CHANNEL 2 /* PWM通道 */
struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */
接下来在main()中写入如下代码:
rt_uint32_t period, pulse;
period = 1000000; /* 周期为0.5ms,单位为纳秒ns */
pulse = 100000; /* PWM脉冲宽度值,单位为纳秒ns */
/* 查找设备 */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
if (pwm_dev == RT_NULL)
{
rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME);
return RT_ERROR;
}
/* 设置PWM周期和脉冲宽度默认值 */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
/* 使能设备 */
rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
还有一点要注意的是,将drv_lcd.c文件中lcd_gpio_init()中对LCD_PWR_PIN的初始化注释掉,不要设置其为通用推挽输出了, 然后编译下载运行,可以观察到其屏幕暗下来了:
四、优化代码
我们将lcd的代码放到一个文件夹里,新建一个线程,在线程中通过检测按键的按下来调节LCD的亮度,代码如下:
#include
#include
#include
#include "drv_lcd.h"
#define KEY1_PIN GET_PIN(B, 2)
#define KEY2_PIN GET_PIN(B, 3)
#define PWM_DEV_NAME "pwm15" /* PWM设备名称 */
#define PWM_DEV_CHANNEL 2 /* PWM通道 */
struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */
/* 定义一个LCD线程句柄结构体指针 */
static rt_thread_t app_lcd_thread = RT_NULL;
void brightness_ctrl(rt_uint32_t bright)
{
if(bright > 100)
bright = 100;
/* 设置PWM周期和脉冲宽度 */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, 1000000, bright * 10000);
}
static void app_lcd_thread_entry(void *parameter)
{
rt_uint32_t ctrl_value = 10;
static int key_up = 1; /* 按键松开标志 */
rt_pin_mode(KEY1_PIN, PIN_MODE_INPUT_PULLUP);
rt_pin_mode(KEY2_PIN, PIN_MODE_INPUT_PULLUP);
/* 查找设备 */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
if (pwm_dev == RT_NULL)
{
rt_kprintf("pwm run failed! can't find %s device!\n", PWM_DEV_NAME);
return ;
}
/* 使能设备 */
rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
brightness_ctrl(ctrl_value);
/* 设置背景色和前景色 */
lcd_set_color(WHITE, BLACK);
/* 在 LCD 上显示字符 */
lcd_show_string(10, 60, 32, "Hello!");
lcd_show_string(10, 100, 32, "RT-Thread");
/* 在 LCD 上画线 */
lcd_draw_line(0, 69+16+24+32, 240, 69+16+24+32);
/* 在 LCD 上画一个同心圆 */
lcd_draw_point(120, 194);
for (int i = 0; i < 46; i += 4)
{
lcd_draw_circle(120, 194, i);
}
lcd_show_string(200, 0, 24, "%3d",ctrl_value);
while (1)
{
/* 检测按键是否按下 */
if (key_up && ((rt_pin_read(KEY1_PIN) == 0) || (rt_pin_read(KEY2_PIN) == 0)) )
{
rt_thread_mdelay(50); /* 延时50ms消抖*/
key_up = 0;
if(rt_pin_read(KEY1_PIN) == 0)
{
rt_kprintf("key1 press \r\n");
if(ctrl_value > 4)
ctrl_value = ctrl_value -5;
brightness_ctrl(ctrl_value);
rt_kprintf("brightness level %d \r\n",ctrl_value);
}
else if(rt_pin_read(KEY2_PIN) == 0)
{
rt_kprintf("key2 press \r\n");
if(ctrl_value < 96)
ctrl_value = ctrl_value +5;
brightness_ctrl(ctrl_value);
rt_kprintf("brightness level %d \r\n",ctrl_value);
}
lcd_show_string(200, 0, 24, "%3d",ctrl_value);
}
else if((rt_pin_read(KEY1_PIN) == 1) && (rt_pin_read(KEY2_PIN) == 1))
{
key_up = 1; /* 按键已松开 */
}
rt_thread_mdelay(100);
}
}
static int app_lcd_init(void)
{
rt_err_t rt_err;
/* 创建LCD线程*/
app_lcd_thread = rt_thread_create("app_lcd thread",
app_lcd_thread_entry, RT_NULL, 2048, 6, 10);
/* 如果获得线程控制块,启动这个线程 */
if (app_lcd_thread != RT_NULL)
rt_err = rt_thread_startup(app_lcd_thread);
else
rt_kprintf("app_lcd_thread create failure !!! \n");
/* 判断线程是否启动成功 */
if( rt_err == RT_EOK)
rt_kprintf("app_lcd_thread startup ok. \n");
else
rt_kprintf("app_lcd_thread startup err. \n");
return rt_err;
}
INIT_APP_EXPORT(app_lcd_init);
可以实现需要的效果