打印
[APM32E1]

【APM32E103xE测评】基于APM32E103的RGB七彩渐变呼吸灯

[复制链接]
1200|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 lilijin1995 于 2022-8-16 14:04 编辑

【APM32E103xE测评】基于APM32E103的RGB七彩渐变呼吸灯附送渐变算法并配置1ms Systick移植了MultiTimer
非常荣幸在“二姨家”申请到了“APM32E103ZE MINI板”,由于手头上有一个简单的控灯项目,所以直接想在APM32E103ZE跑跑看;
本贴分以下几个部分:

  • 硬件介绍:
      硬件主要有12V的RGB灯板和RGB-MOS管驱动板;如下图

另有原理图如下:


  • 软件介绍:
      软件主要分两部分:
      1. MultiTimer移植:          MultiTimer是开源项目 ,一款可无限扩展的软件定时器,作者0x1abin,目前收获 95 个 star,遵循 MIT 开源许可协议。MultiTimer 是一个软件定时器扩展模块,可无限扩展你所需的定时器任务,取代传统的标志位判断方式, 更优雅更便捷地管理程序的时间触发时序。
         首先需要配置Systick为1ms中断一次SysTick_Config(SystemCoreClock / 1000),
int main(void)
{
    SysTick_Config(SystemCoreClock / 1000);
    Board_Init();   
    while(1)
    {
          MultiTimerYield();
    }
}
并在SysTick_Handler中自加
void SysTick_Handler(void)
{
    tick++;
}<span style="background-color: rgb(255, 255, 255);">        </span>
接着我们添加MultiTimer代码
#include "MultiTimer.h"
#include <stdio.h>
/* Timer handle list head. */
static MultiTimer* timerList = NULL;
/* Timer tick */
static PlatformTicksFunction_t platformTicksFunction = NULL;
int MultiTimerInstall(PlatformTicksFunction_t ticksFunc)
{
    platformTicksFunction = ticksFunc;
    return 0;
}
int MultiTimerStart(MultiTimer* timer, uint64_t timing, MultiTimerCallback_t callback, void* userData)
{
    if (!timer || !callback ) {
        return -1;
    }
    MultiTimer** nextTimer = &timerList;
    /* Remove the existing target timer. */
    for (; *nextTimer; nextTimer = &(*nextTimer)->next) {
        if (timer == *nextTimer) {
            *nextTimer = timer->next; /* remove from list */
            break;
        }
    }
    /* Init timer. */
    timer->deadline = platformTicksFunction() + timing;
    timer->callback = callback;
    timer->userData = userData;
    /* Insert timer. */
    for (nextTimer = &timerList;; nextTimer = &(*nextTimer)->next) {
        if (!*nextTimer) {
            timer->next = NULL;
            *nextTimer = timer;
            break;
        }
        if (timer->deadline < (*nextTimer)->deadline) {
            timer->next = *nextTimer;
            *nextTimer = timer;
            break;
        }
    }
    return 0;
}
int MultiTimerStop(MultiTimer* timer)
{
    MultiTimer** nextTimer = &timerList;
    /* Find and remove timer. */
    for (; *nextTimer; nextTimer = &(*nextTimer)->next) {
        MultiTimer* entry = *nextTimer;
        if (entry == timer) {
            *nextTimer = timer->next;
            break;
        }
    }
    return 0;
}
int MultiTimerYield(void)
{
    MultiTimer* entry = timerList;
    for (; entry; entry = entry->next) {
        /* Sorted list, just process with the front part. */
        if (platformTicksFunction() < entry->deadline) {
            return (int)(entry->deadline - platformTicksFunction());
        }
        /* remove expired timer from list */
        timerList = entry->next;

        /* call callback */
        if (entry->callback) {
            entry->callback(entry, entry->userData);
        }
    }
    return 0;
}


最后我们直接开两个定时器即两个线程,一个作为按键扫描,一个作为灯光的控制
#include "Board.h"
#if defined (APM32E103_MINI)
#include "Board_APM32E103_MINI.c"
#include "RGB.h"
#include "KEY.h"
volatile uint64_t tick = 0;
MultiTimer timer1;
MultiTimer timer2;

uint64_t PlatformTicksGetFunc(void)
{
    return tick;
}

