0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

GD32开发实战指南(基础篇) 第9章 呼吸灯

嵌入式大杂烩 2023-05-12 22:22 次阅读

开发环境:

MDK:Keil 5.30

开发板:GD32F207I-EVAL

MCU:GD32F207IK

1 呼吸灯的工作原理

呼吸灯,就是指灯光设备的亮度随着时间由暗到亮逐渐增强,再由亮到暗逐渐衰减,很有节奏感地一起一伏,就像是在呼吸一样,因而被广泛应用于手机电脑电子设备的指示灯中。

要使用数字器件控制灯光的强弱,我们很自然就想到 PWM(脉冲宽度调制)威廉希尔官方网站 。假如以LED 作为灯光设备,且由控制器输出的 PWM 信号可以直接驱动 LED,PWM 信号中的低电平可点亮 LED 灯。当 LED 以较高的频率进行开关(亮灭)切换时,由于视觉暂留效应,人眼是看不到 LED 灯的闪烁现象的,反映到人眼中能感觉到的是亮度的差别。即以一定的时间长度为周期,LED 灯亮的平均时间越长,亮度就越高,反之越暗。因此,我们可以使用高频率的 PWM 信号,通过调制信号的占空比,控制 LED 灯的亮度。

那么具体我们应该控制 LED 灯以怎样的亮度曲线变化能够达到最好的效果呢?亮度随着时间逐渐变强再衰减,可以用两种常见的数学函数表示,分别是半个周期的正弦函数与指数上升曲线及其对称得到的下降曲线。

1683894862773yoqqladc47

相对来说,使用下凹函数曲线灯光处于暗的状态更长,所以指数函数的曲线更符合我们呼吸灯的亮度变化要求。

2 呼吸灯实现

2.1 简单方式

笔者先用最简单的方式来实现,也就是定时改变比较寄存器的值。

1.初始化 GPIO

下面分析具体的定时器配置代码。本实验使用 PB0 作为定时器 PWM 输出通道,先对它进行初始化。作 PWM 输出通道的引脚需要被配置为复用推挽输出模式。

/*
    brief      configure PWM GPIO
    param[in]  none
    param[out] none
    retval     none
*/
static void timer_gpio_init(void)
{
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_AF);

    /* Configure PB0 (TIMER2 CH2) as alternate function */
    gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);
}

2.配置定时器模式

在timer2_init()函数中,完成了呼吸灯所需要的定时器 PWM 输出模式配置。

/*
    brief      configure the Breath LED peripheral
    param[in]  none
    param[out] none
    retval     none
  */
void breath_led_init(void)
{
    /* TIMER2 configuration: generate PWM signals with different duty cycles:
       TIMER2CLK = SystemCoreClock / 120 = 1MHz */
    timer_oc_parameter_struct timer_ocintpara;
    timer_parameter_struct timer_initpara;

    /* configure the GPIO ports */
    timer_gpio_init();

    rcu_periph_clock_enable(RCU_TIMER2);

    timer_deinit(TIMER2);

    /* TIMER1 configuration */
    timer_initpara.prescaler         = 119;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 250;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER2, &timer_initpara);

    /* CH0 configuration in PWM mode 0 */
    timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;
    timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
    timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
    timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
    timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
    timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

    timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_ocintpara);

    /* CH0 configuration in PWM mode 0,duty cycle 25% */
    timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, 0);
    timer_channel_output_mode_config(TIMER2, TIMER_CH_2, TIMER_OC_MODE_PWM0);
    timer_channel_output_shadow_config(TIMER2, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE);

    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(TIMER2);
    /* TIMER2 enable */
    timer_enable(TIMER2);
}

这个定时器的模式配置主要分为三个部分,分别为时基初始化,输出模式初始化。

  • 时基初始化

代码中前面的部分是定时器的时基初始化,这部分主要负责配置定时器的定时周期、时钟频率、计数方式等。它使用到库函数timer_init()函数,利用结构体timer_parameter_struct进行配置,该结构体有以下成员:

  1. period

定时周期,实质是存储到重载寄存器CAR的数值,脉冲计数器从 0 累加到这个值上溢或从这个值自减至 0 下溢。这个数值加 1 然后乘以时钟源周期就是实际定时周期。

