1. 呼吸燈原理
呼吸燈的實作可以通過控制燈的亮度連續變化,當變化的頻率大于24幀時,肉眼看上去就會逐漸變暗,逐漸變亮,
2. PWM控制亮度
PWM通過設定亮度在一段時間內的占空比,亮的百分比多,人眼看到的就亮,反之就是暗,
關于PWM的塊不打算展開說,這里針對呼吸燈的PWM詳細說明,
/*
* 描述 :呼吸燈PWM初始化
* 引數 :
* 無
* 回傳 :
* 無
*/
void bspBreathLedTIMInit(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
g_breathled_tim_handle.Instance = TIM2;
g_breathled_tim_handle.Init.Prescaler = 83;
g_breathled_tim_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
g_breathled_tim_handle.Init.Period = (BREATHLED_PWM_VALUE - 1);
g_breathled_tim_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
g_breathled_tim_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&g_breathled_tim_handle) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&g_breathled_tim_handle, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&g_breathled_tim_handle) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&g_breathled_tim_handle, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&g_breathled_tim_handle, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
GPIO_InitTypeDef GPIO_InitStruct;
BREATHLED_GPIO_CLK_ENABLE();
/* TIM2 GPIO Configuration
* PA3 ------> TIM2_CH4
*/
GPIO_InitStruct.Pin = BREATHLED_GPIO_GREEN_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(BREATHLED_GPIO_TYPE, &GPIO_InitStruct);
HAL_TIM_Base_Start_IT(&g_breathled_tim_handle);
}
我這里使用了PA3引腳,定時器2做實驗,時鐘頻率是84MHz,Prescaler設定(84 - 1) = 83,Period這里設定是(10000 - 1) = 9999,所以定時器是 (83 + 1) * (9999 + 1) / 84000000 = 0.01s = 10ms一個周期,
這個頻率各位可以根據實際情況去計算,盡量設定成看上去平滑,而且對系統也不會造成產生過多中斷,
/*
* 描述 :定時器底層回呼初始化
* 引數 :
* 無
* 回傳 :
* 無
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim_base)
{
if (htim_base->Instance == TIM2)
{
__HAL_RCC_TIM2_CLK_ENABLE();
HAL_NVIC_SetPriority(TIM2_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
}
/*
* 描述 :定時器中斷處理函式
* 引數 :
* 無
* 回傳 :
* 無
*/
void TIM2_IRQHandler(void)
{
HAL_TIM_IRQHandler(&g_breathled_tim_handle);
HAL_TIM_PeriodElapsedCallback(&g_breathled_tim_handle);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM11) {
HAL_IncTick();
}
//呼吸燈
else if (htim->Instance == TIM2)
{
//注意由于產生中斷過快,反轉實作不了
//LED電平反轉
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_3);
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
上面代碼是中斷處理函式,對呼吸燈的引腳電平反轉,是通過設定占空比的引數,定時器自動觸發中斷后反轉電平,
3. 呼吸燈亮度曲線
//PWM設定的Period值
#define BREATHLED_PWM_VALUE 10000
//呼吸燈x軸變暗或變亮步數
#define BREATHLED_X_DIVIDE 100
//呼吸燈x軸總步數
#define BREATHLED_X_TOTAL (BREATHLED_X_DIVIDE * 2)
//呼吸燈y軸比例
#define BREATHLED_Y_RATIO (BREATHLED_PWM_VALUE / BREATHLED_X_DIVIDE)
//亮度變化頻率,也就是幀數,單位毫秒
#define BREATHLED_FRAMES_MS 10
一些宏定義,BREATHLED_PWM_VALUE值是上面PWM設定的Period值,
BREATHLED_X_DIVIDE是x軸,變暗或變亮時,x軸的步數,
BREATHLED_X_TOTAL是x軸變暗加上變亮一個周期的總步數,
BREATHLED_Y_RATIO是y軸比例,
BREATHLED_FRAMES_MS亮度變化頻率,注意這個值必須大于24幀,也就是小于40毫秒的時間,為了更加平滑,可以適量提高幀數,這樣看起來呼吸燈更加流暢,
3.1 線性折線

y = 10000時代表占空比100%,x軸為時間,
/*
* 描述 :呼吸燈函式,由x軸得出y軸值
* 引數 :
* [in] x x軸,時間
* 回傳 :
* y軸值,PWM值
*/
static unsigned int breathLedCurve(unsigned int x)
{
unsigned int y;
if (x < BREATHLED_X_DIVIDE)
{
y = BREATHLED_Y_RATIO * x;
}
else
{
y = -BREATHLED_Y_RATIO * (x - BREATHLED_X_TOTAL);
}
return y;
}
/*
* 描述 :呼吸燈程序
* 引數 :
* 無
* 回傳 :
* 無
*/
void breathLedProgress(void)
{
static unsigned int time_x = 0;
unsigned int pwm_value_y = 0;
while (1)
{
time_x = (time_x + 1) % BREATHLED_X_TOTAL;
pwm_value_y = breathLedCurve(time_x);
//修改占空比
__HAL_TIM_SetCompare(&g_breathled_tim_handle, TIM_CHANNEL_4, pwm_value_y);
sysDelay(BREATHLED_FRAMES_MS);
if (time_x % BREATHLED_X_DIVIDE == 0)
{
sysDelay(500);
}
}
}
實作方式可以參考上面的,但是人眼感受的卻不是線性的,是由于在燈光微亮區,很小的光通量改變也讓人眼感到光通量變化很大,而在光通量比較大的區域,很大的光通量跳躍,人眼感覺到的光通量變化不大,簡單理解為,人眼對亮度暗的比較敏感,而對亮度量的不敏感,
所以你嘗試后發現,當由亮變暗時感覺時間長,由暗變亮時時間短,會有突然變亮的感覺,這個曲線是有缺陷的,
3.2 拋物線曲線

由上圖可知,當亮度由暗變亮時,程式的呼吸燈曲線需要如同圖3,
為了方便我這里使用一元二次曲線,有條件的可以使用對數函式,但是效果或許看起來會差不多,但是對數函式計算量會比一元二次方程大的多,所以衡量之下,選擇通過一元二次曲線來實作,

通過這個曲線,你可能感覺到和線性折線差不多的效果,所以還需要降低由暗變亮時的亮度變化頻率,即可讓人眼看上去平滑,
有需要的可以移步呼吸燈拋物線代碼下載,或者有能力的可以自行參考上面代碼,實作該曲線,
3.3 其他曲線
未完,待研究,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/297207.html
標籤:其他
上一篇:云安卓ARM開發板方案