void RGBTimer1Callback(MultiTimer* timer, void *userData)
{
    switch(RGB_Eff)
    {
    case DYN_RGB:
    {
        GradientDreamColor();
    }
    break;
    case DYN_RED:
    {
        GradientDreamSingleColor(DYN_RED);
    }
    break;
    case DYN_ORANGE:
    {
        GradientDreamSingleColor(DYN_ORANGE);
    }
    break;
    case DYN_YELLOW:
    {
        GradientDreamSingleColor(DYN_YELLOW);
    }
    break;
    case DYN_GREE:
    {
        GradientDreamSingleColor(DYN_GREE);
    }
    break;
    case DYN_CYAN:
    {
        GradientDreamSingleColor(DYN_CYAN);
    }
    break;
    case DYN_BLUE:
    {
        GradientDreamSingleColor(DYN_BLUE);
    }
    break;
    case DYN_PURPLE:
    {
        GradientDreamSingleColor(DYN_PURPLE);
    }
    break;
    case STATICK_RED:
    {
        SetRGB_PWM(0xFF,0x00,0x00);
    }
    break;
    case STATICK_ORANGE:
    {
        SetRGB_PWM(225,69,0);
    }
    break;
    case STATICK_YELLOW:
    {
        SetRGB_PWM(0xFF,0xFF,0x00);
    }
    break;
    case STATICK_GREEN:
    {
        SetRGB_PWM(0x00,0xFF,0x00);
    }
    break;
    case STATICK_CYAN:
    {
        SetRGB_PWM(0x00,0xFF,0xFF);
    }
    break;
    case STATICK_BLUE:
    {
        SetRGB_PWM(0x00,0x00,0xFF);
    }
    break;
    case STATICK_PURPLE:
    {
        SetRGB_PWM(0xFF,0x00,0xFF);
    }
    break;
    case STATICK_WHITE:
    {
        SetRGB_PWM(0xFF,0xFF,0xFF);
    }
    break;
    case STATICK_BLACK:
    {
        SetRGB_PWM(0x00,0x00,0x00);
    }
    break;
    default:
        break;
    }
    MultiTimerStart(timer, 10, RGBTimer1Callback, userData);
}

        2. KEY驱动:
#include "KEY.h"
#include "main.h"
void KEY_Init(void)
{
    GPIO_Config_T GPIO_ConfigStruct;
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
    GPIO_ConfigStruct.pin = GPIO_PIN_0|GPIO_PIN_1;                          //PB0--TMR3_CH3
    GPIO_ConfigStruct.mode = GPIO_MODE_IN_PU;
    GPIO_Config(GPIOA, &GPIO_ConfigStruct);
}
u8 KEY_Handler(void)
{
          static u8 key1bounce=0,key2bounce=0;
          u8 key=0;
                if(GPIO_ReadInputBit(GPIOA,GPIO_PIN_0)==BIT_RESET)
                {
                        key1bounce=1;
                }else{
                        if(key1bounce)
                        {
                                key|=0x01;
                        }
                        key1bounce=0;
                        
                }
                if(GPIO_ReadInputBit(GPIOA,GPIO_PIN_1)==BIT_RESET)
                {
                        key2bounce=1;
                }else{
                        if(key2bounce)
                        {
                                key|=0x02;
                        }
                        key2bounce=0;
                        
                }        
                return key;
}
         3. RGB驱动:
/*!
* [url=home.php?mod=space&uid=288409]@file[/url]        RGB.c
*
* [url=home.php?mod=space&uid=247401]@brief[/url]       This file provides firmware functions to manage Leds and push-buttons
*
* [url=home.php?mod=space&uid=895143]@version[/url]     V1.0.0
*
* [url=home.php?mod=space&uid=212281]@date[/url]        2021-07-26
*
*/

#include "RGB.h"
#include "main.h"

//RGB
RGB_LED_EFFECT RGB_Eff=DYN_RGB;

