返回列表 发新帖我要提问本帖赏金: 10.00元(功能说明)

[国产单片机] 如何利用CX32L003的pwm调节灯光颜色?看完秒懂

[复制链接]
13062|0
 楼主| hejun96 发表于 2021-9-2 09:40 | 显示全部楼层 |阅读模式
#申请原创# @21小跑堂
   在使用CX32L003的pwm功能后,我们用的是涂鸦模组做调光的灯光类产品的开发,因为调光是根据涂鸦模组app调节不同的灯光亮度,颜色渐变达到炫丽的灯光颜色变化,所以对pwm要灵活的调节占空比达到光,色,亮度相互饱和的效果。

        以上显示的是灯带的R,G,B颜色渐变的效果,这个RGB的颜色是根据HSV颜色体系进行转换得到的,HSV(hue,saturation,value)颜色空间的模型对应于圆柱坐标系中的一个圆锥形子集,圆锥的顶面对应于V=1. 它包含RGB模型中的R=1,G=1,B=1 三个面,所代表的颜色较亮。色彩H由绕V轴的旋转角给定。红色对应于 角度0° ,绿色对应于角度120°,蓝色对应于角度240°。在HSV颜色模型中,每一种颜色和它的补色相差180° 。 饱和度S取值从0到1,所以圆锥顶面的半径为1。HSV颜色模型所代表的颜色域是CIE色度图的一个子集,这个 模型中饱和度为百分之百的颜色,其纯度一般小于百分之百。在圆锥的顶点(即原点)处,V=0,H和S无定义, 代表黑色。圆锥的顶面中心处S=0,V=1,H无定义,代表白色。从该点到原点代表亮度渐暗的灰色,即具有不同 灰度的灰色。对于这些点,S=0,H的值无定义。可以说,HSV模型中的V轴对应于RGB颜色空间中的主对角线。 在圆锥顶面的圆周上的颜色,V=1,S=1,这种颜色是纯色。

     也就是说这里H的范围是0~360,而S和V分别是对应0-1000。这里我们把定时器的pwm设置成1kHz,占空比从0-1000范围可调。这里我用TIM2来初始化R,G,B的pwm。
  1. static void Timer2PwmInit(uint16_t arr,uint16_t psc)
  2. {
  3.         /*
  4.         //PC5复用TIM2_CH1
  5.         GPIOC->AFR &= 0XFF0FFFFF;//
  6.         GPIOC->AFR |= 0X00800000;
  7.         
  8.         GPIOC->PUPDR &= 0XFFFFF3FF;
  9.         GPIOC->PUPDR |= 0X00000800;//
  10.         */
  11.         
  12.         //PC5_TIM2-CH1
  13.         /*
  14.         GPIOC->DIRCR &= 0XFFFFFFDF;
  15.         GPIOC->DIRCR |= 0X00000020;
  16.         */
  17.         /*
  18.         //PD3复用TIM2_CH2
  19.         GPIOD->AFR &= 0XFFFF0FFF;//
  20.         GPIOD->AFR |= 0X00008000;
  21.         
  22.         GPIOC->PUPDR &= 0XFFFFFF3F;
  23.         GPIOC->PUPDR |= 0X00000020;//
  24.         */
  25.         //PD3_TIM2_CH2
  26.         /*
  27.         GPIOD->DIRCR &= 0XFFFFFFF7;
  28.         GPIOD->DIRCR |= 0X000000F8;
  29.         */
  30.         
  31.         
  32.         /*
  33.         TIM1->ARR = arr;//设定计数器自动重装值
  34.         TIM1->PSC = psc;//预分频器不分频
  35.         
  36.         TIM1->CCMR1 |= 7<<12;  //OC2模式 TIM1_CH3,TIM1_CH4                 
  37.         TIM1->CCMR1 |= 1<<11;         //CH4预装载使能           
  38.         TIM1->CCER  |= 1<<4;           //OC2 输出使能           
  39.         TIM1->CR1    = 0x0080; //ARPE使能
  40.         TIM1->CR1   |= 0x01;  //使能定时器1               
  41.         
  42.         ///TMR3->CC4 = BK_LEVEL3;///BK_LEVEL_MOST_LOWER;
  43.         setPwmVol(TIM1,1000-1);//即90% 占空比
  44.         setPwmTone(TIM1,1000-1);
  45.         */
  46.         
  47.         GPIO_InitTypeDef         gpioInitStruct;
  48.         ///TIM_HandleTypeDef         tim2InitStruct;
  49.         ///TIM_OC_InitTypeDef  tim2OcInitStruct = {0};
  50.         
  51.         __HAL_RCC_TIM2_CLK_ENABLE();        
  52.         __HAL_RCC_GPIOA_CLK_ENABLE();
  53.         __HAL_RCC_GPIOC_CLK_ENABLE();
  54.         __HAL_RCC_GPIOD_CLK_ENABLE();
  55.         
  56.         gpioInitStruct.Pin = TIM2_CH1OUT_PIN;        
  57.         gpioInitStruct.Mode = GPIO_MODE_AF;
  58.         gpioInitStruct.Pull = GPIO_PULLDOWN;
  59.         gpioInitStruct.OpenDrain = GPIO_PUSHPULL;        
  60.         gpioInitStruct.Debounce.Enable = GPIO_DEBOUNCE_DISABLE;
  61.         gpioInitStruct.SlewRate = GPIO_SLEW_RATE_HIGH;
  62.         gpioInitStruct.DrvStrength = GPIO_DRV_STRENGTH_HIGH;
  63.         gpioInitStruct.Alternate = TIM2_CH1OUT_GPIO_AFN;
  64.         HAL_GPIO_Init(TIM2_CH1OUT_PORT, &gpioInitStruct);               
  65.         
  66.         gpioInitStruct.Pin = TIM2_CH2OUT_PIN;               
  67.         gpioInitStruct.Alternate = TIM2_CH2OUT_GPIO_AFN;
  68.         HAL_GPIO_Init(TIM2_CH2OUT_PORT, &gpioInitStruct);        
  69.         
  70.         gpioInitStruct.Pin = TIM2_CH3OUT_PIN;               
  71.         gpioInitStruct.Alternate = TIM2_CH3OUT_GPIO_AFN;
  72.         HAL_GPIO_Init(TIM2_CH3OUT_PORT, &gpioInitStruct);        
  73.         
  74.         sTim2_Handle.Instance                           = TIM2;
  75.         sTim2_Handle.Init.Period                        = arr;// TIM1_ARR 周期
  76.         sTim2_Handle.Init.Prescaler                 = psc;// 计数器的时钟频率(CK_CNT)等于fCK_PSC/(PSC[15:0]+1) 即计数器的时钟频率=TIMx_FREQ
  77.         sTim2_Handle.Init.ClockDivision         = 0;// CKD 时钟分频因子(Clock division)
  78.         sTim2_Handle.Init.CounterMode                = TIM_COUNTERMODE_UP; // 边沿对齐模式 计数器向上计数
  79.         sTim2_Handle.Init.RepetitionCounter = 0;// TIM1_RCR 重复计数器的值
  80.         sTim2_Handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; // 禁止自动重装载
  81.         HAL_TIM_PWM_Init(&sTim2_Handle);
  82.         
  83.         sTim2_OcInitHandle.OCMode = TIM_OCMODE_PWM1; // PWM模式1 TIM1_CCMR1
  84.         //                sTim1_OcInitHandle.OCPolarity = TIM_OCPOLARITY_LOW; // 输出极性 OC1低电平有效
  85.         //                sTim1_OcInitHandle.OCNPolarity = TIM_OCNPOLARITY_LOW; // OC1N低电平有效
  86.         //                sTim1_OcInitHandle.OCFastMode = TIM_OCFAST_ENABLE; // 输出比较1清’0’使能
  87.         //                sTim1_OcInitHandle.OCIdleState = TIM_OCIDLESTATE_RESET; // MOE=0时,如果实现了OC1N,则死区后OC1=0;
  88.         //                sTim1_OcInitHandle.OCNIdleState = TIM_OCNIDLESTATE_RESET; // MOE=0时,死区后OC1N=0

  89.         ///tim1OcInitStruct.Pulse = TIM1_CH1_PULSEWIDTH;        // CCR 捕获/比较通道         // 占空比值
  90.         
  91.         ///tim1OcInitStruct.Pulse = TIM1_CH2_PULSEWIDTH;
  92.         
  93.         sTim2_OcInitHandle.Pulse = TIM2_CH1_PULSEWIDTH;        
  94.         HAL_TIM_PWM_ConfigChannel(&sTim2_Handle, &sTim2_OcInitHandle, TIM_CHANNEL_1);
  95.         
  96.         sTim2_OcInitHandle.Pulse  = TIM1_CH2_PULSEWIDTH;
  97.         HAL_TIM_PWM_ConfigChannel(&sTim2_Handle, &sTim2_OcInitHandle, TIM_CHANNEL_2);

  98.         sTim2_OcInitHandle.Pulse  = TIM2_CH3_PULSEWIDTH;
  99.         HAL_TIM_PWM_ConfigChannel(&sTim2_Handle, &sTim2_OcInitHandle, TIM_CHANNEL_3);
  100.         /*##-3- Start PWM signals generation #######################################*/
  101.         /* Start channel 1 */
  102.         
  103.         HAL_TIM_PWM_Start(&sTim2_Handle, TIM_CHANNEL_1);
  104.         /* Start channel 2 */
  105.         HAL_TIM_PWM_Start(&sTim2_Handle, TIM_CHANNEL_2);
  106.         /* Start channel 3 */
  107.         HAL_TIM_PWM_Start(&sTim2_Handle, TIM_CHANNEL_3);
  108.         
  109.         /* Start channel 4 */
  110.         
  111. }