本实验中向该成员赋值为 255,即定时周期为(255+1)* T ,T 为定时器的时钟周期。

  1. prescaler

对定时器时钟CLK 的预分频值,分频后作为脉冲计数器TIMERx_CNT的驱动时钟,得到脉冲计数器的时钟频率为:CNT=CLK/(N+1),其中 N 为即为赋给本成员的时钟分频值。

本实验给 prescaler 成员赋值为 119,即对时钟 120 分频,所以定时器的时钟周期 T 为 120/120000000。

  1. clockdivision

时钟分频因子。怎么又出现一个配置时钟分频的呢?要注意这个clockdivision和上面的 prescaler 是不一样的。prescaler 预分频配置是对CLK进行分频,分频后的时钟被输出到脉冲计数器CNT。

本实验中是使用内部时钟CLK 作为定时器时钟源的,没有进行滤波所以配置clockdivision为任何数值都没有影响。

  1. alignedmode

本成员配置的为脉冲计数器 CNT 的计数模式,分别为向上计数,向下计数,及中央对齐模式。向上计数即 CNT 从 0 向上累加到 period 中的值,(重载寄存器 CAR 的值),产生上溢事件;向下计数则 CNT 从period 的值累减至0,产生下溢事件。而中央对齐模式则为向上、向下计数的合体,CNT 从 0 累加到period 的值减 1 时,产生一个上溢事件,然后向下计数到 1 时,产生一个计数器下溢事件,再从 0 开始重新计数。

  • 输出模式配置

在本函数代码的后面是关于定时器的输出模式配置的。通用定时器的输出模式由 timer_oc_parameter_struct类型结构体的主要有以下几个成员:

  1. outputstate

配置输出模式的状态使能或关闭输出。

  1. outputnstate

本成员的参数值即为比较寄存器 CH2CV的数值,当脉冲计数器CNT与CH2CV的比较结果发生变化时,输出脉冲将发生跳变。

  1. ocpolarity

有效电平的极性,把 PWM 模式中的有效电平设置为高电平或低电平。

本实验中向该成员赋值为 TIMER_OC_POLARITY_LOW (有效电平为低电平),因为在上面把输出模式配置为 PWM0 模式,向上计数,所以在 CNT< CH0CV 时,通道 n 输出为低电平,否则为高电平。

  1. ocnpolarity

用于比较有效电平的极性。

本实验中就是通过不断改变比较寄存器CH2CV的值,达到控制 PWM 信号的占空比呈指数曲线变化的目的。在本函数代码中,我们对该成员赋予初始为 0,而改变比较寄存器 CH0CV 值的操作是在中断服务函数中修改的。填充完输出模式初始化结构体后,调用输出模式初始化函数 timer_channel_output_config()对通道进行初始化。

以上是最基本的PWM输出调制实现呼吸灯。

笔者接下来还要讲解一下重映射的输出配置。在这里讲解的是通过重映射 TIMER2_CH2到 PB0 上,由 TIMER2_CH2 输出 PWM 来控制LED的亮度。下面我们介绍通过库函数来配置该功能的步骤。

1)开启 TIMER2时钟以及复用功能时钟,配置 PB0为复用输出。

要使用 TIMER2,我们必须先开启 TIMER2的时钟,这点相信大家看了这么多代码,应该明白了。这里我们还要配置 PB0为复用输出,此时,PB0属于复用功能输出。在此只列出库函数设置 AFIO 时钟的方法。

rcu_periph_clock_enable(RCU_AF);

其余的和前面的配置一样,就不再列出了。

2)初始化 TIMER2,设置 TIMER2的 CAR 和 PSC。

3)设置 TIMER2_CH2 的 PWM 模式,使能 TIMER2的 CH2 输出。

4)使能 TIMER2。

在完成以上设置了之后,我们需要使能 TIMER2。 使能 TIMER2的方法前面已经讲解过:

timer_enable(TIMER2);

5)修改 TIMER2_ CH2CV来控制占空比。