void RGB_Init(void)
{
    GPIO_Config_T GPIO_ConfigStruct;
    TMR_BaseConfig_T TMR_TimeBaseStruct;
    TMR_OCConfig_T OCcongigStruct;

    RCM_EnableAPB2PeriphClock((RCM_APB2_PERIPH_T)(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_GPIOB));
    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR3);

    GPIO_ConfigStruct.pin = GPIO_PIN_6|GPIO_PIN_7;//PA6--TMR3_CH1
    GPIO_ConfigStruct.mode = GPIO_MODE_AF_PP;          //PA7--TMR3_CH2
    GPIO_ConfigStruct.speed = GPIO_SPEED_50MHz;
    GPIO_Config(GPIOA, &GPIO_ConfigStruct);

    GPIO_ConfigStruct.pin = GPIO_PIN_0;                          //PB0--TMR3_CH3
    GPIO_ConfigStruct.mode = GPIO_MODE_AF_PP;
    GPIO_ConfigStruct.speed = GPIO_SPEED_50MHz;
    GPIO_Config(GPIOB, &GPIO_ConfigStruct);

    TMR_TimeBaseStruct.clockDivision = TMR_CLOCK_DIV_1;
    TMR_TimeBaseStruct.countMode = TMR_COUNTER_MODE_UP;
    TMR_TimeBaseStruct.division = 71;
    TMR_TimeBaseStruct.period = 255;
    TMR_ConfigTimeBase(TMR3, &TMR_TimeBaseStruct);

    OCcongigStruct.idleState = TMR_OC_IDLE_STATE_RESET;
    OCcongigStruct.mode = TMR_OC_MODE_PWM1;
    OCcongigStruct.nIdleState = TMR_OC_NIDLE_STATE_RESET;
    OCcongigStruct.nPolarity = TMR_OC_NPOLARITY_HIGH;
    OCcongigStruct.outputNState = TMR_OC_NSTATE_ENABLE;
    OCcongigStruct.outputState = TMR_OC_STATE_ENABLE;
    OCcongigStruct.polarity = TMR_OC_POLARITY_HIGH;
    OCcongigStruct.pulse = 0;
    TMR_ConfigOC1(TMR3, &OCcongigStruct);
    TMR_ConfigOC2(TMR3, &OCcongigStruct);
    TMR_ConfigOC3(TMR3, &OCcongigStruct);

    TMR_ConfigOC1Preload(TMR3, TMR_OC_PRELOAD_ENABLE);
    TMR_ConfigOC2Preload(TMR3, TMR_OC_PRELOAD_ENABLE);
    TMR_ConfigOC3Preload(TMR3, TMR_OC_PRELOAD_ENABLE);
    TMR_EnableAUTOReload(TMR3);
    TMR_Enable(TMR3);
    TMR_EnablePWMOutputs(TMR3);
}
void SetRGB_PWM(u16 R,u16 G,u16 B)
{
    TMR3->CC1=R;
    TMR3->CC2=G;
    TMR3->CC3=B;
}
const u8 R[8]= {255,191,127,0,0,0,150,255};
const u8 G[8]= {0,69,127,255,127,0,2,0};
const u8 B[8]= {0,0,0,0,127,255,134,0};
unsigned char abs0(int num)
{
    if(num>0) return num;
    num = -num;
    return (unsigned char) num;
}
u8 ColorToColor(u8 r0,u8 g0,u8 b0, u8 r1,u8 g1,u8 b1,float* RedStep,float* GreenStep,float* BlueStep)
{
    unsigned char Red0, Green0, Blue0;  // 起始三原色
    unsigned char Red1, Green1, Blue1;  // 结果三原色
    int                          RedMinus, GreenMinus, BlueMinus;        // 颜色差(color1 - color0)
    u8 NStep;
    //unsigned long color;                                                        // 结果色
    // 绿 红 蓝 三原色分解
    Red0   = r0;
    Green0 = g0;
    Blue0  = b0;
    Red1   = r1;
    Green1 = g1;
    Blue1  = b1;
    // 计算需要多少步(取差值的最大值)
    RedMinus   = Red1 - Red0;
    GreenMinus = Green1 - Green0;
    BlueMinus  = Blue1 - Blue0;

    NStep = ( abs0(RedMinus) > abs0(GreenMinus) ) ? abs0(RedMinus):abs0(GreenMinus);
    NStep = ( NStep > abs0(BlueMinus) ) ? NStep:abs0(BlueMinus);

    // 计算出各色步进值
    RedStep[0]   = (float)RedMinus   / NStep;
    GreenStep[0] = (float)GreenMinus / NStep;
    BlueStep[0]  = (float)BlueMinus  / NStep;

    // 渐变开始
//        for(i=0; i<NStep; i++)
//        {
//                Red1   = Red0   + (int)(RedStep   * i);
//                Green1 = Green0 + (int)(GreenStep * i);
//                Blue1  = Blue0  + (int)(BlueStep  * i);
//
//                //color  = Green1<<16 | Red1<<8 | Blue1;         // 合成  绿红蓝
//                SetPWM_for_RGB(Red1,Green1,Blue1);
//
//                DelayMs(1);                                                // 渐变速度
//        }
    // 渐变结束

    return NStep;
}