那么S和V就是直接调节占空比得到,而S是根据一个圆的维度调色得到颜色值,我们这里就用360/1000 得到H,也就对应了RGB的颜色。而S和V是饱和度和明度,也就是颜色的鲜艳程度和亮暗程度。
  1. //将大写字母转化成小写字母
  2. int tolower(int c)
  3. {
  4.         if(c >= 'A' && c <= 'Z')
  5.         {
  6.                 return c + 'a' - 'A';
  7.         }
  8.         else
  9.         {
  10.                 return c;
  11.         }
  12. }

  13. //将十六进制字符串转换成十进制整数
  14. int htoi(char s[],char ucLentoStr)
  15. {
  16.          int i,j;
  17.          int n = 0;
  18.          if (s[0] == '0' && (s[1]=='x' || s[1]=='X'))
  19.          {
  20.                         i = 2;
  21.          }
  22.          else
  23.          {
  24.                  i = 0;
  25.          }
  26.          
  27.                 for (i=0;((s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >='A' && s[i] <= 'Z')) && i<ucLentoStr;i++)
  28.                 {
  29.                  if (tolower(s[i]) > '9')
  30.                  {
  31.                                 n = 16 * n + (10 + tolower(s[i]) - 'a');
  32.                  }
  33.                  else
  34.                  {
  35.                                 n = 16 * n + (tolower(s[i]) - '0');
  36.                  }
  37.                 }
  38.          
  39.          
  40.          return n;
  41. }

  42. static void hsvSlipAdjust(unsigned char *ucHsvString,bool bIfSave)
  43. {
  44.         float fRgbMin,fRgbMax,fRgbAdjust,s,v,r,g,b;
  45.         uint16_t usHsvEepromAddr,i,difs,h,eeprom_addr,usRgbMax,usRgbMin,usRgbAdjust;
  46.         
  47.         char ucNum,ucHsv[UPDATE_COLOR_STR_NUM];
  48.         unsigned char ucEepromHsv[6] = {0};
  49.         
  50.         for(ucNum=0;ucNum<4;ucNum++)
  51.         {
  52.                 CPwmCtrl.ucCh3HueStr[ucNum] = ucHsvString[ucNum];
  53.                 CPwmCtrl.ucCh3SaturationStr[ucNum] = ucHsvString[4 + ucNum];
  54.                 CPwmCtrl.ucCh3ValueStr[ucNum] = ucHsvString[8 + ucNum];
  55.         }
  56.         
  57.         CPwmCtrl.usCh3SaturationTarget = htoi(CPwmCtrl.ucCh3SaturationStr,4);
  58.         CPwmCtrl.usCh3ValueTarget = htoi(CPwmCtrl.ucCh3ValueStr,4);
  59.         
  60.         CPwmCtrl.usCh3HueTarget = htoi(CPwmCtrl.ucCh3HueStr,4);
  61.         
  62.         //HSV to RGB
  63. #if 1
  64.         h = CPwmCtrl.usCh3HueTarget;
  65.         s = CPwmCtrl.usCh3SaturationTarget / 1000.0f;
  66.         v = CPwmCtrl.usCh3ValueTarget / 1000.0f;
  67.         
  68.         
  69.         if(h >= HUE_TOP_VALUE)
  70.         {
  71.                 h = 0.0f;
  72.         }
  73.         if(s == 0.0f)
  74.         {
  75.                 r = v;
  76.                 g = v;
  77.                 b = v;
  78.         }
  79.         else
  80.         {
  81.                 fRgbMax = v;
  82.                 fRgbMin = fRgbMax * (1.0f - s);
  83.                 i = h / 60;
  84.                 difs = h % 60;//factoraial part of H
  85.                
  86.                 fRgbAdjust = (fRgbMax - fRgbMin) * difs / 60.0f;// RGB adjustment amount by hue
  87.                
  88.                 switch(i)
  89.                 {
  90.                         case 0:
  91.                                 
  92.                                 r = fRgbMax;
  93.                                 g = fRgbMin + fRgbAdjust;
  94.                                 b = fRgbMin;
  95.                                 break;
  96.                         
  97.                         case 1:
  98.                                 r = fRgbMax - fRgbAdjust;
  99.                                 g = fRgbMax;
  100.                                 b = fRgbMin;
  101.                         
  102.                                 break;
  103.                                 
  104.                         case 2:
  105.                                 r = fRgbMin;
  106.                                 g = fRgbMax;
  107.                                 b = fRgbMin + fRgbAdjust;
  108.                         
  109.                                 break;
  110.                                 
  111.                         case 3:
  112.                                 r = fRgbMin;
  113.                                 g = fRgbMax - fRgbAdjust;
  114.                                 b = fRgbMax;
  115.                         
  116.                                 break;
  117.                                 
  118.                         case 4:
  119.                                 r = fRgbMin + fRgbAdjust;
  120.                                 g = fRgbMin;
  121.                                 b = fRgbMax;
  122.                         
  123.                                 break;
  124.                                 
  125.                         case 5:
  126.                                 r = fRgbMax;
  127.                                 g = fRgbMin;
  128.                                 b = fRgbMax - fRgbAdjust;
  129.                         
  130.                                 break;
  131.                                 
  132.                         default:
  133.                                 /*
  134.                                 r = fRgbMax;
  135.                                 g = fRgbMin;
  136.                                 b = fRgbMax - fRgbAdjust;
  137.                                 */
  138.                                 break;
  139.                
  140.                 }
  141.         }
  142.         CPwmCtrl.usCh3RedTarget = (uint16_t)round(r * 255);
  143.         CPwmCtrl.usCh3GreenTarget = (uint16_t)round(g * 255);
  144.         CPwmCtrl.usCh3BlueTarget = (uint16_t)round(b * 255);
  145. #else

  146.         fRgbMax = CPwmCtrl.usCh3ValueTarget * RGB_LIMIT / 1000.0f;
  147.         fRgbMin = fRgbMax * ((TIMER_ARR+1) - CPwmCtrl.usCh3SaturationTarget) / 1000.0f;
  148.         
  149.         i = CPwmCtrl.usCh3HueTarget / 60;
  150.         difs = CPwmCtrl.usCh3HueTarget % 60;
  151.         fRgbAdjust = (usRgbMax - usRgbMin)*difs / 60.0f;
  152.         
  153.         switch(i)
  154.         {
  155.                 case 0:
  156.                         CPwmCtrl.usCh3RedTarget = fRgbMax;
  157.                         CPwmCtrl.usCh3GreenTarget = (fRgbMin + fRgbAdjust);
  158.                         CPwmCtrl.usCh3BlueTarget = fRgbMin;
  159.                         break;
  160.                         
  161.                 case 1:
  162.                         CPwmCtrl.usCh3RedTarget = (fRgbMax - fRgbAdjust);
  163.                         CPwmCtrl.usCh3GreenTarget = fRgbMax;
  164.                         CPwmCtrl.usCh3BlueTarget = fRgbMin;
  165.                
  166.                         break;
  167.                         
  168.                 case 2:
  169.                         CPwmCtrl.usCh3RedTarget = fRgbMin;
  170.                         CPwmCtrl.usCh3GreenTarget = fRgbMax;
  171.                         CPwmCtrl.usCh3BlueTarget = (fRgbMin + fRgbAdjust);
  172.                
  173.                         break;
  174.                         
  175.                 case 3:
  176.                         CPwmCtrl.usCh3RedTarget = fRgbMin;
  177.                         CPwmCtrl.usCh3GreenTarget = (fRgbMax - fRgbAdjust);
  178.                         CPwmCtrl.usCh3BlueTarget = fRgbMax / 1000.0f;
  179.                
  180.                         break;
  181.                         
  182.                 case 4:
  183.                         CPwmCtrl.usCh3RedTarget = (fRgbMin + fRgbAdjust);
  184.                         CPwmCtrl.usCh3GreenTarget = fRgbMin;
  185.                         CPwmCtrl.usCh3BlueTarget = fRgbMax;
  186.                
  187.                         break;
  188.                 /*
  189.                 case 5:
  190.                         CPwmCtrl.usCh3RedTarget = fRgbMax;
  191.                         CPwmCtrl.usCh3GreenTarget = fRgbMin;
  192.                         CPwmCtrl.usCh3BlueTarget = (fRgbMax - fRgbAdjust);
  193.                         break;
  194.                 */
  195.                 default:
  196.                         
  197.                         CPwmCtrl.usCh3RedTarget = fRgbMax;
  198.                         CPwmCtrl.usCh3GreenTarget = fRgbMin;
  199.                         CPwmCtrl.usCh3BlueTarget = (fRgbMax - fRgbAdjust);
  200.                         
  201.                         break;
  202.         }
  203.         /*
  204.         if(r > 1000)
  205.         {
  206.                 r = 1000;
  207.         }
  208.         
  209.         if(g > 1000)
  210.         {
  211.                 g = 1000;
  212.         }
  213.         
  214.         if(b > 1000)
  215.         {
  216.                 b = 1000;
  217.         }
  218.         */
  219. #endif
  220. /*
  221.         CPwmCtrl.ucCh3RedTarget = (uint16_t)round(r * 255000);///(uint16_t)(r * (TIMER_ARR + 1) / 255);///(uint16_t)round(r * 25500);
  222.         CPwmCtrl.ucCh3GreenTarget = (uint16_t)round(g * 255000);///(uint16_t)(g * (TIMER_ARR + 1) / 255);///(uint16_t)round(g * 25500);
  223.         CPwmCtrl.ucCh3BlueTarget = (uint16_t)round(b * 255000);///(uint16_t)(b * (TIMER_ARR + 1) / 255);///(uint16_t)round(b * 25500);
  224. */
  225.         pwm_SetPulseWidth(CPwmCtrl.usCh3RedTarget,CH2_RED);
  226.         __asm("nop");
  227.         
  228.         pwm_SetPulseWidth(CPwmCtrl.usCh3GreenTarget,CH2_GREEN);
  229.         __asm("nop");
  230.         
  231.         pwm_SetPulseWidth(CPwmCtrl.usCh3BlueTarget,CH2_BLUE);
  232.         __asm("nop");
  233.         
  234.         if(bIfSave)
  235.         {
  236.                
  237.                 ucEepromHsv[0] = (uint8_t)(CPwmCtrl.usCh3HueTarget >> 8);
  238.                 ucEepromHsv[1] = (uint8_t)CPwmCtrl.usCh3HueTarget;
  239.                 ucEepromHsv[2] = (uint8_t)(CPwmCtrl.usCh3SaturationTarget >> 8);
  240.                 ucEepromHsv[3] = (uint8_t)CPwmCtrl.usCh3SaturationTarget;
  241.                 ucEepromHsv[4] = (uint8_t)(CPwmCtrl.usCh3ValueTarget >> 8);
  242.                 ucEepromHsv[5] = (uint8_t)(CPwmCtrl.usCh3ValueTarget);
  243.                
  244.                 __asm("nop");
  245.                 CAppFlash.WriteBytes(FLASH_PROGRAM_ADDRESS_START,0x06,ucEepromHsv,6);
  246.         }
  247. }
因为涂鸦的协议是用字符串下发HSV的值,例如"023401320128",H=0x0234,S=0x0132,V=0x0128,所以这里先要把字符串转换成十六进制,每次转换4个字符,所以调用htoi函数字符串的每次转换长度为4。这里设置pwm通道的值也可以直接用TIM->CCR寄存器。


  根据公式,每60°一个等分,也就是把360° 6等分,然后根据颜色值在圆上的区域,结合代码理解,每一个case是由H进行选择,
          fRgbMax = v;
         fRgbMin = fRgbMax * (1.0f - s);
          i = h / 60;
          difs = h % 60;//factoraial part of H     
          fRgbAdjust = (fRgbMax - fRgbMin) * difs / 60.0f;// RGB adjustment amount by hue
把S和V代入计算可知,max的值始终大于min的值,当S最大且V最小或者是S最小V最大,fRgbAdjust的值都为0,这样S和V就可以体现出颜色的饱和度和明度了。






本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×

打赏榜单

21小跑堂 打赏了 10.00 元 2021-09-02
理由:恭喜通过原创文章审核!请多多加油哦!

您需要登录后才可以回帖 登录 | 注册

本版积分规则

10

主题

55

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部