最后,在经过以上设置之后, PWM 其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改 TIMER2_CH2CV则可以控制 CH2 的输出占空比。继而控制LED的亮度。在库函数中,修改 TIMER2_CH2CV占空比的函数是:

void timer_channel_output_pulse_value_config(uint32_t timer_periph, uint16_t channel, uint32_t pulse)

通过以上5个步骤,我们就可以控制 TIMER2的 CH2 输出 PWM 波了。

接下来看看主函数的代码:

/*
    brief      main function
    param[in]  none
    param[out] none
    retval     none
*/
int main(void)
{
    uint16_t i = 0;
    FlagStatus breathe_flag = SET;

    //systick init
    sysTick_init();

    /* configure the Breath LED peripheral */
    breath_led_init();

    while(1)
    {
        /* delay a time in milliseconds */
        delay_ms(5);
        if(SET == breathe_flag) 
        {
            i++;
        }
        else
        {
            i--;
        }
        if(250 < i)
        {
            breathe_flag = RESET;
        }
        if(0 >= i)
        {
            breathe_flag = SET;
        }
        /* configure TIMER channel output pulse value */
        //timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, i);
        TIMER_CH2CV(TIMER2) = (uint32_t)i;
    }
}

代码很简单,就是不断改变CH2CV的值从而控制 CH2 的输出占空比。

2.2 中断方式

1.生成指数曲线 PWM 数据

要实现 LED 亮度随着指数曲线变化,我们需要使用占空比呈指数曲线变化的 PWM 信号,而这样的信号由定时器经过查表产生。这个表的数据存储在程序中的数组 indexWave中。

uint8_t indexWave[] = {1,1,2,2,3,4,6,8,10,14,19,25,33,44,59,80,
107,143,191,255,255,191,143,107,80,59,44,33,25,19,14,10,8,6,4,3,2,2,1,1};

这个表有 40 个数字,从图中可以看到这些数字呈指数上升再衰减,正好是呼吸灯的一个控制周期。数字的大小范围是 0255,即把 LED 的亮度分为了 0255 个等级。

假如我们把定时器的脉冲计数器 CNT 上限设置为 255,把这个表的数据一个一个地赋值到定时器的比较寄存器CH2CV中,那么在每个 PWM 周期中,当 CNT的计数值小于比较寄存器 CH2CV的值时, 就会在通道中输出低电平,点亮 LED,而随着 CCR 的值由 LED 亮度表得来,所以 LED 点亮的时间就会呈图中的曲线变化,实现呼吸灯的功能。

这个表的数据是使用 matlab 软件生成的。该代码运行后会生成一个“index_wave.c”的文件,用户把该文件中的数据复制到工程中的数组中即可。

%本代码用于产生呼吸灯使用的指数函数数据
clear;

x = [0 : 8/19 : 8];       %设置序列 ,指数上升
up = 2.^x ;               %求上升指数序列  
up = uint8(up);           %化为8位数据

y = [8: -8/19 :0];       %设置序列 ,指数下降
down = 2.^y ;            %求下降指数序列
down = uint8(down);      %化为8位数据

line = [[0:8/19:8],[8:8/19:16]]         %拼接序列
val = [up , down]                       %拼接输出序列

dlmwrite('index_wave.c',val);       %输出到文件index_wave.c
plot(line,val,'.');                 %显示波形图

2.初始化 GPIO

这部分和前面的一样,没啥好说的。

3.配置定时器模式

这里也差不多,只是将分频系数设置的稍微大些,另外开启了中断。

/*
    brief      configure the Breath LED peripheral
    param[in]  none
    param[out] none
    retval     none
  */
