旧乡故客
发表于: 2018-8-28 11:21:34 | 显示全部楼层

本篇文章主要介绍如何采集并处理由BH1745NUC色彩传感器生成的RGB数据。


传感器

在第1部分中,我们讨论了如何使用DAC和一些负反​​馈来精确控制红色、绿色和蓝色LED的强度。我们现在可以使用RGB LED模块作为单像素显示器 - 即,通过操纵红色、绿色和蓝色光的混合,我们可以产生多种颜色。


我们希望使用这个LED模块复制照亮RGB传感器的光线颜色。如第一篇文章中所述,我们使用由Rohm制造的BH1745NUC颜色传感器(以下简称为BH1745)。这实际上是一个令人印象深刻的设备封装非常小(大约2 mm×2 mm),这也是我们为这个项目使用定制设计PCB的一个原因。尽管体积小,但该部件具有广泛的功能,仅需少量外部组件。以下是数据表中的“典型应用电路”:

RGB_LED2_datasheet1.jpg

电路中需要光学滤波器、四个光电二极管、四个独立的16位ADC以及一些信号调制电路、一个I2C接口和一些中断逻辑,可用于在红色、绿色、蓝色或透明测量值超过时提醒微控制器或者低于可定制的阈值 。


以下是原理图的相关部分:

RGB_LED2_schem1.jpg


收集数据

BH1745的数字部分包括一组21个8位寄存器。除了中断功能之外的所有交互 - 在微控制器和BH1745之间通过标准I2C事务写入或读取这些寄存器来完成。。这里我们将重点介绍BH1745的具体实现细节。


从BH1745控制和检索数据需要三种类型的I2C事务:写入、读取前读取和读取。

●    写入:这些事务用于将数据加载到BH1745的寄存器库中。 slave-address-plus-R / nW字节后的第一个字节指定寄存器地址,然后后面的字节是要加载到寄存器中的数据。

RGB_LED2_datasheet2.jpg

●    读之前写入:如果您熟悉I2C协议,则您知道主机无法在一个事务中写入和读取数据。每个事务都定义为读或写。因此,我们不能使用单个事务来指示寄存器地址,然后从该寄存器读回数据。解决方案是两个独立的事务 - 首先我们将数据写入BH1745以告诉它我们想要读取哪个寄存器,然后我们跟进读取事务以从指定寄存器中检索数据。这个过程中的第一个事务就是我所说的read-before-read事务。

●    读取:这些事务允许主机从读写前事务中传输的任何寄存器地址读取数据。

RGB_LED2_datasheet3.jpg


如您所见,读取事务不限于指定的寄存器地址。如果继续从BH1745读取字节,它将自动递增寄存器地址并从新寄存器发送数据。实际上,您可以使用写入事务执行相同的操作:

RGB_LED2_datasheet4.jpg


我通常避免自动递增功能,因为我重视保持简单原则,这在这里特别相关,因为BH1745的寄存器没有连续排列(即,无效寄存器地址混合在有效寄存器地址中)。但是,我在读取RGBC数据时会使用此功能 - 所有8个字节都是连续的(从地址0x50开始),使用16个单独的单字节事务频繁收集RGBC数据将是非常低效的(8个写入之前 - 读取和8读取)。


另请注意,可以使用重复启动条件(如上图所示)实现读前读和读事务,而不是停止条件,后跟启动条件。如果我们在I2C总线上有多个主设备,这将是更好的选择。但是在这个项目中,我们只有一个master,所以我们将再次调用keep-it-simple原则并使用典型的stop-then-start方法。


处理数据

RGBC数据从BH1745到达为四个16位字,如下所示:

RGB_LED2_datasheet5.jpg

我们可以忽略这个项目的明确数据;我们需要做的就是将R、G和B字转换为8位值,我们可以使用这些值来控制R、G和B LED的强度。首先要意识到BH1745中的三个颜色探测器不是同样敏感的:

RGB_LED2_datasheet6.jpg


从该图可以看出,当G为1时,R约为0.72,B约为0.56。因此,我们需要将R和B值乘以适当的校正因子:

CFR = 10.72 = 1.39,CFB = 10.56 = 1.79

