閃爍定義
光學(xué)閃爍是指人造光源產(chǎn)生的光的脈沖或波動現(xiàn)象。在低頻下,閃爍是肉眼可見的,即人眼能夠感知到光的閃動。然而,當(dāng)頻率超過100 Hz時(shí),雖然閃爍對人眼不再可見,它仍然存在并可能對人體產(chǎn)生一定影響。大部分人造光源,如家庭和商業(yè)辦公室使用的,會在接入電網(wǎng)時(shí)產(chǎn)生閃爍,其頻率通常由所在國家的電力頻率決定,一般為50 Hz或60 Hz。由于電流在光源中的交替流動,這些光源會在50 Hz或60 Hz的電網(wǎng)下產(chǎn)生100 Hz或120 Hz的閃爍頻率。為了消除這種可見閃爍并減少其對人體的潛在影響,許多LED燈采用了脈沖寬度調(diào)制(PWM)的調(diào)光方法,從而實(shí)現(xiàn)更高的閃爍頻率。VD6283傳感器能夠檢測高達(dá)2 kHz的光閃爍頻率,從而為光質(zhì)量的監(jiān)測提供精準(zhǔn)數(shù)據(jù)。
最近在弄ST的課程,需要樣片的可以加群申請:615061293 。

視頻教學(xué)
[https://www.bilibili.com/video/BV1pt4y1f7eh/]
樣品申請
[https://www.wjx.top/vm/OhcKxJk.aspx#]
源碼下載
[https://download.csdn.net/download/qq_24312945/88671493]
參考代碼
[https://www.st.com/zh/ecosystems/x-cube-als.html]
在下面目錄下有對應(yīng)的程序。

這個(gè)應(yīng)用程序演示了如何使用X-NUCLEO-6283A1擴(kuò)展板,通過UART從連接的STM32 Nucleo板向PC傳輸數(shù)據(jù),并在通用應(yīng)用程序(如Tera Term)上顯示閃爍頻率提取結(jié)果。一旦建立連接,用戶可以使用超級終端查看來自板載光傳感器的數(shù)據(jù)。該應(yīng)用程序提供實(shí)時(shí)數(shù)值,并允許用戶通過鍵盤更改增益、曝光時(shí)間和采樣率。這個(gè)示例只能在軟件包安裝文件夾中訪問。

硬件準(zhǔn)備
首先需要準(zhǔn)備一個(gè)開發(fā)板,這里我準(zhǔn)備的是自己繪制的開發(fā)板: 最近在弄ST和瑞薩RA的課程,需要樣片的可以加群申請:615061293 。

開發(fā)板設(shè)置
在手冊種給出了,閃爍手冊可以查看AN5639,資料鏈接如下。

在AN5639手冊中,需要對SB3進(jìn)行連接。


查看X-NUCLEO-6283A1手冊,可以看到VD6283TX的GPIO2連接到MCU的ADC端口0-2。

在本章使用的測試板中,AFLR_1V8接入到MCU的PC0接口。

需要將AFLR_1V8接到開發(fā)板的A0端口中。

生成STM32CUBEMX
用STM32CUBEMX生成例程,這里使用MCU為STM32WB55RG。 配置時(shí)鐘樹,配置時(shí)鐘為32M。

串口配置
查看原理圖,PB6和PB7設(shè)置為開發(fā)板的串口。

配置串口。

IIC配置


配置IIC為快速模式,速度為400k。

X-CUBE-ALS

ADC使用定時(shí)器觸發(fā)采樣
在app_als_adc_utils.c中,定義了ADC使用的頻率,為8000Hz。

定時(shí)器的arr設(shè)置為4000-1,那么定時(shí)器頻率為8000Hz。 Trigger Event Selection :update event 定時(shí)器自動更新。

配置ADC檢測VD6283TX的GPIO2管腳的AD值。 設(shè)置觸發(fā)方式為外部觸發(fā),選擇剛剛配置的TIM2,觸發(fā)方式為上升沿觸發(fā)。

開啟中斷。

KEIL配置

FFT代碼配置
arm_cortexM4lf_math.lib 庫包含了一系列數(shù)學(xué)函數(shù),特別是適用于基于Cortex-M4和Cortex-M7處理器的浮點(diǎn)運(yùn)算單元的優(yōu)化數(shù)學(xué)例程。這些例程涵蓋了常見的數(shù)學(xué)運(yùn)算,如信號處理、濾波、變換等。
arm_math.h 這個(gè)頭文件包含了CMSIS-DSP庫的函數(shù)聲明、宏定義和結(jié)構(gòu)體定義等,可以通過包含這個(gè)頭文件,使用庫中提供的各種數(shù)學(xué)函數(shù),包括信號處理、濾波、變換等。 添加arm_cortexM4lf_math.lib文件。


同時(shí)導(dǎo)入arm_math.h文件。

app_x-cube-als.c
由于需要進(jìn)行FFT算法,所以需要添加對應(yīng)數(shù)學(xué)頭文件。
#define ARM_MATH_CM4
#include "arm_math.h"
#include "app_als_adc_utils.h"
添加對應(yīng)的函數(shù)申明。
#define FLK_CHANNEL    (5U)
/*
 * Increasing the value of the FLK_DATA_SIZE symbol will increase
 * processing time, flicker accuracy and memory footprint
 */
#define FLK_DATA_SIZE (1024U)
#define FFT_SIZE (FLK_DATA_SIZE)
/* Private variables ---------------------------------------------------------*/
static uint8_t is_quit_requested;
static uint8_t is_autogain_requested;
static int16_t flk_data[FLK_DATA_SIZE];
volatile uint8_t ALS_EventDetected;
/*
 * The FFT of a real N-point sequence has even symmetry in the frequency domain.
 * The second half of the data equals the conjugate of the first half flipped in frequency.
 * Looking at the data, we see that we can uniquely represent the FFT using only N/2 complex numbers.
 * These are packed into the output array in alternating real and imaginary components:
 * X = { real[0], imag[0], real[1], imag[1], real[2], imag[2] ... real[(N/2)-1], imag[(N/2)-1 }
 */
static arm_rfft_fast_instance_f32 instance_fft;
static float32_t fft_in[FLK_DATA_SIZE];
static float32_t fft_out_tmp[FFT_SIZE];
static float32_t fft_out[FFT_SIZE/2];
/*
 * The FFT of a real N-point sequence has even symmetry in the frequency domain.
 * The second half of the data equals the conjugate of the first half flipped in frequency.
 * Looking at the data, we see that we can uniquely represent the FFT using only N/2 complex numbers.
 * These are packed into the output array in alternating real and imaginary components:
 * X = { real[0], imag[0], real[1], imag[1], real[2], imag[2] ... real[(N/2)-1], imag[(N/2)-1 }
 */
static arm_rfft_fast_instance_f32 instance_fft;
static void MX_VD6283A1_AnalogFlicker_Process(void); 
static float32_t complex_abs(float32_t real, float32_t complex);
static void init_fft(arm_rfft_fast_instance_f32 *instance, uint32_t size);
static void perform_fft(arm_rfft_fast_instance_f32 *instance, int16_t *data, float32_t *ffti, float32_t *ffto, uint32_t size);
static void find_flk_freq(uint32_t fs, float32_t *ffto, uint32_t *freq, uint8_t skip_dc, uint32_t size);
static int32_t flicker_autogain(uint8_t Instance, uint32_t *pAppliedGain, uint32_t timeoutMs);
static void display_gain(uint32_t gain);
在MX_VD6283A1_LuxCCT_Init()函數(shù)中添加init_fft快速傅里葉變換初始化。 
static void MX_VD6283A1_LuxCCT_Init(void)
{
  /* Initialize Virtual COM Port */
  BSP_COM_Init(COM1);
  printf("VD6283TX Lux / CCT Examplenn");
  display_commands_banner();
    /* initialize ARM FFT library */
    init_fft(&instance_fft, FFT_SIZE);
  status = VD6283A1_LIGHT_SENSOR_Init(LIGHT_SENSOR_INSTANCE_0);
  if (status)
  {
    printf("VD6283A1_LIGHT_SENSOR_Init failedn");
    while(1);
  }
}
初始化完畢之后,添加頻率獲取函數(shù)。
static void MX_VD6283A1_AnalogFlicker_Process(void)
{
    uint32_t fs; /* sampling frequency */
    uint32_t pos = 0; 
    uint32_t flk_freq = 0;
    uint32_t index;
    uint32_t current_gain;
    uint32_t current_exposure;
    /* initialize exposure time */
    VD6283A1_LIGHT_SENSOR_SetExposureTime(LIGHT_SENSOR_INSTANCE_0, 100000);
    VD6283A1_LIGHT_SENSOR_GetExposureTime(LIGHT_SENSOR_INSTANCE_0, ¤t_exposure);
    printf("Exposure set to %lu usn", (unsigned long)current_exposure);
    /* initialize gain */
    flicker_autogain(LIGHT_SENSOR_INSTANCE_0, ¤t_gain, 1);
    printf("Channel %u gain set to", FLK_CHANNEL);
    display_gain(current_gain);
    status = als_adc_start(&fs);
    if (status)
    {
        printf("ADC Start failedn");
        while (1);
    }
    VD6283A1_LIGHT_SENSOR_StartFlicker(LIGHT_SENSOR_INSTANCE_0, FLK_CHANNEL, LIGHT_SENSOR_FLICKER_ANALOG);
    while (!is_quit_requested)
    {
        status = als_adc_get_frame(&flk_data[pos], &index);
        /* fill the ADC frame buffer */
        if (status == 0)
        {
            pos += ADC_FRAME_SIZE;
        }
        /* if the ADC frame buffer is full, then process it */
        if (pos == FLK_DATA_SIZE)
        {
            perform_fft(&instance_fft, flk_data, fft_in, fft_out, FFT_SIZE);
            find_flk_freq(fs, fft_out, &flk_freq, 1, FFT_SIZE);
            pos = 0; /* reset position index */
            printf("Flicker freq: %4lu Hzr", (unsigned long)flk_freq);
            fflush(stdout);
            if (is_autogain_requested == 1)
            {
              VD6283A1_LIGHT_SENSOR_StopFlicker(LIGHT_SENSOR_INSTANCE_0);
              flicker_autogain(LIGHT_SENSOR_INSTANCE_0, ¤t_gain, 1);
              printf("Channel %u gain set to", FLK_CHANNEL);
              display_gain(current_gain);
              VD6283A1_LIGHT_SENSOR_StartFlicker(LIGHT_SENSOR_INSTANCE_0, FLK_CHANNEL, LIGHT_SENSOR_FLICKER_ANALOG);
              is_autogain_requested = 0;
            }
        }
        handle_cmd(get_key());
    }
    als_adc_stop();
    VD6283A1_LIGHT_SENSOR_StopFlicker(LIGHT_SENSOR_INSTANCE_0);
    VD6283A1_LIGHT_SENSOR_DeInit(LIGHT_SENSOR_INSTANCE_0);
    printf("Quitting the demo...n");
    while (1);
}
在MX_X_CUBE_ALS_Process函數(shù)中開啟頻率獲取函數(shù),關(guān)閉光強(qiáng)獲取函數(shù)MX_VD6283A1_LuxCCT_Process。 
添加增益設(shè)置函數(shù)。
/*
 * @brief find and apply appropriate gain value depending on saturation value
 * @warning this function mustn't be called when a capture is ongoing
 */
static int32_t flicker_autogain(uint8_t Instance, uint32_t *pAppliedGain, uint32_t timeoutMs)
{
  int32_t res;
  uint8_t i, j;
  uint8_t idx = 7; /* start with mid-table value */
  const uint8_t sat_limit = 2;
  uint32_t saturation;
  /* duplicate 0x42AB to avoid 100x and keep multiples of 2 for array size */
  const uint16_t Gains[] = {
    0x42AB, 0x42AB, 0x3200, 0x2154, 0x1900, 0x10AB, 0x0A00, 0x0723,
    0x0500, 0x0354, 0x0280, 0x01AB, 0x0140, 0x0100, 0x00D4, 0x00B5
  };
  /* clip timeout value */
  timeoutMs = timeoutMs == 0 ? 1 : timeoutMs;
  timeoutMs = timeoutMs >= 100 ? 100 : timeoutMs;
  for (i = 0; i <= 3; i++)
  {
    VD6283A1_LIGHT_SENSOR_SetGain(Instance, FLK_CHANNEL, Gains[idx]);
    VD6283A1_LIGHT_SENSOR_GetGain(Instance, FLK_CHANNEL, pAppliedGain);
    res = VD6283A1_LIGHT_SENSOR_StartFlicker(Instance, FLK_CHANNEL, LIGHT_SENSOR_FLICKER_ANALOG);
    if (res)
      return res;
    /* read saturation value each ms so we can exit early if saturation detected */
    for (j = 0; j < timeoutMs; j++)
    {
      HAL_Delay(1);
      res = VD6283A1_LIGHT_SENSOR_GetSaturation(Instance, &saturation);
      if (res)
        return res;
      if (saturation > sat_limit)
        break;
    }
    res = VD6283A1_LIGHT_SENSOR_StopFlicker(Instance);
    if (res)
      return res;
    /* update index to next value */
    if (i)
      idx += saturation > sat_limit ? 1 < < (i - 1) : -(1 < < (i - 1));
    else if (saturation > sat_limit)
      idx++;
  }
  /* clip index if it reaches max value */
  if (idx > 15) 
    idx = 15;
  VD6283A1_LIGHT_SENSOR_SetGain(Instance, FLK_CHANNEL, Gains[idx]);
  res = VD6283A1_LIGHT_SENSOR_GetGain(Instance, FLK_CHANNEL, pAppliedGain);
  return res;
}
在下方添加函數(shù)的定義。
/*
 * @brief initilize arm rfft library
 */
static void init_fft(arm_rfft_fast_instance_f32 *instance, uint32_t size)
{
  arm_rfft_fast_init_f32(instance, size);
}
打印增益函數(shù)。
/*
 * @brief normalize, convert and dislay gain 
 */
static void display_gain(uint32_t gain)
{
    uint32_t g = (gain * 100) / 256;
    printf(" %3lu.%02lun", (unsigned long)g / 100, (unsigned long)(g % 100));
}
執(zhí)行FFT。
/*
 * @brief perform fft on the input buffer using arm rfft library
 */
static void perform_fft(arm_rfft_fast_instance_f32 *instance, int16_t *flk, float32_t *ffti, float32_t *ffto, uint32_t size)
{
    uint32_t i;
    uint32_t index = 0;
    /* copy the ADC sampled signal into the fft input buffer
     * this allows to convert the data from int16_t to float32_t */
    for (i = 0; i < size; i++)
    {
        ffti[i] = flk[i];
    }
    /* Perform the FFT on the input buffer:
     * results are packed in a way so that even indexes contain real values
     * and odd indexes contain the complex value of each bin.
     * Therefore the fft_output array contains FFT_SIZE / 2 bins */
    arm_rfft_fast_f32(instance, ffti, fft_out_tmp, 0);
    /* Calculate the magnitude for each bin from the temp fft output buffer */
    for (i = 0; i < size; i += 2)
    {
        ffto[index] = complex_abs(fft_out_tmp[i], fft_out_tmp[i+1]);
        if (ffto[index] < 0) ffto[index] = 0;
        index++;
    }
}
查找峰值頻率值。
/*
 * @brief find peak frequency value
 */
static void find_flk_freq(uint32_t fs, float32_t *ffto, uint32_t *freq, uint8_t skip_dc, uint32_t size)
{
    uint32_t i;
    uint32_t res;
    uint32_t index_max = 0;
    uint32_t limit = size / 2;
    float32_t max_value = -1;
    /* do not take account of the DC value if the flag skip_dc is set */
    skip_dc ? (i = 1) : (i = 0);
    /* run through the output array to detect the peak */
    for (; i < limit; i++)
    {
        if (ffto[i] > max_value)
        {
            index_max = i;
            max_value = ffto[i];
        }
    }
    /* convert index of the bin into frequency */
    res = (index_max * fs) / size;
    /* return the result if the pointer is valid */
    if (freq)
    {
        *freq = res;
    }
}
計(jì)算一個(gè)復(fù)數(shù)的絕對值。
/*
 * @brief compute absolute value of a complex number
 */
static float32_t complex_abs(float32_t real, float32_t complex)
{
  float32_t res;
  arm_sqrt_f32(real * real + complex * complex, &res);
  return res;
}
需要添加函數(shù)
arm_cortexM4lf_math.lib 庫包含了一系列數(shù)學(xué)函數(shù),特別是適用于基于Cortex-M4和Cortex-M7處理器的浮點(diǎn)運(yùn)算單元的優(yōu)化數(shù)學(xué)例程。這些例程涵蓋了常見的數(shù)學(xué)運(yùn)算,如信號處理、濾波、變換等。
arm_math.h 這個(gè)頭文件包含了CMSIS-DSP庫的函數(shù)聲明、宏定義和結(jié)構(gòu)體定義等,可以通過包含這個(gè)頭文件,使用庫中提供的各種數(shù)學(xué)函數(shù),包括信號處理、濾波、變換等。
app_als_adc_utils.c功能主要包括啟動和停止ADC采樣,獲取采樣數(shù)據(jù),ADC采樣速度設(shè)置,以及處理相關(guān)的硬件中斷。 app_als_adc_utils.h是app_als_adc_utils.c對應(yīng)頭文件。
演示結(jié)果
在1K光源下的測試情況。

審核編輯 黃宇
- 
                                光源
                                +關(guān)注
關(guān)注
3文章
770瀏覽量
70917 - 
                                代碼
                                +關(guān)注
關(guān)注
30文章
4929瀏覽量
72800 - 
                                環(huán)境光傳感器
                                +關(guān)注
關(guān)注
3文章
117瀏覽量
22493 
發(fā)布評論請先 登錄
VD6283TX環(huán)境光傳感器驅(qū)動開發(fā)(1)----獲取ID
    
VD6283TX環(huán)境光傳感器驅(qū)動開發(fā)(2)----獲取光強(qiáng)和色溫
    
VD6283TX環(huán)境光傳感器驅(qū)動開發(fā)(3)----測試閃爍頻率代碼
    
環(huán)境光傳感器的介紹
基于藍(lán)牙BLE的環(huán)境光傳感器方案
如何選擇一款環(huán)境光傳感器?環(huán)境光傳感器有哪些應(yīng)用?
環(huán)境光傳感器是如何工作的
環(huán)境光傳感器應(yīng)用
意法半導(dǎo)體發(fā)布了一款多光譜環(huán)境光傳感器VD6281
找方案 | 基于ST環(huán)境光傳感器VD6283TX 針對LED投影機(jī)光源調(diào)變方案
    
VD6283TX環(huán)境光傳感器(1)----獲取光強(qiáng)和色溫
    
STEVAL-PDETECT1:多傳感器融合的存在檢測評估套件
    
          
        
        
VD6283TX環(huán)境光傳感器(2)----移植閃爍頻率代碼
                
 
    
    
           
            
            
                
            
評論