void breath_led_init(void)
{
    /* TIMER2 configuration: generate PWM signals with different duty cycles*/
    timer_oc_parameter_struct timer_ocintpara;
    timer_parameter_struct timer_initpara;

    /* configure the GPIO ports */
    timer_gpio_init();

    rcu_periph_clock_enable(RCU_TIMER2);

    timer_deinit(TIMER2);

    /* TIMER2 configuration */
    timer_initpara.prescaler         = 3999;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 255;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER2, &timer_initpara);

    /* CH2 configuration in PWM mode 0 */
    timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;
    timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
    timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
    timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
    timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
    timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

    timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_ocintpara);

    /* CH2 configuration in PWM mode 0,duty cycle 25% */
    timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, 0);
    timer_channel_output_mode_config(TIMER2, TIMER_CH_2, TIMER_OC_MODE_PWM0);
    timer_channel_output_shadow_config(TIMER2, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE);

    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(TIMER2);

    /* Timer2 interrupt setting, preemptive priority 0, sub-priority 2 */
    nvic_irq_enable(TIMER2_IRQn, 0, 2); 

    /* Enable Timer2 update interrupt */
    timer_interrupt_enable(TIMER2, TIMER_INT_UP);

    /* TIMER2 enable */
    timer_enable(TIMER2);
}

配置好中断,下面就要编写中断服务函数。

/*!
    \\brief      this function handles TIMER2 exception
    \\param[in]  none
    \\param[out] none
    \\retval     none
*/
void TIMER2_IRQHandler(void)
{
    static uint8_t pwm_index = 0; //用于PWM查表
    static uint8_t period_cnt = 0; //用于计算周期数
	
    if(timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP))
    {
        /* 清除TIMER2 中断标志位 */
        timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);

        period_cnt++;
        if(period_cnt >= 10)	//若输出的周期数大于10,输出下一种脉冲宽的PWM波
        {
            //根据PWM表修改定时器的比较寄存器值
            TIMER_CH2CV(TIMER2) = indexWave[pwm_index];
            pwm_index++; //标志PWM表的下一个元素
            //若PWM脉冲表已经输出完成一遍,重置PWM查表标志
            if( pwm_index >=  40)	
            {
                pwm_index=0;
            }
            period_cnt=0; //重置周期计数标志
        }
    }
}

本中断服务函数在每次定时器更新事件发生时执行一次(即 256 个定时器时钟周期)。函数中使用了静态变量 pwm_index 和 period_cnt,它们分别用来查找 PWM 表元素和记录同样占空比的脉冲输出了多少次。

本代码的目的是每 10 次定时器中断更新一次 PWM 表中的数据到比较寄存器中,当遍历完 PWM 表的 40 个元素时,再重头开始遍历 PWM 表,周而复始,重复 LED 的呼吸过程。

整个呼吸过程的时间计算方法如下:

因为定时器的 prescaler 设置为 3999;

所以定时器的时钟频率:fTIMER = 120000000/(prescaler+1) = 30000 Hz

即定时器的时钟周期为:tTIMER = 1/fTIMER = 1/30000 s

因为定时器的 period 设置为 255;

所以定时器的中断周期为:tint= tTIMER * (period+1) =0.00753s

因为 PWM 表有 pwm_index = 40 个亮度占空比数据,同种占空比信号输出 period_cnt =10 次

所以一个呼吸周期 T = tint *40 *10 = 3.41s

3 呼吸灯的实验现象

将程序编译好下载到板子中,将PF6接到PB0上,可一看到LED1像呼吸一样渐渐变明或者渐渐变暗,但是方法二明显比方法一更流畅,效果更好。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Cortex-M
    +关注

    关注

    2

    文章

    229

    浏览量

    29757
  • 呼吸灯
    +关注

    关注

    10

    文章

    110

    浏览量

    42727
  • GD32
    +关注

    关注

    7

    文章

    403

    浏览量

    24346
