1.简介——(摘录于GD官网,从了解MCU开始)
兆易创新于2023.5.11推出了GD32H737/757/759系列超高性能MCU,该系列MCU采用基于Armv7E-M架构的600MHz Arm® Cortex®-M7高性能内核,凭借支持分支预测的6级超标量流水线架构,以及支持高带宽的AXI和AHB总线接口,可实现更高的处理性能。内置了高级DSP硬件加速器和双精度浮点单元(FPU),以及硬件三角函数加速器(TMU)和滤波算法加速器(FAC),大幅减轻了内核的负担并有助于提升处理效率。GD32H7系列MCU最高主频下的工作性能可达1552 DMIPS,CoreMark®测试取得了2888分的出色表现,同主频下的代码执行效率相比市场同类产品提升约10%,相比Cortex®-M4产品的性能提升超过40%。
GD32H7系列MCU配备了1024KB到3840KB的片上Flash及1024KB的SRAM,其中包含512KB可配置超大紧耦合内存(ITCM, DTCM),可确保关键指令与数据的零等待执行;还配备了64KB L1-Cache高速缓存(I-Cache, D-Cache),有效提升CPU处理效率和实时性。外部总线扩展(EXMC)支持访问SDRAM、SRAM、ROM、NOR Flash和NAND Flash等多种片外存储器。GD32H7内置了可实时跟踪指令和数据的宏单元ETM(Embedded Trace Macrocell),提供在不干扰CPU正常运行情况下的高级调试功能。GD32H7内置的大容量存储空间能够支持复杂操作系统及嵌入式AI、机器学习(ML)等多种高级算法,实现兼具高性能和低延迟的实时控制。
GD32H7系列MCU新增了大量通用外设资源,包含8个USART、4个I2C、6个SPI、4个I2S、2个SDIO以及2个八线制OSPI(可向下兼容四线制QSPI)等。配备了2个USB2.0 OTG接口,支持全速(Full Speed)和高速(High Speed)模式。还集成了3路CAN-FD控制器和2路以太网,满足高速互联应用所需。
GD32H7系列MCU提供了出色的图形显示和音视频连接方案。芯片内置了TFT LCD液晶驱动器和图形处理加速器IPA (Image Processing Accelerator), 支持2D图像叠加、旋转、缩放及多种颜色格式转换等功能。还集成了串行音频接口(SAI)和SPDIF音频接口,以及8位至14位的数字摄像头接口,便于视频图像的采集与传输。
GD32H7系列MCU采用1.71V~3.6V供电,支持高级电源管理并提供了三种供电模式(LDO/SMPS/直接供电)和五种低功耗模式,可实施灵活的供电策略以兼顾整体能耗平衡。配备了4个32位通用定时器、12个16位通用定时器、4个64位/32位基本定时器、2个PWM高级定时器。2个14位ADC采样速率可达4MSPS,1个12位ADC采样速率高达5.3MSPS,还集成了快速比较器(COMP)、DAC等高精度模拟外设以支持各类电机控制场景。
GD32H7产品系列支持多种安全机制,为通信过程的数据安全提供保障。内置的硬件加解密支持DES、三重DES或AES算法,以及应用于多种场合的哈希(Hash)算法,确保传输信息的完整性。GD32H7系列MCU Flash/SRAM均支持ECC校验,能够有效提升系统运行的可靠性。还集成了RTDEC模块,可以对AXI或AHB总线数据进行实时解密,保护存储在外部SPI NOR Flash设备中只读固件的机密性。
GD32H7提供了3个全新系列,并与现有产品完美兼容。按资源配置不同,GD32H737系列支持3路CAN 2.0B,GD32H757/GD32H759系列支持3路高速CAN-FD。按管脚封装不同,GD32H757系列具备BGA100和LQFP144/100三种封装选项;GD32H759系列具备BGA176 和LQFP176封装选项,以满足差异化开发需求。
GD32H7可广泛用于数字信号处理、电机变频、电源、储能系统、无人机、音频视频、图形图像等各类应用。得益于超高主频以及大存储容量,该系列MCU也适用于机器学习和人工智能等诸多高端创新场景。
2.GD32H759I-EVAL全功能评估板
GD32H759I-EVAL 评估板使用 GD32H759IMK6 作为主控制器。评估板使用 GD-Link Mini USB接口或者 DC-005 连接器提供 5V 电源。提供包括扩展引脚在内的及 Reset, Boot, Wakeup KEY, Tamper KEY, User KEY, LED, ADC, DAC, CAN, DCI, ETHNET, HPDF, SAI, I2S, I2C_SMbus, OSPI, SPI_LCD, SDIO, SDRAM, TLI_LCD, USB, USART 转 USB 接口等外设资源。(摘录于GD32H759I-EVAL评估板使用指南_Rev2.1)
3.准备开发资料及开发环境
在GD32官网(https://www.gd32mcu.com/cn/download)下载一栏,找到GD32H7 MCU系列,然后在数据手册、用户手册、应用笔记、应用软件以及开发板资料下载相应的资料,如下图所示:
我使用的是KEIL MDK-ARM集成开发环境,在使用之前需要安装刚刚下载的GD32H7xx AddOn软件,部署KEIL的PACK包;在GD32H7xx Firmware Library中提供了GD32H7xx标准固件库和外设示例程序,我们可以基于官方的驱动库,参考这些示例程序来开发我们的项目应用程序。
GD32H759I-EVAL全功能开发板的资料在GD32H7xx_Demo_Suites_V2.0.1.7z\GD32H7xx_Demo_Suites_V2.0.1\GD32H759I_EVAL_Demo_Suites目录下,包含了使用指南、原理图、基于开发板的示例程序等等。
4.搭建基础工程
为了尽快的熟悉和掌握MCU的使用,我们基于官方提供的驱动库,自己创建KEIL工程,基于板载的资源去定义一些硬件驱动程序,结合上层的应用来达到演示的效果;基础工程也是后面所有程序的一个基础,本着从易到难的学习过程,在基础工程中,我们先对开发板的资源做一个了解,然后实现LED显示驱动、用户按键操作驱动、日志打印这几个功能;
按键的应用层,我们基于第三方的开源代码MultiButton来实现对按键的扫描检测和触发事件的处理,通过如下原理图,我们通过Wakeup按键和Tamper按键实现对LED1和LED2翻转显示操作,通过User Key按键来实现熄灭LED1和LED2的操作。
4.1.LED驱动程序
#include "bsp_led.h"
void bsp_LedInit(void)
{
rcu_periph_clock_enable(RCU_GPIOF);
gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_10);
gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10);
gpio_bit_reset(GPIOF, GPIO_PIN_10);
rcu_periph_clock_enable(RCU_GPIOA);
gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_6);
gpio_bit_reset(GPIOA, GPIO_PIN_6);
}
void bsp_Led1On(void)
{
gpio_bit_set(GPIOF, GPIO_PIN_10);
}
void bsp_Led1Off(void)
{
gpio_bit_reset(GPIOF, GPIO_PIN_10);
}
void bsp_Led1Toggle(void)
{
if (gpio_output_bit_get(GPIOF, GPIO_PIN_10) == RESET)
{
gpio_bit_set(GPIOF, GPIO_PIN_10);
}
else
{
gpio_bit_reset(GPIOF, GPIO_PIN_10);
}
}
void bsp_Led2On(void)
{
gpio_bit_set(GPIOA, GPIO_PIN_6);
}
void bsp_Led2Off(void)
{
gpio_bit_reset(GPIOA, GPIO_PIN_6);
}
void bsp_Led2Toggle(void)
{
if (gpio_output_bit_get(GPIOA, GPIO_PIN_6) == RESET)
{
gpio_bit_set(GPIOA, GPIO_PIN_6);
}
else
{
gpio_bit_reset(GPIOA, GPIO_PIN_6);
}
}
4.2.KEY驱动程序
#include "bsp_key.h"
void bsp_KeyInit(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0);
rcu_periph_clock_enable(RCU_GPIOC);
gpio_mode_set(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_13);
rcu_periph_clock_enable(RCU_GPIOF);
gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_8);
}
uint8_t bsp_KeyReadPinLevel(uint8_t Index)
{
uint8_t PinLevel = 0;
switch (Index)
{
case 0:
PinLevel = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
break;
case 1:
PinLevel = gpio_input_bit_get(GPIOC, GPIO_PIN_13);
break;
case 2:
PinLevel = gpio_input_bit_get(GPIOF, GPIO_PIN_8);
break;
default:
break;
}
return (PinLevel);
}
4.3.KEY应用程序
#include "key.h"
#include "bsp_key.h"
#include "bsp_led.h"
#include "multi_button.h"
struct Button keyWakeup;
struct Button keyTamper;
struct Button keyUser;
volatile uint8_t KeyRegisterFlag = 0;
void KEY_WakeupHandler(void *btn)
{
struct Button *handle = (struct Button *)btn;
if (handle->button_id == 0)
{
bsp_Led1Toggle();
}
}
void KEY_TamperHandler(void *btn)
{
struct Button *handle = (struct Button *)btn;
if (handle->button_id == 1)
{
bsp_Led2Toggle();
}
}
void KEY_UserHandler(void *btn)
{
struct Button *handle = (struct Button *)btn;
if (handle->button_id == 2)
{
bsp_Led1Off();
bsp_Led2Off();
}
}
void KEY_Init(void)
{
button_init(&keyWakeup, bsp_KeyReadPinLevel, RESET, 0);
button_init(&keyTamper, bsp_KeyReadPinLevel, RESET, 1);
button_init(&keyUser, bsp_KeyReadPinLevel, RESET, 2);
button_attach(&keyWakeup, PRESS_DOWN, KEY_WakeupHandler);
button_attach(&keyTamper, PRESS_DOWN, KEY_TamperHandler);
button_attach(&keyUser, PRESS_DOWN, KEY_UserHandler);
button_start(&keyWakeup);
button_start(&keyTamper);
button_start(&keyUser);
KeyRegisterFlag = 1;
}
4.4.系统初始化及日志功能实现
#include "platform.h"
volatile uint32_t PLATFORM_DelayTick = 0;
void PLATFORM_InitCache(void)
{
SCB_EnableICache();
SCB_EnableDCache();
}
void PLATFORM_InitSysTick(void)
{
if (SysTick_Config(SystemCoreClock / 1000U))
{
while (1)
{
}
}
NVIC_SetPriority(SysTick_IRQn, 0x00U);
}
void PLATFORM_ConfigDelay(void)
{
/* -----------------------------------------------------------------------
TIMER50 configuration:
TIMER50CLK = 300MHz/300 = 1MHz, the period is 1ms(1MHz/1000 = 1ms).
----------------------------------------------------------------------- */
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(RCU_TIMER50);
timer_deinit(TIMER50);
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = 300 - 1;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 1000 - 1;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER50, &timer_initpara);
nvic_irq_enable(TIMER50_IRQn, 0, 0);
timer_interrupt_flag_clear(TIMER50, TIMER_INT_FLAG_UP);
timer_interrupt_enable(TIMER50, TIMER_INT_UP);
timer_enable(TIMER50);
}
void PLATFORM_DelayMs(uint32_t Tick)
{
PLATFORM_DelayTick = Tick;
while (PLATFORM_DelayTick)
{
}
}
void PLATFORM_InitConsole(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_USART0);
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9);
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10);
usart_deinit(USART0);
usart_word_length_set(USART0, USART_WL_8BIT);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_baudrate_set(USART0, 115200U);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
nvic_irq_enable(USART0_IRQn, 0, 0);
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
usart_interrupt_enable(USART0, USART_INT_RBNE);
usart_enable(USART0);
}
int fputc(int ch, FILE *f)
{
usart_data_transmit(USART0, (uint8_t)ch);
while (RESET == usart_flag_get(USART0, USART_FLAG_TBE))
{
}
return (ch);
}
void PLATFORM_PrintClocks(void)
{
printf("\r\nGD32H759 %s %s", __DATE__, __TIME__);
printf("\r\n");
printf("\r\nCK_SYS Frequency : %7.3f MHz", (double)rcu_clock_freq_get(CK_SYS) / (double)1000000.0);
printf("\r\nCK_AHB Frequency : %7.3f MHz", (double)rcu_clock_freq_get(CK_AHB) / (double)1000000.0);
printf("\r\nCK_APB1 Frequency : %7.3f MHz", (double)rcu_clock_freq_get(CK_APB1) / (double)1000000.0);
printf("\r\nCK_APB2 Frequency : %7.3f MHz", (double)rcu_clock_freq_get(CK_APB2) / (double)1000000.0);
printf("\r\nCK_APB3 Frequency : %7.3f MHz", (double)rcu_clock_freq_get(CK_APB3) / (double)1000000.0);
printf("\r\nCK_APB4 Frequency : %7.3f MHz", (double)rcu_clock_freq_get(CK_APB4) / (double)1000000.0);
printf("\r\n");
}
void PLATFORM_Init(void)
{
PLATFORM_InitCache();
PLATFORM_InitSysTick();
PLATFORM_ConfigDelay();
PLATFORM_InitConsole();
PLATFORM_PrintClocks();
}
4.5.相关中断处理
#include "gd32h7xx_it.h"
#include "key.h"
#include "platform.h"
#include "multi_button.h"
void NMI_Handler(void)
{
while (1)
{
}
}
void HardFault_Handler(void)
{
while (1)
{
}
}
void MemManage_Handler(void)
{
while (1)
{
}
}
void BusFault_Handler(void)
{
while (1)
{
}
}
void UsageFault_Handler(void)
{
while (1)
{
}
}
void DebugMon_Handler(void)
{
while (1)
{
}
}
void SVC_Handler(void)
{
while (1)
{
}
}
void PendSV_Handler(void)
{
while (1)
{
}
}
void SysTick_Handler(void)
{
if (KeyRegisterFlag)
{
button_ticks();
}
}
void TIMER50_IRQHandler(void)
{
if (RESET != timer_interrupt_flag_get(TIMER50, TIMER_INT_FLAG_UP))
{
if (PLATFORM_DelayTick)
{
PLATFORM_DelayTick--;
}
timer_interrupt_flag_clear(TIMER50, TIMER_INT_FLAG_UP);
}
}
void USART0_IRQHandler(void)
{
if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE))
{
printf("%c", (uint8_t)usart_data_receive(USART0));
}
}
5.注意事项及建议
5.1.LED的功能与TLI和USB有复用,User Key的功能与TLI有复用,所以在进行LED和KEY功能实现时,需要设置跳帽,和不与冲突的功能同时使用;
5.2.板载有调试器和CH340实现的USB转TTL接口;板载调试器只是实现了调试功能,没有虚拟串口功能,可以给整个单板供电;CH340部分需要通过跳帽选择USART功能,而且CH340部分的USB接口不能对单板进行供电,所以在调试打印日志的时候,需要接2个USB线;感觉比较麻烦,如果板载调试接口可以实现虚拟串口功能,那既省了CH340电路,又可以一根USB接线工作了;
6.运行结果
11
7.附件: