DDZZ669 发表于 2020-9-18 22:05

【GD32450I-EVAL】+ 03库函数基础使用方法-以按键中断为例

本帖最后由 DDZZ669 于 2020-9-18 22:05 编辑

<p><span style="font-family:Times;">上篇 &quot;<a href="https://bbs.eeworld.com.cn/forum.php?mod=viewthread&amp;tid=1140851&amp;page=1#pid3010171" target="_blank">【GD32450I-EVAL】+ 02软件开发环境配置(KEIL 5)与流水灯测试</a>&quot; 介绍了软件开发环境的配置,这篇我们来看看GD32库函数的基础使用方法。</span></p>

<p><span style="font-family:Times;">之前使用过STM32单片机,对比现在的GD32,使用库函数开发,编程思路基本一致,只是用到的库函数名称不一样,</span></p>

<p><span style="font-family:Times;">下面以光盘资料中的 &quot;<strong>03_GPIO_KeyBoard_Interrupt_mode</strong>&quot;为例,分析一下库函数的使用方法。</span></p>

<p>&nbsp;</p>

<p><span style="font-family:Times;"><span style="font-size:26px;"><strong>1 程序分析</strong></span></span></p>

<p><span style="font-family:Times;">首先看一下例程中的主函数部分:</span></p>

<pre>
<code class="language-cpp">int main(void)
{
    gd_eval_key_init(KEY_TAMPER, KEY_MODE_EXTI);
    gd_eval_led_init(LED1);
    while(1)
    {
    }
}</code></pre>

<div aria-label="代码段 小部件" contenteditable="false" role="region" tabindex="-1">&nbsp;</div>

<p><span style="font-family:Times;">逻辑很简单,就是按键和LED进行初始化配置,然后就进入while(1)死循环中,监测中断的发生。</span></p>

<p>&nbsp;</p>

<p><span style="font-family:Times;"><span style="font-size:24px;"><strong>1.1 按键配置</strong></span></span></p>

<p><span style="font-family:Times;">先来看一下按键的初始化部分:</span></p>

<pre>
<code class="language-cpp">void gd_eval_key_init(key_typedef_enum key_num, keymode_typedef_enum key_mode)
{
    /* enable the key clock */
    rcu_periph_clock_enable(KEY_CLK);
    rcu_periph_clock_enable(RCU_SYSCFG);

    /* configure button pin as input */
    gpio_mode_set(KEY_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE,KEY_PIN);

    if (key_mode == KEY_MODE_EXTI)
    {
      /* enable and set key EXTI interrupt to the lowest priority */
      nvic_irq_enable(KEY_IRQn, 2U, 0U);

      /* connect key EXTI line to key GPIO pin */
      syscfg_exti_line_config(KEY_PORT_SOURCE, KEY_PIN_SOURCE);

      /* configure key EXTI line */
      exti_init(KEY_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
      exti_interrupt_flag_clear(KEY_EXTI_LINE);
    }
}</code></pre>

<p>&nbsp;</p>

<p><span style="font-family:Times;"><span style="font-size:16px;"><strong>1.1.1 时钟配置</strong></span></span></p>

<p cid="n11" mdtype="paragraph"><span style="font-family:Times;">首先是使能按键引脚的时钟源,包括GPIO时钟和RCU时钟,通过</span>rcu_periph_clock_enable() <span style="font-family:Times;">调用函数来完成。</span></p>

<p cid="n12" mdtype="paragraph"><span style="font-family:Times;">该函数就是操作寄存器:</span></p>

<pre>
<code class="language-cpp">void rcu_periph_clock_enable(rcu_periph_enum periph)
{
    RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph));
}</code></pre>

<p>&nbsp;</p>

<p><span style="font-family:Times;"><span style="font-size:16px;"><strong>1.1.2 GPIO配置</strong></span></span></p>

<p><span style="font-family:Times;">然后是按键GPIO的模式配置,通过调用</span>gpio_mode_set() <span style="font-family:Times;">函数来完成,该函数的声明为:</span></p>