收藏 人收藏

    评论

    相关推荐

    GD32开发实战指南(基础) 1 开发环境搭建

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 1 GD32F207I-EVAL
    的头像 发表于 05-07 23:35 1.1w次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>1<b class='flag-5'>章</b> <b class='flag-5'>开发</b>环境搭建

    GD32开发实战指南(基础) 4 GD32启动流程详解(Keil版)

    ,所有的一切都需要由开发者来设置,这里处理器是没有堆栈,没有中断,更没有外围设备,这些工作是需要软件来指定的,而且不同的CPU类型、不同大小的内存和不同种类的外设,其初始化工作都是不同的。本文将以GD32F207IK (基于Cortex-M3)为例进行讲解。
    的头像 发表于 05-10 09:00 1.7w次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>4<b class='flag-5'>章</b> <b class='flag-5'>GD32</b>启动流程详解(Keil版)

    GD32开发实战指南(基础) 7 定时器

    系统滴答定时器一般用来提供“心跳”作用,而GD32定时器最基本功能也是定时,可以设置不同时间长度的定时。定时器除了最基本的定时功能外,定时器与GPIO有挂钩使得它可以发挥强大的作用,比如可以输出
    的头像 发表于 05-11 09:00 1.2w次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>7<b class='flag-5'>章</b> 定时器

    GD32开发实战指南(基础) 8 定时器

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 1 PWM输出的工作原理 脉冲宽度调制(PWM) ,是英文“Pulse Wi
    的头像 发表于 05-12 22:14 7956次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>8<b class='flag-5'>章</b> 定时器

    GD32开发实战指南(基础) 11 CPU的高级代理-DMA

    或者存储器和存储器之间的高速数据传输,因而被广泛地使用。早在 8086 的应用中就已经有 Intel 的 8237 这种典型的 DMA 控制器,而 GD32的 DMA 则是以类似外设的形式添加到 Cortex 内核之外的。可以说,DMA就是CPU的高级代理,DMA大大减轻了CPU的负担。
    的头像 发表于 05-16 08:59 3870次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>11<b class='flag-5'>章</b> CPU的高级代理-DMA

    GD32开发实战指南(基础) 12 ADC

    GD32F2系列有 3 个逐次逼近型的ADC,精度为 12 位,有18个多路复用通道,可以转换来自16个外部通道和2个内部通道的模拟信号。其中ADC0 和 ADC1都有 16 个外部通道, ADC2
    的头像 发表于 05-16 09:03 1.1w次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>12<b class='flag-5'>章</b> ADC

    GD32开发实战指南(基础) 14 内部温度传感器

    GD32 有一个内部的温度传感器,可以用来测量 CPU 及周围的温度(TA)。该温度传感器在内部和 ADCx_IN16 输入通道相连接,此通道把传感器输出的电压转换成数字值。温度传感器模拟输入
    的头像 发表于 05-17 08:58 5362次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>14<b class='flag-5'>章</b> 内部温度传感器

    GD32开发实战指南(基础) 15 低功耗

    GD32的工作电压(VDD)为2.0~3.6V。通过内置的电压调节器提供所需的1.8V电源。当主电源VDD掉电后,通过VBAT脚为实时时钟(RTC)和备份寄存器提供电源。
    的头像 发表于 05-17 08:59 8062次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>15<b class='flag-5'>章</b> 低功耗

    GD32开发实战指南(基础) 16 RTC

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 1 RTC工作原理 1.1 RTC简介
    的头像 发表于 05-18 22:14 7174次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>16<b class='flag-5'>章</b> RTC

    GD32开发实战指南(基础) 17 看门狗

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK GD32 有两个看门狗, 一个是
    的头像 发表于 06-03 16:00 1.1w次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>17<b class='flag-5'>章</b> 看门狗

    【图书分享】《STM32库开发实战指南

    GPIO入门之流水灯 4 深入分析流水灯例程 5 调试程序第二部分 库开发中级 
    发表于 03-13 17:01

    Arduino开发实战指南 AVR

    模块的应用第三应用8打造自己的遥控履带车9
    发表于 08-03 16:14

    GD32 MCU原理及固件库开发指南》 + 初读感悟

    GD32 MCU原理固件库开发指南这本书内容丰富,囊括了GD32中的所有外设,书中首先介绍了如何使用MDK或IAR软件搭建GD32工程环境,让初学者能快速基于工程上手编程。书中主要对
    发表于 03-31 22:11

    GD32 MCU原理及固件库开发指南》+读后感

    2介绍GD32 MCU快速入门与开发平台搭建的方法,包括对软硬件开发平台、调试工具、GD32
    发表于 06-06 21:52

    GD32开发实战指南(基础) 19 程序加密

    GD32通过读取芯片唯一ID号来实现程序的保护,防止被抄袭。96位的产品唯一身份标识所提供的参考号码对任意一个GD32微控制器
    的头像 发表于 05-20 09:10 4148次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>19<b class='flag-5'>章</b> 程序加密