大蔡一 · 1月4日 · 浙江

【GD32微控制器原理与应用+苹果派开发板评测】基于苹果派GD32F303的PWM调光器

首先感谢极术社区、安谋科技学堂以及兆易创新给与的评测机会!

背景:

在刷朋友圈的时候突然刷到极术社区的赠书活动,填完抽奖表后发现还有一个GD32F3苹果派开发板测评活动,目前从事新能源领域的工作,刚好一直再用GD32系列的单片机,索性就参加了测评活动。
首先,我们来做一下芯片参数的硬件测评,也就拿我们的32位MCU的老大哥STM32来跟我们的国产单片机一哥来进行对比,数据部分来自立创商城,不过稍微瞄了一眼数据手册发现立创商城里登记的部分参数不太对,最后还是根据芯片手册改了一下,仅供参考。
image.png
个人总结:根据立创商城的价格来看GD32F303ZET6比STM32F303ZET6的价格低了25%左右,120MHz主频比72MHz主频提升66%,有没有一种买电脑挑CPU的感觉?其次,外设大家都差不多,值得一提的是兆易创新是做NOR FLASH出身所以芯片内的FLASH是自主研发的,根据芯片手册该款芯片具有256KB的零等待区,简单点来说代码执行更快没有延迟,有兴趣的小伙伴可以自己去深入了解一下。

测评想法:

制作使用RTThread的一个PWM调光器(点灯大师),使用一个RGB三原色灯珠,采用3路PWM进行调光控制,用板载的三个按键进行控制,此控制器无论灯珠多少皆可适用。

测评历程:

本人也在公司里参与灯光造型方案的研发和设计,不过比这个会高级很多,由于日常996加上自己很喜欢学东西,基本上也就和007没差了,时间不太够,也是晚上顶着熬夜来写的,刚开始想加的东西很多,RS485和CAN通信,甚至是阿里云平台,后来一估算时间发现根本做不完,这些东西就都砍掉了,最后剩下一个RTOS和PWM调光,主要也是体验苹果派开发板为主。
QQ图片20240103234836.jpg
本人平时从事的工作也是关于GD32系列单片机开发,所以基本GD32系列单片机大部分都有接触过。
QQ图片20240103235012.jpg
同时也比较期待最近发布的GD32H7的开发板,能给我们带来更多的体验。

代码分享:

由于一开始想做的很多,结果框架越搭越大,最后也是无奈时间有限就砍掉了,但是框架还是在的,在嵌入式后期的话架构是很重要的。
image.png
软件架构分为驱动层、中间层、应用层。
使用的是MDK自带的RTThtrad组件,方便易用。
image.png
下面是代码部分
image.png
image.png

static rt_sem_t     beep_sem        = RT_NULL;        //蜂鸣器信号量
static rt_event_t   rgb_led_event   = RT_NULL;      //RGB灯事件集
static rt_mq_t      key_mq          = RT_NULL;      //按键消息队列
static rt_mq_t      duoji_mq        = RT_NULL;
//舵机消息队列,用于传输1字节的指令,
//0:舵机正传+1°
//1: 舵机反转+1°
//2:舵机以每秒10°的速度正转,直到极限角度
//3:舵机以每秒10°的速度反转,直到极限角度

static led_struct led = {0};


void rgb_led_thread_entry(void *parameter)
{
    uint8_t key_value = 0;

    while(1)
    {
        //获取按键值完成相应的操作
        if(RT_EOK == rt_mq_recv(key_mq,&key_value,1,RT_WAITING_FOREVER))
        {
            switch(key_value)
            {
                case 1:led_light_add(&led);break;//增加某一颜色亮度
                case 2:led_colour_change(&led);break;//切换某一颜色
                case 3:led_light_reduce(&led);break;//减少某一颜色亮度
            }
        }
        rt_thread_mdelay(100);        
    }
}
void duoji_thread_entry(void *parameter)
{
    while(1)
    {
        rt_thread_mdelay(500);        
    }
}
void beep_thread_entry(void *parameter)
{
    //按键按下将获取一次信号量
    while(1)
    {
        if(rt_sem_take(beep_sem,RT_WAITING_FOREVER) == RT_EOK)
        {
            beep_once();
        }
    }
}
void key_thread_entry(void *parameter)
{//50ms读取一次按键,使用状态机判断长按和短按,利用消息队列传递指令
    
    
    while(1)
    {
        uint8_t key_num = key_read();    //获取按键按下值
        rt_mq_send(key_mq,&key_num,1);  //按键值压入消息队列
        key_response(key_num,beep_sem);    //按一下蜂鸣器响一下
        key_clear();                    //按键状态清除
        rt_thread_mdelay(50);       
    }
}

int rgb_led_thread_init(void)
{
   /* RGB灯动态线程 */
   rgb_led_thread = rt_thread_create("rgb_led_thread",
                     rgb_led_thread_entry,RT_NULL,
                     RGB_LED_THREAD_STACK_SIZE,
                     RGB_LED_THREAD_PRIORITY,
                     RGB_LED_THREAD_TIMESLICE);
    if(rgb_led_thread != RT_NULL)
        rt_thread_startup(rgb_led_thread);
    else
        return RT_ERROR;
    return RT_EOK;
}
int duoji_thread_init(void)
{
   /* 舵机动态线程 */
   duoji_thread = rt_thread_create("duoji_thread",
                     duoji_thread_entry,RT_NULL,
                     DUOJI_THREAD_STACK_SIZE,
                     DUOJI_THREAD_PRIORITY,
                     DUOJI_THREAD_TIMESLICE);
    if(duoji_thread != RT_NULL)
        rt_thread_startup(duoji_thread);
    else
        return RT_ERROR;
    return RT_EOK;
}
int beep_thread_init(void)
{
   beep_sem = rt_sem_create("beep_sem",0,RT_IPC_FLAG_FIFO);
   /* 蜂鸣器动态线程 */
   beep_thread = rt_thread_create("beep_thread",
                     beep_thread_entry,RT_NULL,
                     BEEP_THREAD_STACK_SIZE,
                     BEEP_THREAD_PRIORITY,
                     BEEP_THREAD_TIMESLICE);
    if(beep_thread != RT_NULL)
        rt_thread_startup(beep_thread);
    else
        return RT_ERROR;
    return RT_EOK;
}
int key_thread_init(void)
{
    key_mq = rt_mq_create("key_mq",1,10,RT_IPC_FLAG_FIFO);
   /* 按键动态线程 */
   key_thread = rt_thread_create("key_thread",
                     key_thread_entry,RT_NULL,
                     KEY_THREAD_STACK_SIZE,
                     KEY_THREAD_PRIORITY,
                     KEY_THREAD_TIMESLICE);
    if(key_thread != RT_NULL)
        rt_thread_startup(key_thread);
    else
        return RT_ERROR;
    return RT_EOK;
}



/* 控制器线程初始化 */
int controller_thread_init(void)
{

    rgb_led_thread_init();
    duoji_thread_init();
    beep_thread_init();
    key_thread_init();
        return RT_EOK;
    return RT_ERROR;
}
INIT_APP_EXPORT(controller_thread_init);`
![image.png](/img/bVb2df)
`key_struct key[4]; 

/* 按键读取函数 */
uint8_t key_read(void)
{
    if(gpio_input_bit_get(KEY1_PORT,KEY1_PIN))
    {
        return 1;
    }
    if(gpio_input_bit_get(KEY2_PORT,KEY2_PIN) == RESET)
    {
        return 2;
    }
    if(gpio_input_bit_get(KEY3_PORT,KEY3_PIN) == RESET)
    {
        return 3;
    }
    return 0;
}
/* 按键响应函数 */
void key_response(uint8_t num,rt_sem_t beep_sem)
{
    if(key[num].state == RELEASE && num != 0)
    {
        key[num].state = PRESS;
        rt_sem_release(beep_sem);
    }
}
/* 按键数据清理函数 */
void key_clear(void)
{
    
    if(gpio_input_bit_get(KEY1_PORT,KEY1_PIN) == RESET)
    {
        key[1].state = RELEASE;
    }
    if(gpio_input_bit_get(KEY2_PORT,KEY2_PIN) == SET)
    {
        key[2].state = RELEASE;
    }
    if(gpio_input_bit_get(KEY3_PORT,KEY3_PIN) == SET)
    {
        key[3].state = RELEASE;
    }    
}
/* 蜂鸣器响应一次 */
void beep_once(void)
{
    gpio_bit_set(BEEP_PORT,BEEP_PIN);
    rt_thread_mdelay(30);
    gpio_bit_reset(BEEP_PORT,BEEP_PIN);
}
    
void led_red_set(uint8_t bright)
{
    timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_1,bright);  
}

void led_green_set(uint8_t bright)
{
    timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_0,bright);        
}

void led_blue_set(uint8_t bright)
{
    timer_channel_output_pulse_value_config(TIMER3,TIMER_CH_0,bright);      
}

void led_light_add(led_struct *led)
{
    switch(led->state)
    {
        case RED_MODE:
        {
            led->red_light++;
            led_red_set(led->red_light);
        }
        break;
        case GREEN_MODE:
        {
            led->green_light++;
            led_green_set(led->green_light);            
        }
        break;
        case BLUE_MODE:
        {
            led->blue_light++;
            led_blue_set(led->blue_light);            
        }
        default:
            break;
    }
}

void led_light_reduce(led_struct *led)
{
    switch(led->state)
    {
        case RED_MODE:
        {
            led->red_light--;
            led_red_set(led->red_light);
        }
        break;
        case GREEN_MODE:
        {
            led->green_light--;
            led_green_set(led->green_light);            
        }
        break;
        case BLUE_MODE:
        {
            led->blue_light--;
            led_blue_set(led->blue_light);            
        }
        default:
            break;
    }
}


void led_colour_change(led_struct *led)
{
    static uint8_t mode = 0;
    mode++;
    switch(mode)
    {
        case 0:led->state = RED_MODE;
        case 1:led->state = GREEN_MODE;
        case 2:led->state = BLUE_MODE;        
    } 
}
`


![image.png](/img/bVb2dg)
`typedef enum
{
    RED,
    GREEN,
    BLUE,
    YELLOW,
}rgb_colour;

typedef struct
{
    rgb_colour colour;
}rgb_led;//RGB灯状态

typedef enum 
{
    RELEASE,    //松开
    PRESS,        //按下
}key_state;//按键状态

typedef enum
{
    RED_MODE,
    GREEN_MODE,
    BLUE_MODE,
}led_state;//灯光模式选择

typedef struct
{
    key_state state;
}key_struct;//按键状态机

typedef struct
{
    led_state state;
    uint8_t   red_light;
    uint8_t   green_light;
    uint8_t   blue_light;
}led_struct;//灯光控制状态机

extern uint8_t key_read(void);
extern void beep_once(void);
extern void key_response(uint8_t num,rt_sem_t beep_sem);
extern void key_clear(void);
extern void led_colour_change(led_struct *led);
extern void led_light_reduce(led_struct *led);
extern void led_light_add(led_struct *led);

后面写的比较赶,没去考虑复用性,大家看看就好了。

6BA8E3A506506140D8B8E11FE3B4E9C9.jpg
这个是PWM调光器的样子,虽然只有一颗灯珠但本质是通用的,我做的灯光控制方案的话涉及到上千个灯珠,同时每个灯珠需要单独控制,能实现1600万种颜色自由变换,整套方案仅仅使用一个IO口实现,略微夸张但事实也是这样的。

个人感想:

在嵌入式前期的学习过程中注重对于各类外设,通信协议的融会贯通,后期更靠软件架构设计,所以在工作中积累经验的同时要注重培养自己的软件架构思维。要成为一名合格的嵌入式工程师就要保持自己终身学习的习惯,海纳百川,这也是我为什么会选择成为一名嵌入式工程师的原因之一。
再次感谢极术社区、安谋科技学堂以及兆易创新给与的评测机会!

推荐阅读
关注数
0
文章数
1
一名特别喜欢捣鼓的嵌入式工程师
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息