[APM32E0] APM32E030 工程模版的新建与GPIO的使用

[复制链接]
 楼主| LIZARD925 发表于 2025-6-14 22:09 | 显示全部楼层 |阅读模式
本帖最后由 LIZARD925 于 2025-6-22 00:24 编辑

#技术资源# #申请原创#
APM32E030系列使用记录--GPIO的使用
我将极海官网APME030的例程,根据B站江科大的例程,新建了一个工程模版,在这里介绍一下模板的结构,并说明如何在这个工程模板上进行GPIO的操作,如点灯、按键等,例程已上传附件,仅供参考。

工程模板的介绍:
336056856d0af71665.png
1、DebugConfig
2、library
3、Listings
4、Objects
5、start
6、user
模版分为这六个文件夹,其中DebugConfig、Listings和Objects文件夹是 Keil MDK 5 自动生成的,DebugConfig文件夹用于存储一些调试配置文件,Listings和Objects文件夹用来存储 Keil MDK 5 编译过程的一些中间文件;library文件夹下放的是APM32E030用到的驱动标准库文件,不需要改;start文件下放的是外设寄存器文件、内核寄存器描述文件、时钟文件等,也不需要更改;user文件夹下,存放中断函数与主函数,可根据自己喜好进行添加。

GPIO操作之点亮LED灯:
我们先打开工程模版,新建一个system 的文件夹,放我们的延时函数,延时函数使用 系统嘀嗒定时器 进行实现,文件添加完成后,记得在工程中添加文件的路径
83984684d81bd9645c.png
将延时函数复制过来即可使用,添加进工程后,如图所示
99846684d66fee22c8.png
  1. #include "apm32e030.h"                  // Device header

  2. /**
  3.   * [url=home.php?mod=space&uid=247401]@brief[/url]  微秒级延时
  4.   * @param  xus 延时时长,范围:0~233015
  5.   * @retval 无
  6.   */
  7. void Delay_us(uint32_t xus)
  8. {
  9.         SysTick->LOAD = 72 * xus;                                //设置定时器重装值
  10.         SysTick->VAL = 0x00;                                        //清空当前计数值
  11.         SysTick->CTRL = 0x00000005;                                //设置时钟源为HCLK,启动定时器
  12.         while(!(SysTick->CTRL & 0x00010000));        //等待计数到0
  13.         SysTick->CTRL = 0x00000004;                                //关闭定时器
  14. }

  15. /**
  16.   * [url=home.php?mod=space&uid=247401]@brief[/url]  毫秒级延时
  17.   * @param  xms 延时时长,范围:0~4294967295
  18.   * @retval 无
  19.   */
  20. void Delay_ms(uint32_t xms)
  21. {
  22.         while(xms--)
  23.         {
  24.                 Delay_us(1000);
  25.         }
  26. }

  27. /**
  28.   * [url=home.php?mod=space&uid=247401]@brief[/url]  秒级延时
  29.   * @param  xs 延时时长,范围:0~4294967295
  30.   * @retval 无
  31.   */
  32. void Delay_s(uint32_t xs)
  33. {
  34.         while(xs--)
  35.         {
  36.                 Delay_ms(1000);
  37.         }
  38. }
此时我们写一个简单的IO翻转来测试延时函数的准确性,使用逻辑分析仪进行测量,可看到时间和我们设置的时间相差无几,IO的初始化也可参考例程进行更改
62170684d75e661fae.png 61256684d75fcb783a.png
  1. int main (void)
  2. {
  3.         RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOB);        //打开GPIOB的时钟

  4.         GPIO_Config_T GPIO_InitStructure;   
  5.         
  6.         GPIO_InitStructure.mode = GPIO_MODE_OUT;            // 输出模式
  7.         GPIO_InitStructure.pin = GPIO_PIN_6;      
  8.         GPIO_InitStructure.speed = GPIO_SPEED_50MHz;        
  9.         GPIO_InitStructure.outtype = GPIO_OUT_TYPE_PP;        //推挽输出
  10.         GPIO_Config(GPIOB,&GPIO_InitStructure);

  11.         while(1)
  12.         {
  13.                 Delay_ms(500);
  14.                 GPIO_WriteBitValue(GPIOB,GPIO_PIN_6,0);
  15.                 Delay_ms(500);
  16.                 GPIO_WriteBitValue(GPIOB,GPIO_PIN_6,1);
  17.         }
  18. }
此时可看到延时函数没问题,我们将点灯程序进行封装,添加hardware文件夹,放入led.c\led.h文件,如图所示,硬件原理图在此链接进行下载:
  1. https://www.geehy.com/uploads/tool/APM32E030R%20Micro-EVB%20V1.0.SchDoc.pdf
949706856d5ab6c765.png 766116856d58c29bb5.png
添加好后,我们根据原理图初始化对应的GPIO口,初始化PB6和PB7这个两个引脚,调用后测试无误,为流水灯的现象,具体代码如下:



  1. #include "LED.h"

  2. void LED_init(void)
  3. {
  4.         RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOB);        //打开GPIOB的时钟

  5.         GPIO_Config_T GPIO_InitStructure;   
  6.         
  7.         GPIO_InitStructure.mode = GPIO_MODE_OUT;            // 输出模式
  8.         GPIO_InitStructure.pin = GPIO_PIN_6|GPIO_PIN_7;      
  9.         GPIO_InitStructure.speed = GPIO_SPEED_50MHz;        
  10.         GPIO_InitStructure.outtype = GPIO_OUT_TYPE_PP;        //推挽输出
  11.         GPIO_Config(GPIOB,&GPIO_InitStructure);
  12.         GPIO_SetBit(GPIOB, GPIO_PIN_6|GPIO_PIN_7);                //初始化LED为灭
  13. }

  14. void LED1_on(void)
  15. {
  16.         GPIO_ClearBit(GPIOB,GPIO_PIN_6);  //亮
  17. }

  18. void LED1_off(void)
  19. {
  20.         GPIO_SetBit(GPIOB,GPIO_PIN_6);  //灭
  21. }

  22. void LED2_on(void)
  23. {
  24.         GPIO_ClearBit(GPIOB,GPIO_PIN_7);  //亮
  25. }

  26. void LED2_off(void)
  27. {
  28.         GPIO_SetBit(GPIOB,GPIO_PIN_7);  //灭
  29. }

  30. void LED1_turn(void)
  31. {
  32.         if(GPIO_ReadOutputBit(GPIOB,GPIO_PIN_6)==0)  //电平反转
  33.         {
  34.                 GPIO_SetBit(GPIOB,GPIO_PIN_6);
  35.         }
  36.         else
  37.         {
  38.                 GPIO_ClearBit(GPIOB,GPIO_PIN_6);
  39.         }
  40. }

  41. void LED2_turn(void)
  42. {
  43.         if(GPIO_ReadOutputBit(GPIOB,GPIO_PIN_7)==0)  //电平反转
  44.         {
  45.                 GPIO_SetBit(GPIOB,GPIO_PIN_7);
  46.         }
  47.         else
  48.         {
  49.                 GPIO_ClearBit(GPIOB,GPIO_PIN_7);
  50.         }
  51. }

  1. #ifndef __LED_H__
  2. #define __LED_H__

  3. #include "apm32e030.h"                  // Device header
  4. #include "apm32e030_gpio.h"
  5. #include "apm32e030_rcm.h"

  6. void LED_init(void);

  7. void LED1_on(void);
  8. void LED1_off(void);
  9. void LED2_on(void);
  10. void LED2_off(void);
  11. void LED1_turn(void);
  12. void LED2_turn(void);
  13.                
  14. #endif
  1. #include "apm32e030.h"                  // Device header
  2. #include "Delay.h"
  3. #include "LED.h"


  4. int main (void)
  5. {
  6.         LED_init();
  7.         while(1)
  8.         {
  9.                 LED1_turn();
  10.                 Delay_ms(500);
  11.                 LED2_turn();

  12.         }
  13. }

GPIO操作之按键输入
此例程主要验证APM32E030的GPIO输入功能,对于普通的按键检测,只需初始化GPIO为输入模式,并配置引脚上下拉,在while1中一直读取即可,GPIO初始化:
  1. void key_init(void)
  2. {
  3.         RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);        //打开GPIOA的时钟
  4.         GPIO_Config_T GPIO_InitStructure;   
  5.         
  6.         GPIO_InitStructure.mode = GPIO_MODE_IN;                    // 输入模式
  7.         GPIO_InitStructure.pin = GPIO_PIN_0|GPIO_PIN_1;      
  8.         GPIO_InitStructure.speed = GPIO_SPEED_50MHz;        
  9.         GPIO_InitStructure.pupd = GPIO_PUPD_PU;                        //上拉输入
  10.         GPIO_Config(GPIOA,&GPIO_InitStructure);
  11. }
进行按键的消抖与按键状态的读取,使用的都是通用的函数:
  1. uint8_t key_getnum(void)
  2. {
  3.         uint8_t keynum=0;
  4.         if(GPIO_ReadInputBit(GPIOA,GPIO_PIN_0)==0)//判断按键是否按下
  5.         {
  6.                 Delay_ms(10);
  7.                 while(GPIO_ReadInputBit(GPIOA,GPIO_PIN_0)==0);
  8.                 Delay_ms(10);
  9.                 keynum=1;        
  10.         }
  11.         if(GPIO_ReadInputBit(GPIOA,GPIO_PIN_1)==0)//判断按键是否按下
  12.         {
  13.                 Delay_ms(10);
  14.                 while(GPIO_ReadInputBit(GPIOA,GPIO_PIN_1)==0);
  15.                 Delay_ms(10);
  16.                 keynum=2;
  17.         }
  18.         return keynum;

  19. }