<pre>
<code class="language-cpp">void gpio_mode_set(uint32_t gpio_periph,uint32_t mode,uint32_t pull_up_down,uint32_t pin);</code></pre>

<p><span style="font-family:Times;">参数:</span></p>

<p style="margin-left: 40px;"><strong>gpio_periph</strong> :<span style="font-family:Times;">GPIO外设选</span></p>

<p style="margin-left: 80px;"><span style="font-family:Times;">GPIOx (x = A,B,C,D,E,F,G,H,I)</span></p>

<p style="margin-left: 40px;"><font face="monospace"><b>mode:</b></font><span style="font-family:Times;">GPIO 引脚模式</span></p>

<p cid="n28" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_MODE_INPUT: 输入模式</span></p>

<p cid="n30" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_MODE_OUTPUT: 输出模式</span></p>

<p cid="n32" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_MODE_AF: 复用功能模式</span></p>

<p cid="n34" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_MODE_ANALOG: 模拟信号模式</span></p>

<p style="margin-left: 40px;"><font face="monospace"><b>pull_up_dowm:</b></font><span style="font-family:Times;">GPIO 引脚 &ldquo;上/下拉&rdquo; 设置</span></p>

<p cid="n39" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_PUPD_NONE: 无上/下拉</span></p>

<p cid="n41" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_PUPD_PULLUP: 上拉模式</span></p>

<p cid="n43" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_PUPD_PULLDOWN: 下拉模式</span></p>

<p style="margin-left: 40px;"><font face="monospace"><b>pin:</b></font><span style="font-family:Times;">GPIO 引脚选择</span></p>

<p style="margin-left: 80px;"><span style="font-family:Times;">GPIO_PIN_x (x=0..15), GPIO_PIN_ALL</span></p>

<p><span style="font-family:Times;">另外,这里GPIO外设与引脚使用了数组与宏定义管理,如:</span></p>

<pre>
<code class="language-cpp"> gpio_mode_set(KEY_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE,KEY_PIN);</code></pre>

<p cid="n51" mdtype="paragraph"><span style="font-family:Times;">这句中的</span>KEY_PORT <span style="font-family:Times;">与</span>KEY_PIN <span style="font-family:Times;">(函数调用时传入的key_num为KEY_TAMPER),找到其定义,在 &quot;gd32f450i_eval.h&quot;与&quot;gd32f450i_eval.c&quot;中有:</span></p>

<pre>
<code class="language-cpp">#define TAMPER_KEY_PIN                   GPIO_PIN_13
#define TAMPER_KEY_GPIO_PORT             GPIOC

static uint32_t KEY_PORT = {WAKEUP_KEY_GPIO_PORT,
                                  TAMPER_KEY_GPIO_PORT,
                                  USER_KEY_GPIO_PORT};

static uint32_t KEY_PIN = {WAKEUP_KEY_PIN, TAMPER_KEY_PIN,USER_KEY_PIN};</code></pre>

<p><span style="font-family:Times;">所以板子上Tamper这个用户按键对应的PC13脚就是通过这样的方式定义的。</span></p>

<p>&nbsp;</p>

<p><span style="font-family:Times;"><span style="font-size:16px;"><strong>1.1.3&nbsp;中断配置</strong></span></span></p>

<p><span style="font-family:Times;">接着是中断的配置,包括NVIC的配置和EXTI配置:</span></p>

<pre>
<code class="language-cpp">nvic_irq_enable(KEY_IRQn, 2U, 0U);
syscfg_exti_line_config(KEY_PORT_SOURCE, KEY_PIN_SOURCE);</code></pre>

<p><span style="font-family:Times;">配置完后,将设置的参数初始化进行生效:</span></p>

