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

在使用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。

static void Timer2PwmInit(uint16_t arr,uint16_t psc)

{

        /*

        //PC5复用TIM2_CH1

        GPIOC->AFR &= 0XFF0FFFFF;//

        GPIOC->AFR |= 0X00800000;

        

        GPIOC->PUPDR &= 0XFFFFF3FF;

        GPIOC->PUPDR |= 0X00000800;//

        */

        

        //PC5_TIM2-CH1

        /*

        GPIOC->DIRCR &= 0XFFFFFFDF;

        GPIOC->DIRCR |= 0X00000020;

        */

        /*

        //PD3复用TIM2_CH2

        GPIOD->AFR &= 0XFFFF0FFF;//

        GPIOD->AFR |= 0X00008000;

        

        GPIOC->PUPDR &= 0XFFFFFF3F;

        GPIOC->PUPDR |= 0X00000020;//

        */

        //PD3_TIM2_CH2

        /*

        GPIOD->DIRCR &= 0XFFFFFFF7;

        GPIOD->DIRCR |= 0X000000F8;

        */

        

        

        /*

        TIM1->ARR = arr;//设定计数器自动重装值 

        TIM1->PSC = psc;//预分频器不分频

        

        TIM1->CCMR1 |= 7<<12;  //OC2模式 TIM1_CH3,TIM1_CH4                 

        TIM1->CCMR1 |= 1<<11;         //CH4预装载使能           

        TIM1->CCER  |= 1<<4;           //OC2 输出使能           

        TIM1->CR1    = 0x0080; //ARPE使能 

        TIM1->CR1   |= 0x01;  //使能定时器1                

        

        ///TMR3->CC4 = BK_LEVEL3;///BK_LEVEL_MOST_LOWER;

        setPwmVol(TIM1,1000-1);//即90% 占空比

        setPwmTone(TIM1,1000-1);

        */

        

        GPIO_InitTypeDef         gpioInitStruct;

        ///TIM_HandleTypeDef         tim2InitStruct;

        ///TIM_OC_InitTypeDef  tim2OcInitStruct = {0};

        

        __HAL_RCC_TIM2_CLK_ENABLE();        

        __HAL_RCC_GPIOA_CLK_ENABLE();

        __HAL_RCC_GPIOC_CLK_ENABLE();

        __HAL_RCC_GPIOD_CLK_ENABLE();

        

        gpioInitStruct.Pin = TIM2_CH1OUT_PIN;        

        gpioInitStruct.Mode = GPIO_MODE_AF;

        gpioInitStruct.Pull = GPIO_PULLDOWN;

        gpioInitStruct.OpenDrain = GPIO_PUSHPULL;        

        gpioInitStruct.Debounce.Enable = GPIO_DEBOUNCE_DISABLE;

        gpioInitStruct.SlewRate = GPIO_SLEW_RATE_HIGH;

        gpioInitStruct.DrvStrength = GPIO_DRV_STRENGTH_HIGH;

        gpioInitStruct.Alternate = TIM2_CH1OUT_GPIO_AFN;

        HAL_GPIO_Init(TIM2_CH1OUT_PORT, &gpioInitStruct);                

        

        gpioInitStruct.Pin = TIM2_CH2OUT_PIN;                

        gpioInitStruct.Alternate = TIM2_CH2OUT_GPIO_AFN;

        HAL_GPIO_Init(TIM2_CH2OUT_PORT, &gpioInitStruct);        

        

        gpioInitStruct.Pin = TIM2_CH3OUT_PIN;                

        gpioInitStruct.Alternate = TIM2_CH3OUT_GPIO_AFN;

        HAL_GPIO_Init(TIM2_CH3OUT_PORT, &gpioInitStruct);        

        

        sTim2_Handle.Instance                           = TIM2;

        sTim2_Handle.Init.Period                        = arr;// TIM1_ARR 周期

        sTim2_Handle.Init.Prescaler                 = psc;// 计数器的时钟频率(CK_CNT)等于fCK_PSC/(PSC[15:0]+1) 即计数器的时钟频率=TIMx_FREQ

        sTim2_Handle.Init.ClockDivision         = 0;// CKD 时钟分频因子(Clock division) 

        sTim2_Handle.Init.CounterMode                = TIM_COUNTERMODE_UP; // 边沿对齐模式 计数器向上计数

        sTim2_Handle.Init.RepetitionCounter = 0;// TIM1_RCR 重复计数器的值

        sTim2_Handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; // 禁止自动重装载

        HAL_TIM_PWM_Init(&sTim2_Handle);

        

        sTim2_OcInitHandle.OCMode = TIM_OCMODE_PWM1; // PWM模式1 TIM1_CCMR1

        //                sTim1_OcInitHandle.OCPolarity = TIM_OCPOLARITY_LOW; // 输出极性 OC1低电平有效

        //                sTim1_OcInitHandle.OCNPolarity = TIM_OCNPOLARITY_LOW; // OC1N低电平有效

        //                sTim1_OcInitHandle.OCFastMode = TIM_OCFAST_ENABLE; // 输出比较1清’0’使能

        //                sTim1_OcInitHandle.OCIdleState = TIM_OCIDLESTATE_RESET; // MOE=0时,如果实现了OC1N,则死区后OC1=0;

        //                sTim1_OcInitHandle.OCNIdleState = TIM_OCNIDLESTATE_RESET; // MOE=0时,死区后OC1N=0



        ///tim1OcInitStruct.Pulse = TIM1_CH1_PULSEWIDTH;        // CCR 捕获/比较通道         // 占空比值

        

        ///tim1OcInitStruct.Pulse = TIM1_CH2_PULSEWIDTH;

        

        sTim2_OcInitHandle.Pulse = TIM2_CH1_PULSEWIDTH;        

        HAL_TIM_PWM_ConfigChannel(&sTim2_Handle, &sTim2_OcInitHandle, TIM_CHANNEL_1);

        

        sTim2_OcInitHandle.Pulse  = TIM1_CH2_PULSEWIDTH;

        HAL_TIM_PWM_ConfigChannel(&sTim2_Handle, &sTim2_OcInitHandle, TIM_CHANNEL_2);



        sTim2_OcInitHandle.Pulse  = TIM2_CH3_PULSEWIDTH;

        HAL_TIM_PWM_ConfigChannel(&sTim2_Handle, &sTim2_OcInitHandle, TIM_CHANNEL_3);

        /*##-3- Start PWM signals generation #######################################*/ 

        /* Start channel 1 */

        

        HAL_TIM_PWM_Start(&sTim2_Handle, TIM_CHANNEL_1);

        /* Start channel 2 */

        HAL_TIM_PWM_Start(&sTim2_Handle, TIM_CHANNEL_2);

        /* Start channel 3 */

        HAL_TIM_PWM_Start(&sTim2_Handle, TIM_CHANNEL_3);

        

        /* Start channel 4 */

        

}