此时只需在主函数中调用读取,进行按键的判断
304866856d8571d6e9.png

GPIO操作之按键中断输入
对于外部中断输入的验证,我们需要打开引脚的复用,并打开 SYSCFG 时钟,配置中断线与触发边沿,并设置中断优先级为15;
用户手册中也有对SYSCFG 的介绍:主要用于管理地址映射和控制中断,具体是指:控制部分 IO 口上的I2C 的超快模式;TMR16,TMR17,USART1 和 ADC 的 DMA 触发源的重映射;存储器到代码起始区的重映射;管理连接到 GPIO 的外部中断
  1. void key_eint_init(void)        //中断按键
  2. {
  3.         RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);        //打开GPIOA的时钟
  4.         RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
  5.       
  6.         GPIO_Config_T GPIO_InitStructure;   
  7.       
  8.         GPIO_InitStructure.mode = GPIO_MODE_IN;                    // 输入模式
  9.         GPIO_InitStructure.pin = GPIO_PIN_0|GPIO_PIN_1;      
  10.         GPIO_InitStructure.speed = GPIO_SPEED_50MHz;      
  11.         GPIO_InitStructure.pupd = GPIO_PUPD_PU;                        //上拉输入
  12.         GPIO_Config(GPIOA,&GPIO_InitStructure);
  13.       
  14.         SYSCFG_EINTLine(SYSCFG_PORT_GPIOA, SYSCFG_PIN_0);  //跟管脚
  15.         SYSCFG_EINTLine(SYSCFG_PORT_GPIOA, SYSCFG_PIN_1);
  16.       
  17.         EINT_Config_T EXTI_InitStructure;
  18.         EXTI_InitStructure.line = EINT_LINE0|EINT_LINE1;
  19.         EXTI_InitStructure.lineCmd = ENABLE;
  20.         EXTI_InitStructure.mode = EINT_MODE_INTERRUPT;
  21.         EXTI_InitStructure.trigger = EINT_TRIGGER_FALLING;
  22.         EINT_Config(&EXTI_InitStructure);
  23.       
  24.         NVIC_EnableIRQRequest(EINT0_1_IRQn, 0x0f);
  25. }

此时,即可使用中断的方式进行按键的读取,在中断中加入自己的逻辑:
  1. void EINT0_1_IRQHandler(void)
  2. {
  3.         if (EINT_ReadStatusFlag(EINT_LINE0) == SET)
  4.     {
  5.         EINT_ClearStatusFlag(EINT_LINE0);
  6.                 LED1_turn();
  7.     }

  8.     if (EINT_ReadStatusFlag(EINT_LINE1) == SET)
  9.     {
  10.         EINT_ClearStatusFlag(EINT_LINE1);
  11.                 LED2_turn();
  12.     }
  13. }
外部中断的中断函数具体在此处:

具体的中断可看用户手册:
728686856db2e7738e.png


2-1 APM32E030 工程模板.zip

168.18 KB, 下载次数: 5

3-1 APM32E030-LED流水灯.zip

170.65 KB, 下载次数: 2

3-2 APM32E030-按键控制LED.zip

172.43 KB, 下载次数: 1

jobszheng 发表于 2025-6-15 20:40 来自手机 | 显示全部楼层
这个小芯片可玩性还是蛮高的。
幻影书记 发表于 2025-6-18 15:36 | 显示全部楼层
这个延时函数没有必要了吧!
哪程序需要ns级的延时啊?
雾里闲逛 发表于 2025-6-18 17:06 | 显示全部楼层
我觉得LED的初始化还是初始化为开漏模式为好。
当关闭LED灯时,直接断开电路。
梦之一瞥 发表于 2025-6-28 22:31 | 显示全部楼层
楼主,您这工程模板的层级划分是不是太多了
DawnFervor 发表于 2025-6-28 23:20 | 显示全部楼层
我倒是觉得楼主的分层还是挺实用的。
keil的文件夹,官方提供的库文件夹,自己的文件夹。
在编写程序的过程中,只更新自己的文件夹即可。
FrostShimmer 发表于 2025-6-29 16:44 | 显示全部楼层
看楼主把LED的控制放到了hardware里面。我挺犹豫的是放到hardware里面,还是放到board.c/.h里面。
星云狂想曲 发表于 2025-6-30 20:46 | 显示全部楼层
楼主使用逻辑分析仪来测量这个LED灯的闪烁频率,时间上面准不?
我倒是一直觉得示波器看着并不准呢
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

19

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部

17

主题

19

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部