void GradientDreamSingleColor(RGB_LED_EFFECT type)
{
    unsigned char Red, Green, Blue;  // 结果三原色
    static  u8  first=0,tick=0,cStep=0,tp=1,rgb=0;
    static float RedStep, GreenStep, BlueStep;
    static RGB_LED_EFFECT typelast=DYN_RED;
    if(typelast!=type)
    {
        SetRGB_PWM(0,0,0);
    }
    typelast=type;

    if(first==0)
    {
        first=1;
        switch(type)
        {
        case DYN_RED:
        {
            rgb=0;
            if(tp==0)
            {
                tp=1;
                cStep=ColorToColor(R[rgb],G[rgb],B[rgb],0,0,0,&RedStep,&GreenStep,&BlueStep);
            } else {
                tp=0;
                cStep=ColorToColor(0,0,0,R[rgb],G[rgb],B[rgb],&RedStep,&GreenStep,&BlueStep);
            }
        }
        break;
        case DYN_ORANGE:
        {
            rgb=1;
            if(tp==0)
            {
                tp=1;
                cStep=ColorToColor(R[rgb],G[rgb],B[rgb],0,0,0,&RedStep,&GreenStep,&BlueStep);
            } else {
                tp=0;
                cStep=ColorToColor(0,0,0,R[rgb],G[rgb],B[rgb],&RedStep,&GreenStep,&BlueStep);
            }
        }
        break;
        case DYN_YELLOW:
        {
            rgb=2;
            if(tp==0)
            {
                tp=1;
                cStep=ColorToColor(R[rgb],G[rgb],B[rgb],0,0,0,&RedStep,&GreenStep,&BlueStep);
            } else {
                tp=0;
                cStep=ColorToColor(0,0,0,R[rgb],G[rgb],B[rgb],&RedStep,&GreenStep,&BlueStep);
            }
        }
        break;
        case DYN_GREE:
        {
            rgb=3;
            if(tp==0)
            {
                tp=1;
                cStep=ColorToColor(R[rgb],G[rgb],B[rgb],0,0,0,&RedStep,&GreenStep,&BlueStep);
            } else {
                tp=0;
                cStep=ColorToColor(0,0,0,R[rgb],G[rgb],B[rgb],&RedStep,&GreenStep,&BlueStep);
            }
        }
        break;
        case DYN_CYAN:
        {
            rgb=4;
            if(tp==0)
            {
                tp=1;
                cStep=ColorToColor(R[rgb],G[rgb],B[rgb],0,0,0,&RedStep,&GreenStep,&BlueStep);
            } else {
                tp=0;
                cStep=ColorToColor(0,0,0,R[rgb],G[rgb],B[rgb],&RedStep,&GreenStep,&BlueStep);
            }
        }
        break;
        case DYN_BLUE:
        {
            rgb=5;
            if(tp==0)
            {
                tp=1;
                cStep=ColorToColor(R[rgb],G[rgb],B[rgb],0,0,0,&RedStep,&GreenStep,&BlueStep);
            } else {
                tp=0;
                cStep=ColorToColor(0,0,0,R[rgb],G[rgb],B[rgb],&RedStep,&GreenStep,&BlueStep);
            }
        }
        break;
        case DYN_PURPLE:
        {
            rgb=6;
            if(tp==0)
            {
                tp=1;
                cStep=ColorToColor(R[rgb],G[rgb],B[rgb],0,0,0,&RedStep,&GreenStep,&BlueStep);
            } else {
                tp=0;
                cStep=ColorToColor(0,0,0,R[rgb],G[rgb],B[rgb],&RedStep,&GreenStep,&BlueStep);
            }
        }
        break;
        default:
            break;


        }

    } else {

        if(tick<cStep)
        {
            if(tp==0)
            {
                Red   =0+ (int)(RedStep   * tick);
                Green = 0+ (int)(GreenStep * tick);
                Blue  =  0+(int)(BlueStep  *tick);
            } else {
                Red   =R[rgb]   + (int)(RedStep   * tick);
                Green = G[rgb]   + (int)(GreenStep * tick);
                Blue  = B[rgb]   + (int)(BlueStep  *tick);
            }

            SetRGB_PWM(Red,Green,Blue);

        } else {
            tick=0;
            first=0;
        }
        tick++;

    }
}


