SmartOS/App/Dimmer.cpp

421 lines
8.9 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "Kernel\Sys.h"
#include "Device/Pwm.h"
#include "Config.h"
#include "Dimmer.h"
/******************************** 调光配置 ********************************/
DimmerConfig::DimmerConfig()
{
_Name = "Dimmer";
_Start = &Values;
_End = &TagEnd;
Init();
SaveLast = true;
PowerOn = 1; // 默认上电开灯
Speed = 10;
Status = 0x0; // 默认呼吸灯动感模式
for (int i = 0; i < 4; i++) Values[i] = 0xFF;
}
static DimmerConfig* _cfg = nullptr;
static ByteArray _bak;
static uint _saveTask = 0;
static void SaveTask(void* param)
{
if (!_cfg) return;
// 比较数据,如果不同则保存
auto bs = _cfg->ToArray();
if (_bak.Length() == 0 || bs != _bak)
{
_bak = bs;
_cfg->Save();
}
}
static DimmerConfig* InitConfig()
{
static DimmerConfig cfg;
if (!_cfg)
{
_cfg = &cfg;
_cfg->Load();
_bak = cfg.ToArray();
if (!_saveTask) _saveTask = Sys.AddTask(SaveTask, nullptr, 1000, 10000, "保存配置");
}
return _cfg;
}
/******************************** 调光Dimmer ********************************/
Dimmer::Dimmer()
{
_Pwm = nullptr;
Opened = false;
Min = 0;
Max = 255;
Config = nullptr;
_task = 0;
_taskAnimate = 0;
for (int i = 0; i < 4; i++)
{
_Pulse[i] = 0;
_Current[i] = 0;
}
_NextStatus = 0xFF;
}
Dimmer::~Dimmer()
{
if (Opened) Close();
}
void Dimmer::Init(TIMER timer, int channels)
{
// 初始化Pwm指定定时器、频率、通道
if (!_Pwm && timer != Timer0) _Pwm = new Pwm(timer);
Init(_Pwm, channels);
}
void Dimmer::Init(Pwm* pwm, int channels)
{
Config = InitConfig();
if (pwm)
{
_Pwm = pwm;
pwm->SetFrequency(100);
for (int i = 0; i < channels; i++)
{
// 打开通道初始值为0渐变效果
pwm->Enabled[i] = true;
pwm->Pulse[i] = 0;
}
}
}
void Dimmer::Open()
{
if (Opened) return;
// 配置数据备份,用于比较保存
auto& cfg = *Config;
// 先打开Pwm再设置脉宽否则可能因为周期还没来得及计算
// 是否上电默认打开
if (cfg.PowerOn) _Pwm->Open();
// 是否恢复上次保存?
if (cfg.SaveLast)
SetPulse(cfg.Values);
else
{
// 出厂默认最大亮度100%
byte vs[] = { 0xFF, 0xFF, 0xFF, 0xFF };
SetPulse(vs);
}
Opened = true;
}
void Dimmer::Close()
{
if (!Opened) return;
_Pwm->Close();
Sys.RemoveTask(_task);
Sys.RemoveTask(_taskAnimate);
Opened = false;
}
// 调光亮度等级公式2^(x/32) * 256 - 1
static const ushort Levels[] = { 261,266,272,278,284,291,297,303,310,317,324,331,338,346,353,361,369,377,385,394,402,411,420,430,439,449,458,469,479,489,500,511,522,534,545,557,570,582,595,608,621,635,649,663,678,692,708,723,739,755,772,789,806,824,842,860,879,898,918,938,959,980,1001,1023,1045,1068,1092,1116,1140,1165,1191,1217,1243,1271,1299,1327,1356,1386,1416,1447,1479,1511,1544,1578,1613,1648,1684,1721,1759,1797,1837,1877,1918,1960,2003,2047,2092,2138,2185,2232,2281,2331,2382,2434,2488,2542,2598,2655,2713,2773,2833,2895,2959,3024,3090,3157,3227,3297,3370,3443,3519,3596,3675,3755,3837,3921,4007,4095,4185,4276,4370,4466,4564,4663,4766,4870,4977,5086,5197,5311,5427,5546,5667,5792,5918,6048,6181,6316,6454,6596,6740,6888,7038,7193,7350,7511,7676,7844,8015,8191,8370,8554,8741,8932,9128,9328,9532,9741,9954,10172,10395,10623,10855,11093,11336,11584,11838,12097,12362,12633,12909,13192,13481,13776,14078,14386,14701,15023,15352,15688,16032,16383,16742,17108,17483,17866,18257,18657,19065,19483,19910,20346,20791,21246,21712,22187,22673,23169,23677,24195,24725,25267,25820,26385,26963,27553,28157,28773,29404,30047,30705,31378,32065,32767,33485,34218,34967,35733,36515,37315,38132,38967,39820,40692,41583,42494,43424,44375,45347,46340,47355,48392,49451,50534,51641,52772,53927,55108,56315,57548,58808,60096,61412,62756,64131,65535 };
/*static byte GetX(byte y)
{
int d = y << 8;
for(int i=0; i<ArrayLength(Levels); i++)
{
if(d == Levels[i]) return i;
if(d > Levels[i]) return i;
}
return ArrayLength(Levels) - 1;
}*/
// 刷新任务。产生渐变效果
void Dimmer::FlushTask()
{
auto& pwm = *_Pwm;
int end = 0;
// 每一个通道产生渐变效果
for (int i = 0; i < 4; i++)
{
if (pwm.Enabled[i])
{
byte x = _Current[i];
if (x < _Pulse[i])
x++;
else if (x > _Pulse[i])
x--;
else
end++;
// 向前跳一级
auto cur = Levels[x];
//pwm.SetDuty(i, cur);
pwm.Pulse[i] = (((int)cur + 1) * pwm.Period) >> 16;
//debug_printf("Dimmer::Flush %d Pulse=%d => %d x=%d\r\n", i+1, cur, _Pulse[i], x);
_Current[i] = x;
}
else
end++;
}
// 刷新数据
pwm.Flush();
// 所有通道都结束,则关闭任务
if (end == 4)
{
debug_printf("所有通道结束 \r\n");
Sys.SetTask(_task, false);
// 渐变完成后关闭Pwm输出
if (_NextStatus != 0xFF)
{
_NextStatus = 0xFF;
pwm.Close();
}
}
}
void Dimmer::Set(byte vs[4])
{
// 关闭状态不许修改目标值,避免无法结束
if (!_Pwm->Opened) return;
auto& pwm = *_Pwm;
auto& cfg = *Config;
// 等分计算步长
for (int i = 0; i < 4; i++)
{
if (!pwm.Enabled[i]) continue;
byte d = vs[i];
// 最大最小值
if (d < Min) d = Min;
if (d > Max) d = Max;
// 修正数据
vs[i] = d;
if (cfg.SaveLast) cfg.Values[i] = d;
}
SetPulse(vs);
}
void Dimmer::SetPulse(byte vs[4])
{
auto& pwm = *_Pwm;
auto& cfg = *Config;
debug_printf("开始调节……\r\n");
if (cfg.Speed > 0 && !_Closing)
{
// 等分计算步长
for (int i = 0; i < 4; i++)
{
if (!pwm.Enabled[i]) continue;
byte d = vs[i];
// 最大最小值
if (d < Min) d = Min;
if (d > Max) d = Max;
_Pulse[i] = d;
#if DEBUG
byte x = _Current[i];
debug_printf("Dimmer::Set %d Pulse=%d => %d x=%d=>%d\r\n", i + 1, Levels[x], Levels[d], x, d);
#endif
}
if (!_task) _task = Sys.AddTask(&Dimmer::FlushTask, this, 10, cfg.Speed, "灯光渐变");
Sys.SetTask(_task, true, 0);
}
else
{
for (int i = 0; i < 4; i++)
{
if (!pwm.Enabled[i]) continue;
#if DEBUG
auto cur = pwm.Pulse[i];
#endif
pwm.Pulse[i] = ((int)vs[i] * pwm.Period) >> 8;
debug_printf("Dimmer::Set %d Pulse=%d => %d\r\n", i + 1, cur, pwm.Pulse[i]);
}
// 刷新数据
pwm.Flush();
}
}
void Dimmer::Change(byte mode)
{
// 先停了动感模式
if (_taskAnimate) Sys.SetTask(_taskAnimate, false);
_Closing = false;
auto& cfg = *Config;
if (mode == 0x01)
{
// 关闭动感模式
if (cfg.PowerOn) cfg.Status = 0;
// 渐变打开
if (!_Pwm->Opened)
{
_Pwm->Open();
// 打开时使用最高亮度,如果保存最后一次配置,则使用最后一次
byte vs[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
auto ps = vs;
if (cfg.SaveLast) ps = cfg.Values;
debug_printf("Dimmer::Change 打开,调节到上一次亮度 {%d, %d, %d, %d} \r\n", ps[0], ps[1], ps[2], ps[3]);
for (int i = 0; i < 4; i++)
_Current[i] = Min;
SetPulse(ps);
}
}
else if (mode == 0x00)
{
debug_printf("Dimmer::Change 渐变关闭到最低亮度,然后完全关闭 \r\n");
// 渐变关闭
if (_Pwm->Opened)
{
byte vs[4] = { 0, 0, 0, 0 };
SetPulse(vs);
//_Pwm->Close();
// 最后关闭Pwm设为非0xFF关闭一次
_NextStatus = 0x00;
_Closing = true;
return;
}
}
else if (mode >= 0x10)
{
if (!_Pwm->Opened) _Pwm->Open();
// 如果设置是保持开灯,则记录最后状态
if (cfg.PowerOn) cfg.Status = mode;
// 根据256级计算总耗时
Animate(mode, (cfg.Speed << 8) + 500);
}
}
void Dimmer::AnimateTask()
{
// 配置数据备份,用于比较保存
auto& cfg = *Config;
if (cfg.Speed <= 0) return;
bool on = _AnimateData[1] > 0;
byte vs1[4];
byte vs2[4];
Buffer bs1(vs1, 4);
Buffer bs2(vs2, 4);
bs1.Set(0, 0, 4);
bs2.Set(0, 0, 4);
switch (_AnimateData[0])
{
case 0x10: // 亮暗呼吸灯
{
//bs1.Set(0, 0, 4);
bs1.Set(0xFF, 0, 4);
break;
}
case 0x11: // 单通道1
{
vs2[0] = 0xFF;
break;
}
case 0x12: // 单通道2
{
vs2[1] = 0xFF;
break;
}
case 0x13: // 单通道3
{
vs2[2] = 0xFF;
break;
}
case 0x14: // 单通道4
{
vs2[3] = 0xFF;
break;
}
case 0x15: // 通道交错
{
vs1[1] = vs1[3] = 0xFF;
vs2[1] = vs2[3] = 0;
break;
}
}
SetPulse(on ? vs1 : vs2);
on = !on;
_AnimateData[1] = on;
}
void Dimmer::Animate(byte mode, int ms)
{
// 非动感模式关闭任务
if (mode < 0x10)
{
Sys.SetTask(_taskAnimate, false);
return;
}
debug_printf("Dimmer::Animate 动感模式 0x%02x ms=%d \r\n", mode, ms);
_AnimateData[0] = mode;
// 打开通道
if (!_Pwm->Opened) _Pwm->Open();
if (!_taskAnimate)
_taskAnimate = Sys.AddTask(&Dimmer::AnimateTask, this, 0, ms, "动感模式");
else
{
Sys.SetTaskPeriod(_taskAnimate, ms);
Sys.SetTask(_taskAnimate, true, 0);
}
}