那么S和V就是直接调节占空比得到,而S是根据一个圆的维度调色得到颜色值,我们这里就用360/1000 得到H,也就对应了RGB的颜色。而S和V是饱和度和明度,也就是颜色的鲜艳程度和亮暗程度。

//将大写字母转化成小写字母

int tolower(int c)

{

        if(c >= 'A' && c <= 'Z')

        {

                return c + 'a' - 'A';

        }

        else 

        {

                return c;

        }

}



//将十六进制字符串转换成十进制整数

int htoi(char s[],char ucLentoStr)

{

         int i,j;

         int n = 0;

         if (s[0] == '0' && (s[1]=='x' || s[1]=='X'))

         {

                        i = 2;

         }

         else

         {

                 i = 0;

         }

         

                for (i=0;((s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >='A' && s[i] <= 'Z')) && i<ucLentoStr;i++)

                {

                 if (tolower(s[i]) > '9')

                 {

                                n = 16 * n + (10 + tolower(s[i]) - 'a');

                 }

                 else

                 {

                                n = 16 * n + (tolower(s[i]) - '0');

                 }

                } 

         

         

         return n;

}



static void hsvSlipAdjust(unsigned char *ucHsvString,bool bIfSave)

{

        float fRgbMin,fRgbMax,fRgbAdjust,s,v,r,g,b;

        uint16_t usHsvEepromAddr,i,difs,h,eeprom_addr,usRgbMax,usRgbMin,usRgbAdjust;

        

        char ucNum,ucHsv[UPDATE_COLOR_STR_NUM];

        unsigned char ucEepromHsv[6] = {0};

        

        for(ucNum=0;ucNum<4;ucNum++)

        {

                CPwmCtrl.ucCh3HueStr[ucNum] = ucHsvString[ucNum];

                CPwmCtrl.ucCh3SaturationStr[ucNum] = ucHsvString[4 + ucNum];

                CPwmCtrl.ucCh3ValueStr[ucNum] = ucHsvString[8 + ucNum];

        }

        

        CPwmCtrl.usCh3SaturationTarget = htoi(CPwmCtrl.ucCh3SaturationStr,4);

        CPwmCtrl.usCh3ValueTarget = htoi(CPwmCtrl.ucCh3ValueStr,4);

        

        CPwmCtrl.usCh3HueTarget = htoi(CPwmCtrl.ucCh3HueStr,4);

        

        //HSV to RGB

#if 1

        h = CPwmCtrl.usCh3HueTarget;

        s = CPwmCtrl.usCh3SaturationTarget / 1000.0f;

        v = CPwmCtrl.usCh3ValueTarget / 1000.0f;

        

        

        if(h >= HUE_TOP_VALUE)

        {

                h = 0.0f;

        }

        if(s == 0.0f)

        {

                r = v;

                g = v;

                b = v;

        }

        else

        {

                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 

                

                switch(i)

                {

                        case 0:

                                

                                r = fRgbMax;

                                g = fRgbMin + fRgbAdjust;

                                b = fRgbMin;

                                break;

                        

                        case 1:

                                r = fRgbMax - fRgbAdjust;

                                g = fRgbMax;

                                b = fRgbMin;

                        

                                break;

                                

                        case 2:

                                r = fRgbMin;

                                g = fRgbMax;

                                b = fRgbMin + fRgbAdjust;

                        

                                break;

                                

                        case 3:

                                r = fRgbMin;

                                g = fRgbMax - fRgbAdjust;

                                b = fRgbMax;

                        

                                break;

                                

                        case 4:

                                r = fRgbMin + fRgbAdjust;

                                g = fRgbMin;

                                b = fRgbMax;

                        

                                break;

                                

                        case 5:

                                r = fRgbMax;

                                g = fRgbMin;

                                b = fRgbMax - fRgbAdjust;

                        

                                break;

                                

                        default:

                                /*

                                r = fRgbMax;

                                g = fRgbMin;

                                b = fRgbMax - fRgbAdjust;

                                */

                                break;

                

                }

        }

        CPwmCtrl.usCh3RedTarget = (uint16_t)round(r * 255);

        CPwmCtrl.usCh3GreenTarget = (uint16_t)round(g * 255);

        CPwmCtrl.usCh3BlueTarget = (uint16_t)round(b * 255);

#else



        fRgbMax = CPwmCtrl.usCh3ValueTarget * RGB_LIMIT / 1000.0f;

        fRgbMin = fRgbMax * ((TIMER_ARR+1) - CPwmCtrl.usCh3SaturationTarget) / 1000.0f;

        

        i = CPwmCtrl.usCh3HueTarget / 60;

        difs = CPwmCtrl.usCh3HueTarget % 60;

        fRgbAdjust = (usRgbMax - usRgbMin)*difs / 60.0f;

        

        switch(i)

        {

                case 0:

                        CPwmCtrl.usCh3RedTarget = fRgbMax;

                        CPwmCtrl.usCh3GreenTarget = (fRgbMin + fRgbAdjust);

                        CPwmCtrl.usCh3BlueTarget = fRgbMin;

                        break;

                        

                case 1:

                        CPwmCtrl.usCh3RedTarget = (fRgbMax - fRgbAdjust);

                        CPwmCtrl.usCh3GreenTarget = fRgbMax;

                        CPwmCtrl.usCh3BlueTarget = fRgbMin;

                

                        break;

                        

                case 2:

                        CPwmCtrl.usCh3RedTarget = fRgbMin;

                        CPwmCtrl.usCh3GreenTarget = fRgbMax;

                        CPwmCtrl.usCh3BlueTarget = (fRgbMin + fRgbAdjust);

                

                        break;

                        

                case 3:

                        CPwmCtrl.usCh3RedTarget = fRgbMin;

                        CPwmCtrl.usCh3GreenTarget = (fRgbMax - fRgbAdjust);

                        CPwmCtrl.usCh3BlueTarget = fRgbMax / 1000.0f;

                

                        break;

                        

                case 4:

                        CPwmCtrl.usCh3RedTarget = (fRgbMin + fRgbAdjust);

                        CPwmCtrl.usCh3GreenTarget = fRgbMin;

                        CPwmCtrl.usCh3BlueTarget = fRgbMax;

                

                        break;

                /*

                case 5:

                        CPwmCtrl.usCh3RedTarget = fRgbMax;

                        CPwmCtrl.usCh3GreenTarget = fRgbMin;

                        CPwmCtrl.usCh3BlueTarget = (fRgbMax - fRgbAdjust);

                        break;

                */

                default:

                        

                        CPwmCtrl.usCh3RedTarget = fRgbMax;

                        CPwmCtrl.usCh3GreenTarget = fRgbMin;

                        CPwmCtrl.usCh3BlueTarget = (fRgbMax - fRgbAdjust);

                        

                        break;

        }

        /*

        if(r > 1000)

        {

                r = 1000;

        }

        

        if(g > 1000)

        {

                g = 1000;

        }

        

        if(b > 1000)

        {

                b = 1000;

        }

        */

#endif

/*

        CPwmCtrl.ucCh3RedTarget = (uint16_t)round(r * 255000);///(uint16_t)(r * (TIMER_ARR + 1) / 255);///(uint16_t)round(r * 25500);

        CPwmCtrl.ucCh3GreenTarget = (uint16_t)round(g * 255000);///(uint16_t)(g * (TIMER_ARR + 1) / 255);///(uint16_t)round(g * 25500);

        CPwmCtrl.ucCh3BlueTarget = (uint16_t)round(b * 255000);///(uint16_t)(b * (TIMER_ARR + 1) / 255);///(uint16_t)round(b * 25500);

*/

        pwm_SetPulseWidth(CPwmCtrl.usCh3RedTarget,CH2_RED);

        __asm("nop");

        

        pwm_SetPulseWidth(CPwmCtrl.usCh3GreenTarget,CH2_GREEN);

        __asm("nop");

        

        pwm_SetPulseWidth(CPwmCtrl.usCh3BlueTarget,CH2_BLUE);

        __asm("nop");

        

        if(bIfSave)

        {

                

                ucEepromHsv[0] = (uint8_t)(CPwmCtrl.usCh3HueTarget >> 8);

                ucEepromHsv[1] = (uint8_t)CPwmCtrl.usCh3HueTarget;

                ucEepromHsv[2] = (uint8_t)(CPwmCtrl.usCh3SaturationTarget >> 8);

                ucEepromHsv[3] = (uint8_t)CPwmCtrl.usCh3SaturationTarget;

                ucEepromHsv[4] = (uint8_t)(CPwmCtrl.usCh3ValueTarget >> 8);

                ucEepromHsv[5] = (uint8_t)(CPwmCtrl.usCh3ValueTarget);

                

                __asm("nop");

                CAppFlash.WriteBytes(FLASH_PROGRAM_ADDRESS_START,0x06,ucEepromHsv,6);

        }

}

因为涂鸦的协议是用字符串下发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就可以体现出颜色的饱和度和明度了。
---------------------
作者:hejun96
链接:https://bbs.21ic.com/icview-3160936-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值