void GradientDreamColor(void)
{
    unsigned char Red, Green, Blue;  // 结果三原色
    static  u8  first=0,i=0,tick=0,cStep=0;
    static float RedStep, GreenStep, BlueStep;

    static  u16  delaytick=0;
    if(delaytick++>5)
    {
        delaytick=0;
        if(first==0)
        {
            first=1;
            cStep=ColorToColor(R[i],G[i],B[i],R[i+1],G[i+1],B[i+1],&RedStep,&GreenStep,&BlueStep);
        } else {

            if(tick<cStep)
            {
                Red   =R[i]   + (int)(RedStep   * tick);
                Green = G[i]   + (int)(GreenStep * tick);
                Blue  = B[i]   + (int)(BlueStep  *tick);
                SetRGB_PWM(Red,Green,Blue);

            } else {
                tick=0;
                if(++i>=7)
                {
                    i=0;

                }
                first=0;
            }
            tick++;
        }
    }
}



最后实验可以看我们的视频:
https://www.bilibili.com/video/BV1ZT411A77M/?vd_source=2bbde87de845d5220b1d8ba075c12fb0

并附上代码

APM32E10x_SDK_V1.0.zip

1.62 MB

使用特权

评论回复
沙发
lajfda001| | 2022-8-16 10:17 | 只看该作者
代码整洁,没有任何的瑕疵,感谢楼主的分享。

使用特权

评论回复
板凳
单片小菜| | 2022-8-16 10:51 | 只看该作者
你用的是最小系统板吗?还是别的?

使用特权

评论回复
地板
jflahdink09| | 2022-8-16 11:17 | 只看该作者
可以传一下视频吗?看看视频的效果如何

使用特权

评论回复
5
lilijin1995|  楼主 | 2022-8-16 13:58 | 只看该作者
jflahdink09 发表于 2022-8-16 11:17
可以传一下视频吗?看看视频的效果如何

https://www.bilibili.com/video/BV1ZT411A77M/?vd_source=2bbde87de845d5220b1d8ba075c12fb0

使用特权

评论回复
6
lilijin1995|  楼主 | 2022-8-16 13:59 | 只看该作者
单片小菜 发表于 2022-8-16 10:51
你用的是最小系统板吗?还是别的?

二姨家发的APM32E103xE Mini板

使用特权

评论回复
7
单片小菜| | 2022-8-16 16:20 | 只看该作者
lilijin1995 发表于 2022-8-16 13:59
二姨家发的APM32E103xE Mini板

厉害的,不错,不错的!

使用特权

评论回复
8
juliestephen| | 2022-8-16 19:53 | 只看该作者
这个如何调整占空比呢?  

使用特权

评论回复
9
lilijin1995|  楼主 | 2022-8-17 10:08 | 只看该作者
juliestephen 发表于 2022-8-16 19:53
这个如何调整占空比呢?
void SetRGB_PWM(u16 R,u16 G,u16 B)
{
    TMR3->CC1=R;
    TMR3->CC2=G;
    TMR3->CC3=B;
}



设置比较值就可以

使用特权

评论回复
10
wengh2016| | 2022-8-18 17:23 | 只看该作者
使用pwm吗   

使用特权

评论回复
11
duo点| | 2022-8-18 17:30 | 只看该作者
厉害了,不错不错

使用特权

评论回复
12
duo点| | 2022-8-18 17:30 | 只看该作者
支持一下

使用特权

评论回复
13
uiint| | 2022-8-18 17:58 | 只看该作者
APM32E103性能怎么样  

使用特权

评论回复
14
lilijin1995|  楼主 | 2022-9-7 09:23 | 只看该作者
uiint 发表于 2022-8-18 17:58
APM32E103性能怎么样

强大,很强

使用特权

评论回复
15
lzbf| | 2022-9-8 08:43 | 只看该作者
这个可以驱动ws8212吗

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

54

主题

162

帖子

4

粉丝