现在我们需要以强调入射光的颜色特征的方式修改数据。我们的目标是“测量”颜色,无论照射光电探测器的光的总强度如何。因此,我们需要以一种标准化测量绝对值的方式缩放RGB值,同时保留相对值 - 换句话说,我们最大化整体强度,同时保持入射光中红色、绿色和蓝色的比例。为了实现这一点,我们将三次测量中的最高值乘以任何因子将该最高测量值增加到最大值,然后我们将另外两次测量值乘以相同的因子。此评论的代码摘录阐明了整个过程:

  1. //extract the 16-bit R, G, and B values from the received data
  2. R_word = (I2C_RcvData[1] << 8) | I2C_RcvData[0];
  3. G_word = (I2C_RcvData[3] << 8) | I2C_RcvData[2];
  4. B_word = (I2C_RcvData[5] << 8) | I2C_RcvData[4];

  5. //apply correction factors based on the relative sensitivity of the three photodetectors
  6. R_intensity = R_word * 1.39;
  7. G_intensity = G_word * 1;
  8. B_intensity = B_word * 1.79;

  9. //determine which intensity is the highest of the three
  10. if(R_intensity >= G_intensity && R_intensity >= B_intensity)
  11.         MaxIntensity = R_intensity;
  12. else if(G_intensity >= R_intensity && G_intensity >= B_intensity)
  13.         MaxIntensity = G_intensity;
  14. else               
  15.         MaxIntensity = B_intensity;

  16. /*Now we scale each measurement into the range 0 to 100.
  17. * This preserves the relative ratios of the three
  18. * intensities but standardizes the absolute intensities.
  19. * Thus, the appearance of the LED module is
  20. * determined by the proportion of red, green,
  21. * and blue light, not by the overall intensity of
  22. * the incident light.*/
  23. R_scaled = (R_intensity/MaxIntensity)*100;
  24. G_scaled = (G_intensity/MaxIntensity)*100;
  25. B_scaled = (B_intensity/MaxIntensity)*100;
复制代码

请注意,最终值被缩放,使得最大值为100。您可以从上一篇文章中回忆起DAC具有8位分辨率,因此我们可以高达255.那么为什么我要将LED强度限制为100而不是使用完整的8位范围?因为盯着这个高达20毫安的LED模块正在弄乱我的视线!光是如此聚焦和光谱纯净,似乎以加重的方式混淆眼睛。


详细步骤

以下是配置BH1745、收集数据、处理数据和更新DAC的整个过程。

1.    写入寄存器0x42以启用RGBC转换。

2.    延迟1秒(或测量之间的一些其他适当间隔)。

3.    写入寄存器地址0x50以准备读取RGBC数据。

4.    读取8字节的RGBC数据(对于这个项目,我们只需要前6个字节)。

5.    等到I2C事务完成。

6.    将16位RGB数据读入三个无符号16位变量。

7.    将这三个变量转换为浮点值,同时将每个变量乘以适当的校正因子。

8.    找到三者中的最大值,并如上所述缩放每个值;将结果存储在无符号的8位变量中。

9.    通过将8位值加载到适当的DAC通道来更新LED颜色。


固件

您可以打开“hwconf”文件以访问端口引脚和外设的配置详细信息。另请注意,这些源文件包含一些代码,直到我们合并USB连接时才需要这些代码。

以下是一些比较重要的代码段。 首先,main()函数:

  1. int main(void) {

  2.         unsigned char R_scaled, G_scaled, B_scaled;
  3.         unsigned int R_word, G_word, B_word;
  4.         float R_intensity, G_intensity, B_intensity, MaxIntensity;

  5.         // Call hardware initialization routine
  6.         enter_DefaultMode_from_RESET();

  7.         //enable global interrupts
  8.         IE_EA = 1;

  9.         //tell the RGBC sensor to start performing measurements
  10.         I2C_MasterWrite(RGBC_Tx_EnableConv);

  11.         while (1)
  12.         {
  13.                 Delay_10ms(100);        //the LED is updated once per second

  14.                 //load the proper register address (for reading) into the RGBC sensor
  15.                 I2C_MasterWrite(RGBC_Tx_SetReadRGBC);

  16.                 //read the RGBC data
  17.                 I2C_MasterRead(RGBC_Rx_RGBC);

  18.                 //wait until the read transaction is complete
  19.                 while(I2C_State != MstR_DATA_READY);
  20.                 I2C_State = IDLE;

  21.                 //extract the 16-bit R, G, and B values from the received data
  22.                 R_word = (I2C_RcvData[1] << 8) | I2C_RcvData[0];
  23.                 G_word = (I2C_RcvData[3] << 8) | I2C_RcvData[2];
  24.                 B_word = (I2C_RcvData[5] << 8) | I2C_RcvData[4];

  25.                 //apply correction factors based on the relative sensitivity of the three photodetectors
  26.                 R_intensity = R_word * 1.39;
  27.                 G_intensity = G_word * 1;
  28.                 B_intensity = B_word * 1.79;

  29.                 //determine which intensity is the highest of the three
  30.                 if(R_intensity >= G_intensity && R_intensity >= B_intensity)
  31.                         MaxIntensity = R_intensity;
  32.                 else if(G_intensity >= R_intensity && G_intensity >= B_intensity)
  33.                         MaxIntensity = G_intensity;
  34.                 else
  35.                         MaxIntensity = B_intensity;

  36.                 /*Now we scale each measurement into the range 0 to 100.
  37.                  * This preserves the relative ratios of the three
  38.                  * intensities but standardizes the absolute intensities.
  39.                  * Thus, the appearance of the LED module is
  40.                  * determined by the proportion of red, green,
  41.                  * and blue light, not by the overall intensity of
  42.                  * the incident light.*/
  43.                 R_scaled = (R_intensity/MaxIntensity)*100;
  44.                 G_scaled = (G_intensity/MaxIntensity)*100;
  45.                 B_scaled = (B_intensity/MaxIntensity)*100;

  46.                 /* This can be used to turn off the LED if the measured
  47.                  * light intensity is too low. This helps to avoid distracting
  48.                  * color changes related to irrelevant RGB variations detected
  49.                  * during low-intensity lighting conditions. This functionality
  50.                  * was used with the "Christmas lights" demonstration to make
  51.                  * the LED turn off when the sensor was not illuminated by one
  52.                  * of the lights.*/
  53.                 if(MaxIntensity < 100)
  54.                 {
  55.                         R_scaled = 0;
  56.                         G_scaled = 0;
  57.                         B_scaled = 0;
  58.                 }

  59.                 //update each DAC channel with the scaled values
  60.                 UpdateDAC(DAC_RGB_R, R_scaled);
  61.                 UpdateDAC(DAC_RGB_G, G_scaled);
  62.                 UpdateDAC(DAC_RGB_B, B_scaled);
  63.         }
  64. }
