前言
AT官方的教程 雅特力AT32 MCU在VSCode(Visual Studio Code)环境上进行开发 - - 21ic电子技术开发论坛
或官网雅特力科技 : 32位微控制器的创新领导者! (arterytek.com)
的AP Note:AN0130,提供了VScode上使用Cmake和Cotex Debug进行开发的方法。
本人更习惯用EIDE,可以提供方便的项目管理工具,所以这篇文章旨在用VScode的EIDE开发AT32F4单片机。
准备工作
硬件:
- AT32F421C8T7 最小系统板(其他AT32F4系列同理)
- ATLink(或者JLink,本文使用ATLink+OpenOCD)
软件:
-
OpenOCD (只能使用AT官网提供的发行版,github上开源的ArteryTek/openocd 似乎在Flash driver上有点问题,不能兼容FT2232器件的烧录器)
-
适用于Arm的GCC工具链
-
MinGW
-
VScode :安装EIDE和Cortex Debug扩展。
代码:
- 由AT官方 AN0130中的 project template 修改而来,以适用于最小系统(只含有一个8M HEXT,一个32.768用于RTC,一个LED和UserKey均连接PC13引脚,其余数字引脚全引出)
EIDE工程创建
新建工程,设置如图:
然后写一个控制PC13引脚的 blink LED 程序。用到最基础的外设CRM、systick,GPIO。
标准库文件都在 ./libraries/drivers 中,不需要改动。
startup.s文件,linker文件和svd文件在 ./project 中,不要改动。
需要自己改/写的,只有
- main.c:配置SCKL和AHB(至systick和CPU core)时钟。使能APB(至GPIOC)外设时钟。初始化GPIOC。进入 blink LED 主循环。
#include "at32f421_clock.h" #include "systick.h" /** * @brief main function. * @param none * @retval none */ int main(void) { system_clock_config(); gpio_init_type gpio_init_struct; /* enable the led clock */ crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE); /* set default parameter */ gpio_default_para_init(&gpio_init_struct); /* configure the led gpio */ gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; gpio_init_struct.gpio_pins = GPIO_PINS_13; // gpio_init_struct.gpio_pull = GPIO_PULL_NONE; gpio_init(GPIOC, &gpio_init_struct); delay_init(); while (1) { gpio_bits_set(GPIOC,GPIO_PINS_13); delay_ms(400); gpio_bits_reset(GPIOC,GPIO_PINS_13); delay_ms(400); } }
- systick.c:定义了 us, ms, s 的延时函数。特别注意,在使用延时函数前要配置systick CTRL寄存器(的bit 2),指定systick时钟来源是CPU FCLK还是它的8分频。
#include "systick.h" #define STEP_DELAY_MS 50 /* delay variable */ static __IO uint32_t fac_us; static __IO uint32_t fac_ms; /** * @brief initialize delay function * @param none * @retval none */ void delay_init() { /* configure systick */ systick_clock_source_config(SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV); fac_us = system_core_clock / (1000000U); fac_ms = fac_us * (1000U); } /** * @brief inserts a delay time. * @param nus: specifies the delay time length, in microsecond. * @retval none */ void delay_us(uint32_t nus) { uint32_t temp = 0; SysTick->LOAD = (uint32_t)(nus * fac_us); SysTick->VAL = 0x00; SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; do { temp = SysTick->CTRL; } while ((temp & 0x01) && !(temp & (1 << 16))); SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; SysTick->VAL = 0x00; } /** * @brief inserts a delay time. * @param nms: specifies the delay time length, in milliseconds. * @retval none */ void delay_ms(uint16_t nms) { uint32_t temp = 0; while (nms) { if (nms > STEP_DELAY_MS) { SysTick->LOAD = (uint32_t)(STEP_DELAY_MS * fac_ms); nms -= STEP_DELAY_MS; } else { SysTick->LOAD = (uint32_t)(nms * fac_ms); nms = 0; } SysTick->VAL = 0x00; SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; do { temp = SysTick->CTRL; } while ((temp & 0x01) && !(temp & (1 << 16))); SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; SysTick->VAL = 0x00; } } /** * @brief inserts a delay time. * @param sec: specifies the delay time, in seconds. * @retval none */ void delay_sec(uint16_t sec) { uint16_t index; for (index = 0; index < sec; index++) { delay_ms(500); delay_ms(500); } }
- at32f421_clock.c:当要改变时钟树时才需改动,此处不动。
/** ************************************************************************** * @file at32f421_clock.c * @version v2.0.5 * @date 2022-04-02 * @brief system clock config program ************************************************************************** * Copyright notice & Disclaimer * * The software Board Support Package (BSP) that is made available to * download from Artery official website is the copyrighted work of Artery. * Artery authorizes customers to use, copy, and distribute the BSP * software and its related documentation for the purpose of design and * development in conjunction with Artery microcontrollers. Use of the * software is governed by this copyright notice and the following disclaimer. * * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES, * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS, * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS, * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * ************************************************************************** */ /* includes ------------------------------------------------------------------*/ #include "at32f421_clock.h" /** @addtogroup AT32F421_periph_template * @{ */ /** @addtogroup 421_System_clock_configuration System_clock_configuration * @{ */ /** * @brief system clock config program * @note the system clock is configured as follow: * - system clock = hext * pll_mult * - system clock source = pll (hext) * - hext = 8000000 * - sclk = 120000000 * - ahbdiv = 1 * - ahbclk = 120000000 * - apb2div = 1 * - apb2clk = 120000000 * - apb1div = 1 * - apb1clk = 120000000 * - pll_mult = 15 * - flash_wtcyc = 3 cycle * @param none * @retval none */ void system_clock_config(void) { /* config flash psr register */ flash_psr_set(FLASH_WAIT_CYCLE_3); /* reset crm */ crm_reset(); crm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE); /* wait till hext is ready */ while(crm_hext_stable_wait() == ERROR) { } /* config pll clock resource */ crm_pll_config(CRM_PLL_SOURCE_HEXT, CRM_PLL_MULT_15); /* enable pll */ crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE); /* wait till pll is ready */ while(crm_flag_get(CRM_PLL_STABLE_FLAG) != SET) { } /* config ahbclk */ crm_ahb_div_set(CRM_AHB_DIV_1); /* config apb2clk */ crm_apb2_div_set(CRM_APB2_DIV_1); /* config apb1clk */ crm_apb1_div_set(CRM_APB1_DIV_1); /* enable auto step mode */ crm_auto_step_mode_enable(TRUE); /* select pll as system clock source */ crm_sysclk_switch(CRM_SCLK_PLL); /* wait till pll is used as system clock source */ while(crm_sysclk_switch_status_get() != CRM_SCLK_PLL) { } /* disable auto step mode */ crm_auto_step_mode_enable(FALSE); /* update system_core_clock global variable */ system_core_clock_update(); } /** * @} */ /** * @} */
-
at32f421_int.c:当需要自定义中断服务程序时才作改动,此处不动。
/** ************************************************************************** * @file at32f421_int.c * @version v2.0.5 * @date 2022-04-02 * @brief main interrupt service routines. ************************************************************************** * Copyright notice & Disclaimer * * The software Board Support Package (BSP) that is made available to * download from Artery official website is the copyrighted work of Artery. * Artery authorizes customers to use, copy, and distribute the BSP * software and its related documentation for the purpose of design and * development in conjunction with Artery microcontrollers. Use of the * software is governed by this copyright notice and the following disclaimer. * * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES, * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS, * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS, * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * ************************************************************************** */ /* includes ------------------------------------------------------------------*/ #include "at32f421_int.h" /** @addtogroup AT32F421_periph_template * @{ */ /** @addtogroup 421_LED_toggle * @{ */ /** * @brief this function handles nmi exception. * @param none * @retval none */ void NMI_Handler(void) { } /** * @brief this function handles hard fault exception. * @param none * @retval none */ void HardFault_Handler(void) { /* go to infinite loop when hard fault exception occurs */ while(1) { } } /** * @brief this function handles memory manage exception. * @param none * @retval none */ void MemManage_Handler(void) { /* go to infinite loop when memory manage exception occurs */ while(1) { } } /** * @brief this function handles bus fault exception. * @param none * @retval none */ void BusFault_Handler(void) { /* go to infinite loop when bus fault exception occurs */ while(1) { } } /** * @brief this function handles usage fault exception. * @param none * @retval none */ void UsageFault_Handler(void) { /* go to infinite loop when usage fault exception occurs */ while(1) { } } /** * @brief this function handles svcall exception. * @param none * @retval none */ void SVC_Handler(void) { } /** * @brief this function handles debug monitor exception. * @param none * @retval none */ void DebugMon_Handler(void) { } /** * @brief this function handles pendsv_handler exception. * @param none * @retval none */ void PendSV_Handler(void) { } /** * @brief this function handles systick handler. * @param none * @retval none */ void SysTick_Handler(void) { } /** * @} */ /** * @} */
编译下载
按F7编译。
如果有报错,请检查EIDE项目设置。尤其是
- 编译器配置,环境变量,路径问题等
- 包含库目录
- 预处理器宏定义
烧录
注意正确选择OpenOCD的 interface 和 target。
这里贴一下EIDE工程配置文件:
{
"name": "AT32F421_eide_temp",
"type": "ARM",
"dependenceList": [],
"srcDirs": [
".eide/deps",
"libraries",
"project"
],
"virtualFolder": {
"name": "<virtual_root>",
"files": [],
"folders": []
},
"outDir": "build",
"deviceName": null,
"packDir": null,
"miscInfo": {
"uid": "4d69a5f133c15cd63d6e4f3e0be364f3"
},
"targets": {
"Debug": {
"excludeList": [
"libraries/cmsis/dsp",
"libraries/cmsis/cm4/device_support/startup/iar",
"libraries/cmsis/cm4/device_support/startup/mdk",
"project/src/startup_at32f421.s"
],
"toolchain": "GCC",
"compileConfig": {
"cpuType": "Cortex-M4",
"floatingPointHardware": "none",
"useCustomScatterFile": false,
"scatterFilePath": "project/misc/AT32F421x8_FLASH.ld",
"storageLayout": {
"RAM": [
{
"tag": "IRAM",
"id": 1,
"mem": {
"startAddr": "0x20000000",
"size": "0x5000"
},
"isChecked": true,
"noInit": false
}
],
"ROM": [
{
"tag": "IROM",
"id": 1,
"mem": {
"startAddr": "0x08000000",
"size": "0x10000"
},
"isChecked": true,
"isStartup": true
}
]
},
"options": "null"
},
"uploader": "OpenOCD",
"uploadConfig": {
"bin": "",
"target": "at32f421xx",
"interface": "atlink",
"baseAddr": "0x08000000"
},
"uploadConfigMap": {
"JLink": {
"bin": "",
"baseAddr": "",
"cpuInfo": {
"vendor": "null",
"cpuName": "null"
},
"proType": 1,
"speed": 8000,
"otherCmds": ""
}
},
"custom_dep": {
"name": "default",
"incList": [
".eide/deps",
".",
"project/inc",
"libraries/cmsis/cm4/core_support",
"libraries/cmsis/cm4/device_support",
"libraries/drivers/inc",
"c:\\SysGCC\\arm-eabi\\arm-none-eabi\\include"
],
"libList": [],
"sourceDirList": [],
"defineList": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"AT32F421C8T7",
"USE_STDPERIPH_DRIVER"
]
}
}
},
"version": "3.3"
}
调试
配置好launch.json后,按F5直接开启调试。
{
"version": "0.2.0",
"configurations": [
{
"cwd": "${workspaceRoot}",
"type": "cortex-debug",
"request": "launch",
"name": "openocd-launch",
"armToolchainPath": "C:/Users/hyq/.eide/tools/gcc_arm/10 2021.10/bin",
"gdbPath": "C:/Users/hyq/.eide/tools/gcc_arm/10 2021.10/bin/arm-none-eabi-gdb.exe",
"interface": "swd",
"servertype": "openocd",
"configFiles": [
"D:/OpenHardware/AT32/Tool/vscode/Tool/OpenOCD_V2.0.2/scripts/interface/atlink.cfg",
"D:/OpenHardware/AT32/Tool/vscode/Tool/OpenOCD_V2.0.2/scripts/target/at32f421xx.cfg"
],
"executable": "build/Debug/AT32F421_eide_temp.elf",
"runToMain": true,
"svdFile": "./project/misc/AT32F421xx_v2.svd",
}
]
}
Cortex Debug扩展需要依赖于launch.json文件,并且会使用系统环境变量Path里的openocd.exe路径作为默认的OpenOCD,所以这里有一个坑,因为本人电脑的系统上装有多个OpenOCD,并且配置地比较乱(EIDE上也填写过OpenOCD路径),所以如果不给Cortex Debug指定你希望使用的OpenOCD版本,很可能会出错。
因此最好在settings.json中指定扩展使用的OpenOCD路径:
"cortex-debug.openocdPath": "D:\\OpenHardware\\AT32\\Tool\\vscode\\Tool\\OpenOCD_V2.0.2\\bin\\openocd.exe",
输出EIDE模板
生成的 EIDE template 文件(.ept) 会在项目的根目录下。
之后再新建AT32F4的项目时,导入模板即可。