亮度控制一般有两种方案,DC 调光和 PWM 调光。
但是某些奇怪的单片机设计直接把 TFT 屏的背光灯绑定在电源上,所以你只能开关背光,不能控制电压;所以只能被迫使用 PWM 调光。
然而对于单片机/RTOS 来说,每个 tick 都花一部分性能在控制方面可能有比较大的性能损耗,加上如果设备是无线电相关的话,PWM 带来的噪音可能会辐射到无线电部分导致未知的结果,所以只能采取第三种方案:伪颜色。
本质上 ILI9341 IC 的驱动当中是一个像素点一个像素点地画图
void lcd_write_pixel(ui::Color pixel) {
lcd_write_data(pixel.v);
}
这就相当简单了,直接把颜色通过浮点运算化为更“黑”的颜色即可实现“伪”亮度控制
void darken_color(ui::Color& pixel, float darken_level) {
uint16_t r = (pixel.v >> 11) & 0x1F;
uint16_t g = (pixel.v >> 5) & 0x3F;
uint16_t b = pixel.v & 0x1F;
r = static_cast<uint16_t>(r / darken_level); // darken
g = static_cast<uint16_t>(g / darken_level);
b = static_cast<uint16_t>(b / darken_level);
pixel.v = (r << 11) | (g << 5) | b; // combine back
}
但是浮点运算在单片机上相当慢,最后出来而结果可以用,但是渲染速度肉眼可见的下降。最后我的方案是损失一些调控精度,用移位运算代替浮点运算
void darken_color(ui::Color& pixel, uint8_t darken_level_shift) {
// TODO: 1. do we need edge control?
// currently didn't see and issue without edge control
// but maybe hurts screen hardware without one?
// TODO: 2. de-color mode for accessibility
// TODO: 3. high contrast mode for accessibility
uint16_t r = (pixel.v >> 11) & 0x1F; // extract
uint16_t g = (pixel.v >> 5) & 0x3F;
uint16_t b = pixel.v & 0x1F;
r = r >> darken_level_shift;
g = g >> darken_level_shift;
b = b >> darken_level_shift;
pixel.v = (r << 11) | (g << 5) | b;
}
其中 darken_level_shift 是一个 uint8,0 代表 100%亮度,1 代表 50%亮度,以此类推
最后出来的结果终于可以用了:
void lcd_write_pixels(ui::Color pixel, size_t n) {
if (get_dark_cover()) {
darken_color(pixel, get_brightness()); // Darken
}
void lcd_write_pixels_unrolled8(ui::Color pixel, size_t n) {
if (get_dark_cover()) {
darken_color(pixel, get_brightness()); // Darken
}
![]() |
1
cd605426287 131 天前
6666 关注一 bo
|