复制代码

此代码配置并启动I2C事务:

  1. unsigned char I2C_SlaveAddr;        //global variable for current slave address
  2. unsigned char I2C_NumReadBytes;        //number of bytes to be read
  3. unsigned char idata *I2C_WriteBufferPtr;        //pointer to bytes to be transmitted
  4. unsigned char I2C_FinalWriteAddress;        //the ISR uses this to determine which byte is the final byte

  5. /*These "transaction arrays" contain all the information needed for a particular I2C transaction*/

  6. //write to register address 0x42 to enable RGBC conversions and keep the gain at default (1x)
  7. unsigned char idata RGBC_Tx_EnableConv[4] = {RGB_SENS_ADDR, 2, 0x42, 0x10};

  8. //write to register address 0x42 to enable RGBC conversions and set the gain to 16x
  9. //(so far it appears that it is best to leave the gain at 1x)
  10. unsigned char idata RGBC_Tx_EnableConv_16x[4] = {RGB_SENS_ADDR, 2, 0x42, 0x12};

  11. //set the read address to 0x50, which is the beginning of the registers that hold RGBC data
  12. unsigned char idata RGBC_Tx_SetReadRGBC[3] = {RGB_SENS_ADDR, 1, 0x50};

  13. //read RGBC data (after setting the read address using RGBC_Tx_SetReadRGBC )
  14. unsigned char idata RGBC_Rx_RGBC[3] = {RGB_SENS_ADDR, RGBC_DATA_LEN};


  15. void I2C_MasterWrite(unsigned char* PtrtoCmdBuffer)        //function argument is simply the name of the transaction array
  16. {
  17.         //ensure that we are not interrupting an ongoing transaction
  18.         while(I2C_State != IDLE);

  19.         I2C_State = MstW_STA_SENT;        //first state is "start condition generated"
  20.         I2C_SlaveAddr = PtrtoCmdBuffer[0];        //copy the slave address from the transaction array to the global variable
  21.         I2C_WriteBufferPtr = PtrtoCmdBuffer + 2;        //set the address of the first data byte in the transaction array
  22.         I2C_FinalWriteAddress = I2C_WriteBufferPtr + (PtrtoCmdBuffer[1] - 1);        //set the final address based on the number of bytes to be transmitted

  23.         SFRPAGE = SMB0_PAGE;
  24.         SMB0CN0_STA = 1;        //initiate the transaction by setting the start-condition bit
  25. }

  26. void I2C_MasterRead(unsigned char* PtrtoCmdBuffer)        //function argument is simply the name of the transaction array
  27. {
  28.         //ensure that we are not interrupting an ongoing transaction
  29.         while(I2C_State != IDLE);

  30.         I2C_State = MstR_STA_SENT;        //first state is "start condition generated"
  31.         I2C_SlaveAddr = PtrtoCmdBuffer[0];        //copy the slave address from the transaction array to the global variable
  32.         I2C_NumReadBytes = PtrtoCmdBuffer[1];        //copy the number of bytes to be read from the transaction array to the global variable

  33.         SFRPAGE = SMB0_PAGE;
  34.         SMB0CN0_STA = 1;        //initiate the transaction by setting the start-condition bit
  35. }
复制代码

跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 29 | 回复: 32



手机版|

GMT+8, 2024-5-7 02:50 , Processed in 0.039091 second(s), 6 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

快速回复 返回顶部 返回列表