<pre>
<code class="language-cpp">exti_init(KEY_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
exti_interrupt_flag_clear(KEY_EXTI_LINE);</code></pre>

<p>&nbsp;</p>

<p><span style="font-family:Times;"><span style="font-size:24px;"><strong>1.2&nbsp;LED配置</strong></span></span></p>

<p><span style="font-family:Times;">再来看一下LED的初始化部分:</span></p>

<pre>
<code class="language-cpp">void gd_eval_led_init (led_typedef_enum lednum)
{
    /* enable the led clock */
    rcu_periph_clock_enable(GPIO_CLK);
   
    /* configure led GPIO port */
    gpio_mode_set(GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,GPIO_PIN);
    gpio_output_options_set(GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN);

    GPIO_BC(GPIO_PORT) = GPIO_PIN;
}</code></pre>

<p cid="n63" mdtype="paragraph"><span style="font-family:Times;">首先还是调用</span>rcu_periph_clock_enable() <span style="font-family:Times;">函数来使能时钟,然后调用</span>gpio_mode_set() <span style="font-family:Times;">函数来设置模式。</span></p>

<p cid="n64" mdtype="paragraph"><span style="font-family:Times;">另外,LED的控制引脚作为输出模式,还要调用</span>gpio_output_options_set() <span style="font-family:Times;">函数来设置输出配置:</span></p>

<pre>
<code class="language-cpp">void gpio_output_options_set(uint32_t gpio_periph,uint8_t otype,uint32_t speed,uint32_t pin);</code></pre>

<p><span style="font-family:Times;">参数:</span></p>

<p style="margin-left: 40px;"><strong>gpio_periph</strong> <span style="font-family:Times;">: GPIO外设选择</span></p>

<p style="margin-left: 80px;"><span style="font-family:Times;">GPIOx (x = A,B,C,D,E,F,G,H,I)</span></p>

<p style="margin-left: 40px;"><strong>otype</strong> :&nbsp;<span style="font-family:Times;">GPIO 引脚输出模式</span></p>

<p cid="n77" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_OTYPE_PP: 推挽模式(push pull mode)</span></p>

<p cid="n79" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_OTYPE_OD: 开漏模式(open drain mode)</span></p>

<p cid="n79" mdtype="paragraph" style="margin-left: 40px;"><strong>speed </strong>:<span style="font-family:Times;">&nbsp;GPIO 引脚最大输出速度(频率)</span></p>

<p cid="n84" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_OSPEED_2MHZ: 2MHz</span></p>

<p cid="n86" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_OSPEED_25MHZ: 25MHz</span></p>

<p cid="n88" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_OSPEED_50MHZ: 50MHz</span></p>

<p cid="n90" mdtype="paragraph" style="margin-left: 80px;"><span style="font-family:Times;">GPIO_OSPEED_200MHZ: 200MHz</span></p>

<p style="margin-left: 40px;"><strong>pin</strong> :&nbsp;<span style="font-family:Times;">GPIO 引脚选择</span></p>

<p style="margin-left: 80px;"><span style="font-family:Times;">GPIO_PIN_x (x=0..15), GPIO_PIN_ALL</span></p>

<p cid="n96" mdtype="paragraph"><span style="font-family:Times;">程序中使用的是:推挽模式输出,速度50MHz。LED</span></p>

<p cid="n97" mdtype="paragraph"><span style="font-family:Times;">最后,使用</span>GPIO_BC() <span style="font-family:Times;">将GPIO引脚清零(Bit Clear),即低电平,这样,LED默认就是熄灭状态。</span></p>

<p cid="n97" mdtype="paragraph">&nbsp;</p>

<p cid="n97" mdtype="paragraph"><span style="font-family:Times;"><span style="font-size:24px;"><strong>1.3&nbsp;中断处理函数</strong></span></span></p>

<p><span style="font-family:Times;">中断函数在&ldquo;gd32f4xx_it.c&rdquo;文件中:</span></p>

<pre>
<code class="language-cpp">void EXTI10_15_IRQHandler(void)
{
    if(RESET != exti_interrupt_flag_get(EXTI_13))
    {
      gd_eval_led_toggle(LED1);
    }
    exti_interrupt_flag_clear(EXTI_13);
}</code></pre>

<p cid="n101" mdtype="paragraph"><span style="font-family:Times;">当检测到按键按下时,会触发中断处理函数,这时,调用</span>gd_eval_led_toggle() <span style="font-family:Times;">来翻转电平,实现LED的点亮。</span></p>

<p cid="n102" mdtype="paragraph"><span style="font-family:Times;">这个函数实际是调用库函数中的</span>GPIO_TG() <span style="font-family:Times;">函数:</span></p>

<pre>
<code class="language-cpp">void gd_eval_led_toggle(led_typedef_enum lednum)
{
    GPIO_TG(GPIO_PORT) = GPIO_PIN;
}</code></pre>

<p><span style="font-family:Times;">翻转过LED后,调用</span>exti_interrupt_flag_clear() <span style="font-family:Times;">来清除中断标志。</span></p>

<p>&nbsp;</p>

<p><span style="font-family:Times;"><span style="font-size:26px;"><strong>2 程序编译下载</strong></span></span></p>

<p><span style="font-family:Times;">编译一下,可以看到如下编译信息:</span></p>

<pre>
<code class="language-bash">...省略若干行
compiling gd32f450i_eval.c...
assembling startup_gd32f450.s...
linking...
Program Size: Code=1564 RO-data=460 RW-data=100 ZI-data=1028
FromELF: creating hex file...
".\output\GD32450I_EVAL.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed:00:00:14</code></pre>

<p><span style="font-family:Times;">这里顺便说名一下这几个<strong>Program Size</strong>:</span></p>

<p style="margin-left: 40px;"><span style="font-family:Times;"><strong>Code</strong> 是代码占用的空间。 Code=1564,即1564字节,1K多。</span></p>

<p style="margin-left: 40px;"><span style="font-family:Times;"><strong>RO-data</strong> 是 Read Only 只读<strong>常量</strong>的大小,如const型。 RO-data=460,即460字节。</span></p>

<p style="margin-left: 40px;"><span style="font-family:Times;"><strong>RW-data</strong> 是 Read Write 可读可写<strong>变量</strong>的大小,初始化时已经赋值的。 RW-data=100,即100字节。</span></p>

<p style="margin-left: 40px;"><span style="font-family:Times;"><strong>ZI-data</strong> 是 Zero Initialize 没有初始化的可读可写变量的大小,没有初始化的变量都赋值一个0。 ZI-data=1028,即1028字节,1K多一点。</span></p>

<p><span style="font-family:Times;">其中:</span></p>

<p style="margin-left: 40px;"><span style="font-family:Times;"><strong>RW + ZI</strong>就是程序总共使用的RAM字节数。</span></p>

<p style="margin-left: 40px;"><span style="font-family:Times;">RAM就是掉电后丢失的那种(类比计算机的内存,从来存放程序运行时的数据),这个板子的RAM(内部SRAM)有256KB。</span></p>

<p style="margin-left: 40px;"><span style="font-family:Times;"><strong>Code + RO + RW</strong>就是程序下载到Flash中的字节数。</span></p>

<p style="margin-left: 40px;"><span style="font-family:Times;">Flash就是掉电后不会丢失的那种,这个板子的内部Flash有3MB。</span></p>

<p><span style="font-family:Times;"><strong>注</strong>:</span></p>

<p><span style="font-family:Times;">&nbsp; &nbsp; &nbsp; 虽然RAM中存储了RW与ZI变量数据,但Flash中也要存RW,因为RAM掉电后数据就丢失,而Flash可以不包含ZI,因为ZI数据都是0,只要程序运行前将ZI数据所在的区域都清0即可,包含进去则浪费存储空间。</span></p>

<p>&nbsp;</p>

<p><span style="font-family:Times;"><span style="font-size:26px;"><strong>3 效果展示</strong></span></span></p>

<p>按下按键,LED亮起,再次按下,LED熄灭。</p>

<p><span style="font-family:Times;"></span></p>

<p>&nbsp;</p>

okhxyyo 发表于 2020-9-27 10:58

<p><a href="https://bbs.eeworld.com.cn/thread-1140981-1-1.html" target="_blank">兆易GD32450I-EVAL</a></p>

<p>汇总贴:<a href="https://bbs.eeworld.com.cn/thread-1140981-1-1.html">https://bbs.eeworld.com.cn/thread-1140981-1-1.html</a></p>
页: [1]
查看完整版本: 【GD32450I-EVAL】+ 03库函数基础使用方法-以按键中断为例