From 4b8b47bc9b5a56eb15c527d44880590a891d965f Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Sun, 10 May 2026 17:28:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0GD32E508=20DCU?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E5=B7=A5=E7=A8=8B=E7=9A=84=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增完整的CMSIS解决方案配置,包含AC6/GCC/IAR/CLANG编译器支持 - 添加项目配置文件,定义了设备层、系统层、应用层的模块结构 - 配置ARM::CMSIS和GigaDevice::GD32E50x_DFP软件包依赖 - 创建多层代码架构:应用入口(main)、系统层(sys)、设备层(device) - 实现任务管理系统,包含任务调度器和任务执行器分离设计 - 集成ADC、CANFD、定时器、串口、温度采样等核心功能模块 - 添加详细的README文档说明项目结构和代码组织方式 - 配置AC6链接脚本和运行时环境文件 - 设置Git忽略规则以排除编译产物和临时文件 --- .gitignore | 87 + DCU_APP.cbuild-pack.yml | 8 + DCU_APP.csolution.yml | 48 + .../GD32E508_DCU_APP.cproject.yml | 82 + .../GD32E508_DCU_APP.cproject.yml.bak_dma | 82 + GD32E508_DCU_APP/README.md | 539 +++++ .../GD32E508VE/ac6_linker_script.sct.src | 109 + .../RTE/Device/GD32E508VE/gd32e50x_adc.c | 1107 +++++++++ .../GD32E508VE/gd32e50x_adc.c.base@1.5.0 | 1107 +++++++++ .../RTE/Device/GD32E508VE/gd32e50x_can.c | 1615 +++++++++++++ .../GD32E508VE/gd32e50x_can.c.base@1.0.0 | 1615 +++++++++++++ .../RTE/Device/GD32E508VE/gd32e50x_crc.c | 239 ++ .../GD32E508VE/gd32e50x_crc.c.base@1.5.0 | 239 ++ .../RTE/Device/GD32E508VE/gd32e50x_dbg.c | 148 ++ .../GD32E508VE/gd32e50x_dbg.c.base@1.5.0 | 148 ++ .../RTE/Device/GD32E508VE/gd32e50x_dma.c | 752 ++++++ .../GD32E508VE/gd32e50x_dma.c.base@1.5.0 | 752 ++++++ .../RTE/Device/GD32E508VE/gd32e50x_exti.c | 249 ++ .../GD32E508VE/gd32e50x_exti.c.base@1.5.0 | 249 ++ .../RTE/Device/GD32E508VE/gd32e50x_fmc.c | 875 +++++++ .../GD32E508VE/gd32e50x_fmc.c.base@1.5.0 | 875 +++++++ .../RTE/Device/GD32E508VE/gd32e50x_gpio.c | 699 ++++++ .../GD32E508VE/gd32e50x_gpio.c.base@1.5.0 | 699 ++++++ .../RTE/Device/GD32E508VE/gd32e50x_misc.c | 180 ++ .../GD32E508VE/gd32e50x_misc.c.base@1.5.0 | 180 ++ .../RTE/Device/GD32E508VE/gd32e50x_pmu.c | 522 +++++ .../GD32E508VE/gd32e50x_pmu.c.base@1.5.0 | 522 +++++ .../RTE/Device/GD32E508VE/gd32e50x_rcu.c | 1621 +++++++++++++ .../GD32E508VE/gd32e50x_rcu.c.base@1.5.0 | 1621 +++++++++++++ .../RTE/Device/GD32E508VE/gd32e50x_spi.c | 895 ++++++++ .../GD32E508VE/gd32e50x_spi.c.base@1.5.0 | 895 ++++++++ .../RTE/Device/GD32E508VE/gd32e50x_timer.c | 2039 +++++++++++++++++ .../GD32E508VE/gd32e50x_timer.c.base@1.5.0 | 2039 +++++++++++++++++ .../RTE/Device/GD32E508VE/gd32e50x_usart.c | 1764 ++++++++++++++ .../GD32E508VE/gd32e50x_usart.c.base@1.5.0 | 1764 ++++++++++++++ .../Device/GD32E508VE/regions_GD32E508VE.h | 101 + .../Device/GD32E508VE/startup_gd32e50x_cl.s | 434 ++++ .../startup_gd32e50x_cl.s.base@1.5.0 | 434 ++++ .../RTE/Device/GD32E508VE/system_gd32e50x.c | 1175 ++++++++++ .../GD32E508VE/system_gd32e50x.c.base@1.2.4 | 1175 ++++++++++ GD32E508_DCU_APP/device/ADC.cpp | 126 + GD32E508_DCU_APP/device/ADC.cpp.bak_config | 126 + GD32E508_DCU_APP/device/ADC.cpp.bak_dma | 119 + GD32E508_DCU_APP/device/ADC.cpp.bak_oop | 218 ++ GD32E508_DCU_APP/device/ADC.cpp.bak_sch | 182 ++ GD32E508_DCU_APP/device/CANFD.cpp | 232 ++ .../device/CANFD.cpp.bak_align_app | 278 +++ GD32E508_DCU_APP/device/CANFD.cpp.bak_can_isr | 283 +++ GD32E508_DCU_APP/device/CANFD.cpp.bak_canrate | 274 +++ GD32E508_DCU_APP/device/CANFD.cpp.bak_canrcu | 274 +++ .../device/CANFD.cpp.bak_hideimpl | 225 ++ GD32E508_DCU_APP/device/CANFD.cpp.bak_oop | 326 +++ GD32E508_DCU_APP/device/CANFD.cpp.bak_pa8mode | 319 +++ GD32E508_DCU_APP/device/CANFD.cpp.bak_sch | 300 +++ GD32E508_DCU_APP/device/Timer.cpp | 51 + GD32E508_DCU_APP/device/Timer.cpp.bak_dma | 65 + GD32E508_DCU_APP/device/Timer.cpp.bak_oop | 65 + GD32E508_DCU_APP/device/Timer.h | 18 + GD32E508_DCU_APP/device/Timer.h.bak_oop | 17 + GD32E508_DCU_APP/device/adc.h | 43 + GD32E508_DCU_APP/device/adc.h.bak_dma | 78 + GD32E508_DCU_APP/device/adc.h.bak_oop | 114 + GD32E508_DCU_APP/device/adc.h.bak_sch | 94 + GD32E508_DCU_APP/device/canfd.h | 74 + GD32E508_DCU_APP/device/canfd.h.bak_hideimpl | 81 + GD32E508_DCU_APP/device/canfd.h.bak_oop | 99 + GD32E508_DCU_APP/device/canfd.h.bak_sch | 99 + GD32E508_DCU_APP/device/temp_sensor.cpp | 17 + .../device/temp_sensor.cpp.bak_oop | 25 + GD32E508_DCU_APP/device/temp_sensor.h | 20 + GD32E508_DCU_APP/device/temp_sensor.h.bak_oop | 33 + GD32E508_DCU_APP/device/usart0.cpp | 77 + GD32E508_DCU_APP/device/usart0.cpp.bak_oop | 109 + GD32E508_DCU_APP/device/usart0.cpp.bak_txisr | 54 + GD32E508_DCU_APP/device/usart0.h | 29 + GD32E508_DCU_APP/device/usart0.h.bak_oop | 25 + GD32E508_DCU_APP/main.cpp | 37 + GD32E508_DCU_APP/main.cpp.bak_config | 34 + GD32E508_DCU_APP/main.cpp.bak_oop | 35 + GD32E508_DCU_APP/main.h | 59 + GD32E508_DCU_APP/main.h.bak_oop | 47 + GD32E508_DCU_APP/sys/config.h | 79 + GD32E508_DCU_APP/sys/config.h.bak_canrate | 79 + GD32E508_DCU_APP/sys/config.h.bak_canrcu | 77 + GD32E508_DCU_APP/sys/config.h.bak_sch | 77 + GD32E508_DCU_APP/sys/config.h.bak_txisr | 77 + GD32E508_DCU_APP/sys/initial.cpp | 26 + GD32E508_DCU_APP/sys/initial.cpp.bak_dma | 28 + .../sys/initial.cpp.bak_fixinclude | 28 + GD32E508_DCU_APP/sys/initial.cpp.bak_oop | 31 + GD32E508_DCU_APP/sys/initial.cpp.bak_restore | 28 + GD32E508_DCU_APP/sys/initial.h | 13 + GD32E508_DCU_APP/sys/initial.h.bak_oop | 17 + GD32E508_DCU_APP/sys/systick.cpp | 29 + .../sys/systick.cpp.bak_fixinclude | 38 + GD32E508_DCU_APP/sys/systick.cpp.bak_oop | 38 + GD32E508_DCU_APP/sys/systick.cpp.bak_restore | 38 + GD32E508_DCU_APP/sys/systick.h | 20 + GD32E508_DCU_APP/sys/systick.h.bak_oop | 26 + .../sys/task_manager.c.bak_20260510_0448 | 63 + GD32E508_DCU_APP/sys/task_manager.cpp | 69 + .../sys/task_manager.cpp.bak_config | 85 + .../sys/task_manager.cpp.bak_fixinclude | 85 + GD32E508_DCU_APP/sys/task_manager.cpp.bak_oop | 86 + .../sys/task_manager.cpp.bak_restore | 85 + GD32E508_DCU_APP/sys/task_manager.h | 20 + .../sys/task_manager.h.bak_20260510_0448 | 11 + GD32E508_DCU_APP/sys/task_manager.h.bak_oop | 42 + GD32E508_DCU_APP/sys/task_runner.cpp | 106 + .../sys/task_runner.cpp.bak_fixinclude | 179 ++ GD32E508_DCU_APP/sys/task_runner.cpp.bak_oop | 186 ++ .../sys/task_runner.cpp.bak_restore | 179 ++ GD32E508_DCU_APP/sys/task_runner.cpp.bak_sch | 179 ++ GD32E508_DCU_APP/sys/task_runner.h | 29 + GD32E508_DCU_APP/sys/task_runner.h.bak_oop | 25 + vcpkg-configuration.json | 16 + 116 files changed, 36659 insertions(+) create mode 100644 .gitignore create mode 100644 DCU_APP.cbuild-pack.yml create mode 100644 DCU_APP.csolution.yml create mode 100644 GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml create mode 100644 GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml.bak_dma create mode 100644 GD32E508_DCU_APP/README.md create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/ac6_linker_script.sct.src create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_adc.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_adc.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_can.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_can.c.base@1.0.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_crc.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_crc.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dbg.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dbg.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dma.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dma.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exti.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exti.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_fmc.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_fmc.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_gpio.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_gpio.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_misc.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_misc.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_pmu.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_pmu.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_rcu.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_rcu.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_spi.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_spi.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_timer.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_timer.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_usart.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_usart.c.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/regions_GD32E508VE.h create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s.base@1.5.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c.base@1.2.4 create mode 100644 GD32E508_DCU_APP/device/ADC.cpp create mode 100644 GD32E508_DCU_APP/device/ADC.cpp.bak_config create mode 100644 GD32E508_DCU_APP/device/ADC.cpp.bak_dma create mode 100644 GD32E508_DCU_APP/device/ADC.cpp.bak_oop create mode 100644 GD32E508_DCU_APP/device/ADC.cpp.bak_sch create mode 100644 GD32E508_DCU_APP/device/CANFD.cpp create mode 100644 GD32E508_DCU_APP/device/CANFD.cpp.bak_align_app create mode 100644 GD32E508_DCU_APP/device/CANFD.cpp.bak_can_isr create mode 100644 GD32E508_DCU_APP/device/CANFD.cpp.bak_canrate create mode 100644 GD32E508_DCU_APP/device/CANFD.cpp.bak_canrcu create mode 100644 GD32E508_DCU_APP/device/CANFD.cpp.bak_hideimpl create mode 100644 GD32E508_DCU_APP/device/CANFD.cpp.bak_oop create mode 100644 GD32E508_DCU_APP/device/CANFD.cpp.bak_pa8mode create mode 100644 GD32E508_DCU_APP/device/CANFD.cpp.bak_sch create mode 100644 GD32E508_DCU_APP/device/Timer.cpp create mode 100644 GD32E508_DCU_APP/device/Timer.cpp.bak_dma create mode 100644 GD32E508_DCU_APP/device/Timer.cpp.bak_oop create mode 100644 GD32E508_DCU_APP/device/Timer.h create mode 100644 GD32E508_DCU_APP/device/Timer.h.bak_oop create mode 100644 GD32E508_DCU_APP/device/adc.h create mode 100644 GD32E508_DCU_APP/device/adc.h.bak_dma create mode 100644 GD32E508_DCU_APP/device/adc.h.bak_oop create mode 100644 GD32E508_DCU_APP/device/adc.h.bak_sch create mode 100644 GD32E508_DCU_APP/device/canfd.h create mode 100644 GD32E508_DCU_APP/device/canfd.h.bak_hideimpl create mode 100644 GD32E508_DCU_APP/device/canfd.h.bak_oop create mode 100644 GD32E508_DCU_APP/device/canfd.h.bak_sch create mode 100644 GD32E508_DCU_APP/device/temp_sensor.cpp create mode 100644 GD32E508_DCU_APP/device/temp_sensor.cpp.bak_oop create mode 100644 GD32E508_DCU_APP/device/temp_sensor.h create mode 100644 GD32E508_DCU_APP/device/temp_sensor.h.bak_oop create mode 100644 GD32E508_DCU_APP/device/usart0.cpp create mode 100644 GD32E508_DCU_APP/device/usart0.cpp.bak_oop create mode 100644 GD32E508_DCU_APP/device/usart0.cpp.bak_txisr create mode 100644 GD32E508_DCU_APP/device/usart0.h create mode 100644 GD32E508_DCU_APP/device/usart0.h.bak_oop create mode 100644 GD32E508_DCU_APP/main.cpp create mode 100644 GD32E508_DCU_APP/main.cpp.bak_config create mode 100644 GD32E508_DCU_APP/main.cpp.bak_oop create mode 100644 GD32E508_DCU_APP/main.h create mode 100644 GD32E508_DCU_APP/main.h.bak_oop create mode 100644 GD32E508_DCU_APP/sys/config.h create mode 100644 GD32E508_DCU_APP/sys/config.h.bak_canrate create mode 100644 GD32E508_DCU_APP/sys/config.h.bak_canrcu create mode 100644 GD32E508_DCU_APP/sys/config.h.bak_sch create mode 100644 GD32E508_DCU_APP/sys/config.h.bak_txisr create mode 100644 GD32E508_DCU_APP/sys/initial.cpp create mode 100644 GD32E508_DCU_APP/sys/initial.cpp.bak_dma create mode 100644 GD32E508_DCU_APP/sys/initial.cpp.bak_fixinclude create mode 100644 GD32E508_DCU_APP/sys/initial.cpp.bak_oop create mode 100644 GD32E508_DCU_APP/sys/initial.cpp.bak_restore create mode 100644 GD32E508_DCU_APP/sys/initial.h create mode 100644 GD32E508_DCU_APP/sys/initial.h.bak_oop create mode 100644 GD32E508_DCU_APP/sys/systick.cpp create mode 100644 GD32E508_DCU_APP/sys/systick.cpp.bak_fixinclude create mode 100644 GD32E508_DCU_APP/sys/systick.cpp.bak_oop create mode 100644 GD32E508_DCU_APP/sys/systick.cpp.bak_restore create mode 100644 GD32E508_DCU_APP/sys/systick.h create mode 100644 GD32E508_DCU_APP/sys/systick.h.bak_oop create mode 100644 GD32E508_DCU_APP/sys/task_manager.c.bak_20260510_0448 create mode 100644 GD32E508_DCU_APP/sys/task_manager.cpp create mode 100644 GD32E508_DCU_APP/sys/task_manager.cpp.bak_config create mode 100644 GD32E508_DCU_APP/sys/task_manager.cpp.bak_fixinclude create mode 100644 GD32E508_DCU_APP/sys/task_manager.cpp.bak_oop create mode 100644 GD32E508_DCU_APP/sys/task_manager.cpp.bak_restore create mode 100644 GD32E508_DCU_APP/sys/task_manager.h create mode 100644 GD32E508_DCU_APP/sys/task_manager.h.bak_20260510_0448 create mode 100644 GD32E508_DCU_APP/sys/task_manager.h.bak_oop create mode 100644 GD32E508_DCU_APP/sys/task_runner.cpp create mode 100644 GD32E508_DCU_APP/sys/task_runner.cpp.bak_fixinclude create mode 100644 GD32E508_DCU_APP/sys/task_runner.cpp.bak_oop create mode 100644 GD32E508_DCU_APP/sys/task_runner.cpp.bak_restore create mode 100644 GD32E508_DCU_APP/sys/task_runner.cpp.bak_sch create mode 100644 GD32E508_DCU_APP/sys/task_runner.h create mode 100644 GD32E508_DCU_APP/sys/task_runner.h.bak_oop create mode 100644 vcpkg-configuration.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..672b633 --- /dev/null +++ b/.gitignore @@ -0,0 +1,87 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# Clangd +.clangd + +# uv2csolution generated log file +uv2csolution.log + +# CMSIS Solution files and folders not to be tracked +# generated RTE files +**/RTE/_*/RTE_Components.h +**/RTE/_*/Pre_Include_*.h + +# default directory of build artifacts +**/out +# directory containing intermediate files to be regenerated +**/tmp + +# Update config files must be merged before commit +*.update@* + +# cbuild output files +*.cbuild.yml +*.cbuild-idx.yml +*.cbuild-run.yml +*.cbuild-set.yml + +# .vscode JSON and debug adapter files generated by CMSIS Solution +.vscode/cmsis.json +.vscode/launch.json +.vscode/tasks.json +.vscode/settings.json +.cmd.jlink + +# legacy intermediate project file +*.cprj diff --git a/DCU_APP.cbuild-pack.yml b/DCU_APP.cbuild-pack.yml new file mode 100644 index 0000000..ade6bb5 --- /dev/null +++ b/DCU_APP.cbuild-pack.yml @@ -0,0 +1,8 @@ +cbuild-pack: + resolved-packs: + - resolved-pack: ARM::CMSIS@6.1.0 + selected-by-pack: + - ARM::CMSIS + - resolved-pack: GigaDevice::GD32E50x_DFP@1.9.0 + selected-by-pack: + - GigaDevice::GD32E50x_DFP diff --git a/DCU_APP.csolution.yml b/DCU_APP.csolution.yml new file mode 100644 index 0000000..e136bbe --- /dev/null +++ b/DCU_APP.csolution.yml @@ -0,0 +1,48 @@ +# A solution is a collection of related projects that share same base configuration. +solution: + created-for: CMSIS-Toolbox@2.12.0 + cdefault: # use CMSIS-Toolbox default Compiler options + + # Compilers that can be selected + select-compiler: + - compiler: AC6 + - compiler: GCC + - compiler: IAR + - compiler: CLANG + + # List of miscellaneous tool-specific controls + misc: + - for-compiler: AC6 # GDB requires DWARF 5, remove when using uVision Debugger + C-CPP: + - -gdwarf-5 + ASM: + - -gdwarf-5 + + # List the packs that define the device and/or board. + packs: + - pack: GigaDevice::GD32E50x_DFP + - pack: ARM::CMSIS + + # List different hardware targets that are used to deploy the solution. + target-types: + - type: GD32E508VE + device: GD32E508VE + target-set: + - set: + images: + - project-context: GD32E508_DCU_APP.Debug + + # List of different build configurations. + build-types: + - type: Debug + debug: on + optimize: none + + - type: Release + debug: off + optimize: balanced + + # List related projects. + projects: + - project: GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml + compiler: AC6 diff --git a/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml b/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml new file mode 100644 index 0000000..8b07390 --- /dev/null +++ b/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml @@ -0,0 +1,82 @@ +# A project translates into one executable or library. +project: + + groups: + - group: Source Files + files: + - file: ./main.h + - file: ./main.cpp + - group: sys + files: + - file: ./sys/initial.h + - file: ./sys/initial.cpp + - file: ./sys/systick.h + - file: ./sys/systick.cpp + - file: ./sys/task_manager.h + - file: ./sys/task_manager.cpp + - file: ./sys/task_runner.h + - file: ./sys/task_runner.cpp + - file: ./sys/config.h + - group: device + files: + - file: ./device/usart0.h + - file: ./device/usart0.cpp + - file: ./device/adc.h + - file: ./device/ADC.cpp + - file: ./device/canfd.h + - file: ./device/CANFD.cpp + - file: ./device/Timer.h + - file: ./device/Timer.cpp + - file: ./device/temp_sensor.h + - file: ./device/temp_sensor.cpp + + # List components to use for your application. + # A software component is a re-usable unit that may be configurable. + components: + - component: ARM::CMSIS:CORE + - component: Device:Startup + - component: Device:GD32E50x_StdPeripherals:ADC + - component: Device:GD32E50x_StdPeripherals:CAN + - component: Device:GD32E50x_StdPeripherals:CRC + - component: Device:GD32E50x_StdPeripherals:DBG + - component: Device:GD32E50x_StdPeripherals:DMA + - component: Device:GD32E50x_StdPeripherals:EXTI + - component: Device:GD32E50x_StdPeripherals:FMC + - component: Device:GD32E50x_StdPeripherals:GPIO + - component: Device:GD32E50x_StdPeripherals:MISC + - component: Device:GD32E50x_StdPeripherals:PMU + - component: Device:GD32E50x_StdPeripherals:RCU + - component: Device:GD32E50x_StdPeripherals:SPI_I2S + - component: Device:GD32E50x_StdPeripherals:TIMER + - component: Device:GD32E50x_StdPeripherals:USART + - component: Device:GD32E50x_libopt + + # List executable file formats to be generated. + output: + type: + - elf + - hex + - map + + misc: + - for-compiler: AC6 + C-CPP: + - -DGD32E508 + - -DGD32E50X + - -gdwarf-5 + - -D__MICROLIB + - -fshort-enums + - -fshort-wchar + - -Wno-register + C: + - -std=gnu11 + CPP: + - -std=gnu++17 + ASM: + - -DGD32E508 + - -DGD32E50X + - -gdwarf-5 + - -D__MICROLIB + Link: + - --library_type=microlib + - --entry=Reset_Handler diff --git a/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml.bak_dma b/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml.bak_dma new file mode 100644 index 0000000..8b07390 --- /dev/null +++ b/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml.bak_dma @@ -0,0 +1,82 @@ +# A project translates into one executable or library. +project: + + groups: + - group: Source Files + files: + - file: ./main.h + - file: ./main.cpp + - group: sys + files: + - file: ./sys/initial.h + - file: ./sys/initial.cpp + - file: ./sys/systick.h + - file: ./sys/systick.cpp + - file: ./sys/task_manager.h + - file: ./sys/task_manager.cpp + - file: ./sys/task_runner.h + - file: ./sys/task_runner.cpp + - file: ./sys/config.h + - group: device + files: + - file: ./device/usart0.h + - file: ./device/usart0.cpp + - file: ./device/adc.h + - file: ./device/ADC.cpp + - file: ./device/canfd.h + - file: ./device/CANFD.cpp + - file: ./device/Timer.h + - file: ./device/Timer.cpp + - file: ./device/temp_sensor.h + - file: ./device/temp_sensor.cpp + + # List components to use for your application. + # A software component is a re-usable unit that may be configurable. + components: + - component: ARM::CMSIS:CORE + - component: Device:Startup + - component: Device:GD32E50x_StdPeripherals:ADC + - component: Device:GD32E50x_StdPeripherals:CAN + - component: Device:GD32E50x_StdPeripherals:CRC + - component: Device:GD32E50x_StdPeripherals:DBG + - component: Device:GD32E50x_StdPeripherals:DMA + - component: Device:GD32E50x_StdPeripherals:EXTI + - component: Device:GD32E50x_StdPeripherals:FMC + - component: Device:GD32E50x_StdPeripherals:GPIO + - component: Device:GD32E50x_StdPeripherals:MISC + - component: Device:GD32E50x_StdPeripherals:PMU + - component: Device:GD32E50x_StdPeripherals:RCU + - component: Device:GD32E50x_StdPeripherals:SPI_I2S + - component: Device:GD32E50x_StdPeripherals:TIMER + - component: Device:GD32E50x_StdPeripherals:USART + - component: Device:GD32E50x_libopt + + # List executable file formats to be generated. + output: + type: + - elf + - hex + - map + + misc: + - for-compiler: AC6 + C-CPP: + - -DGD32E508 + - -DGD32E50X + - -gdwarf-5 + - -D__MICROLIB + - -fshort-enums + - -fshort-wchar + - -Wno-register + C: + - -std=gnu11 + CPP: + - -std=gnu++17 + ASM: + - -DGD32E508 + - -DGD32E50X + - -gdwarf-5 + - -D__MICROLIB + Link: + - --library_type=microlib + - --entry=Reset_Handler diff --git a/GD32E508_DCU_APP/README.md b/GD32E508_DCU_APP/README.md new file mode 100644 index 0000000..090def8 --- /dev/null +++ b/GD32E508_DCU_APP/README.md @@ -0,0 +1,539 @@ +# GD32E508_DCU_APP README + +本 README 用来介绍 `/workzone/ROBOT_V2/DCU/DCU_APP/GD32E508_DCU_APP` 目录下各个代码文件的作用、分层关系和阅读顺序。 + +--- + +## 1. 项目概览 + +这是一个基于 **GD32E508** 的 DCU 应用工程,当前代码已经整理为: + +- **应用入口层**:`main.cpp` / `main.h` +- **系统层(sys)**:负责初始化、节拍、任务调度、任务执行 +- **设备层(device)**:负责 ADC、CANFD、Timer、串口、温度采样封装 +- **RTE / Device 层**:芯片启动文件、链接脚本、厂商外设驱动 + +当前整体执行逻辑是: + +1. `main.cpp` 调用 `app_init()` 完成硬件初始化 +2. `task_runner_init()` 初始化任务运行时数据 +3. `Timer` 周期中断驱动 `task_manager` 置位任务标志 +4. 主循环中 `task_runner_run_pending()` 读取并执行任务 +5. `ADC / CANFD / temp_sensor / usart0` 等模块提供具体功能 + +--- + +## 2. 推荐阅读顺序 + +如果第一次接手这个工程,建议按这个顺序看: + +1. `main.cpp` +2. `main.h` +3. `sys/initial.*` +4. `sys/task_manager.*` +5. `sys/task_runner.*` +6. `device/Timer.*` +7. `device/ADC.*` +8. `device/temp_sensor.*` +9. `device/CANFD.*` +10. `device/usart0.*` +11. `sys/systick.*` +12. `RTE/Device/GD32E508VE/*` + +这样能先看清主流程,再逐层下钻。 + +--- + +## 3. 应用层代码文件说明 + +### 3.1 根目录文件 + +#### `main.cpp` +**作用:** +- 应用主入口 +- 调用系统初始化 +- 启动任务运行时 +- 进入主循环执行待处理任务 + +**当前职责很轻:** +- 不直接处理 ADC +- 不直接处理 CANFD +- 不直接实现业务任务逻辑 + +这是当前工程的“总入口 + 调度外壳”。 + +--- + +#### `main.h` +**作用:** +- 全局基础头文件 +- 引入 `RTE_Components.h` 和芯片头文件 +- 定义系统任务枚举 `system_task_t` +- 定义调度器状态结构 `task_scheduler_t` +- 声明全局调度器实例 `g_scheduler` + +**这里的核心内容:** +- 任务类型定义 +- 调度节拍状态定义 + +可以把它理解为“任务系统公共协议头”。 + +--- + +## 4. 系统层 `sys/` 文件说明 + +### `sys/initial.h` +**作用:** +- 声明应用初始化函数 `app_init()` + +这是初始化入口的头文件。 + +--- + +### `sys/initial.cpp` +**作用:** +- 实现 `app_init()` +- 负责按顺序初始化各基础模块 + +**当前初始化内容包括:** +- SysTick +- USART0 GPIO +- USART0 串口 +- 温度采样模块 +- CANFD 模块 +- 任务调度器 +- 用户定时器 + +这是系统启动时的“初始化编排器”。 + +--- + +### `sys/systick.h` +**作用:** +- 声明系统毫秒节拍相关接口 + +**当前提供:** +- `systick_config_ms()` +- `delay_ms()` + +--- + +### `sys/systick.cpp` +**作用:** +- 实现 SysTick 中断处理 +- 维护毫秒计数器 +- 提供毫秒级阻塞延时 + +**当前功能:** +- `SysTick_Handler()`:系统滴答中断 +- `delay_ms()`:基于滴答计数的阻塞延时 +- `systick_config_ms()`:配置 1ms 节拍 + +这是系统时间基准模块。 + +--- + +### `sys/task_manager.h` +**作用:** +- 声明任务调度器接口 + +**当前提供:** +- `task_manager_init()` +- `task_manager_tick_100us()` +- `task_manager_take_pending()` +- `task_manager_set()` + +--- + +### `sys/task_manager.cpp` +**作用:** +- 维护全局任务标志位和节拍计数 +- 根据 100us 节拍分发不同周期任务 + +**当前任务节拍划分:** +- 500us:采样 VREF 原始值 +- 1ms:采样温度原始值 + 计算工程值 +- 10ms:CAN 接收轮询 + 接收解析 +- 100ms:CAN 发送准备 + CAN 发送 + 状态灯任务 +- 1s:心跳任务 + 诊断打印任务 + +这是“什么时候执行任务”的核心模块。 + +--- + +### `sys/task_runner.h` +**作用:** +- 声明任务执行器接口 + +**当前提供:** +- `task_runner_init()` +- `task_runner_run_pending()` + +--- + +### `sys/task_runner.cpp` +**作用:** +- 真正执行任务逻辑 +- 保存应用运行时缓存 +- 将任务位映射到具体函数 + +**当前内部任务包括:** +- ADC 原始采样任务 +- ADC 工程值计算任务 +- CAN RX 轮询任务 +- CAN RX 解析任务 +- CAN TX 准备任务 +- CAN TX 发送任务 +- 状态灯任务(占位) +- 心跳任务(占位) +- 诊断打印任务 + +**当前内部运行时缓存:** +- 最近一次温度采样结果 +- 最近一次 VREF 原始值 +- 最近一次温度原始值 +- 最近一次接收 CAN 帧 ID / 长度 / 数据 + +这是“执行什么任务”的核心模块。 + +--- + +## 5. 设备层 `device/` 文件说明 + +### `device/adc.h` +**作用:** +- 定义 ADC 采样结果结构 `adc_sample_t` +- 声明 ADC 硬件初始化、采样、换算接口 + +**主要接口:** +- `adc_hw_init()` +- `adc_single_read()` +- `adc_read_vref_raw()` +- `adc_read_temp_raw()` +- `adc_calculate_vdda()` +- `adc_calculate_temperature()` +- `adc_collect_sample()` + +--- + +### `device/ADC.cpp` +**作用:** +- 实现内部温度/VREF 的 ADC 采样流程 +- 提供原始值读取与工程值换算 + +**当前能力:** +- 初始化 ADC0 +- 读取内部参考电压 +- 读取内部温度通道 +- 根据参考电压估算 VDDA +- 根据内部温度特性曲线估算温度 +- 输出完整采样结构 + +这是 ADC 采样核心模块。 + +--- + +### `device/temp_sensor.h` +**作用:** +- 对温度采样模块做轻量封装 +- 将 `adc_sample_t` 别名为 `temp_sensor_data_t` + +**主要接口:** +- `temp_sensor_init()` +- `temp_sensor_sample()` + +--- + +### `device/temp_sensor.cpp` +**作用:** +- 基于 ADC 模块提供温度采样初始化和采样包装 + +**本质上:** +- `temp_sensor_init()` → 调用 `adc_hw_init()` +- `temp_sensor_sample()` → 调用 `adc_collect_sample()` + +这是一个轻量适配层,让上层按“温度传感器模块”来理解,而不直接依赖 ADC 细节。 + +--- + +### `device/usart0.h` +**作用:** +- 声明 USART0 串口相关接口 + +**当前提供:** +- `usart0_gpio_config()` +- `usart0_config()` +- `fputc()` + +--- + +### `device/usart0.cpp` +**作用:** +- 配置 USART0 的 GPIO 和串口参数 +- 重定向 `printf` 到 USART0 + +**主要用途:** +- 启动信息输出 +- 运行时诊断打印 + +这是当前工程的串口调试输出模块。 + +--- + +### `device/Timer.h` +**作用:** +- 声明用户任务定时器初始化接口 + +**当前提供:** +- `user_timer_init()` + +--- + +### `device/Timer.cpp` +**作用:** +- 配置 TIMER2 +- 配置 TIMER2 中断 +- 在中断里驱动任务系统节拍 + +**当前逻辑:** +- `user_timer_init()` 完成 TIMER2 配置与 NVIC 配置 +- `TIMER2_IRQHandler()` 在每次更新中断中调用 `task_manager_tick_100us()` + +这是任务节拍的硬件入口模块。 + +--- + +### `device/canfd.h` +**作用:** +- 定义 CANFD 模块对外接口 +- 定义诊断结构 `canfd_diag_t` +- 定义状态发送载荷结构 `canfd_status_payload_t` + +**主要接口:** +- `canfd_init()` +- `canfd_poll()` +- `canfd_prepare_status()` +- `canfd_send_prepared()` +- `canfd_has_rx_frame()` +- `canfd_consume_rx_frame()` +- `canfd_get_diag()` + +--- + +### `device/CANFD.cpp` +**作用:** +- 初始化 CANFD 工作模式 +- 轮询接收 FIFO +- 缓存接收帧 +- 构建状态发送帧 +- 发送准备好的 FD 帧 +- 维护 CANFD 诊断统计信息 + +**内部拆分特点:** +- RX 和 TX 已拆成两段式 +- 接收侧:`poll -> cache -> consume` +- 发送侧:`prepare -> send` + +**当前内部缓存:** +- `g_rx_frame`:单帧接收缓存 +- `g_tx_buffer`:待发送帧缓存 +- `g_can_diag`:收发与错误统计 + +这是当前最重要的通信模块之一。 + +--- + +## 6. RTE / Device 层文件说明 + +下面这些文件主要属于: + +- 芯片启动代码 +- 链接脚本 +- 厂商标准外设驱动 +- 自动生成的运行环境文件 + +**原则:** +- 这些文件通常不是业务逻辑主战场 +- 不建议随意直接修改 +- 如果一定要改,最好确认是可持续维护的变更 + +--- + +### `RTE/_Debug_GD32E508VE/RTE_Components.h` +**作用:** +- 自动生成的组件配置头文件 +- 指定当前工程启用哪些外设组件 +- 定义 `CMSIS_device_header` + +这是编译入口层配置文件之一。 + +--- + +### `RTE/Device/GD32E508VE/startup_gd32e50x_cl.s` +**作用:** +- 启动汇编文件 +- 定义中断向量表 +- 复位入口 +- 弱符号中断处理默认实现 + +这是 MCU 上电启动时最底层的入口。 + +--- + +### `RTE/Device/GD32E508VE/system_gd32e50x.c` +**作用:** +- 系统时钟配置与系统变量维护 +- 提供 `SystemCoreClock` 相关支持 + +这是 CMSIS 系统层实现文件。 + +--- + +### `RTE/Device/GD32E508VE/ac6_linker_script.sct.src` +**作用:** +- AC6 工具链链接脚本模板 +- 控制代码段、数据段、栈、堆等布局 + +--- + +### `RTE/Device/GD32E508VE/regions_GD32E508VE.h` +**作用:** +- 链接内存区域定义头文件 +- 配合链接脚本使用 + +--- + +## 7. 厂商外设驱动源文件 + +以下文件属于厂商标准外设驱动,主要为 `gd32e50x_xxx.c`: + +### `gd32e50x_adc.c` +**作用:** ADC 外设驱动实现。 + +### `gd32e50x_can.c` +**作用:** CAN / CANFD 相关底层驱动实现。 +**说明:** 当前项目的 CANFD 能力依赖这个文件中的 GD32E508 分支实现。 + +### `gd32e50x_crc.c` +**作用:** CRC 外设驱动实现。 + +### `gd32e50x_dbg.c` +**作用:** 调试控制相关外设驱动实现。 + +### `gd32e50x_dma.c` +**作用:** DMA 外设驱动实现。 + +### `gd32e50x_exti.c` +**作用:** 外部中断控制驱动实现。 + +### `gd32e50x_fmc.c` +**作用:** Flash / FMC 相关驱动实现。 + +### `gd32e50x_gpio.c` +**作用:** GPIO 外设驱动实现。 + +### `gd32e50x_misc.c` +**作用:** 中断优先级 / 杂项系统控制驱动实现。 + +### `gd32e50x_pmu.c` +**作用:** 电源管理单元驱动实现。 + +### `gd32e50x_rcu.c` +**作用:** 时钟控制单元驱动实现。 + +### `gd32e50x_spi.c` +**作用:** SPI 外设驱动实现。 + +### `gd32e50x_usart.c` +**作用:** USART 外设驱动实现。 + +--- + +## 8. `.base@版本号` 文件说明 + +例如: + +- `gd32e50x_can.c.base@1.0.0` +- `gd32e50x_adc.c.base@1.5.0` +- `startup_gd32e50x_cl.s.base@1.5.0` + +这些文件通常表示: + +- 原始基线版本 +- 或工具生成时附带的参考副本 + +**建议:** +- 默认不要动 +- 真要比对升级差异时再看 + +--- + +## 9. 当前应用层模块关系图(逻辑上) + +```text +main.cpp + ├─ sys/initial.cpp + │ ├─ sys/systick.cpp + │ ├─ device/usart0.cpp + │ ├─ device/temp_sensor.cpp + │ │ └─ device/ADC.cpp + │ ├─ device/CANFD.cpp + │ ├─ sys/task_manager.cpp + │ └─ device/Timer.cpp + │ └─ TIMER2_IRQHandler -> task_manager_tick_100us() + │ + └─ sys/task_runner.cpp + ├─ device/ADC.cpp + ├─ device/CANFD.cpp + └─ sys/task_manager.cpp +``` + +--- + +## 10. 当前代码特点总结 + +### 已完成的结构化工作 +- 应用层 `.c` 已整理为 `.cpp` +- `main` 已瘦身 +- 任务管理与任务执行已分离 +- CANFD 收发流程已拆成两段式 +- ADC / 温度 / 定时器 / 串口 已模块化 + +### 当前仍可继续演进的方向 +- 将 `task_runner.cpp` 再拆成 `task_adc.cpp / task_can.cpp / task_diag.cpp` +- 将 CANFD 单帧缓存升级为轻量环形缓冲 +- 将协议帧定义抽到单独文件中 +- 完善状态灯任务与心跳任务 + +--- + +## 11. 维护建议 + +### 建议优先修改的位置 +如果是改业务逻辑,优先看: +- `sys/task_runner.cpp` +- `device/CANFD.cpp` +- `device/ADC.cpp` +- `sys/task_manager.cpp` + +### 不建议随便改的位置 +除非你明确知道在干什么,否则不要轻易改: +- `RTE/Device/GD32E508VE/startup_gd32e50x_cl.s` +- `RTE/Device/GD32E508VE/system_gd32e50x.c` +- `RTE/Device/GD32E508VE/gd32e50x_*.c` +- 链接脚本相关文件 + +--- + +## 12. 后续建议 + +如果准备继续把这个项目做成更长期可维护的结构,下一步最值的动作是: + +1. 增加 `protocol/` 或 `frames/` 目录,专门放 CANFD 帧定义 +2. 将 `task_runner.cpp` 按任务领域继续拆文件 +3. 给应用层全部源码统一补齐函数级注释和参数说明 +4. 根据目标总线速率进一步细化 CANFD nominal/data timing 参数 + +--- + +如果后面代码结构继续变化,记得同步更新本 README,避免它变成过期文档。 diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/ac6_linker_script.sct.src b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/ac6_linker_script.sct.src new file mode 100644 index 0000000..7820e1f --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/ac6_linker_script.sct.src @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* ---------------------------------------------------------------------------- + Stack seal size definition + *----------------------------------------------------------------------------*/ +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#define __STACKSEAL_SIZE 8 +#else +#define __STACKSEAL_SIZE 0 +#endif + +/*---------------------------------------------------------------------------- + Scatter File Definitions definition + *----------------------------------------------------------------------------*/ + +LR_ROM0 __ROM0_BASE __ROM0_SIZE { + + ER_ROM0 __ROM0_BASE __ROM0_SIZE { + *.o (RESET, +First) + *(InRoot$$Sections) + *(+RO +XO) + } + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + ER_CMSE_VENEER AlignExpr(+0, 32) (__ROM0_SIZE - AlignExpr(ImageLength(ER_ROM0), 32)) { + *(Veneer$$CMSE) + } +#endif + + RW_NOINIT __RAM0_BASE UNINIT (__RAM0_SIZE - __HEAP_SIZE - __STACK_SIZE - __STACKSEAL_SIZE) { + *.o(.bss.noinit) + *.o(.bss.noinit.*) + } + + RW_RAM0 AlignExpr(+0, 8) (__RAM0_SIZE - __HEAP_SIZE - __STACK_SIZE - __STACKSEAL_SIZE - AlignExpr(ImageLength(RW_NOINIT), 8)) { + *(+RW +ZI) + } + +#if __HEAP_SIZE > 0 + ARM_LIB_HEAP (AlignExpr(+0, 8)) EMPTY __HEAP_SIZE { ; Reserve empty region for heap + } +#endif + + ARM_LIB_STACK (__RAM0_BASE + __RAM0_SIZE - __STACKSEAL_SIZE) EMPTY -__STACK_SIZE { ; Reserve empty region for stack + } + +#if __STACKSEAL_SIZE > 0 + STACKSEAL +0 EMPTY __STACKSEAL_SIZE { ; Reserve empty region for stack seal immediately after stack + } +#endif + +#if __RAM1_SIZE > 0 + RW_RAM1 __RAM1_BASE __RAM1_SIZE { + .ANY (+RW +ZI) + } +#endif + +#if __RAM2_SIZE > 0 + RW_RAM2 __RAM2_BASE __RAM2_SIZE { + .ANY (+RW +ZI) + } +#endif + +#if __RAM3_SIZE > 0 + RW_RAM3 __RAM3_BASE __RAM3_SIZE { + .ANY (+RW +ZI) + } +#endif +} + +#if __ROM1_SIZE > 0 +LR_ROM1 __ROM1_BASE __ROM1_SIZE { + ER_ROM1 +0 __ROM1_SIZE { + .ANY (+RO +XO) + } +} +#endif + +#if __ROM2_SIZE > 0 +LR_ROM2 __ROM2_BASE __ROM2_SIZE { + ER_ROM2 +0 __ROM2_SIZE { + .ANY (+RO +XO) + } +} +#endif + +#if __ROM3_SIZE > 0 +LR_ROM3 __ROM3_BASE __ROM3_SIZE { + ER_ROM3 +0 __ROM3_SIZE { + .ANY (+RO +XO) + } +} +#endif diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_adc.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_adc.c new file mode 100644 index 0000000..b028f9f --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_adc.c @@ -0,0 +1,1107 @@ +/*! + \file gd32e50x_adc.c + \brief ADC driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_adc.h" + +/*! + \brief reset ADC + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_deinit(uint32_t adc_periph) +{ + switch(adc_periph){ + case ADC0: + rcu_periph_reset_enable(RCU_ADC0RST); + rcu_periph_reset_disable(RCU_ADC0RST); + break; + case ADC1: + rcu_periph_reset_enable(RCU_ADC1RST); + rcu_periph_reset_disable(RCU_ADC1RST); + break; +#if (defined(GD32E50X_HD) || defined(GD32EPRT)) + case ADC2: + rcu_periph_reset_enable(RCU_ADC2RST); + rcu_periph_reset_disable(RCU_ADC2RST); + break; +#endif + default: + break; + } +} + +/*! + \brief enable ADC interface + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_enable(uint32_t adc_periph) +{ + if(RESET == (ADC_CTL1(adc_periph) & ADC_CTL1_ADCON)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_ADCON; + } +} + +/*! + \brief disable ADC interface + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_disable(uint32_t adc_periph) +{ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_ADCON); +} + +/*! + \brief ADC calibration and reset calibration + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_calibration_enable(uint32_t adc_periph) +{ + /* reset the selected ADC calibration registers */ + ADC_CTL1(adc_periph) |= (uint32_t) ADC_CTL1_RSTCLB; + /* check the RSTCLB bit state */ + while((ADC_CTL1(adc_periph) & ADC_CTL1_RSTCLB)){ + } + /* enable ADC calibration process */ + ADC_CTL1(adc_periph) |= ADC_CTL1_CLB; + /* check the CLB bit state */ + while((ADC_CTL1(adc_periph) & ADC_CTL1_CLB)){ + } +} + +/*! + \brief configure ADC calibration number + \param[in] clb_num: calibration number + only one parameter can be selected which is shown as below: + \arg ADC_CALIBRATION_NUM1: calibrate once + \arg ADC_CALIBRATION_NUM2: calibrate twice + \arg ADC_CALIBRATION_NUM4: calibrate 4 times + \arg ADC_CALIBRATION_NUM8: calibrate 8 times + \arg ADC_CALIBRATION_NUM16: calibrate 16 times + \arg ADC_CALIBRATION_NUM32: calibrate 32 times + \param[out] none + \retval none +*/ +void adc_calibration_number(uint32_t adc_periph, uint32_t clb_num) +{ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_CLBNUM); + ADC_CTL1(adc_periph) |= clb_num; +} + +/*! + \brief enable DMA request + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_dma_mode_enable(uint32_t adc_periph) +{ + ADC_CTL1(adc_periph) |= (uint32_t)(ADC_CTL1_DMA); +} + +/*! + \brief disable DMA request + \param[in] adc_periph: ADCx,x=0,1,2 + \param[out] none + \retval none +*/ +void adc_dma_mode_disable(uint32_t adc_periph) +{ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_DMA); +} + +/*! + \brief enable the temperature sensor and vrefint channel + \param[in] none + \param[out] none + \retval none +*/ +void adc_tempsensor_vrefint_enable(void) +{ + /* enable the temperature sensor and vrefint channel */ + ADC_CTL1(ADC0) |= ADC_CTL1_TSVREN; +} + +/*! + \brief disable the temperature sensor and vrefint channel + \param[in] none + \param[out] none + \retval none +*/ +void adc_tempsensor_vrefint_disable(void) +{ + /* disable the temperature sensor and vrefint channel */ + ADC_CTL1(ADC0) &= ~ADC_CTL1_TSVREN; +} + +/*! + \brief configure ADC discontinuous mode + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: select the channel group + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \arg ADC_CHANNEL_DISCON_DISABLE: disable discontinuous mode of regular and inserted channel + \param[in] length: number of conversions in discontinuous mode,the number can be 1..8 + for regular channel, the number has no effect for inserted channel + \param[out] none + \retval none +*/ +void adc_discontinuous_mode_config(uint32_t adc_periph, uint8_t adc_channel_group, uint8_t length) +{ + ADC_CTL0(adc_periph) &= ~((uint32_t)( ADC_CTL0_DISRC | ADC_CTL0_DISIC )); + switch(adc_channel_group){ + case ADC_REGULAR_CHANNEL: + /* configure the number of conversions in discontinuous mode */ + ADC_CTL0(adc_periph) &= ~((uint32_t)ADC_CTL0_DISNUM); + ADC_CTL0(adc_periph) |= CTL0_DISNUM(((uint32_t)length - 1U)); + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_DISRC; + break; + case ADC_INSERTED_CHANNEL: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_DISIC; + break; + case ADC_CHANNEL_DISCON_DISABLE: + default: + break; + } +} + +/*! + \brief configure the ADC0 sync mode + \param[in] mode: ADC0 mode + only one parameter can be selected which is shown as below: + \arg ADC_MODE_FREE: all the ADCs work independently + \arg ADC_DAUL_REGULAL_PARALLEL_INSERTED_PARALLEL: ADC0 and ADC1 work in combined regular parallel + inserted parallel mode + \arg ADC_DAUL_REGULAL_PARALLEL_INSERTED_ROTATION: ADC0 and ADC1 work in combined regular parallel + trigger rotation mode + \arg ADC_DAUL_INSERTED_PARALLEL_REGULAL_FOLLOWUP_FAST: ADC0 and ADC1 work in combined inserted parallel + follow-up fast mode + \arg ADC_DAUL_INSERTED_PARALLEL_REGULAL_FOLLOWUP_SLOW: ADC0 and ADC1 work in combined inserted parallel + follow-up slow mode + \arg ADC_DAUL_INSERTED_PARALLEL: ADC0 and ADC1 work in inserted parallel mode only + \arg ADC_DAUL_REGULAL_PARALLEL: ADC0 and ADC1 work in regular parallel mode only + \arg ADC_DAUL_REGULAL_FOLLOWUP_FAST: ADC0 and ADC1 work in follow-up fast mode only + \arg ADC_DAUL_REGULAL_FOLLOWUP_SLOW: ADC0 and ADC1 work in follow-up slow mode only + \arg ADC_DAUL_INSERTED_TRRIGGER_ROTATION: ADC0 and ADC1 work in trigger rotation mode only + \param[out] none + \retval none +*/ +void adc_mode_config(uint32_t mode) +{ + ADC_CTL0(ADC0) &= ~((uint32_t)ADC_CTL0_SYNCM); + ADC_CTL0(ADC0) |= mode; +} + +/*! + \brief configure ADC special function + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] function: the function to configure + one or more parameters can be selected which is shown as below: + \arg ADC_SCAN_MODE: scan mode select + \arg ADC_INSERTED_CHANNEL_AUTO: inserted channel group convert automatically + \arg ADC_CONTINUOUS_MODE: continuous mode select + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void adc_special_function_config(uint32_t adc_periph , uint32_t function , ControlStatus newvalue) +{ + if(newvalue){ + /* enable ADC scan mode */ + if(RESET != (function & ADC_SCAN_MODE)){ + ADC_CTL0(adc_periph) |= (uint32_t)ADC_SCAN_MODE; + } + /* enable ADC inserted channel group convert automatically */ + if(RESET != (function & ADC_INSERTED_CHANNEL_AUTO)){ + ADC_CTL0(adc_periph) |= (uint32_t)ADC_INSERTED_CHANNEL_AUTO; + } + /* enable ADC continuous mode */ + if(RESET != (function & ADC_CONTINUOUS_MODE)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CONTINUOUS_MODE; + } + }else{ + /* disable ADC scan mode */ + if(RESET != (function & ADC_SCAN_MODE)){ + ADC_CTL0(adc_periph) &= ~((uint32_t)ADC_SCAN_MODE); + } + /* disable ADC inserted channel group convert automatically */ + if(RESET != (function & ADC_INSERTED_CHANNEL_AUTO)){ + ADC_CTL0(adc_periph) &= ~((uint32_t)ADC_INSERTED_CHANNEL_AUTO); + } + /* disable ADC continuous mode */ + if(RESET != (function & ADC_CONTINUOUS_MODE)){ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CONTINUOUS_MODE); + } + } +} + +/*! + \brief configure ADC data alignment + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] data_alignment: data alignment select + only one parameter can be selected which is shown as below: + \arg ADC_DATAALIGN_RIGHT: right alignment + \arg ADC_DATAALIGN_LEFT: left alignment + \param[out] none + \retval none +*/ +void adc_data_alignment_config(uint32_t adc_periph , uint32_t data_alignment) +{ + if(ADC_DATAALIGN_RIGHT != data_alignment){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_DAL; + }else{ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_DAL); + } +} + +/*! + \brief configure the length of regular channel group or inserted channel group + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: select the channel group + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \param[in] length: the length of the channel + regular channel 1-16 + inserted channel 1-4 + \param[out] none + \retval none +*/ +void adc_channel_length_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t length) +{ + switch(adc_channel_group){ + case ADC_REGULAR_CHANNEL: + /* configure the length of regular channel group */ + ADC_RSQ0(adc_periph) &= ~((uint32_t)ADC_RSQ0_RL); + ADC_RSQ0(adc_periph) |= RSQ0_RL((uint32_t)(length-1U)); + break; + case ADC_INSERTED_CHANNEL: + /* configure the length of inserted channel group */ + ADC_ISQ(adc_periph) &= ~((uint32_t)ADC_ISQ_IL); + ADC_ISQ(adc_periph) |= ISQ_IL((uint32_t)(length-1U)); + break; + default: + break; + } +} + +/*! + \brief configure ADC regular channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] rank: the regular group sequence rank,this parameter must be between 0 to 15 + \param[in] adc_channel: the selected ADC channel + only one parameter can be selected which is shown as below: + \arg ADC_CHANNEL_x(x=0..17)(x=16 and x=17 are only for ADC0): ADC Channelx + \param[in] sample_time: the sample time value + only one parameter can be selected which is shown as below: + \arg ADC_SAMPLETIME_1POINT5: 1.5 cycles + \arg ADC_SAMPLETIME_7POINT5: 7.5 cycles + \arg ADC_SAMPLETIME_13POINT5: 13.5 cycles + \arg ADC_SAMPLETIME_28POINT5: 28.5 cycles + \arg ADC_SAMPLETIME_41POINT5: 41.5 cycles + \arg ADC_SAMPLETIME_55POINT5: 55.5 cycles + \arg ADC_SAMPLETIME_71POINT5: 71.5 cycles + \arg ADC_SAMPLETIME_239POINT5: 239.5 cycles + \param[out] none + \retval none +*/ +void adc_regular_channel_config(uint32_t adc_periph, uint8_t rank, uint8_t adc_channel, uint32_t sample_time) +{ + uint32_t rsq,sampt; + + /* configure ADC regular sequence */ + if(rank < 6U){ + rsq = ADC_RSQ2(adc_periph); + rsq &= ~((uint32_t)(ADC_RSQX_RSQN << (5U*rank))); + rsq |= ((uint32_t)adc_channel << (5U*rank)); + ADC_RSQ2(adc_periph) = rsq; + }else if(rank < 12U){ + rsq = ADC_RSQ1(adc_periph); + rsq &= ~((uint32_t)(ADC_RSQX_RSQN << (5U*(rank-6U)))); + rsq |= ((uint32_t)adc_channel << (5U*(rank-6U))); + ADC_RSQ1(adc_periph) = rsq; + }else if(rank < 16U){ + rsq = ADC_RSQ0(adc_periph); + rsq &= ~((uint32_t)(ADC_RSQX_RSQN << (5U*(rank-12U)))); + rsq |= ((uint32_t)adc_channel << (5U*(rank-12U))); + ADC_RSQ0(adc_periph) = rsq; + }else{ + } + + /* configure ADC sampling time */ + if(adc_channel < 10U){ + sampt = ADC_SAMPT1(adc_periph); + sampt &= ~((uint32_t)(ADC_SAMPTX_SPTN << (3U*adc_channel))); + sampt |= (uint32_t)(sample_time << (3U*adc_channel)); + ADC_SAMPT1(adc_periph) = sampt; + }else if(adc_channel < 18U){ + sampt = ADC_SAMPT0(adc_periph); + sampt &= ~((uint32_t)(ADC_SAMPTX_SPTN << (3U*(adc_channel-10U)))); + sampt |= (uint32_t)(sample_time << (3U*(adc_channel-10U))); + ADC_SAMPT0(adc_periph) = sampt; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief configure ADC inserted channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] rank: the inserted group sequencer rank,this parameter must be between 0 to 3 + \param[in] adc_channel: the selected ADC channel + only one parameter can be selected which is shown as below: + \arg ADC_CHANNEL_x(x=0..17)(x=16 and x=17 are only for ADC0): ADC Channelx + \param[in] sample_time: The sample time value + only one parameter can be selected which is shown as below: + \arg ADC_SAMPLETIME_1POINT5: 1.5 cycles + \arg ADC_SAMPLETIME_7POINT5: 7.5 cycles + \arg ADC_SAMPLETIME_13POINT5: 13.5 cycles + \arg ADC_SAMPLETIME_28POINT5: 28.5 cycles + \arg ADC_SAMPLETIME_41POINT5: 41.5 cycles + \arg ADC_SAMPLETIME_55POINT5: 55.5 cycles + \arg ADC_SAMPLETIME_71POINT5: 71.5 cycles + \arg ADC_SAMPLETIME_239POINT5: 239.5 cycles + \param[out] none + \retval none +*/ +void adc_inserted_channel_config(uint32_t adc_periph, uint8_t rank, uint8_t adc_channel, uint32_t sample_time) +{ + uint8_t inserted_length; + uint32_t isq, sampt; + + /* get inserted channel group length */ + inserted_length = (uint8_t)GET_BITS(ADC_ISQ(adc_periph), 20U, 21U); + + /* the channel number is written to these bits to select a channel as the nth conversion in the inserted channel group */ + isq = ADC_ISQ(adc_periph); + isq &= ~((uint32_t)(ADC_ISQ_ISQN << (5U * ((3U + rank) - inserted_length)))); + isq |= ((uint32_t)adc_channel << (5U * ((3U + rank) - inserted_length))); + ADC_ISQ(adc_periph) = isq; + + /* ADC sampling time config */ + if(adc_channel < 10U){ + sampt = ADC_SAMPT1(adc_periph); + sampt &= ~((uint32_t)(ADC_SAMPTX_SPTN << (3U*adc_channel))); + sampt |= (uint32_t) sample_time << (3U*adc_channel); + ADC_SAMPT1(adc_periph) = sampt; + }else if(adc_channel < 18U){ + sampt = ADC_SAMPT0(adc_periph); + sampt &= ~((uint32_t)(ADC_SAMPTX_SPTN << (3U*(adc_channel-10U)))); + sampt |= ((uint32_t)sample_time << (3U*(adc_channel-10U))); + ADC_SAMPT0(adc_periph) = sampt; + }else{ + } +} + +/*! + \brief configure ADC inserted channel offset + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] inserted_channel : insert channel select + only one parameter can be selected which is shown as below: + \arg ADC_INSERTED_CHANNEL_0: ADC inserted channel0 + \arg ADC_INSERTED_CHANNEL_1: ADC inserted channel1 + \arg ADC_INSERTED_CHANNEL_2: ADC inserted channel2 + \arg ADC_INSERTED_CHANNEL_3: ADC inserted channel3 + \param[in] offset : the offset data + \param[out] none + \retval none +*/ +void adc_inserted_channel_offset_config(uint32_t adc_periph, uint8_t inserted_channel, uint16_t offset) +{ + uint8_t inserted_length; + uint32_t num = 0U; + + inserted_length = (uint8_t)GET_BITS(ADC_ISQ(adc_periph), 20U, 21U); + num = 3U - (inserted_length - inserted_channel); + + if(num <= 3U){ + /* calculate the offset of the register */ + num = num * 4U; + /* configure the offset of the selected channels */ + REG32((adc_periph) + 0x14U + num) = IOFFX_IOFF((uint32_t)offset); + } +} + +/*! + \brief configure differential mode for ADC channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel: the channel use differential mode + one or more parameters can be selected which is shown as below: + \arg ADC_DIFFERENTIAL_MODE_CHANNEL_x(x=0..14), ADC_DIFFERENTIAL_MODE_CHANNEL_ALL: ADC channel for differential mode(just for channel0~channel14) + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void adc_channel_differential_mode_config(uint32_t adc_periph, uint32_t adc_channel, ControlStatus newvalue) +{ + if(ENABLE == newvalue){ + ADC_DIFCTL(adc_periph) |= (uint32_t)adc_channel; + }else{ + ADC_DIFCTL(adc_periph) &= ~((uint32_t)adc_channel); + } +} + +/*! + \brief configure ADC external trigger + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: select the channel group + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void adc_external_trigger_config(uint32_t adc_periph, uint8_t adc_channel_group, ControlStatus newvalue) +{ + if(newvalue){ + /* external trigger enable for regular channel */ + if(RESET != (adc_channel_group & ADC_REGULAR_CHANNEL)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_ETERC; + } + /* external trigger enable for inserted channel */ + if(RESET != (adc_channel_group & ADC_INSERTED_CHANNEL)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_ETEIC; + } + }else{ + /* external trigger disable for regular channel */ + if(RESET != (adc_channel_group & ADC_REGULAR_CHANNEL)){ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_ETERC); + } + /* external trigger disable for inserted channel */ + if(RESET != (adc_channel_group & ADC_INSERTED_CHANNEL)){ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_ETEIC); + } + } +} + +/*! + \brief configure ADC external trigger source + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: select the channel group + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \param[in] external_trigger_source: regular or inserted group trigger source + only one parameter can be selected which is shown as below: + for regular channel: + \arg ADC0_1_EXTTRIG_REGULAR_T0_CH0: TIMER0 CH0 event select + \arg ADC0_1_EXTTRIG_REGULAR_T0_CH1: TIMER0 CH1 event select + \arg ADC0_1_EXTTRIG_REGULAR_T0_CH2: TIMER0 CH2 event select + \arg ADC0_1_EXTTRIG_REGULAR_T1_CH1: TIMER1 CH1 event select + \arg ADC0_1_EXTTRIG_REGULAR_T2_TRGO: TIMER2 TRGO event select + \arg ADC0_1_EXTTRIG_REGULAR_T3_CH3: TIMER3 CH3 event select + \arg ADC0_1_EXTTRIG_REGULAR_T7_TRGO: TIMER7 TRGO event select + \arg ADC0_1_EXTTRIG_REGULAR_EXTI_11: external interrupt line 11 + \arg ADC0_1_EXTTRIG_REGULAR_SHRTIMER_ADCTRG0: SHRTIMER_ADCTRG0 output select(for GD32E50X_HD and GD32E50X_CL devices) + \arg ADC0_1_EXTTRIG_REGULAR_SHRTIMER_ADCTRG2: SHRTIMER_ADCTRG2 output select(for GD32E50X_HD and GD32E50X_CL devices) + \arg ADC2_EXTTRIG_REGULAR_T2_CH0: TIMER2 CH0 event select + \arg ADC2_EXTTRIG_REGULAR_T1_CH2: TIMER1 CH2 event select + \arg ADC2_EXTTRIG_REGULAR_T0_CH2: TIMER0 CH2 event select + \arg ADC2_EXTTRIG_REGULAR_T7_CH0: TIMER7 CH0 event select + \arg ADC2_EXTTRIG_REGULAR_T7_TRGO: TIMER7 TRGO event select + \arg ADC2_EXTTRIG_REGULAR_T4_CH0: TIMER4 CH0 event select + \arg ADC2_EXTTRIG_REGULAR_T4_CH2: TIMER4 CH2 event select + \arg ADC0_1_2_EXTTRIG_REGULAR_NONE: software trigger + for inserted channel: + \arg ADC0_1_EXTTRIG_INSERTED_T0_TRGO: TIMER0 TRGO event select + \arg ADC0_1_EXTTRIG_INSERTED_T0_CH3: TIMER0 CH3 event select + \arg ADC0_1_EXTTRIG_INSERTED_T1_TRGO: TIMER1 TRGO event select + \arg ADC0_1_EXTTRIG_INSERTED_T1_CH0: TIMER1 CH0 event select + \arg ADC0_1_EXTTRIG_INSERTED_T2_CH3: TIMER2 CH3 event select + \arg ADC0_1_EXTTRIG_INSERTED_T3_TRGO: TIMER3 TRGO event select + \arg ADC0_1_EXTTRIG_INSERTED_T7_CH3: TIMER7 CH3 event select + \arg ADC0_1_EXTTRIG_INSERTED_EXTI_15: external interrupt line 15 + \arg ADC0_1_EXTTRIG_INSERTED_SHRTIMER_ADCTRG1: SHRTIMER_ADCTRG1 output select(for GD32E50X_HD and GD32E50X_CL devices) + \arg ADC0_1_EXTTRIG_INSERTED_SHRTIMER_ADCTRG3: SHRTIMER_ADCTRG3 output select(for GD32E50X_HD and GD32E50X_CL devices) + \arg ADC2_EXTTRIG_INSERTED_T0_TRGO: TIMER0 TRGO event select + \arg ADC2_EXTTRIG_INSERTED_T0_CH3: TIMER0 CH3 event select + \arg ADC2_EXTTRIG_INSERTED_T3_CH2: TIMER3 CH2 event select + \arg ADC2_EXTTRIG_INSERTED_T7_CH1: TIMER7 CH1 event select + \arg ADC2_EXTTRIG_INSERTED_T7_CH3: TIMER7 CH3 event select + \arg ADC2_EXTTRIG_INSERTED_T4_TRGO: TIMER4 TRGO event select + \arg ADC2_EXTTRIG_INSERTED_T4_CH3: TIMER4 CH3 event select + \arg ADC0_1_2_EXTTRIG_INSERTED_NONE: software trigger + \param[out] none + \retval none +*/ +void adc_external_trigger_source_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t external_trigger_source) +{ + switch(adc_channel_group){ + case ADC_REGULAR_CHANNEL: + /* external trigger select for regular channel */ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_ETSRC); + ADC_CTL1(adc_periph) |= (uint32_t)external_trigger_source; + break; + case ADC_INSERTED_CHANNEL: + /* external trigger select for inserted channel */ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_ETSIC); + ADC_CTL1(adc_periph) |= (uint32_t)external_trigger_source; + break; + default: + break; + } +} + +/*! + \brief enable ADC software trigger + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: select the channel group + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \param[out] none + \retval none +*/ +void adc_software_trigger_enable(uint32_t adc_periph, uint8_t adc_channel_group) +{ + /* enable regular group channel software trigger */ + if(RESET != (adc_channel_group & ADC_REGULAR_CHANNEL)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_SWRCST; + } + /* enable inserted channel group software trigger */ + if(RESET != (adc_channel_group & ADC_INSERTED_CHANNEL)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_SWICST; + } +} + +/*! + \brief read ADC regular group data register + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] none + \param[out] none + \retval the conversion value: 0~0xFFFF +*/ +uint16_t adc_regular_data_read(uint32_t adc_periph) +{ + return (uint16_t)(ADC_RDATA(adc_periph)); +} + +/*! + \brief read ADC inserted group data register + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] inserted_channel: inserted channel select + only one parameter can be selected which is shown as below: + \arg ADC_INSERTED_CHANNEL_0: ADC inserted channel0 + \arg ADC_INSERTED_CHANNEL_1: ADC inserted channel1 + \arg ADC_INSERTED_CHANNEL_2: ADC inserted channel2 + \arg ADC_INSERTED_CHANNEL_3: ADC inserted channel3 + \param[out] none + \retval the conversion value: 0~0xFFFF +*/ +uint16_t adc_inserted_data_read(uint32_t adc_periph , uint8_t inserted_channel) +{ + uint32_t idata; + /* read the data of the selected channel */ + switch(inserted_channel){ + case ADC_INSERTED_CHANNEL_0: + idata = ADC_IDATA0(adc_periph); + break; + case ADC_INSERTED_CHANNEL_1: + idata = ADC_IDATA1(adc_periph); + break; + case ADC_INSERTED_CHANNEL_2: + idata = ADC_IDATA2(adc_periph); + break; + case ADC_INSERTED_CHANNEL_3: + idata = ADC_IDATA3(adc_periph); + break; + default: + idata = 0U; + break; + } + return (uint16_t)idata; +} + +/*! + \brief read the last ADC0 and ADC1 conversion result data in sync mode + \param[in] none + \param[out] none + \retval the conversion value: 0~0xFFFFFFFF +*/ +uint32_t adc_sync_mode_convert_value_read(void) +{ + /* return conversion value */ + return ADC_RDATA(ADC0); +} + +/*! + \brief configure ADC analog watchdog 0 single channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel: the selected ADC channel + only one parameter can be selected which is shown as below: + \arg ADC_CHANNEL_x: ADC Channelx(x=0..17)(x=16 and x=17 are only for ADC0) + \param[out] none + \retval none +*/ +void adc_watchdog0_single_channel_enable(uint32_t adc_periph, uint8_t adc_channel) +{ + ADC_CTL0(adc_periph) &= (uint32_t)~(ADC_CTL0_RWD0EN | ADC_CTL0_IWD0EN | ADC_CTL0_WD0SC | ADC_CTL0_WD0CHSEL); + + ADC_CTL0(adc_periph) |= (uint32_t)adc_channel; + ADC_CTL0(adc_periph) |= (uint32_t)(ADC_CTL0_RWD0EN | ADC_CTL0_IWD0EN | ADC_CTL0_WD0SC); +} + +/*! + \brief configure ADC analog watchdog 0 group channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: the channel group use analog watchdog 0 + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \arg ADC_REGULAR_INSERTED_CHANNEL: both regular and inserted group + \param[out] none + \retval none +*/ +void adc_watchdog0_group_channel_enable(uint32_t adc_periph, uint8_t adc_channel_group) +{ + ADC_CTL0(adc_periph) &= (uint32_t)~(ADC_CTL0_RWD0EN | ADC_CTL0_IWD0EN | ADC_CTL0_WD0SC); + /* select the group */ + switch(adc_channel_group){ + case ADC_REGULAR_CHANNEL: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_RWD0EN; + break; + case ADC_INSERTED_CHANNEL: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_IWD0EN; + break; + case ADC_REGULAR_INSERTED_CHANNEL: + ADC_CTL0(adc_periph) |= (uint32_t)(ADC_CTL0_RWD0EN | ADC_CTL0_IWD0EN); + break; + default: + break; + } +} + +/*! + \brief disable ADC analog watchdog 0 + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_watchdog0_disable(uint32_t adc_periph) +{ + ADC_CTL0(adc_periph) &= (uint32_t)~(ADC_CTL0_RWD0EN | ADC_CTL0_IWD0EN | ADC_CTL0_WD0SC | ADC_CTL0_WD0CHSEL); +} + +/*! + \brief configure ADC analog watchdog 1 channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel: the channel use analog watchdog 1 + one or more parameters can be selected which is shown as below: + \arg ADC_AWD1_2_SELECTION_CHANNEL_x, ADC_AWD1_2_SELECTION_CHANNEL_ALL: ADC channel analog watchdog 1/2 selection + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void adc_watchdog1_channel_config(uint32_t adc_periph, uint32_t adc_channel, ControlStatus newvalue) +{ + if(ENABLE == newvalue){ + ADC_WD1SR(adc_periph) |= (uint32_t)adc_channel; + }else{ + ADC_WD1SR(adc_periph) &= ~((uint32_t)adc_channel); + } +} + +/*! + \brief configure ADC analog watchdog 2 channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel: the channel use analog watchdog 2 + one or more parameters can be selected which is shown as below: + \arg ADC_AWD1_2_SELECTION_CHANNEL_x, ADC_AWD1_2_SELECTION_CHANNEL_ALL: ADC channel analog watchdog 1/2 selection + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void adc_watchdog2_channel_config(uint32_t adc_periph, uint32_t adc_channel, ControlStatus newvalue) +{ + if(ENABLE == newvalue){ + ADC_WD2SR(adc_periph) |= (uint32_t)adc_channel; + }else{ + ADC_WD2SR(adc_periph) &= ~((uint32_t)adc_channel); + } +} + +/*! + \brief disable ADC analog watchdog 1 + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_watchdog1_disable(uint32_t adc_periph) +{ + ADC_WD1SR(adc_periph) &= (uint32_t)~(ADC_WD1SR_AWD1CS); +} + +/*! + \brief disable ADC analog watchdog 2 + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_watchdog2_disable(uint32_t adc_periph) +{ + ADC_WD2SR(adc_periph) &= (uint32_t)~(ADC_WD2SR_AWD2CS); +} + +/*! + \brief configure ADC analog watchdog 0 threshold + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] low_threshold: analog watchdog 0 low threshold, 0..4095 + \param[in] high_threshold: analog watchdog 0 high threshold, 0..4095 + \param[out] none + \retval none +*/ +void adc_watchdog0_threshold_config(uint32_t adc_periph, uint16_t low_threshold, uint16_t high_threshold) +{ + ADC_WDLT0(adc_periph) = (uint32_t)WDLT0_WDLT0(low_threshold); + ADC_WDHT0(adc_periph) = (uint32_t)WDHT0_WDHT0(high_threshold); +} + +/*! + \brief configure ADC analog watchdog 1 threshold + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] low_threshold: analog watchdog 1 low threshold, 0..255 + \param[in] high_threshold: analog watchdog 1 high threshold, 0..255 + \param[out] none + \retval none +*/ +void adc_watchdog1_threshold_config(uint32_t adc_periph, uint8_t low_threshold, uint8_t high_threshold) +{ + ADC_WDT1(adc_periph) &= ~((uint32_t)(ADC_WDT1_WDLT1 | ADC_WDT1_WDHT1)); + /* configure ADC analog watchdog 1 threshold */ + ADC_WDT1(adc_periph) |= (uint32_t)WDT1_WDLT1(low_threshold); + ADC_WDT1(adc_periph) |= (uint32_t)WDT1_WDHT1(high_threshold); +} + +/*! + \brief configure ADC analog watchdog 2 threshold + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] low_threshold: analog watchdog 2 low threshold, 0..255 + \param[in] high_threshold: analog watchdog 2 high threshold, 0..255 + \param[out] none + \retval none +*/ +void adc_watchdog2_threshold_config(uint32_t adc_periph, uint8_t low_threshold, uint8_t high_threshold) +{ + ADC_WDT2(adc_periph) &= ~((uint32_t)(ADC_WDT2_WDLT2 | ADC_WDT2_WDHT2)); + /* configure ADC analog watchdog 2 threshold */ + ADC_WDT2(adc_periph) |= (uint32_t)WDT2_WDLT2(low_threshold); + ADC_WDT2(adc_periph) |= (uint32_t)WDT2_WDHT2(high_threshold); +} + +/*! + \brief configure ADC resolution + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] resolution: ADC resolution + only one parameter can be selected which is shown as below: + \arg ADC_RESOLUTION_12B: 12-bit ADC resolution + \arg ADC_RESOLUTION_10B: 10-bit ADC resolution + \arg ADC_RESOLUTION_8B: 8-bit ADC resolution + \arg ADC_RESOLUTION_6B: 6-bit ADC resolution + \param[out] none + \retval none +*/ +void adc_resolution_config(uint32_t adc_periph, uint32_t resolution) +{ + ADC_OVSAMPCTL(adc_periph) &= ~((uint32_t)ADC_OVSAMPCTL_DRES); + ADC_OVSAMPCTL(adc_periph) |= (uint32_t)resolution; +} + +/*! + \brief configure ADC oversample mode + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] mode: ADC oversampling mode + only one parameter can be selected which is shown as below: + \arg ADC_OVERSAMPLING_ALL_CONVERT: all oversampled conversions for a channel are done consecutively after a trigger + \arg ADC_OVERSAMPLING_ONE_CONVERT: each oversampled conversion for a channel needs a trigger + \param[in] shift: ADC oversampling shift + only one parameter can be selected which is shown as below: + \arg ADC_OVERSAMPLING_SHIFT_NONE: no oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_1B: 1-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_2B: 2-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_3B: 3-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_4B: 3-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_5B: 5-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_6B: 6-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_7B: 7-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_8B: 8-bit oversampling shift + \param[in] ratio: ADC oversampling ratio + only one parameter can be selected which is shown as below: + \arg ADC_OVERSAMPLING_RATIO_MUL2: oversampling ratio multiple 2 + \arg ADC_OVERSAMPLING_RATIO_MUL4: oversampling ratio multiple 4 + \arg ADC_OVERSAMPLING_RATIO_MUL8: oversampling ratio multiple 8 + \arg ADC_OVERSAMPLING_RATIO_MUL16: oversampling ratio multiple 16 + \arg ADC_OVERSAMPLING_RATIO_MUL32: oversampling ratio multiple 32 + \arg ADC_OVERSAMPLING_RATIO_MUL64: oversampling ratio multiple 64 + \arg ADC_OVERSAMPLING_RATIO_MUL128: oversampling ratio multiple 128 + \arg ADC_OVERSAMPLING_RATIO_MUL256: oversampling ratio multiple 256 + \param[out] none + \retval none +*/ +void adc_oversample_mode_config(uint32_t adc_periph, uint32_t mode, uint16_t shift, uint8_t ratio) +{ + /* configure ADC oversampling mode */ + if(ADC_OVERSAMPLING_ONE_CONVERT == mode){ + ADC_OVSAMPCTL(adc_periph) |= (uint32_t)ADC_OVSAMPCTL_TOVS; + }else{ + ADC_OVSAMPCTL(adc_periph) &= ~((uint32_t)ADC_OVSAMPCTL_TOVS); + } + /* configure the shift and ratio */ + ADC_OVSAMPCTL(adc_periph) &= ~((uint32_t)(ADC_OVSAMPCTL_OVSR | ADC_OVSAMPCTL_OVSS)); + ADC_OVSAMPCTL(adc_periph) |= ((uint32_t)shift | (uint32_t)ratio); +} + +/*! + \brief enable ADC oversample mode + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_oversample_mode_enable(uint32_t adc_periph) +{ + ADC_OVSAMPCTL(adc_periph) |= (uint32_t)ADC_OVSAMPCTL_OVSEN; +} + +/*! + \brief disable ADC oversample mode + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_oversample_mode_disable(uint32_t adc_periph) +{ + ADC_OVSAMPCTL(adc_periph) &= ~((uint32_t)ADC_OVSAMPCTL_OVSEN); +} + +/*! + \brief get the ADC flag + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] flag: the ADC flag bits + only one parameter can be selected which is shown as below: + \arg ADC_FLAG_WDE0: analog watchdog 0 event flag + \arg ADC_FLAG_EOC: end of group conversion flag + \arg ADC_FLAG_EOIC: end of inserted group conversion flag + \arg ADC_FLAG_STIC: start flag of inserted channel group + \arg ADC_FLAG_STRC: start flag of regular channel group + \arg ADC_FLAG_WDE1: analog watchdog 1 event flag + \arg ADC_FLAG_WDE2: analog watchdog 2 event flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus adc_flag_get(uint32_t adc_periph, uint32_t flag) +{ + FlagStatus reval = RESET; + if(ADC_STAT(adc_periph) & flag){ + reval = SET; + } + return reval; +} + +/*! + \brief clear the ADC flag + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] flag: the ADC flag + one or more parameters can be selected which is shown as below: + \arg ADC_FLAG_WDE0: analog watchdog 0 event flag + \arg ADC_FLAG_EOC: end of group conversion flag + \arg ADC_FLAG_EOIC: end of inserted group conversion flag + \arg ADC_FLAG_STIC: start flag of inserted channel group + \arg ADC_FLAG_STRC: start flag of regular channel group + \arg ADC_FLAG_WDE1: analog watchdog 1 event flag + \arg ADC_FLAG_WDE2: analog watchdog 2 event flag + \param[out] none + \retval none +*/ +void adc_flag_clear(uint32_t adc_periph , uint32_t flag) +{ + ADC_STAT(adc_periph) &= ~((uint32_t)flag); +} + +/*! + \brief enable ADC interrupt + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] interrupt: the ADC interrupt + one or more parameters can be selected which is shown as below: + \arg ADC_INT_WDE0: analog watchdog 0 interrupt flag + \arg ADC_INT_EOC: end of group conversion interrupt flag + \arg ADC_INT_EOIC: end of inserted group conversion interrupt flag + \arg ADC_INT_WDE1: analog watchdog 1 interrupt flag + \arg ADC_INT_WDE2: analog watchdog 2 interrupt flag + \param[out] none + \retval none +*/ +void adc_interrupt_enable(uint32_t adc_periph , uint32_t interrupt) +{ + switch(interrupt){ + /* enable analog watchdog 0 interrupt */ + case ADC_INT_WDE0: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_WDE0IE; + break; + /* enable end of group conversion interrupt */ + case ADC_INT_EOC: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_EOCIE; + break; + /* enable end of inserted group conversion interrupt */ + case ADC_INT_EOIC: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_EOICIE; + break; + /* enable analog watchdog 1 interrupt */ + case ADC_INT_WDE1: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_WDE1IE; + break; + /* enable analog watchdog 2 interrupt */ + case ADC_INT_WDE2: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_WDE2IE; + break; + default: + break; + } +} + +/*! + \brief disable ADC interrupt + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] interrupt: the ADC interrupt flag + one or more parameters can be selected which is shown as below: + \arg ADC_INT_WDE0: analog watchdog 0 interrupt flag + \arg ADC_INT_EOC: end of group conversion interrupt flag + \arg ADC_INT_EOIC: end of inserted group conversion interrupt flag + \arg ADC_INT_WDE1: analog watchdog 1 interrupt flag + \arg ADC_INT_WDE2: analog watchdog 2 interrupt flag + \param[out] none + \retval none +*/ +void adc_interrupt_disable(uint32_t adc_periph, uint32_t interrupt) +{ + switch(interrupt){ + /* disable analog watchdog 0 interrupt */ + case ADC_INT_WDE0: + ADC_CTL0(adc_periph) &= ~(uint32_t)ADC_CTL0_WDE0IE; + break; + /* disable end of group conversion interrupt */ + case ADC_INT_EOC: + ADC_CTL0(adc_periph) &= ~(uint32_t)ADC_CTL0_EOCIE; + break; + /* disable end of inserted group conversion interrupt */ + case ADC_INT_EOIC: + ADC_CTL0(adc_periph) &= ~(uint32_t)ADC_CTL0_EOICIE; + break; + /* disable analog watchdog 1 interrupt */ + case ADC_INT_WDE1: + ADC_CTL0(adc_periph) &= ~(uint32_t)ADC_CTL0_WDE1IE; + break; + /* disable analog watchdog 2 interrupt */ + case ADC_INT_WDE2: + ADC_CTL0(adc_periph) &= ~(uint32_t)ADC_CTL0_WDE1IE; + break; + default: + break; + } +} + +/*! + \brief get ADC interrupt flag + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] int_flag: the ADC interrupt + only one parameter can be selected which is shown as below: + \arg ADC_INT_FLAG_WDE0: analog watchdog 0 interrupt flag + \arg ADC_INT_FLAG_EOC: end of group conversion interrupt flag + \arg ADC_INT_FLAG_EOIC: end of inserted group conversion interrupt + \arg ADC_INT_FLAG_WDE1: analog watchdog 1 interrupt flag + \arg ADC_INT_FLAG_WDE2: analog watchdog 2 interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus adc_interrupt_flag_get(uint32_t adc_periph , uint32_t int_flag) +{ + FlagStatus interrupt_flag = RESET; + uint32_t state; + /* check the interrupt bits */ + switch(int_flag){ + case ADC_INT_FLAG_WDE0: + state = ADC_STAT(adc_periph) & ADC_STAT_WDE0; + if((ADC_CTL0(adc_periph) & ADC_CTL0_WDE0IE) && state){ + interrupt_flag = SET; + } + break; + case ADC_INT_FLAG_EOC: + state = ADC_STAT(adc_periph) & ADC_STAT_EOC; + if((ADC_CTL0(adc_periph) & ADC_CTL0_EOCIE) && state){ + interrupt_flag = SET; + } + break; + case ADC_INT_FLAG_EOIC: + state = ADC_STAT(adc_periph) & ADC_STAT_EOIC; + if((ADC_CTL0(adc_periph) & ADC_CTL0_EOICIE) && state){ + interrupt_flag = SET; + } + break; + case ADC_INT_FLAG_WDE1: + state = ADC_STAT(adc_periph) & ADC_STAT_WDE1; + if((ADC_CTL0(adc_periph) & ADC_CTL0_WDE1IE) && state){ + interrupt_flag = SET; + } + break; + case ADC_INT_FLAG_WDE2: + state = ADC_STAT(adc_periph) & ADC_STAT_WDE2; + if((ADC_CTL0(adc_periph) & ADC_CTL0_WDE2IE) && state){ + interrupt_flag = SET; + } + break; + default: + break; + } + return interrupt_flag; +} + +/*! + \brief clear ADC interrupt flag + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] int_flag: the ADC interrupt flag + only one parameter can be selected which is shown as below: + \arg ADC_INT_FLAG_WDE0: analog watchdog 0 interrupt flag + \arg ADC_INT_FLAG_EOC: end of group conversion interrupt flag + \arg ADC_INT_FLAG_EOIC: end of inserted group conversion interrupt flag + \arg ADC_INT_FLAG_WDE1: analog watchdog 1 interrupt flag + \arg ADC_INT_FLAG_WDE2: analog watchdog 2 interrupt flag + \param[out] none + \retval none +*/ +void adc_interrupt_flag_clear(uint32_t adc_periph, uint32_t int_flag) +{ + ADC_STAT(adc_periph) &= ~((uint32_t)int_flag); +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_adc.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_adc.c.base@1.5.0 new file mode 100644 index 0000000..b028f9f --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_adc.c.base@1.5.0 @@ -0,0 +1,1107 @@ +/*! + \file gd32e50x_adc.c + \brief ADC driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_adc.h" + +/*! + \brief reset ADC + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_deinit(uint32_t adc_periph) +{ + switch(adc_periph){ + case ADC0: + rcu_periph_reset_enable(RCU_ADC0RST); + rcu_periph_reset_disable(RCU_ADC0RST); + break; + case ADC1: + rcu_periph_reset_enable(RCU_ADC1RST); + rcu_periph_reset_disable(RCU_ADC1RST); + break; +#if (defined(GD32E50X_HD) || defined(GD32EPRT)) + case ADC2: + rcu_periph_reset_enable(RCU_ADC2RST); + rcu_periph_reset_disable(RCU_ADC2RST); + break; +#endif + default: + break; + } +} + +/*! + \brief enable ADC interface + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_enable(uint32_t adc_periph) +{ + if(RESET == (ADC_CTL1(adc_periph) & ADC_CTL1_ADCON)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_ADCON; + } +} + +/*! + \brief disable ADC interface + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_disable(uint32_t adc_periph) +{ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_ADCON); +} + +/*! + \brief ADC calibration and reset calibration + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_calibration_enable(uint32_t adc_periph) +{ + /* reset the selected ADC calibration registers */ + ADC_CTL1(adc_periph) |= (uint32_t) ADC_CTL1_RSTCLB; + /* check the RSTCLB bit state */ + while((ADC_CTL1(adc_periph) & ADC_CTL1_RSTCLB)){ + } + /* enable ADC calibration process */ + ADC_CTL1(adc_periph) |= ADC_CTL1_CLB; + /* check the CLB bit state */ + while((ADC_CTL1(adc_periph) & ADC_CTL1_CLB)){ + } +} + +/*! + \brief configure ADC calibration number + \param[in] clb_num: calibration number + only one parameter can be selected which is shown as below: + \arg ADC_CALIBRATION_NUM1: calibrate once + \arg ADC_CALIBRATION_NUM2: calibrate twice + \arg ADC_CALIBRATION_NUM4: calibrate 4 times + \arg ADC_CALIBRATION_NUM8: calibrate 8 times + \arg ADC_CALIBRATION_NUM16: calibrate 16 times + \arg ADC_CALIBRATION_NUM32: calibrate 32 times + \param[out] none + \retval none +*/ +void adc_calibration_number(uint32_t adc_periph, uint32_t clb_num) +{ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_CLBNUM); + ADC_CTL1(adc_periph) |= clb_num; +} + +/*! + \brief enable DMA request + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_dma_mode_enable(uint32_t adc_periph) +{ + ADC_CTL1(adc_periph) |= (uint32_t)(ADC_CTL1_DMA); +} + +/*! + \brief disable DMA request + \param[in] adc_periph: ADCx,x=0,1,2 + \param[out] none + \retval none +*/ +void adc_dma_mode_disable(uint32_t adc_periph) +{ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_DMA); +} + +/*! + \brief enable the temperature sensor and vrefint channel + \param[in] none + \param[out] none + \retval none +*/ +void adc_tempsensor_vrefint_enable(void) +{ + /* enable the temperature sensor and vrefint channel */ + ADC_CTL1(ADC0) |= ADC_CTL1_TSVREN; +} + +/*! + \brief disable the temperature sensor and vrefint channel + \param[in] none + \param[out] none + \retval none +*/ +void adc_tempsensor_vrefint_disable(void) +{ + /* disable the temperature sensor and vrefint channel */ + ADC_CTL1(ADC0) &= ~ADC_CTL1_TSVREN; +} + +/*! + \brief configure ADC discontinuous mode + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: select the channel group + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \arg ADC_CHANNEL_DISCON_DISABLE: disable discontinuous mode of regular and inserted channel + \param[in] length: number of conversions in discontinuous mode,the number can be 1..8 + for regular channel, the number has no effect for inserted channel + \param[out] none + \retval none +*/ +void adc_discontinuous_mode_config(uint32_t adc_periph, uint8_t adc_channel_group, uint8_t length) +{ + ADC_CTL0(adc_periph) &= ~((uint32_t)( ADC_CTL0_DISRC | ADC_CTL0_DISIC )); + switch(adc_channel_group){ + case ADC_REGULAR_CHANNEL: + /* configure the number of conversions in discontinuous mode */ + ADC_CTL0(adc_periph) &= ~((uint32_t)ADC_CTL0_DISNUM); + ADC_CTL0(adc_periph) |= CTL0_DISNUM(((uint32_t)length - 1U)); + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_DISRC; + break; + case ADC_INSERTED_CHANNEL: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_DISIC; + break; + case ADC_CHANNEL_DISCON_DISABLE: + default: + break; + } +} + +/*! + \brief configure the ADC0 sync mode + \param[in] mode: ADC0 mode + only one parameter can be selected which is shown as below: + \arg ADC_MODE_FREE: all the ADCs work independently + \arg ADC_DAUL_REGULAL_PARALLEL_INSERTED_PARALLEL: ADC0 and ADC1 work in combined regular parallel + inserted parallel mode + \arg ADC_DAUL_REGULAL_PARALLEL_INSERTED_ROTATION: ADC0 and ADC1 work in combined regular parallel + trigger rotation mode + \arg ADC_DAUL_INSERTED_PARALLEL_REGULAL_FOLLOWUP_FAST: ADC0 and ADC1 work in combined inserted parallel + follow-up fast mode + \arg ADC_DAUL_INSERTED_PARALLEL_REGULAL_FOLLOWUP_SLOW: ADC0 and ADC1 work in combined inserted parallel + follow-up slow mode + \arg ADC_DAUL_INSERTED_PARALLEL: ADC0 and ADC1 work in inserted parallel mode only + \arg ADC_DAUL_REGULAL_PARALLEL: ADC0 and ADC1 work in regular parallel mode only + \arg ADC_DAUL_REGULAL_FOLLOWUP_FAST: ADC0 and ADC1 work in follow-up fast mode only + \arg ADC_DAUL_REGULAL_FOLLOWUP_SLOW: ADC0 and ADC1 work in follow-up slow mode only + \arg ADC_DAUL_INSERTED_TRRIGGER_ROTATION: ADC0 and ADC1 work in trigger rotation mode only + \param[out] none + \retval none +*/ +void adc_mode_config(uint32_t mode) +{ + ADC_CTL0(ADC0) &= ~((uint32_t)ADC_CTL0_SYNCM); + ADC_CTL0(ADC0) |= mode; +} + +/*! + \brief configure ADC special function + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] function: the function to configure + one or more parameters can be selected which is shown as below: + \arg ADC_SCAN_MODE: scan mode select + \arg ADC_INSERTED_CHANNEL_AUTO: inserted channel group convert automatically + \arg ADC_CONTINUOUS_MODE: continuous mode select + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void adc_special_function_config(uint32_t adc_periph , uint32_t function , ControlStatus newvalue) +{ + if(newvalue){ + /* enable ADC scan mode */ + if(RESET != (function & ADC_SCAN_MODE)){ + ADC_CTL0(adc_periph) |= (uint32_t)ADC_SCAN_MODE; + } + /* enable ADC inserted channel group convert automatically */ + if(RESET != (function & ADC_INSERTED_CHANNEL_AUTO)){ + ADC_CTL0(adc_periph) |= (uint32_t)ADC_INSERTED_CHANNEL_AUTO; + } + /* enable ADC continuous mode */ + if(RESET != (function & ADC_CONTINUOUS_MODE)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CONTINUOUS_MODE; + } + }else{ + /* disable ADC scan mode */ + if(RESET != (function & ADC_SCAN_MODE)){ + ADC_CTL0(adc_periph) &= ~((uint32_t)ADC_SCAN_MODE); + } + /* disable ADC inserted channel group convert automatically */ + if(RESET != (function & ADC_INSERTED_CHANNEL_AUTO)){ + ADC_CTL0(adc_periph) &= ~((uint32_t)ADC_INSERTED_CHANNEL_AUTO); + } + /* disable ADC continuous mode */ + if(RESET != (function & ADC_CONTINUOUS_MODE)){ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CONTINUOUS_MODE); + } + } +} + +/*! + \brief configure ADC data alignment + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] data_alignment: data alignment select + only one parameter can be selected which is shown as below: + \arg ADC_DATAALIGN_RIGHT: right alignment + \arg ADC_DATAALIGN_LEFT: left alignment + \param[out] none + \retval none +*/ +void adc_data_alignment_config(uint32_t adc_periph , uint32_t data_alignment) +{ + if(ADC_DATAALIGN_RIGHT != data_alignment){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_DAL; + }else{ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_DAL); + } +} + +/*! + \brief configure the length of regular channel group or inserted channel group + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: select the channel group + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \param[in] length: the length of the channel + regular channel 1-16 + inserted channel 1-4 + \param[out] none + \retval none +*/ +void adc_channel_length_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t length) +{ + switch(adc_channel_group){ + case ADC_REGULAR_CHANNEL: + /* configure the length of regular channel group */ + ADC_RSQ0(adc_periph) &= ~((uint32_t)ADC_RSQ0_RL); + ADC_RSQ0(adc_periph) |= RSQ0_RL((uint32_t)(length-1U)); + break; + case ADC_INSERTED_CHANNEL: + /* configure the length of inserted channel group */ + ADC_ISQ(adc_periph) &= ~((uint32_t)ADC_ISQ_IL); + ADC_ISQ(adc_periph) |= ISQ_IL((uint32_t)(length-1U)); + break; + default: + break; + } +} + +/*! + \brief configure ADC regular channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] rank: the regular group sequence rank,this parameter must be between 0 to 15 + \param[in] adc_channel: the selected ADC channel + only one parameter can be selected which is shown as below: + \arg ADC_CHANNEL_x(x=0..17)(x=16 and x=17 are only for ADC0): ADC Channelx + \param[in] sample_time: the sample time value + only one parameter can be selected which is shown as below: + \arg ADC_SAMPLETIME_1POINT5: 1.5 cycles + \arg ADC_SAMPLETIME_7POINT5: 7.5 cycles + \arg ADC_SAMPLETIME_13POINT5: 13.5 cycles + \arg ADC_SAMPLETIME_28POINT5: 28.5 cycles + \arg ADC_SAMPLETIME_41POINT5: 41.5 cycles + \arg ADC_SAMPLETIME_55POINT5: 55.5 cycles + \arg ADC_SAMPLETIME_71POINT5: 71.5 cycles + \arg ADC_SAMPLETIME_239POINT5: 239.5 cycles + \param[out] none + \retval none +*/ +void adc_regular_channel_config(uint32_t adc_periph, uint8_t rank, uint8_t adc_channel, uint32_t sample_time) +{ + uint32_t rsq,sampt; + + /* configure ADC regular sequence */ + if(rank < 6U){ + rsq = ADC_RSQ2(adc_periph); + rsq &= ~((uint32_t)(ADC_RSQX_RSQN << (5U*rank))); + rsq |= ((uint32_t)adc_channel << (5U*rank)); + ADC_RSQ2(adc_periph) = rsq; + }else if(rank < 12U){ + rsq = ADC_RSQ1(adc_periph); + rsq &= ~((uint32_t)(ADC_RSQX_RSQN << (5U*(rank-6U)))); + rsq |= ((uint32_t)adc_channel << (5U*(rank-6U))); + ADC_RSQ1(adc_periph) = rsq; + }else if(rank < 16U){ + rsq = ADC_RSQ0(adc_periph); + rsq &= ~((uint32_t)(ADC_RSQX_RSQN << (5U*(rank-12U)))); + rsq |= ((uint32_t)adc_channel << (5U*(rank-12U))); + ADC_RSQ0(adc_periph) = rsq; + }else{ + } + + /* configure ADC sampling time */ + if(adc_channel < 10U){ + sampt = ADC_SAMPT1(adc_periph); + sampt &= ~((uint32_t)(ADC_SAMPTX_SPTN << (3U*adc_channel))); + sampt |= (uint32_t)(sample_time << (3U*adc_channel)); + ADC_SAMPT1(adc_periph) = sampt; + }else if(adc_channel < 18U){ + sampt = ADC_SAMPT0(adc_periph); + sampt &= ~((uint32_t)(ADC_SAMPTX_SPTN << (3U*(adc_channel-10U)))); + sampt |= (uint32_t)(sample_time << (3U*(adc_channel-10U))); + ADC_SAMPT0(adc_periph) = sampt; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief configure ADC inserted channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] rank: the inserted group sequencer rank,this parameter must be between 0 to 3 + \param[in] adc_channel: the selected ADC channel + only one parameter can be selected which is shown as below: + \arg ADC_CHANNEL_x(x=0..17)(x=16 and x=17 are only for ADC0): ADC Channelx + \param[in] sample_time: The sample time value + only one parameter can be selected which is shown as below: + \arg ADC_SAMPLETIME_1POINT5: 1.5 cycles + \arg ADC_SAMPLETIME_7POINT5: 7.5 cycles + \arg ADC_SAMPLETIME_13POINT5: 13.5 cycles + \arg ADC_SAMPLETIME_28POINT5: 28.5 cycles + \arg ADC_SAMPLETIME_41POINT5: 41.5 cycles + \arg ADC_SAMPLETIME_55POINT5: 55.5 cycles + \arg ADC_SAMPLETIME_71POINT5: 71.5 cycles + \arg ADC_SAMPLETIME_239POINT5: 239.5 cycles + \param[out] none + \retval none +*/ +void adc_inserted_channel_config(uint32_t adc_periph, uint8_t rank, uint8_t adc_channel, uint32_t sample_time) +{ + uint8_t inserted_length; + uint32_t isq, sampt; + + /* get inserted channel group length */ + inserted_length = (uint8_t)GET_BITS(ADC_ISQ(adc_periph), 20U, 21U); + + /* the channel number is written to these bits to select a channel as the nth conversion in the inserted channel group */ + isq = ADC_ISQ(adc_periph); + isq &= ~((uint32_t)(ADC_ISQ_ISQN << (5U * ((3U + rank) - inserted_length)))); + isq |= ((uint32_t)adc_channel << (5U * ((3U + rank) - inserted_length))); + ADC_ISQ(adc_periph) = isq; + + /* ADC sampling time config */ + if(adc_channel < 10U){ + sampt = ADC_SAMPT1(adc_periph); + sampt &= ~((uint32_t)(ADC_SAMPTX_SPTN << (3U*adc_channel))); + sampt |= (uint32_t) sample_time << (3U*adc_channel); + ADC_SAMPT1(adc_periph) = sampt; + }else if(adc_channel < 18U){ + sampt = ADC_SAMPT0(adc_periph); + sampt &= ~((uint32_t)(ADC_SAMPTX_SPTN << (3U*(adc_channel-10U)))); + sampt |= ((uint32_t)sample_time << (3U*(adc_channel-10U))); + ADC_SAMPT0(adc_periph) = sampt; + }else{ + } +} + +/*! + \brief configure ADC inserted channel offset + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] inserted_channel : insert channel select + only one parameter can be selected which is shown as below: + \arg ADC_INSERTED_CHANNEL_0: ADC inserted channel0 + \arg ADC_INSERTED_CHANNEL_1: ADC inserted channel1 + \arg ADC_INSERTED_CHANNEL_2: ADC inserted channel2 + \arg ADC_INSERTED_CHANNEL_3: ADC inserted channel3 + \param[in] offset : the offset data + \param[out] none + \retval none +*/ +void adc_inserted_channel_offset_config(uint32_t adc_periph, uint8_t inserted_channel, uint16_t offset) +{ + uint8_t inserted_length; + uint32_t num = 0U; + + inserted_length = (uint8_t)GET_BITS(ADC_ISQ(adc_periph), 20U, 21U); + num = 3U - (inserted_length - inserted_channel); + + if(num <= 3U){ + /* calculate the offset of the register */ + num = num * 4U; + /* configure the offset of the selected channels */ + REG32((adc_periph) + 0x14U + num) = IOFFX_IOFF((uint32_t)offset); + } +} + +/*! + \brief configure differential mode for ADC channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel: the channel use differential mode + one or more parameters can be selected which is shown as below: + \arg ADC_DIFFERENTIAL_MODE_CHANNEL_x(x=0..14), ADC_DIFFERENTIAL_MODE_CHANNEL_ALL: ADC channel for differential mode(just for channel0~channel14) + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void adc_channel_differential_mode_config(uint32_t adc_periph, uint32_t adc_channel, ControlStatus newvalue) +{ + if(ENABLE == newvalue){ + ADC_DIFCTL(adc_periph) |= (uint32_t)adc_channel; + }else{ + ADC_DIFCTL(adc_periph) &= ~((uint32_t)adc_channel); + } +} + +/*! + \brief configure ADC external trigger + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: select the channel group + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void adc_external_trigger_config(uint32_t adc_periph, uint8_t adc_channel_group, ControlStatus newvalue) +{ + if(newvalue){ + /* external trigger enable for regular channel */ + if(RESET != (adc_channel_group & ADC_REGULAR_CHANNEL)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_ETERC; + } + /* external trigger enable for inserted channel */ + if(RESET != (adc_channel_group & ADC_INSERTED_CHANNEL)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_ETEIC; + } + }else{ + /* external trigger disable for regular channel */ + if(RESET != (adc_channel_group & ADC_REGULAR_CHANNEL)){ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_ETERC); + } + /* external trigger disable for inserted channel */ + if(RESET != (adc_channel_group & ADC_INSERTED_CHANNEL)){ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_ETEIC); + } + } +} + +/*! + \brief configure ADC external trigger source + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: select the channel group + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \param[in] external_trigger_source: regular or inserted group trigger source + only one parameter can be selected which is shown as below: + for regular channel: + \arg ADC0_1_EXTTRIG_REGULAR_T0_CH0: TIMER0 CH0 event select + \arg ADC0_1_EXTTRIG_REGULAR_T0_CH1: TIMER0 CH1 event select + \arg ADC0_1_EXTTRIG_REGULAR_T0_CH2: TIMER0 CH2 event select + \arg ADC0_1_EXTTRIG_REGULAR_T1_CH1: TIMER1 CH1 event select + \arg ADC0_1_EXTTRIG_REGULAR_T2_TRGO: TIMER2 TRGO event select + \arg ADC0_1_EXTTRIG_REGULAR_T3_CH3: TIMER3 CH3 event select + \arg ADC0_1_EXTTRIG_REGULAR_T7_TRGO: TIMER7 TRGO event select + \arg ADC0_1_EXTTRIG_REGULAR_EXTI_11: external interrupt line 11 + \arg ADC0_1_EXTTRIG_REGULAR_SHRTIMER_ADCTRG0: SHRTIMER_ADCTRG0 output select(for GD32E50X_HD and GD32E50X_CL devices) + \arg ADC0_1_EXTTRIG_REGULAR_SHRTIMER_ADCTRG2: SHRTIMER_ADCTRG2 output select(for GD32E50X_HD and GD32E50X_CL devices) + \arg ADC2_EXTTRIG_REGULAR_T2_CH0: TIMER2 CH0 event select + \arg ADC2_EXTTRIG_REGULAR_T1_CH2: TIMER1 CH2 event select + \arg ADC2_EXTTRIG_REGULAR_T0_CH2: TIMER0 CH2 event select + \arg ADC2_EXTTRIG_REGULAR_T7_CH0: TIMER7 CH0 event select + \arg ADC2_EXTTRIG_REGULAR_T7_TRGO: TIMER7 TRGO event select + \arg ADC2_EXTTRIG_REGULAR_T4_CH0: TIMER4 CH0 event select + \arg ADC2_EXTTRIG_REGULAR_T4_CH2: TIMER4 CH2 event select + \arg ADC0_1_2_EXTTRIG_REGULAR_NONE: software trigger + for inserted channel: + \arg ADC0_1_EXTTRIG_INSERTED_T0_TRGO: TIMER0 TRGO event select + \arg ADC0_1_EXTTRIG_INSERTED_T0_CH3: TIMER0 CH3 event select + \arg ADC0_1_EXTTRIG_INSERTED_T1_TRGO: TIMER1 TRGO event select + \arg ADC0_1_EXTTRIG_INSERTED_T1_CH0: TIMER1 CH0 event select + \arg ADC0_1_EXTTRIG_INSERTED_T2_CH3: TIMER2 CH3 event select + \arg ADC0_1_EXTTRIG_INSERTED_T3_TRGO: TIMER3 TRGO event select + \arg ADC0_1_EXTTRIG_INSERTED_T7_CH3: TIMER7 CH3 event select + \arg ADC0_1_EXTTRIG_INSERTED_EXTI_15: external interrupt line 15 + \arg ADC0_1_EXTTRIG_INSERTED_SHRTIMER_ADCTRG1: SHRTIMER_ADCTRG1 output select(for GD32E50X_HD and GD32E50X_CL devices) + \arg ADC0_1_EXTTRIG_INSERTED_SHRTIMER_ADCTRG3: SHRTIMER_ADCTRG3 output select(for GD32E50X_HD and GD32E50X_CL devices) + \arg ADC2_EXTTRIG_INSERTED_T0_TRGO: TIMER0 TRGO event select + \arg ADC2_EXTTRIG_INSERTED_T0_CH3: TIMER0 CH3 event select + \arg ADC2_EXTTRIG_INSERTED_T3_CH2: TIMER3 CH2 event select + \arg ADC2_EXTTRIG_INSERTED_T7_CH1: TIMER7 CH1 event select + \arg ADC2_EXTTRIG_INSERTED_T7_CH3: TIMER7 CH3 event select + \arg ADC2_EXTTRIG_INSERTED_T4_TRGO: TIMER4 TRGO event select + \arg ADC2_EXTTRIG_INSERTED_T4_CH3: TIMER4 CH3 event select + \arg ADC0_1_2_EXTTRIG_INSERTED_NONE: software trigger + \param[out] none + \retval none +*/ +void adc_external_trigger_source_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t external_trigger_source) +{ + switch(adc_channel_group){ + case ADC_REGULAR_CHANNEL: + /* external trigger select for regular channel */ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_ETSRC); + ADC_CTL1(adc_periph) |= (uint32_t)external_trigger_source; + break; + case ADC_INSERTED_CHANNEL: + /* external trigger select for inserted channel */ + ADC_CTL1(adc_periph) &= ~((uint32_t)ADC_CTL1_ETSIC); + ADC_CTL1(adc_periph) |= (uint32_t)external_trigger_source; + break; + default: + break; + } +} + +/*! + \brief enable ADC software trigger + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: select the channel group + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \param[out] none + \retval none +*/ +void adc_software_trigger_enable(uint32_t adc_periph, uint8_t adc_channel_group) +{ + /* enable regular group channel software trigger */ + if(RESET != (adc_channel_group & ADC_REGULAR_CHANNEL)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_SWRCST; + } + /* enable inserted channel group software trigger */ + if(RESET != (adc_channel_group & ADC_INSERTED_CHANNEL)){ + ADC_CTL1(adc_periph) |= (uint32_t)ADC_CTL1_SWICST; + } +} + +/*! + \brief read ADC regular group data register + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] none + \param[out] none + \retval the conversion value: 0~0xFFFF +*/ +uint16_t adc_regular_data_read(uint32_t adc_periph) +{ + return (uint16_t)(ADC_RDATA(adc_periph)); +} + +/*! + \brief read ADC inserted group data register + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] inserted_channel: inserted channel select + only one parameter can be selected which is shown as below: + \arg ADC_INSERTED_CHANNEL_0: ADC inserted channel0 + \arg ADC_INSERTED_CHANNEL_1: ADC inserted channel1 + \arg ADC_INSERTED_CHANNEL_2: ADC inserted channel2 + \arg ADC_INSERTED_CHANNEL_3: ADC inserted channel3 + \param[out] none + \retval the conversion value: 0~0xFFFF +*/ +uint16_t adc_inserted_data_read(uint32_t adc_periph , uint8_t inserted_channel) +{ + uint32_t idata; + /* read the data of the selected channel */ + switch(inserted_channel){ + case ADC_INSERTED_CHANNEL_0: + idata = ADC_IDATA0(adc_periph); + break; + case ADC_INSERTED_CHANNEL_1: + idata = ADC_IDATA1(adc_periph); + break; + case ADC_INSERTED_CHANNEL_2: + idata = ADC_IDATA2(adc_periph); + break; + case ADC_INSERTED_CHANNEL_3: + idata = ADC_IDATA3(adc_periph); + break; + default: + idata = 0U; + break; + } + return (uint16_t)idata; +} + +/*! + \brief read the last ADC0 and ADC1 conversion result data in sync mode + \param[in] none + \param[out] none + \retval the conversion value: 0~0xFFFFFFFF +*/ +uint32_t adc_sync_mode_convert_value_read(void) +{ + /* return conversion value */ + return ADC_RDATA(ADC0); +} + +/*! + \brief configure ADC analog watchdog 0 single channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel: the selected ADC channel + only one parameter can be selected which is shown as below: + \arg ADC_CHANNEL_x: ADC Channelx(x=0..17)(x=16 and x=17 are only for ADC0) + \param[out] none + \retval none +*/ +void adc_watchdog0_single_channel_enable(uint32_t adc_periph, uint8_t adc_channel) +{ + ADC_CTL0(adc_periph) &= (uint32_t)~(ADC_CTL0_RWD0EN | ADC_CTL0_IWD0EN | ADC_CTL0_WD0SC | ADC_CTL0_WD0CHSEL); + + ADC_CTL0(adc_periph) |= (uint32_t)adc_channel; + ADC_CTL0(adc_periph) |= (uint32_t)(ADC_CTL0_RWD0EN | ADC_CTL0_IWD0EN | ADC_CTL0_WD0SC); +} + +/*! + \brief configure ADC analog watchdog 0 group channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel_group: the channel group use analog watchdog 0 + only one parameter can be selected which is shown as below: + \arg ADC_REGULAR_CHANNEL: regular channel group + \arg ADC_INSERTED_CHANNEL: inserted channel group + \arg ADC_REGULAR_INSERTED_CHANNEL: both regular and inserted group + \param[out] none + \retval none +*/ +void adc_watchdog0_group_channel_enable(uint32_t adc_periph, uint8_t adc_channel_group) +{ + ADC_CTL0(adc_periph) &= (uint32_t)~(ADC_CTL0_RWD0EN | ADC_CTL0_IWD0EN | ADC_CTL0_WD0SC); + /* select the group */ + switch(adc_channel_group){ + case ADC_REGULAR_CHANNEL: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_RWD0EN; + break; + case ADC_INSERTED_CHANNEL: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_IWD0EN; + break; + case ADC_REGULAR_INSERTED_CHANNEL: + ADC_CTL0(adc_periph) |= (uint32_t)(ADC_CTL0_RWD0EN | ADC_CTL0_IWD0EN); + break; + default: + break; + } +} + +/*! + \brief disable ADC analog watchdog 0 + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_watchdog0_disable(uint32_t adc_periph) +{ + ADC_CTL0(adc_periph) &= (uint32_t)~(ADC_CTL0_RWD0EN | ADC_CTL0_IWD0EN | ADC_CTL0_WD0SC | ADC_CTL0_WD0CHSEL); +} + +/*! + \brief configure ADC analog watchdog 1 channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel: the channel use analog watchdog 1 + one or more parameters can be selected which is shown as below: + \arg ADC_AWD1_2_SELECTION_CHANNEL_x, ADC_AWD1_2_SELECTION_CHANNEL_ALL: ADC channel analog watchdog 1/2 selection + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void adc_watchdog1_channel_config(uint32_t adc_periph, uint32_t adc_channel, ControlStatus newvalue) +{ + if(ENABLE == newvalue){ + ADC_WD1SR(adc_periph) |= (uint32_t)adc_channel; + }else{ + ADC_WD1SR(adc_periph) &= ~((uint32_t)adc_channel); + } +} + +/*! + \brief configure ADC analog watchdog 2 channel + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] adc_channel: the channel use analog watchdog 2 + one or more parameters can be selected which is shown as below: + \arg ADC_AWD1_2_SELECTION_CHANNEL_x, ADC_AWD1_2_SELECTION_CHANNEL_ALL: ADC channel analog watchdog 1/2 selection + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void adc_watchdog2_channel_config(uint32_t adc_periph, uint32_t adc_channel, ControlStatus newvalue) +{ + if(ENABLE == newvalue){ + ADC_WD2SR(adc_periph) |= (uint32_t)adc_channel; + }else{ + ADC_WD2SR(adc_periph) &= ~((uint32_t)adc_channel); + } +} + +/*! + \brief disable ADC analog watchdog 1 + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_watchdog1_disable(uint32_t adc_periph) +{ + ADC_WD1SR(adc_periph) &= (uint32_t)~(ADC_WD1SR_AWD1CS); +} + +/*! + \brief disable ADC analog watchdog 2 + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_watchdog2_disable(uint32_t adc_periph) +{ + ADC_WD2SR(adc_periph) &= (uint32_t)~(ADC_WD2SR_AWD2CS); +} + +/*! + \brief configure ADC analog watchdog 0 threshold + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] low_threshold: analog watchdog 0 low threshold, 0..4095 + \param[in] high_threshold: analog watchdog 0 high threshold, 0..4095 + \param[out] none + \retval none +*/ +void adc_watchdog0_threshold_config(uint32_t adc_periph, uint16_t low_threshold, uint16_t high_threshold) +{ + ADC_WDLT0(adc_periph) = (uint32_t)WDLT0_WDLT0(low_threshold); + ADC_WDHT0(adc_periph) = (uint32_t)WDHT0_WDHT0(high_threshold); +} + +/*! + \brief configure ADC analog watchdog 1 threshold + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] low_threshold: analog watchdog 1 low threshold, 0..255 + \param[in] high_threshold: analog watchdog 1 high threshold, 0..255 + \param[out] none + \retval none +*/ +void adc_watchdog1_threshold_config(uint32_t adc_periph, uint8_t low_threshold, uint8_t high_threshold) +{ + ADC_WDT1(adc_periph) &= ~((uint32_t)(ADC_WDT1_WDLT1 | ADC_WDT1_WDHT1)); + /* configure ADC analog watchdog 1 threshold */ + ADC_WDT1(adc_periph) |= (uint32_t)WDT1_WDLT1(low_threshold); + ADC_WDT1(adc_periph) |= (uint32_t)WDT1_WDHT1(high_threshold); +} + +/*! + \brief configure ADC analog watchdog 2 threshold + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] low_threshold: analog watchdog 2 low threshold, 0..255 + \param[in] high_threshold: analog watchdog 2 high threshold, 0..255 + \param[out] none + \retval none +*/ +void adc_watchdog2_threshold_config(uint32_t adc_periph, uint8_t low_threshold, uint8_t high_threshold) +{ + ADC_WDT2(adc_periph) &= ~((uint32_t)(ADC_WDT2_WDLT2 | ADC_WDT2_WDHT2)); + /* configure ADC analog watchdog 2 threshold */ + ADC_WDT2(adc_periph) |= (uint32_t)WDT2_WDLT2(low_threshold); + ADC_WDT2(adc_periph) |= (uint32_t)WDT2_WDHT2(high_threshold); +} + +/*! + \brief configure ADC resolution + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] resolution: ADC resolution + only one parameter can be selected which is shown as below: + \arg ADC_RESOLUTION_12B: 12-bit ADC resolution + \arg ADC_RESOLUTION_10B: 10-bit ADC resolution + \arg ADC_RESOLUTION_8B: 8-bit ADC resolution + \arg ADC_RESOLUTION_6B: 6-bit ADC resolution + \param[out] none + \retval none +*/ +void adc_resolution_config(uint32_t adc_periph, uint32_t resolution) +{ + ADC_OVSAMPCTL(adc_periph) &= ~((uint32_t)ADC_OVSAMPCTL_DRES); + ADC_OVSAMPCTL(adc_periph) |= (uint32_t)resolution; +} + +/*! + \brief configure ADC oversample mode + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] mode: ADC oversampling mode + only one parameter can be selected which is shown as below: + \arg ADC_OVERSAMPLING_ALL_CONVERT: all oversampled conversions for a channel are done consecutively after a trigger + \arg ADC_OVERSAMPLING_ONE_CONVERT: each oversampled conversion for a channel needs a trigger + \param[in] shift: ADC oversampling shift + only one parameter can be selected which is shown as below: + \arg ADC_OVERSAMPLING_SHIFT_NONE: no oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_1B: 1-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_2B: 2-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_3B: 3-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_4B: 3-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_5B: 5-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_6B: 6-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_7B: 7-bit oversampling shift + \arg ADC_OVERSAMPLING_SHIFT_8B: 8-bit oversampling shift + \param[in] ratio: ADC oversampling ratio + only one parameter can be selected which is shown as below: + \arg ADC_OVERSAMPLING_RATIO_MUL2: oversampling ratio multiple 2 + \arg ADC_OVERSAMPLING_RATIO_MUL4: oversampling ratio multiple 4 + \arg ADC_OVERSAMPLING_RATIO_MUL8: oversampling ratio multiple 8 + \arg ADC_OVERSAMPLING_RATIO_MUL16: oversampling ratio multiple 16 + \arg ADC_OVERSAMPLING_RATIO_MUL32: oversampling ratio multiple 32 + \arg ADC_OVERSAMPLING_RATIO_MUL64: oversampling ratio multiple 64 + \arg ADC_OVERSAMPLING_RATIO_MUL128: oversampling ratio multiple 128 + \arg ADC_OVERSAMPLING_RATIO_MUL256: oversampling ratio multiple 256 + \param[out] none + \retval none +*/ +void adc_oversample_mode_config(uint32_t adc_periph, uint32_t mode, uint16_t shift, uint8_t ratio) +{ + /* configure ADC oversampling mode */ + if(ADC_OVERSAMPLING_ONE_CONVERT == mode){ + ADC_OVSAMPCTL(adc_periph) |= (uint32_t)ADC_OVSAMPCTL_TOVS; + }else{ + ADC_OVSAMPCTL(adc_periph) &= ~((uint32_t)ADC_OVSAMPCTL_TOVS); + } + /* configure the shift and ratio */ + ADC_OVSAMPCTL(adc_periph) &= ~((uint32_t)(ADC_OVSAMPCTL_OVSR | ADC_OVSAMPCTL_OVSS)); + ADC_OVSAMPCTL(adc_periph) |= ((uint32_t)shift | (uint32_t)ratio); +} + +/*! + \brief enable ADC oversample mode + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_oversample_mode_enable(uint32_t adc_periph) +{ + ADC_OVSAMPCTL(adc_periph) |= (uint32_t)ADC_OVSAMPCTL_OVSEN; +} + +/*! + \brief disable ADC oversample mode + \param[in] adc_periph: ADCx, x=0,1,2 + \param[out] none + \retval none +*/ +void adc_oversample_mode_disable(uint32_t adc_periph) +{ + ADC_OVSAMPCTL(adc_periph) &= ~((uint32_t)ADC_OVSAMPCTL_OVSEN); +} + +/*! + \brief get the ADC flag + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] flag: the ADC flag bits + only one parameter can be selected which is shown as below: + \arg ADC_FLAG_WDE0: analog watchdog 0 event flag + \arg ADC_FLAG_EOC: end of group conversion flag + \arg ADC_FLAG_EOIC: end of inserted group conversion flag + \arg ADC_FLAG_STIC: start flag of inserted channel group + \arg ADC_FLAG_STRC: start flag of regular channel group + \arg ADC_FLAG_WDE1: analog watchdog 1 event flag + \arg ADC_FLAG_WDE2: analog watchdog 2 event flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus adc_flag_get(uint32_t adc_periph, uint32_t flag) +{ + FlagStatus reval = RESET; + if(ADC_STAT(adc_periph) & flag){ + reval = SET; + } + return reval; +} + +/*! + \brief clear the ADC flag + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] flag: the ADC flag + one or more parameters can be selected which is shown as below: + \arg ADC_FLAG_WDE0: analog watchdog 0 event flag + \arg ADC_FLAG_EOC: end of group conversion flag + \arg ADC_FLAG_EOIC: end of inserted group conversion flag + \arg ADC_FLAG_STIC: start flag of inserted channel group + \arg ADC_FLAG_STRC: start flag of regular channel group + \arg ADC_FLAG_WDE1: analog watchdog 1 event flag + \arg ADC_FLAG_WDE2: analog watchdog 2 event flag + \param[out] none + \retval none +*/ +void adc_flag_clear(uint32_t adc_periph , uint32_t flag) +{ + ADC_STAT(adc_periph) &= ~((uint32_t)flag); +} + +/*! + \brief enable ADC interrupt + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] interrupt: the ADC interrupt + one or more parameters can be selected which is shown as below: + \arg ADC_INT_WDE0: analog watchdog 0 interrupt flag + \arg ADC_INT_EOC: end of group conversion interrupt flag + \arg ADC_INT_EOIC: end of inserted group conversion interrupt flag + \arg ADC_INT_WDE1: analog watchdog 1 interrupt flag + \arg ADC_INT_WDE2: analog watchdog 2 interrupt flag + \param[out] none + \retval none +*/ +void adc_interrupt_enable(uint32_t adc_periph , uint32_t interrupt) +{ + switch(interrupt){ + /* enable analog watchdog 0 interrupt */ + case ADC_INT_WDE0: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_WDE0IE; + break; + /* enable end of group conversion interrupt */ + case ADC_INT_EOC: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_EOCIE; + break; + /* enable end of inserted group conversion interrupt */ + case ADC_INT_EOIC: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_EOICIE; + break; + /* enable analog watchdog 1 interrupt */ + case ADC_INT_WDE1: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_WDE1IE; + break; + /* enable analog watchdog 2 interrupt */ + case ADC_INT_WDE2: + ADC_CTL0(adc_periph) |= (uint32_t)ADC_CTL0_WDE2IE; + break; + default: + break; + } +} + +/*! + \brief disable ADC interrupt + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] interrupt: the ADC interrupt flag + one or more parameters can be selected which is shown as below: + \arg ADC_INT_WDE0: analog watchdog 0 interrupt flag + \arg ADC_INT_EOC: end of group conversion interrupt flag + \arg ADC_INT_EOIC: end of inserted group conversion interrupt flag + \arg ADC_INT_WDE1: analog watchdog 1 interrupt flag + \arg ADC_INT_WDE2: analog watchdog 2 interrupt flag + \param[out] none + \retval none +*/ +void adc_interrupt_disable(uint32_t adc_periph, uint32_t interrupt) +{ + switch(interrupt){ + /* disable analog watchdog 0 interrupt */ + case ADC_INT_WDE0: + ADC_CTL0(adc_periph) &= ~(uint32_t)ADC_CTL0_WDE0IE; + break; + /* disable end of group conversion interrupt */ + case ADC_INT_EOC: + ADC_CTL0(adc_periph) &= ~(uint32_t)ADC_CTL0_EOCIE; + break; + /* disable end of inserted group conversion interrupt */ + case ADC_INT_EOIC: + ADC_CTL0(adc_periph) &= ~(uint32_t)ADC_CTL0_EOICIE; + break; + /* disable analog watchdog 1 interrupt */ + case ADC_INT_WDE1: + ADC_CTL0(adc_periph) &= ~(uint32_t)ADC_CTL0_WDE1IE; + break; + /* disable analog watchdog 2 interrupt */ + case ADC_INT_WDE2: + ADC_CTL0(adc_periph) &= ~(uint32_t)ADC_CTL0_WDE1IE; + break; + default: + break; + } +} + +/*! + \brief get ADC interrupt flag + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] int_flag: the ADC interrupt + only one parameter can be selected which is shown as below: + \arg ADC_INT_FLAG_WDE0: analog watchdog 0 interrupt flag + \arg ADC_INT_FLAG_EOC: end of group conversion interrupt flag + \arg ADC_INT_FLAG_EOIC: end of inserted group conversion interrupt + \arg ADC_INT_FLAG_WDE1: analog watchdog 1 interrupt flag + \arg ADC_INT_FLAG_WDE2: analog watchdog 2 interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus adc_interrupt_flag_get(uint32_t adc_periph , uint32_t int_flag) +{ + FlagStatus interrupt_flag = RESET; + uint32_t state; + /* check the interrupt bits */ + switch(int_flag){ + case ADC_INT_FLAG_WDE0: + state = ADC_STAT(adc_periph) & ADC_STAT_WDE0; + if((ADC_CTL0(adc_periph) & ADC_CTL0_WDE0IE) && state){ + interrupt_flag = SET; + } + break; + case ADC_INT_FLAG_EOC: + state = ADC_STAT(adc_periph) & ADC_STAT_EOC; + if((ADC_CTL0(adc_periph) & ADC_CTL0_EOCIE) && state){ + interrupt_flag = SET; + } + break; + case ADC_INT_FLAG_EOIC: + state = ADC_STAT(adc_periph) & ADC_STAT_EOIC; + if((ADC_CTL0(adc_periph) & ADC_CTL0_EOICIE) && state){ + interrupt_flag = SET; + } + break; + case ADC_INT_FLAG_WDE1: + state = ADC_STAT(adc_periph) & ADC_STAT_WDE1; + if((ADC_CTL0(adc_periph) & ADC_CTL0_WDE1IE) && state){ + interrupt_flag = SET; + } + break; + case ADC_INT_FLAG_WDE2: + state = ADC_STAT(adc_periph) & ADC_STAT_WDE2; + if((ADC_CTL0(adc_periph) & ADC_CTL0_WDE2IE) && state){ + interrupt_flag = SET; + } + break; + default: + break; + } + return interrupt_flag; +} + +/*! + \brief clear ADC interrupt flag + \param[in] adc_periph: ADCx, x=0,1,2 + \param[in] int_flag: the ADC interrupt flag + only one parameter can be selected which is shown as below: + \arg ADC_INT_FLAG_WDE0: analog watchdog 0 interrupt flag + \arg ADC_INT_FLAG_EOC: end of group conversion interrupt flag + \arg ADC_INT_FLAG_EOIC: end of inserted group conversion interrupt flag + \arg ADC_INT_FLAG_WDE1: analog watchdog 1 interrupt flag + \arg ADC_INT_FLAG_WDE2: analog watchdog 2 interrupt flag + \param[out] none + \retval none +*/ +void adc_interrupt_flag_clear(uint32_t adc_periph, uint32_t int_flag) +{ + ADC_STAT(adc_periph) &= ~((uint32_t)int_flag); +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_can.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_can.c new file mode 100644 index 0000000..ba6eb61 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_can.c @@ -0,0 +1,1615 @@ +/*! + \file gd32e50x_can.c + \brief CAN driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ +#ifndef GD32EPRT +#include "gd32e50x_can.h" +#include + +#define CAN_ERROR_HANDLE(s) do{}while(1) + +/* This table can be used to calculate data length in FD mode */ +const uint8_t g_can_fdlength_table[] = {12, 16, 20, 24, 32, 48, 64}; + +/*! + \brief deinitialize CAN + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_deinit(uint32_t can_periph) +{ +#if defined(GD32E50X_CL) || defined(GD32E508) + switch(can_periph) { + case CAN0: + rcu_periph_reset_enable(RCU_CAN0RST); + rcu_periph_reset_disable(RCU_CAN0RST); + break; + case CAN1: + rcu_periph_reset_enable(RCU_CAN1RST); + rcu_periph_reset_disable(RCU_CAN1RST); + break; + case CAN2: + rcu_periph_reset_enable(RCU_CAN2RST); + rcu_periph_reset_disable(RCU_CAN2RST); + break; + default: + break; + } +#else + switch(can_periph) { + case CAN0: + rcu_periph_reset_enable(RCU_CAN0RST); + rcu_periph_reset_disable(RCU_CAN0RST); + break; + case CAN1: + rcu_periph_reset_enable(RCU_CAN1RST); + rcu_periph_reset_disable(RCU_CAN1RST); + break; + default: + break; + } +#endif +} + +/*! + \brief initialize CAN parameter struct + \param[in] type: the type of CAN parameter struct + only one parameter can be selected which is shown as below: + \arg CAN_INIT_STRUCT: the CAN initial struct + \arg CAN_FILTER_STRUCT: the CAN filter struct + \arg CAN_FD_FRAME_STRUCT: the CAN FD initial struct + \arg CAN_TX_MESSAGE_STRUCT: the CAN TX message struct + \arg CAN_RX_MESSAGE_STRUCT: the CAN RX message struct + \param[in] p_struct: the pointer of the specific struct + \param[out] none + \retval none +*/ +void can_struct_para_init(can_struct_type_enum type, void *p_struct) +{ + uint8_t i; + + if(NULL == p_struct) { + CAN_ERROR_HANDLE("struct parameter can not be NULL \r\n"); + } + /* get type of the struct */ + switch(type) { + /* used for can_init() */ + case CAN_INIT_STRUCT: + ((can_parameter_struct *)p_struct)->auto_bus_off_recovery = DISABLE; + ((can_parameter_struct *)p_struct)->auto_retrans = DISABLE; + ((can_parameter_struct *)p_struct)->auto_wake_up = DISABLE; + ((can_parameter_struct *)p_struct)->prescaler = 0x0400U; + ((can_parameter_struct *)p_struct)->rec_fifo_overwrite = DISABLE; + ((can_parameter_struct *)p_struct)->resync_jump_width = CAN_BT_SJW_1TQ; + ((can_parameter_struct *)p_struct)->time_segment_1 = CAN_BT_BS1_3TQ; + ((can_parameter_struct *)p_struct)->time_segment_2 = CAN_BT_BS2_1TQ; + ((can_parameter_struct *)p_struct)->time_triggered = DISABLE; + ((can_parameter_struct *)p_struct)->trans_fifo_order = DISABLE; + ((can_parameter_struct *)p_struct)->working_mode = CAN_NORMAL_MODE; + break; + /* used for can_filter_init() */ + case CAN_FILTER_STRUCT: + ((can_filter_parameter_struct *)p_struct)->filter_bits = CAN_FILTERBITS_32BIT; + ((can_filter_parameter_struct *)p_struct)->filter_enable = DISABLE; + ((can_filter_parameter_struct *)p_struct)->filter_fifo_number = CAN_FIFO0; + ((can_filter_parameter_struct *)p_struct)->filter_list_high = 0x0000U; + ((can_filter_parameter_struct *)p_struct)->filter_list_low = 0x0000U; + ((can_filter_parameter_struct *)p_struct)->filter_mask_high = 0x0000U; + ((can_filter_parameter_struct *)p_struct)->filter_mask_low = 0x0000U; + ((can_filter_parameter_struct *)p_struct)->filter_mode = CAN_FILTERMODE_MASK; + ((can_filter_parameter_struct *)p_struct)->filter_number = 0U; + break; +#ifdef GD32E508 + /* used for can_fd_init() */ + case CAN_FD_FRAME_STRUCT: + ((can_fdframe_struct *)p_struct)->data_prescaler = 0x0400U; + ((can_fdframe_struct *)p_struct)->data_resync_jump_width = 1U - 1U; + ((can_fdframe_struct *)p_struct)->data_time_segment_1 = 3U - 1U; + ((can_fdframe_struct *)p_struct)->data_time_segment_2 = 2U - 1U; + ((can_fdframe_struct *)p_struct)->delay_compensation = DISABLE; + ((can_fdframe_struct *)p_struct)->esi_mode = CAN_ESIMOD_HARDWARE; + ((can_fdframe_struct *)p_struct)->excp_event_detect = ENABLE; + ((can_fdframe_struct *)p_struct)->fd_frame = DISABLE; + ((can_fdframe_struct *)p_struct)->iso_bosch = CAN_FDMOD_ISO; + ((can_fdframe_struct *)p_struct)->p_delay_compensation = 0U; + break; + /* used for can_message_transmit() */ + case CAN_TX_MESSAGE_STRUCT: + ((can_trasnmit_message_struct *)p_struct)->fd_brs = CAN_BRS_DISABLE; + ((can_trasnmit_message_struct *)p_struct)->fd_esi = CAN_ESI_DOMINANT; + ((can_trasnmit_message_struct *)p_struct)->fd_flag = CAN_FDF_CLASSIC; + + for(i = 0U; i < 64U; i++) { + ((can_trasnmit_message_struct *)p_struct)->tx_data[i] = 0U; + } + + ((can_trasnmit_message_struct *)p_struct)->tx_dlen = 0u; + ((can_trasnmit_message_struct *)p_struct)->tx_efid = 0U; + ((can_trasnmit_message_struct *)p_struct)->tx_ff = (uint8_t)CAN_FF_STANDARD; + ((can_trasnmit_message_struct *)p_struct)->tx_ft = (uint8_t)CAN_FT_DATA; + ((can_trasnmit_message_struct *)p_struct)->tx_sfid = 0U; + break; + /* used for can_message_receive() */ + case CAN_RX_MESSAGE_STRUCT: + ((can_receive_message_struct *)p_struct)->fd_brs = CAN_BRS_DISABLE; + ((can_receive_message_struct *)p_struct)->fd_esi = CAN_ESI_DOMINANT; + ((can_receive_message_struct *)p_struct)->fd_flag = CAN_FDF_CLASSIC; + + for(i = 0U; i < 64U; i++) { + ((can_receive_message_struct *)p_struct)->rx_data[i] = 0U; + } + + ((can_receive_message_struct *)p_struct)->rx_dlen = 0U; + ((can_receive_message_struct *)p_struct)->rx_efid = 0U; + ((can_receive_message_struct *)p_struct)->rx_ff = (uint8_t)CAN_FF_STANDARD; + ((can_receive_message_struct *)p_struct)->rx_fi = 0U; + ((can_receive_message_struct *)p_struct)->rx_ft = (uint8_t)CAN_FT_DATA; + ((can_receive_message_struct *)p_struct)->rx_sfid = 0U; + break; +#else + /* used for can_message_transmit() */ + case CAN_TX_MESSAGE_STRUCT: + for(i = 0U; i < 8U; i++) { + ((can_trasnmit_message_struct *)p_struct)->tx_data[i] = 0U; + } + + ((can_trasnmit_message_struct *)p_struct)->tx_dlen = 0u; + ((can_trasnmit_message_struct *)p_struct)->tx_efid = 0U; + ((can_trasnmit_message_struct *)p_struct)->tx_ff = (uint8_t)CAN_FF_STANDARD; + ((can_trasnmit_message_struct *)p_struct)->tx_ft = (uint8_t)CAN_FT_DATA; + ((can_trasnmit_message_struct *)p_struct)->tx_sfid = 0U; + break; + /* used for can_message_receive() */ + case CAN_RX_MESSAGE_STRUCT: + for(i = 0U; i < 8U; i++) { + ((can_receive_message_struct *)p_struct)->rx_data[i] = 0U; + } + + ((can_receive_message_struct *)p_struct)->rx_dlen = 0U; + ((can_receive_message_struct *)p_struct)->rx_efid = 0U; + ((can_receive_message_struct *)p_struct)->rx_ff = (uint8_t)CAN_FF_STANDARD; + ((can_receive_message_struct *)p_struct)->rx_fi = 0U; + ((can_receive_message_struct *)p_struct)->rx_ft = (uint8_t)CAN_FT_DATA; + ((can_receive_message_struct *)p_struct)->rx_sfid = 0U; + break; + +#endif /* GD32E508 */ + default: + CAN_ERROR_HANDLE("parameter is invalid \r\n"); + break; + } +} + +/*! + \brief initialize CAN + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] can_parameter_init: parameters for CAN initializtion + \arg working_mode: CAN_NORMAL_MODE, CAN_LOOPBACK_MODE, CAN_SILENT_MODE, CAN_SILENT_LOOPBACK_MODE + \arg resync_jump_width: 0x00 - 0x07 + \arg time_segment_1: 0x00 - 0x7F + \arg time_segment_2: 0x00 - 0x1F + \arg time_triggered: ENABLE or DISABLE + \arg auto_bus_off_recovery: ENABLE or DISABLE + \arg auto_wake_up: ENABLE or DISABLE + \arg auto_retrans: ENABLE or DISABLE + \arg rec_fifo_overwrite: ENABLE or DISABLE + \arg trans_fifo_order: ENABLE or DISABLE + \arg prescaler: 0x0001 - 0x0400 + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus can_init(uint32_t can_periph, can_parameter_struct *can_parameter_init) +{ + uint32_t timeout = CAN_TIMEOUT; + ErrStatus flag = ERROR; +#ifdef GD32E508 + uint32_t fdctl_status; +#endif /* GD32E508 */ + + /* disable sleep mode */ + CAN_CTL(can_periph) &= ~CAN_CTL_SLPWMOD; + /* enable initialize mode */ + CAN_CTL(can_periph) |= CAN_CTL_IWMOD; + /* wait ACK */ + while((CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + /* check initialize working success */ + if(CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) { + flag = ERROR; + } else { +#ifdef GD32E508 + /* set the bit timing register */ + fdctl_status = CAN_FDCTL(can_periph); + if(CAN_FDCTL_FDEN != (fdctl_status & CAN_FDCTL_FDEN)) { + /* CAN FD disable, should frist enable, then write */ + fdctl_status = fdctl_status | CAN_FDCTL_FDEN; + CAN_FDCTL(can_periph) = fdctl_status; + CAN_BT(can_periph) = (BT_MODE((uint32_t)can_parameter_init->working_mode) | \ + BT_SJW((uint32_t)can_parameter_init->resync_jump_width) | \ + BT_BS1((uint32_t)can_parameter_init->time_segment_1) | \ + BT_BS2((uint32_t)can_parameter_init->time_segment_2) | \ + BT_BAUDPSC(((uint32_t)(can_parameter_init->prescaler) - 1U))); + fdctl_status = fdctl_status & (~CAN_FDCTL_FDEN); + CAN_FDCTL(can_periph) = fdctl_status; + } else { + /* CAN FD enable */ + CAN_BT(can_periph) = (BT_MODE((uint32_t)can_parameter_init->working_mode) | \ + BT_SJW((uint32_t)can_parameter_init->resync_jump_width) | \ + BT_BS1((uint32_t)can_parameter_init->time_segment_1) | \ + BT_BS2((uint32_t)can_parameter_init->time_segment_2) | \ + BT_BAUDPSC(((uint32_t)(can_parameter_init->prescaler) - 1U))); + } +#else + /* set the bit timing register */ + CAN_BT(can_periph) = (BT_MODE((uint32_t)can_parameter_init->working_mode) | \ + BT_SJW((uint32_t)can_parameter_init->resync_jump_width) | \ + BT_BS1((uint32_t)can_parameter_init->time_segment_1) | \ + BT_BS2((uint32_t)can_parameter_init->time_segment_2) | \ + BT_BAUDPSC(((uint32_t)(can_parameter_init->prescaler) - 1U))); + +#endif /* GD32E508 */ + /* time trigger communication mode */ + if(ENABLE == can_parameter_init->time_triggered) { + CAN_CTL(can_periph) |= CAN_CTL_TTC; + } else { + CAN_CTL(can_periph) &= ~CAN_CTL_TTC; + } + /* automatic bus-off managment */ + if(ENABLE == can_parameter_init->auto_bus_off_recovery) { + CAN_CTL(can_periph) |= CAN_CTL_ABOR; + } else { + CAN_CTL(can_periph) &= ~CAN_CTL_ABOR; + } + /* automatic wakeup mode */ + if(ENABLE == can_parameter_init->auto_wake_up) { + CAN_CTL(can_periph) |= CAN_CTL_AWU; + } else { + CAN_CTL(can_periph) &= ~CAN_CTL_AWU; + } + /* automatic retransmission mode */ + if(ENABLE == can_parameter_init->auto_retrans) { + CAN_CTL(can_periph) &= ~CAN_CTL_ARD; + } else { + CAN_CTL(can_periph) |= CAN_CTL_ARD; + } + /* receive fifo overwrite mode */ + if(ENABLE == can_parameter_init->rec_fifo_overwrite) { + CAN_CTL(can_periph) &= ~CAN_CTL_RFOD; + } else { + CAN_CTL(can_periph) |= CAN_CTL_RFOD; + } + /* transmit fifo order */ + if(ENABLE == can_parameter_init->trans_fifo_order) { + CAN_CTL(can_periph) |= CAN_CTL_TFO; + } else { + CAN_CTL(can_periph) &= ~CAN_CTL_TFO; + } + /* disable initialize mode */ + CAN_CTL(can_periph) &= ~CAN_CTL_IWMOD; + timeout = CAN_TIMEOUT; + /* wait the ACK */ + while((CAN_STAT_IWS == (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + /* check exit initialize mode */ + if(0U != timeout) { + flag = SUCCESS; + } + } + return flag; +} + +/*! + \brief initialize CAN filter + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] can_filter_parameter_init: struct for CAN filter initialization + \arg filter_list_high: 0x0000 - 0xFFFF + \arg filter_list_low: 0x0000 - 0xFFFF + \arg filter_mask_high: 0x0000 - 0xFFFF + \arg filter_mask_low: 0x0000 - 0xFFFF + \arg filter_fifo_number: CAN_FIFO0, CAN_FIFO1 + \arg filter_number: In CAN0(0 - 27), In CAN2(0 - 14) + \arg filter_mode: CAN_FILTERMODE_MASK, CAN_FILTERMODE_LIST + \arg filter_bits: CAN_FILTERBITS_32BIT, CAN_FILTERBITS_16BIT + \arg filter_enable: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void can_filter_init(uint32_t can_periph, can_filter_parameter_struct *can_filter_parameter_init) +{ + uint32_t val = 0U; + + val = ((uint32_t)1) << (can_filter_parameter_init->filter_number); + if(CAN1 == can_periph) { + can_periph = CAN0; + } + /* filter lock disable */ + CAN_FCTL(can_periph) |= CAN_FCTL_FLD; + /* disable filter */ + CAN_FW(can_periph) &= ~(uint32_t)val; + + /* filter 16 bits */ + if(CAN_FILTERBITS_16BIT == can_filter_parameter_init->filter_bits) { + /* set filter 16 bits */ + CAN_FSCFG(can_periph) &= ~(uint32_t)val; + /* first 16 bits list and first 16 bits mask or first 16 bits list and second 16 bits list */ + CAN_FDATA0(can_periph, can_filter_parameter_init->filter_number) = \ + FDATA_MASK_HIGH((can_filter_parameter_init->filter_mask_low) & CAN_FILTER_MASK_16BITS) | \ + FDATA_MASK_LOW((can_filter_parameter_init->filter_list_low) & CAN_FILTER_MASK_16BITS); + /* second 16 bits list and second 16 bits mask or third 16 bits list and fourth 16 bits list */ + CAN_FDATA1(can_periph, can_filter_parameter_init->filter_number) = \ + FDATA_MASK_HIGH((can_filter_parameter_init->filter_mask_high) & CAN_FILTER_MASK_16BITS) | \ + FDATA_MASK_LOW((can_filter_parameter_init->filter_list_high) & CAN_FILTER_MASK_16BITS); + } + /* filter 32 bits */ + if(CAN_FILTERBITS_32BIT == can_filter_parameter_init->filter_bits) { + /* set filter 32 bits */ + CAN_FSCFG(can_periph) |= (uint32_t)val; + /* 32 bits list or first 32 bits list */ + CAN_FDATA0(can_periph, can_filter_parameter_init->filter_number) = \ + FDATA_MASK_HIGH((can_filter_parameter_init->filter_list_high) & CAN_FILTER_MASK_16BITS) | + FDATA_MASK_LOW((can_filter_parameter_init->filter_list_low) & CAN_FILTER_MASK_16BITS); + /* 32 bits mask or second 32 bits list */ + CAN_FDATA1(can_periph, can_filter_parameter_init->filter_number) = \ + FDATA_MASK_HIGH((can_filter_parameter_init->filter_mask_high) & CAN_FILTER_MASK_16BITS) | + FDATA_MASK_LOW((can_filter_parameter_init->filter_mask_low) & CAN_FILTER_MASK_16BITS); + } + + /* filter mode */ + if(CAN_FILTERMODE_MASK == can_filter_parameter_init->filter_mode) { + /* mask mode */ + CAN_FMCFG(can_periph) &= ~(uint32_t)val; + } else { + /* list mode */ + CAN_FMCFG(can_periph) |= (uint32_t)val; + } + + /* filter FIFO */ + if(CAN_FIFO0 == (can_filter_parameter_init->filter_fifo_number)) { + /* FIFO0 */ + CAN_FAFIFO(can_periph) &= ~(uint32_t)val; + } else { + /* FIFO1 */ + CAN_FAFIFO(can_periph) |= (uint32_t)val; + } + + /* filter working */ + if(ENABLE == can_filter_parameter_init->filter_enable) { + + CAN_FW(can_periph) |= (uint32_t)val; + } + + /* filter lock enable */ + CAN_FCTL(can_periph) &= ~CAN_FCTL_FLD; +} + +/*! + \brief CAN filter mask mode initialization + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] id: extended(11-bits) or standard(29-bits) identifier + \arg 0x00000000 - 0x1FFFFFFF + \param[in] mask: extended(11-bits) or standard(29-bits) identifier mask + \arg 0x00000000 - 0x1FFFFFFF + \param[in] format_fifo: format and fifo states + only one parameter can be selected which is shown as below: + \arg CAN_STANDARD_FIFO0 + \arg CAN_STANDARD_FIFO1 + \arg CAN_EXTENDED_FIFO0 + \arg CAN_EXTENDED_FIFO1 + \param[in] filter_number: filter sequence number + \arg In CAN0:0 - 27 ,In CAN2:0 - 14 + \param[out] none + \retval none +*/ +void can_filter_mask_mode_init(uint32_t can_periph, uint32_t id, uint32_t mask, can_format_fifo_enum format_fifo, uint16_t filter_number) +{ + can_filter_parameter_struct can_filter; + + /* Initialize the filter structure */ + can_struct_para_init(CAN_FILTER_STRUCT, &can_filter); + if(CAN1 == can_periph) { + can_periph = CAN0; + } + /* filter config */ + can_filter.filter_number = filter_number; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_enable = ENABLE; + + switch(format_fifo) { + /* standard FIFO 0 */ + case CAN_STANDARD_FIFO0: + can_filter.filter_fifo_number = CAN_FIFO0; + /* configure SFID[10:0] */ + can_filter.filter_list_high = (uint16_t)id << 5; + can_filter.filter_list_low = 0x0000U; + /* configure SFID[10:0] mask */ + can_filter.filter_mask_high = (uint16_t)mask << 5; + /* both data and remote frames can be received */ + can_filter.filter_mask_low = (uint16_t)(1U << 2U); + + break; + /* standard FIFO 1 */ + case CAN_STANDARD_FIFO1: + can_filter.filter_fifo_number = CAN_FIFO1; + /* configure SFID[10:0] */ + can_filter.filter_list_high = (uint16_t)id << 5; + can_filter.filter_list_low = 0x0000U; + /* configure SFID[10:0] mask */ + can_filter.filter_mask_high = (uint16_t)mask << 5; + /* both data and remote frames can be received */ + can_filter.filter_mask_low = (uint16_t)(1U << 2U); + + break; + /* extended FIFO 0 */ + case CAN_EXTENDED_FIFO0: + can_filter.filter_fifo_number = CAN_FIFO0; + /* configure EFID[28:13] */ + can_filter.filter_list_high = (uint16_t)(id >> 13); + /* configure EFID[12:0] and frame format bit set */ + can_filter.filter_list_low = ((uint16_t)(id << 3)) | (1U << 2); + /* configure EFID[28:13] mask */ + can_filter.filter_mask_high = (uint16_t)(mask >> 13); + /* configure EFID[12:0] and frame format bit mask */ + /* both data and remote frames can be received */ + can_filter.filter_mask_low = ((uint16_t)(mask << 3)) | (1U << 2); + + break; + /* extended FIFO 1 */ + case CAN_EXTENDED_FIFO1: + can_filter.filter_fifo_number = CAN_FIFO1; + /* configure EFID[28:13] */ + can_filter.filter_list_high = (uint16_t)(id >> 13); + /* configure EFID[12:0] and frame format bit set */ + can_filter.filter_list_low = ((uint16_t)(id << 3)) | (1U << 2); + /* configure EFID[28:13] mask */ + can_filter.filter_mask_high = (uint16_t)(mask >> 13); + /* configure EFID[12:0] and frame format bit mask */ + /* both data and remote frames can be received */ + can_filter.filter_mask_low = ((uint16_t)(mask << 3)) | (1U << 2); + + break; + default: + CAN_ERROR_HANDLE("parameter is invalid \r\n"); + } + + can_filter_init(can_periph, &can_filter); +} + +/*! + \brief CAN communication mode configure + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] mode: communication mode + only one parameter can be selected which is shown as below: + \arg CAN_NORMAL_MODE + \arg CAN_LOOPBACK_MODE + \arg CAN_SILENT_MODE + \arg CAN_SILENT_LOOPBACK_MODE + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus can_monitor_mode_set(uint32_t can_periph, uint8_t mode) +{ + ErrStatus reval = SUCCESS; + uint32_t timeout = CAN_TIMEOUT; + + if(mode == (mode & CAN_SILENT_LOOPBACK_MODE)) { + /* disable sleep mode */ + CAN_CTL(can_periph) &= (~(uint32_t)CAN_CTL_SLPWMOD); + /* set initialize mode */ + CAN_CTL(can_periph) |= (uint8_t)CAN_CTL_IWMOD; + /* wait the acknowledge */ + timeout = CAN_TIMEOUT; + while((CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + + if(CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) { + reval = ERROR; + } else { + CAN_BT(can_periph) &= ~BT_MODE(3); + CAN_BT(can_periph) |= BT_MODE(mode); + + timeout = CAN_TIMEOUT; + /* enter normal mode */ + CAN_CTL(can_periph) &= ~(uint32_t)(CAN_CTL_SLPWMOD | CAN_CTL_IWMOD); + /* wait the acknowledge */ + while((0U != (CAN_STAT(can_periph) & (CAN_STAT_IWS | CAN_STAT_SLPWS))) && (0U != timeout)) { + timeout--; + } + if(0U != (CAN_STAT(can_periph) & (CAN_STAT_IWS | CAN_STAT_SLPWS))) { + reval = ERROR; + } + } + } else { + reval = ERROR; + } + + return reval; +} + +#ifdef GD32E508 +/*! + \brief initialize CAN FD function + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] can_fdframe_init: parameters for CAN FD initializtion + \arg fd_frame: ENABLE or DISABLE + \arg excp_event_detect: ENABLE or DISABLE + \arg delay_compensation: ENABLE or DISABLE + \arg p_delay_compensation: the pointer of tdc struct + can_fd_tdc_struct: + tdc_mode: CAN_TDCMOD_CALC_AND_OFFSET or CAN_TDCMOD_OFFSET + tdc_filter: 0x00 - 0x07 + tdc_offset: 0x00 - 0x07 + \arg iso_bosch: CAN_FDMOD_ISO or CAN_FDMOD_BOSCH + \arg esi_mode: CAN_ESIMOD_HARDWARE or CAN_ESIMOD_SOFTWARE + \arg data_resync_jump_width: 0x00 - 0x07 + \arg data_time_segment_1: 0x00 - 0x0F + \arg data_time_segment_2: 0x00 - 0x07 + \arg data_prescaler: 0x0001 - 0x0400 + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus can_fd_init(uint32_t can_periph, can_fdframe_struct *can_fdframe_init) +{ + uint32_t timeout = CAN_TIMEOUT; + uint32_t tempreg = 0U; + + /* check null pointer */ + if(0 == can_fdframe_init) { + return ERROR; + } + /* disable sleep mode */ + CAN_CTL(can_periph) &= ~CAN_CTL_SLPWMOD; + /* enable initialize mode */ + CAN_CTL(can_periph) |= CAN_CTL_IWMOD; + /* wait ACK */ + while((CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + /* check initialize working success */ + if(CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) { + return ERROR; + } else { + /* set the data bit timing register */ + CAN_DBT(can_periph) = (BT_DSJW((uint32_t)can_fdframe_init->data_resync_jump_width) | \ + BT_DBS1((uint32_t)can_fdframe_init->data_time_segment_1) | \ + BT_DBS2((uint32_t)can_fdframe_init->data_time_segment_2) | \ + BT_BAUDPSC(((uint32_t)can_fdframe_init->data_prescaler) - 1U)); + + tempreg = can_fdframe_init->esi_mode | can_fdframe_init->iso_bosch; + + /* Protocol exception event detection */ + if(ENABLE == can_fdframe_init->excp_event_detect) { + tempreg &= ~CAN_FDCTL_PRED; + } else { + tempreg |= CAN_FDCTL_PRED; + } + + /* Transmitter delay compensation mode */ + if(ENABLE == can_fdframe_init->delay_compensation) { + tempreg |= CAN_FDCTL_TDCEN; + /* p_delay_compensation pointer should be config when TDC mode is enabled */ + if(0 != can_fdframe_init->p_delay_compensation) { + tempreg |= (can_fdframe_init->p_delay_compensation->tdc_mode & CAN_FDCTL_TDCMOD); + CAN_FDTDC(can_periph) = (FDTDC_TDCF(can_fdframe_init->p_delay_compensation->tdc_filter) | FDTDC_TDCO( + can_fdframe_init->p_delay_compensation->tdc_offset)); + } else { + return ERROR; + } + } else { + /* Transmitter delay compensation mode is disabled */ + tempreg &= ~CAN_FDCTL_TDCEN; + } + + /* FD operation mode */ + if(ENABLE == can_fdframe_init->fd_frame) { + tempreg |= CAN_FDCTL_FDEN; + } else { + tempreg &= ~CAN_FDCTL_FDEN; + } + CAN_FDCTL(can_periph) = tempreg; + + /* disable initialize mode */ + CAN_CTL(can_periph) &= ~CAN_CTL_IWMOD; + timeout = CAN_TIMEOUT; + /* wait the ACK */ + while((CAN_STAT_IWS == (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + /* check exit initialize mode */ + if(0U == timeout) { + return ERROR; + } + } + + return SUCCESS; +} + +/*! + \brief CAN FD frame function enable + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_fd_function_enable(uint32_t can_periph) +{ + CAN_FDCTL(can_periph) |= CAN_FDCTL_FDEN; +} + +/*! + \brief CAN FD frame function disable + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL + \param[out] none + \retval none +*/ +void can_fd_function_disable(uint32_t can_periph) +{ + CAN_FDCTL(can_periph) &= ~CAN_FDCTL_FDEN; +} +#endif /* GD32E508 */ + +/*! + \brief set CAN1 fliter start bank number + \param[in] start_bank: CAN1 start bank number + only one parameter can be selected which is shown as below: + \arg (1..27) + \param[out] none + \retval none +*/ +void can1_filter_start_bank(uint8_t start_bank) +{ + /* filter lock disable */ + CAN_FCTL(CAN0) |= CAN_FCTL_FLD; + /* set CAN1 filter start number */ + CAN_FCTL(CAN0) &= ~(uint32_t)CAN_FCTL_HBC1F; + CAN_FCTL(CAN0) |= FCTL_HBC1F(start_bank); + /* filter lock enable */ + CAN_FCTL(CAN0) &= ~CAN_FCTL_FLD; +} + +/*! + \brief enable CAN debug freeze + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_debug_freeze_enable(uint32_t can_periph) +{ + /* set DFZ bit */ + CAN_CTL(can_periph) |= CAN_CTL_DFZ; +#ifdef GD32E50X_CL + switch(can_periph) { + case CAN0: + dbg_periph_enable(DBG_CAN0_HOLD); + break; + case CAN1: + dbg_periph_enable(DBG_CAN1_HOLD); + break; + case CAN2: + dbg_periph_enable(DBG_CAN2_HOLD); + break; + default: + break; + } +#else + switch(can_periph) { + case CAN0: + dbg_periph_enable(DBG_CAN0_HOLD); + break; + case CAN1: + dbg_periph_enable(DBG_CAN1_HOLD); + break; + default: + break; + } +#endif +} + +/*! + \brief disable CAN debug freeze + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_debug_freeze_disable(uint32_t can_periph) +{ + /* set DFZ bit */ + CAN_CTL(can_periph) &= ~CAN_CTL_DFZ; +#ifdef GD32E50X_CL + switch(can_periph) { + case CAN0: + dbg_periph_disable(DBG_CAN0_HOLD); + break; + case CAN1: + dbg_periph_disable(DBG_CAN1_HOLD); + break; + case CAN2: + dbg_periph_disable(DBG_CAN2_HOLD); + break; + default: + break; +#else + switch(can_periph) { + case CAN0: + dbg_periph_disable(DBG_CAN0_HOLD); + break; + case CAN1: + dbg_periph_disable(DBG_CAN1_HOLD); + break; + default: + break; +#endif + } +} + +/*! + \brief enable CAN time trigger mode + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_time_trigger_mode_enable(uint32_t can_periph) +{ + uint8_t mailbox_number; + + /* enable the tcc mode */ + CAN_CTL(can_periph) |= CAN_CTL_TTC; + /* enable time stamp */ + for(mailbox_number = 0U; mailbox_number < 3U; mailbox_number++) { + CAN_TMP(can_periph, mailbox_number) |= CAN_TMP_TSEN; + } +} + +/*! + \brief disable CAN time trigger mode + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_time_trigger_mode_disable(uint32_t can_periph) +{ + uint8_t mailbox_number; + + /* disable the TCC mode */ + CAN_CTL(can_periph) &= ~CAN_CTL_TTC; + /* reset TSEN bits */ + for(mailbox_number = 0U; mailbox_number < 3U; mailbox_number++) { + CAN_TMP(can_periph, mailbox_number) &= ~CAN_TMP_TSEN; + } +} + +/*! + \brief transmit CAN message + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] transmit_message: struct for CAN transmit message + \arg tx_sfid: 0x00000000 - 0x000007FF + \arg tx_efid: 0x00000000 - 0x1FFFFFFF + \arg tx_ff: CAN_FF_STANDARD, CAN_FF_EXTENDED + \arg tx_ft: CAN_FT_DATA, CAN_FT_REMOTE + \arg tx_dlen: 0 - 8 (FD mode: 0 - 8, or 12, 16, 20, 24, 32, 48, 64) + \arg tx_data[]: 0x00 - 0xFF + \param[out] none + \retval mailbox_number +*/ +uint8_t can_message_transmit(uint32_t can_periph, can_trasnmit_message_struct *transmit_message) +{ + uint8_t mailbox_number = CAN_MAILBOX0; + volatile uint32_t p_temp; +#ifdef GD32E508 + uint8_t i = 0U; + uint8_t hit = 0U; + uint32_t canfd_en = 0U; + uint32_t reg_temp = 0U; +#endif /* GD32E508 */ + + /* select one empty mailbox */ + if(CAN_TSTAT_TME0 == (CAN_TSTAT(can_periph)&CAN_TSTAT_TME0)) { + mailbox_number = CAN_MAILBOX0; + } else if(CAN_TSTAT_TME1 == (CAN_TSTAT(can_periph)&CAN_TSTAT_TME1)) { + mailbox_number = CAN_MAILBOX1; + } else if(CAN_TSTAT_TME2 == (CAN_TSTAT(can_periph)&CAN_TSTAT_TME2)) { + mailbox_number = CAN_MAILBOX2; + } else { + mailbox_number = CAN_NOMAILBOX; + } + /* return no mailbox empty */ + if(CAN_NOMAILBOX == mailbox_number) { + return CAN_NOMAILBOX; + } + + CAN_TMI(can_periph, mailbox_number) &= CAN_TMI_TEN; + if(CAN_FF_STANDARD == transmit_message->tx_ff) { + /* set transmit mailbox standard identifier */ + CAN_TMI(can_periph, mailbox_number) |= (uint32_t)(TMI_SFID(transmit_message->tx_sfid) | \ + transmit_message->tx_ft); + } else { + /* set transmit mailbox extended identifier */ + CAN_TMI(can_periph, mailbox_number) |= (uint32_t)(TMI_EFID(transmit_message->tx_efid) | \ + transmit_message->tx_ff | \ + transmit_message->tx_ft); + } + +#ifdef GD32E508 + if(CAN_FDF_CLASSIC == transmit_message->fd_flag) { + /* set the data length */ + CAN_TMP(can_periph, mailbox_number) &= ~(CAN_TMP_DLENC | CAN_TMP_ESI | CAN_TMP_BRS | CAN_TMP_FDF); + /* Classic CAN frame data length does not exceed 8 */ + if (transmit_message->tx_dlen > 8U) { + transmit_message->tx_dlen = 8U; + } + CAN_TMP(can_periph, mailbox_number) |= transmit_message->tx_dlen; + /* set the data */ + CAN_TMDATA0(can_periph, mailbox_number) = TMDATA0_DB3(transmit_message->tx_data[3]) | \ + TMDATA0_DB2(transmit_message->tx_data[2]) | \ + TMDATA0_DB1(transmit_message->tx_data[1]) | \ + TMDATA0_DB0(transmit_message->tx_data[0]); + CAN_TMDATA1(can_periph, mailbox_number) = TMDATA1_DB7(transmit_message->tx_data[7]) | \ + TMDATA1_DB6(transmit_message->tx_data[6]) | \ + TMDATA1_DB5(transmit_message->tx_data[5]) | \ + TMDATA1_DB4(transmit_message->tx_data[4]); + } else { + canfd_en = CAN_FDCTL(can_periph) & CAN_FDCTL_FDEN; + /* check FD funciton has been enabled */ + if(canfd_en) { + if(transmit_message->tx_dlen <= 8U) { + /* set the data length */ + reg_temp |= transmit_message->tx_dlen; + } else { + /* data length greater than 8 */ + for(i = 0U; i < 7U; i++) { + if(transmit_message->tx_dlen == g_can_fdlength_table[i]) { + hit = 1U; + break; + } + } + /* data length is valid */ + if(1U == hit) { + reg_temp |= 9U + i; + } else { + CAN_ERROR_HANDLE("dlen is invalid \r\n"); + } + } + reg_temp |= (((uint32_t)transmit_message->fd_brs << 5U) | ((uint32_t)transmit_message->fd_esi << 4U) | ((uint32_t)transmit_message->fd_flag << 7U)); + CAN_TMP(can_periph, mailbox_number) = reg_temp; + + /* set the data */ + i = transmit_message->tx_dlen / 4U; + + /* data length is 5-7 need send 2 word */ + if((1U == i) && (4U != transmit_message->tx_dlen)) { + i++; + } + p_temp = (uint32_t)transmit_message->tx_data; + if((0U == i)) { + CAN_TMDATA0(can_periph, mailbox_number) = *(uint32_t *)p_temp; + } else { + for(; i > 0U; i--) { + CAN_TMDATA0(can_periph, mailbox_number) = *(uint32_t *)p_temp; + p_temp = ((uint32_t)((uint32_t)p_temp + 4U)); + } + } + + } else { + CAN_ERROR_HANDLE("CAN FD function disabled \r\n"); + } + } +#else + /* set the data length */ + CAN_TMP(can_periph, mailbox_number) &= ~CAN_TMP_DLENC; + CAN_TMP(can_periph, mailbox_number) |= transmit_message->tx_dlen; + /* set the data */ + CAN_TMDATA0(can_periph, mailbox_number) = TMDATA0_DB3(transmit_message->tx_data[3]) | \ + TMDATA0_DB2(transmit_message->tx_data[2]) | \ + TMDATA0_DB1(transmit_message->tx_data[1]) | \ + TMDATA0_DB0(transmit_message->tx_data[0]); + CAN_TMDATA1(can_periph, mailbox_number) = TMDATA1_DB7(transmit_message->tx_data[7]) | \ + TMDATA1_DB6(transmit_message->tx_data[6]) | \ + TMDATA1_DB5(transmit_message->tx_data[5]) | \ + TMDATA1_DB4(transmit_message->tx_data[4]); +#endif /* GD32E508 */ + /* enable transmission */ + CAN_TMI(can_periph, mailbox_number) |= CAN_TMI_TEN; + + return mailbox_number; +} + +/*! + \brief get CAN transmit state + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] mailbox_number + only one parameter can be selected which is shown as below: + \arg CAN_MAILBOX(x=0,1,2) + \param[out] none + \retval can_transmit_state_enum +*/ +can_transmit_state_enum can_transmit_states(uint32_t can_periph, uint8_t mailbox_number) +{ + can_transmit_state_enum state = CAN_TRANSMIT_FAILED; + uint32_t val = 0U; + + /* check selected mailbox state */ + switch(mailbox_number) { + /* mailbox0 */ + case CAN_MAILBOX0: + val = CAN_TSTAT(can_periph) & (CAN_TSTAT_MTF0 | CAN_TSTAT_MTFNERR0 | CAN_TSTAT_TME0); + break; + /* mailbox1 */ + case CAN_MAILBOX1: + val = CAN_TSTAT(can_periph) & (CAN_TSTAT_MTF1 | CAN_TSTAT_MTFNERR1 | CAN_TSTAT_TME1); + break; + /* mailbox2 */ + case CAN_MAILBOX2: + val = CAN_TSTAT(can_periph) & (CAN_TSTAT_MTF2 | CAN_TSTAT_MTFNERR2 | CAN_TSTAT_TME2); + break; + default: + val = CAN_TRANSMIT_FAILED; + break; + } + + switch(val) { + /* transmit pending */ + case(CAN_STATE_PENDING): + state = CAN_TRANSMIT_PENDING; + break; + /* mailbox0 transmit succeeded */ + case(CAN_TSTAT_MTF0 | CAN_TSTAT_MTFNERR0 | CAN_TSTAT_TME0): + state = CAN_TRANSMIT_OK; + break; + /* mailbox1 transmit succeeded */ + case(CAN_TSTAT_MTF1 | CAN_TSTAT_MTFNERR1 | CAN_TSTAT_TME1): + state = CAN_TRANSMIT_OK; + break; + /* mailbox2 transmit succeeded */ + case(CAN_TSTAT_MTF2 | CAN_TSTAT_MTFNERR2 | CAN_TSTAT_TME2): + state = CAN_TRANSMIT_OK; + break; + /* transmit failed */ + default: + state = CAN_TRANSMIT_FAILED; + break; + } + return state; +} + +/*! + \brief stop CAN transmission + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] mailbox_number + only one parameter can be selected which is shown as below: + \arg CAN_MAILBOXx(x=0,1,2) + \param[out] none + \retval ErrStatus: ERROR or SUCCESS +*/ +ErrStatus can_transmission_stop(uint32_t can_periph, uint8_t mailbox_number) +{ + ErrStatus reval = SUCCESS; + /* timeout for CAN_TSTAT_MSTx bits */ + uint32_t timeout = CAN_TIMEOUT; + uint32_t reg_value0 = 0U; + uint32_t reg_value1 = 0U; + + /* get the status of transmit FIFO order */ + reg_value0 = CAN_CTL(can_periph) & CAN_CTL_TFO; + + if(CAN_MAILBOX0 == mailbox_number) { + reg_value1 = CAN_TSTAT(can_periph) & (CAN_TSTAT_TMLS0 | CAN_ALL_MAILBOX_EMPTY); + if((CAN_CTL_TFO == reg_value0) && (CAN_TSTAT_TMLS0 == reg_value1)){ + reval = ERROR; + } else { + CAN_TSTAT(can_periph) |= CAN_TSTAT_MST0; + while((CAN_TSTAT_MST0 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST0)) && (0U != timeout)) { + timeout--; + } + if(CAN_TSTAT_MST0 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST0)){ + reval = ERROR; + } + } + } else if(CAN_MAILBOX1 == mailbox_number) { + reg_value1 = CAN_TSTAT(can_periph) & (CAN_TSTAT_TMLS1 | CAN_ALL_MAILBOX_EMPTY); + if((CAN_CTL_TFO == reg_value0) && (CAN_TSTAT_TMLS1 == reg_value1)){ + reval = ERROR; + }else{ + CAN_TSTAT(can_periph) |= CAN_TSTAT_MST1; + while((CAN_TSTAT_MST1 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST1)) && (0U != timeout)) { + timeout--; + } + if(CAN_TSTAT_MST1 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST1)){ + reval = ERROR; + } + } + + } else if(CAN_MAILBOX2 == mailbox_number) { + reg_value1 = CAN_TSTAT(can_periph) & (CAN_TSTAT_TMLS2 | CAN_ALL_MAILBOX_EMPTY); + if((CAN_CTL_TFO == reg_value0) && (CAN_TSTAT_TMLS2 == reg_value1)){ + reval = ERROR; + }else{ + CAN_TSTAT(can_periph) |= CAN_TSTAT_MST2; + while((CAN_TSTAT_MST2 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST2)) && (0U != timeout)) { + timeout--; + } + if(CAN_TSTAT_MST2 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST2)){ + reval = ERROR; + } + } + } else { + /* illegal parameters */ + } + return reval; +} + +/*! + \brief CAN receive message + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] fifo_number + \arg CAN_FIFOx(x=0,1) + \param[out] receive_message: struct for CAN receive message + \arg rx_sfid: 0x00000000 - 0x000007FF + \arg rx_efid: 0x00000000 - 0x1FFFFFFF + \arg rx_ff: CAN_FF_STANDARD, CAN_FF_EXTENDED + \arg rx_ft: CAN_FT_DATA, CAN_FT_REMOTE + \arg rx_dlen: 0 - 8 (FD mode: 0 - 8, or 12, 16, 20, 24, 32, 48, 64) + \arg rx_data[]: 0x00 - 0xFF + \arg rx_fi: 0 - 27 + \retval none +*/ +void can_message_receive(uint32_t can_periph, uint8_t fifo_number, can_receive_message_struct *receive_message) +{ + volatile uint32_t p_temp; +#ifdef GD32E508 + uint32_t canfd_en = 0U; + uint32_t data_temp; + uint8_t canfd_recv_cnt = 0U; + uint8_t i; +#endif /* GD32E508 */ + + /* get the frame format */ + receive_message->rx_ff = (uint8_t)(CAN_RFIFOMI_FF & CAN_RFIFOMI(can_periph, fifo_number)); + if(CAN_FF_STANDARD == receive_message->rx_ff) { + /* get standard identifier */ + receive_message->rx_sfid = (uint32_t)(GET_RFIFOMI_SFID(CAN_RFIFOMI(can_periph, fifo_number))); + } else { + /* get extended identifier */ + receive_message->rx_efid = (uint32_t)(GET_RFIFOMI_EFID(CAN_RFIFOMI(can_periph, fifo_number))); + } + + /* get frame type */ + receive_message->rx_ft = (uint8_t)(CAN_RFIFOMI_FT & CAN_RFIFOMI(can_periph, fifo_number)); + /* filtering index */ + receive_message->rx_fi = (uint8_t)(GET_RFIFOMP_FI(CAN_RFIFOMP(can_periph, fifo_number))); + +#ifdef GD32E508 + receive_message->fd_flag = (uint8_t)((CAN_RFIFOMP_FDF & CAN_RFIFOMP(can_periph, fifo_number)) >> 7); + + canfd_en = CAN_FDCTL(can_periph) & CAN_FDCTL_FDEN; + if(!canfd_en) { + if(CAN_FDF_CLASSIC == receive_message->fd_flag) { + /* get recevie data length */ + receive_message->rx_dlen = (uint8_t)(GET_RFIFOMP_DLENC(CAN_RFIFOMP(can_periph, fifo_number))); + /* receive data */ + receive_message->rx_data[0] = (uint8_t)(GET_RFIFOMDATA0_DB0(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message->rx_data[1] = (uint8_t)(GET_RFIFOMDATA0_DB1(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message->rx_data[2] = (uint8_t)(GET_RFIFOMDATA0_DB2(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message->rx_data[3] = (uint8_t)(GET_RFIFOMDATA0_DB3(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message->rx_data[4] = (uint8_t)(GET_RFIFOMDATA1_DB4(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message->rx_data[5] = (uint8_t)(GET_RFIFOMDATA1_DB5(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message->rx_data[6] = (uint8_t)(GET_RFIFOMDATA1_DB6(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message->rx_data[7] = (uint8_t)(GET_RFIFOMDATA1_DB7(CAN_RFIFOMDATA1(can_periph, fifo_number))); + } else { + CAN_ERROR_HANDLE("CAN FD function disabled \r\n"); + } + } else { + /* check FD funciton has been enabled */ + /* get recevie data length */ + canfd_recv_cnt = (uint8_t)(GET_RFIFOMP_DLENC(CAN_RFIFOMP(can_periph, fifo_number))); + + if(canfd_recv_cnt <= 8U) { + /* set the data length */ + receive_message->rx_dlen = canfd_recv_cnt; + } else { + receive_message->rx_dlen = g_can_fdlength_table[canfd_recv_cnt - 9U]; + } + + receive_message->fd_brs = (uint8_t)((CAN_RFIFOMP(can_periph, fifo_number) & CAN_RFIFOMP_BRS) >> 5); + receive_message->fd_esi = (uint8_t)((CAN_RFIFOMP(can_periph, fifo_number) & CAN_RFIFOMP_ESI) >> 4); + + /* get the data */ + i = receive_message->rx_dlen / 4U; + + /* data length is 5-7 need receive 2 word */ + if((1U == i) && (4U != receive_message->rx_dlen)) { + i++; + } + p_temp = (uint32_t)(uint32_t)receive_message->rx_data; + if(0U == i) { + data_temp = CAN_RFIFOMDATA0(can_periph, fifo_number); + *(uint32_t *)p_temp = data_temp; + } else { + /* get the data by reading from CAN_RFIFOMDATA0 register*/ + for(; i > 0U; i--) { + data_temp = CAN_RFIFOMDATA0(can_periph, fifo_number); + *(uint32_t *)p_temp = data_temp; + p_temp = ((uint32_t)((uint32_t)p_temp + 4U)); + } + } + } +#else + /* get recevie data length */ + receive_message->rx_dlen = (uint8_t)(GET_RFIFOMP_DLENC(CAN_RFIFOMP(can_periph, fifo_number))); + + /* receive data */ + receive_message -> rx_data[0] = (uint8_t)(GET_RFIFOMDATA0_DB0(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message -> rx_data[1] = (uint8_t)(GET_RFIFOMDATA0_DB1(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message -> rx_data[2] = (uint8_t)(GET_RFIFOMDATA0_DB2(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message -> rx_data[3] = (uint8_t)(GET_RFIFOMDATA0_DB3(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message -> rx_data[4] = (uint8_t)(GET_RFIFOMDATA1_DB4(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message -> rx_data[5] = (uint8_t)(GET_RFIFOMDATA1_DB5(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message -> rx_data[6] = (uint8_t)(GET_RFIFOMDATA1_DB6(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message -> rx_data[7] = (uint8_t)(GET_RFIFOMDATA1_DB7(CAN_RFIFOMDATA1(can_periph, fifo_number))); + +#endif /* GD32E508 */ + /* release FIFO */ + if(CAN_FIFO0 == fifo_number) { + CAN_RFIFO0(can_periph) |= CAN_RFIFO0_RFD0; + } else { + CAN_RFIFO1(can_periph) |= CAN_RFIFO1_RFD1; + } +} + +/*! + \brief release FIFO0 + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] fifo_number + only one parameter can be selected which is shown as below: + \arg CAN_FIFOx(x=0,1) + \param[out] none + \retval none +*/ +void can_fifo_release(uint32_t can_periph, uint8_t fifo_number) +{ + if(CAN_FIFO0 == fifo_number) { + CAN_RFIFO0(can_periph) |= CAN_RFIFO0_RFD0; + } else if(CAN_FIFO1 == fifo_number) { + CAN_RFIFO1(can_periph) |= CAN_RFIFO1_RFD1; + } else { + /* illegal parameters */ + CAN_ERROR_HANDLE("CAN FIFO NUM is invalid \r\n"); + } +} + +/*! + \brief CAN receive message length + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] fifo_number + only one parameter can be selected which is shown as below: + \arg CAN_FIFOx(x=0,1) + \param[out] none + \retval message length +*/ +uint8_t can_receive_message_length_get(uint32_t can_periph, uint8_t fifo_number) +{ + uint8_t val = 0U; + + if(CAN_FIFO0 == fifo_number) { + /* FIFO0 */ + val = (uint8_t)(CAN_RFIFO0(can_periph) & CAN_RFIF_RFL_MASK); + } else if(CAN_FIFO1 == fifo_number) { + /* FIFO1 */ + val = (uint8_t)(CAN_RFIFO1(can_periph) & CAN_RFIF_RFL_MASK); + } else { + /* illegal parameters */ + } + return val; +} + +/*! + \brief set CAN working mode + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] can_working_mode + only one parameter can be selected which is shown as below: + \arg CAN_MODE_INITIALIZE + \arg CAN_MODE_NORMAL + \arg CAN_MODE_SLEEP + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus can_working_mode_set(uint32_t can_periph, uint8_t working_mode) +{ + ErrStatus flag = ERROR; + /* timeout for IWS or also for SLPWS bits */ + uint32_t timeout = CAN_TIMEOUT; + + if(CAN_MODE_INITIALIZE == working_mode) { + /* disable sleep mode */ + CAN_CTL(can_periph) &= (~(uint32_t)CAN_CTL_SLPWMOD); + /* set initialize mode */ + CAN_CTL(can_periph) |= (uint8_t)CAN_CTL_IWMOD; + /* wait the acknowledge */ + while((CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + if(CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) { + flag = ERROR; + } else { + flag = SUCCESS; + } + } else if(CAN_MODE_NORMAL == working_mode) { + /* enter normal mode */ + CAN_CTL(can_periph) &= ~(uint32_t)(CAN_CTL_SLPWMOD | CAN_CTL_IWMOD); + /* wait the acknowledge */ + while((0U != (CAN_STAT(can_periph) & (CAN_STAT_IWS | CAN_STAT_SLPWS))) && (0U != timeout)) { + timeout--; + } + if(0U != (CAN_STAT(can_periph) & (CAN_STAT_IWS | CAN_STAT_SLPWS))) { + flag = ERROR; + } else { + flag = SUCCESS; + } + } else if(CAN_MODE_SLEEP == working_mode) { + /* disable initialize mode */ + CAN_CTL(can_periph) &= (~(uint32_t)CAN_CTL_IWMOD); + /* set sleep mode */ + CAN_CTL(can_periph) |= (uint8_t)CAN_CTL_SLPWMOD; + /* wait the acknowledge */ + while((CAN_STAT_SLPWS != (CAN_STAT(can_periph) & CAN_STAT_SLPWS)) && (0U != timeout)) { + timeout--; + } + if(CAN_STAT_SLPWS != (CAN_STAT(can_periph) & CAN_STAT_SLPWS)) { + flag = ERROR; + } else { + flag = SUCCESS; + } + } else { + flag = ERROR; + } + return flag; +} + +/*! + \brief wake up CAN + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus can_wakeup(uint32_t can_periph) +{ + ErrStatus flag = ERROR; + uint32_t timeout = CAN_TIMEOUT; + + /* wakeup */ + CAN_CTL(can_periph) &= ~CAN_CTL_SLPWMOD; + + while((0U != (CAN_STAT(can_periph) & CAN_STAT_SLPWS)) && (0x00U != timeout)) { + timeout--; + } + /* check state */ + if(0U != (CAN_STAT(can_periph) & CAN_STAT_SLPWS)) { + flag = ERROR; + } else { + flag = SUCCESS; + } + return flag; +} + +/*! + \brief get CAN error type + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval can_error_enum + \arg CAN_ERROR_NONE: no error + \arg CAN_ERROR_FILL: fill error + \arg CAN_ERROR_FORMATE: format error + \arg CAN_ERROR_ACK: ACK error + \arg CAN_ERROR_BITRECESSIVE: bit recessive + \arg CAN_ERROR_BITDOMINANTER: bit dominant error + \arg CAN_ERROR_CRC: CRC error + \arg CAN_ERROR_SOFTWARECFG: software configure +*/ +can_error_enum can_error_get(uint32_t can_periph) +{ + can_error_enum error; + error = CAN_ERROR_NONE; + + /* get error type */ + error = (can_error_enum)(GET_ERR_ERRN(CAN_ERR(can_periph))); + return error; +} + +/*! + \brief get CAN receive error number + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL + \param[out] none + \retval error number +*/ +uint8_t can_receive_error_number_get(uint32_t can_periph) +{ + uint8_t val; + + /* get error count */ + val = (uint8_t)(GET_ERR_RECNT(CAN_ERR(can_periph))); + return val; +} + +/*! + \brief get CAN transmit error number + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval error number +*/ +uint8_t can_transmit_error_number_get(uint32_t can_periph) +{ + uint8_t val; + + val = (uint8_t)(GET_ERR_TECNT(CAN_ERR(can_periph))); + return val; +} + +/*! + \brief enable CAN interrupt + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] interrupt + one or more parameters can be selected which are shown as below: + \arg CAN_INT_TME: transmit mailbox empty interrupt enable + \arg CAN_INT_RFNE0: receive FIFO0 not empty interrupt enable + \arg CAN_INT_RFF0: receive FIFO0 full interrupt enable + \arg CAN_INT_RFO0: receive FIFO0 overfull interrupt enable + \arg CAN_INT_RFNE1: receive FIFO1 not empty interrupt enable + \arg CAN_INT_RFF1: receive FIFO1 full interrupt enable + \arg CAN_INT_RFO1: receive FIFO1 overfull interrupt enable + \arg CAN_INT_WERR: warning error interrupt enable + \arg CAN_INT_PERR: passive error interrupt enable + \arg CAN_INT_BO: bus-off interrupt enable + \arg CAN_INT_ERRN: error number interrupt enable + \arg CAN_INT_ERR: error interrupt enable + \arg CAN_INT_WU: wakeup interrupt enable + \arg CAN_INT_SLPW: sleep working interrupt enable + \param[out] none + \retval none +*/ +void can_interrupt_enable(uint32_t can_periph, uint32_t interrupt) +{ + CAN_INTEN(can_periph) |= interrupt; +} + +/*! + \brief disable CAN interrupt + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] interrupt + one or more parameters can be selected which are shown as below: + \arg CAN_INT_TME: transmit mailbox empty interrupt enable + \arg CAN_INT_RFNE0: receive FIFO0 not empty interrupt enable + \arg CAN_INT_RFF0: receive FIFO0 full interrupt enable + \arg CAN_INT_RFO0: receive FIFO0 overfull interrupt enable + \arg CAN_INT_RFNE1: receive FIFO1 not empty interrupt enable + \arg CAN_INT_RFF1: receive FIFO1 full interrupt enable + \arg CAN_INT_RFO1: receive FIFO1 overfull interrupt enable + \arg CAN_INT_WERR: warning error interrupt enable + \arg CAN_INT_PERR: passive error interrupt enable + \arg CAN_INT_BO: bus-off interrupt enable + \arg CAN_INT_ERRN: error number interrupt enable + \arg CAN_INT_ERR: error interrupt enable + \arg CAN_INT_WU: wakeup interrupt enable + \arg CAN_INT_SLPW: sleep working interrupt enable + \param[out] none + \retval none +*/ +void can_interrupt_disable(uint32_t can_periph, uint32_t interrupt) +{ + CAN_INTEN(can_periph) &= ~interrupt; +} + +/*! + \brief get CAN flag state + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] flag: CAN flags, refer to can_flag_enum + only one parameter can be selected which is shown as below: + \arg CAN_FLAG_RXL: RX level + \arg CAN_FLAG_LASTRX: last sample value of RX pin + \arg CAN_FLAG_RS: receiving state + \arg CAN_FLAG_TS: transmitting state + \arg CAN_FLAG_SLPIF: status change flag of entering sleep working mode + \arg CAN_FLAG_WUIF: status change flag of wakeup from sleep working mode + \arg CAN_FLAG_ERRIF: error flag + \arg CAN_FLAG_SLPWS: sleep working state + \arg CAN_FLAG_IWS: initial working state + \arg CAN_FLAG_TMLS2: transmit mailbox 2 last sending in Tx FIFO + \arg CAN_FLAG_TMLS1: transmit mailbox 1 last sending in Tx FIFO + \arg CAN_FLAG_TMLS0: transmit mailbox 0 last sending in Tx FIFO + \arg CAN_FLAG_TME2: transmit mailbox 2 empty + \arg CAN_FLAG_TME1: transmit mailbox 1 empty + \arg CAN_FLAG_TME0: transmit mailbox 0 empty + \arg CAN_FLAG_MTE2: mailbox 2 transmit error + \arg CAN_FLAG_MTE1: mailbox 1 transmit error + \arg CAN_FLAG_MTE0: mailbox 0 transmit error + \arg CAN_FLAG_MAL2: mailbox 2 arbitration lost + \arg CAN_FLAG_MAL1: mailbox 1 arbitration lost + \arg CAN_FLAG_MAL0: mailbox 0 arbitration lost + \arg CAN_FLAG_MTFNERR2: mailbox 2 transmit finished with no error + \arg CAN_FLAG_MTFNERR1: mailbox 1 transmit finished with no error + \arg CAN_FLAG_MTFNERR0: mailbox 0 transmit finished with no error + \arg CAN_FLAG_MTF2: mailbox 2 transmit finished + \arg CAN_FLAG_MTF1: mailbox 1 transmit finished + \arg CAN_FLAG_MTF0: mailbox 0 transmit finished + \arg CAN_FLAG_RFO0: receive FIFO0 overfull + \arg CAN_FLAG_RFF0: receive FIFO0 full + \arg CAN_FLAG_RFO1: receive FIFO1 overfull + \arg CAN_FLAG_RFF1: receive FIFO1 full + \arg CAN_FLAG_BOERR: bus-off error + \arg CAN_FLAG_PERR: passive error + \arg CAN_FLAG_WERR: warning error + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus can_flag_get(uint32_t can_periph, can_flag_enum flag) +{ + /* get flag and interrupt enable state */ + if(RESET != (CAN_REG_VAL(can_periph, flag) & BIT(CAN_BIT_POS(flag)))) { + return SET; + } else { + return RESET; + } +} + +/*! + \brief clear CAN flag state + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] flag: CAN flags, refer to can_flag_enum + only one parameter can be selected which is shown as below: + \arg CAN_FLAG_SLPIF: status change flag of entering sleep working mode + \arg CAN_FLAG_WUIF: status change flag of wakeup from sleep working mode + \arg CAN_FLAG_ERRIF: error flag + \arg CAN_FLAG_MTE2: mailbox 2 transmit error + \arg CAN_FLAG_MTE1: mailbox 1 transmit error + \arg CAN_FLAG_MTE0: mailbox 0 transmit error + \arg CAN_FLAG_MAL2: mailbox 2 arbitration lost + \arg CAN_FLAG_MAL1: mailbox 1 arbitration lost + \arg CAN_FLAG_MAL0: mailbox 0 arbitration lost + \arg CAN_FLAG_MTFNERR2: mailbox 2 transmit finished with no error + \arg CAN_FLAG_MTFNERR1: mailbox 1 transmit finished with no error + \arg CAN_FLAG_MTFNERR0: mailbox 0 transmit finished with no error + \arg CAN_FLAG_MTF2: mailbox 2 transmit finished + \arg CAN_FLAG_MTF1: mailbox 1 transmit finished + \arg CAN_FLAG_MTF0: mailbox 0 transmit finished + \arg CAN_FLAG_RFO0: receive FIFO0 overfull + \arg CAN_FLAG_RFF0: receive FIFO0 full + \arg CAN_FLAG_RFO1: receive FIFO1 overfull + \arg CAN_FLAG_RFF1: receive FIFO1 full + \param[out] none + \retval none +*/ +void can_flag_clear(uint32_t can_periph, can_flag_enum flag) +{ + CAN_REG_VAL(can_periph, flag) |= BIT(CAN_BIT_POS(flag)); +} + +/*! + \brief get CAN interrupt flag state + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] int_flag: CAN interrupt flags, refer to can_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg CAN_INT_FLAG_SLPIF: status change interrupt flag of sleep working mode entering + \arg CAN_INT_FLAG_WUIF: status change interrupt flag of wakeup from sleep working mode + \arg CAN_INT_FLAG_ERRIF: error interrupt flag + \arg CAN_INT_FLAG_MTF2: mailbox 2 transmit finished interrupt flag + \arg CAN_INT_FLAG_MTF1: mailbox 1 transmit finished interrupt flag + \arg CAN_INT_FLAG_MTF0: mailbox 0 transmit finished interrupt flag + \arg CAN_INT_FLAG_RFO0: receive FIFO0 overfull interrupt flag + \arg CAN_INT_FLAG_RFF0: receive FIFO0 full interrupt flag + \arg CAN_INT_FLAG_RFL0: receive FIFO0 not empty interrupt flag + \arg CAN_INT_FLAG_RFO1: receive FIFO1 overfull interrupt flag + \arg CAN_INT_FLAG_RFF1: receive FIFO1 full interrupt flag + \arg CAN_INT_FLAG_RFL1: receive FIFO1 not empty interrupt flag + \arg CAN_INT_FLAG_ERRN: error number interrupt flag + \arg CAN_INT_FLAG_BOERR: bus-off error interrupt flag + \arg CAN_INT_FLAG_PERR: passive error interrupt flag + \arg CAN_INT_FLAG_WERR: warning error interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus can_interrupt_flag_get(uint32_t can_periph, can_interrupt_flag_enum int_flag) +{ + uint32_t ret1 = RESET; + uint32_t ret2 = RESET; + + /* get the staus of interrupt flag */ + if(int_flag == CAN_INT_FLAG_RFL0) { + ret1 = can_receive_message_length_get(can_periph, CAN_FIFO0); + } else if(int_flag == CAN_INT_FLAG_RFL1) { + ret1 = can_receive_message_length_get(can_periph, CAN_FIFO1); + } else if(int_flag == CAN_INT_FLAG_ERRN) { + ret1 = can_error_get(can_periph); + } else { + ret1 = CAN_REG_VALS(can_periph, int_flag) & BIT(CAN_BIT_POS0(int_flag)); + } + /* get the staus of interrupt enale bit */ + ret2 = CAN_INTEN(can_periph) & BIT(CAN_BIT_POS1(int_flag)); + if(ret1 && ret2) { + return SET; + } else { + return RESET; + } +} + +/*! + \brief clear CAN interrupt flag state + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] int_flag: CAN interrupt flags, refer to can_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg CAN_INT_FLAG_SLPIF: status change interrupt flag of sleep working mode entering + \arg CAN_INT_FLAG_WUIF: status change interrupt flag of wakeup from sleep working mode + \arg CAN_INT_FLAG_ERRIF: error interrupt flag + \arg CAN_INT_FLAG_MTF2: mailbox 2 transmit finished interrupt flag + \arg CAN_INT_FLAG_MTF1: mailbox 1 transmit finished interrupt flag + \arg CAN_INT_FLAG_MTF0: mailbox 0 transmit finished interrupt flag + \arg CAN_INT_FLAG_RFO0: receive FIFO0 overfull interrupt flag + \arg CAN_INT_FLAG_RFF0: receive FIFO0 full interrupt flag + \arg CAN_INT_FLAG_RFO1: receive FIFO1 overfull interrupt flag + \arg CAN_INT_FLAG_RFF1: receive FIFO1 full interrupt flag + \param[out] none + \retval none +*/ +void can_interrupt_flag_clear(uint32_t can_periph, can_interrupt_flag_enum int_flag) +{ + CAN_REG_VALS(can_periph, int_flag) |= BIT(CAN_BIT_POS0(int_flag)); +} +#endif /* GD32EPRT */ diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_can.c.base@1.0.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_can.c.base@1.0.0 new file mode 100644 index 0000000..ba6eb61 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_can.c.base@1.0.0 @@ -0,0 +1,1615 @@ +/*! + \file gd32e50x_can.c + \brief CAN driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ +#ifndef GD32EPRT +#include "gd32e50x_can.h" +#include + +#define CAN_ERROR_HANDLE(s) do{}while(1) + +/* This table can be used to calculate data length in FD mode */ +const uint8_t g_can_fdlength_table[] = {12, 16, 20, 24, 32, 48, 64}; + +/*! + \brief deinitialize CAN + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_deinit(uint32_t can_periph) +{ +#if defined(GD32E50X_CL) || defined(GD32E508) + switch(can_periph) { + case CAN0: + rcu_periph_reset_enable(RCU_CAN0RST); + rcu_periph_reset_disable(RCU_CAN0RST); + break; + case CAN1: + rcu_periph_reset_enable(RCU_CAN1RST); + rcu_periph_reset_disable(RCU_CAN1RST); + break; + case CAN2: + rcu_periph_reset_enable(RCU_CAN2RST); + rcu_periph_reset_disable(RCU_CAN2RST); + break; + default: + break; + } +#else + switch(can_periph) { + case CAN0: + rcu_periph_reset_enable(RCU_CAN0RST); + rcu_periph_reset_disable(RCU_CAN0RST); + break; + case CAN1: + rcu_periph_reset_enable(RCU_CAN1RST); + rcu_periph_reset_disable(RCU_CAN1RST); + break; + default: + break; + } +#endif +} + +/*! + \brief initialize CAN parameter struct + \param[in] type: the type of CAN parameter struct + only one parameter can be selected which is shown as below: + \arg CAN_INIT_STRUCT: the CAN initial struct + \arg CAN_FILTER_STRUCT: the CAN filter struct + \arg CAN_FD_FRAME_STRUCT: the CAN FD initial struct + \arg CAN_TX_MESSAGE_STRUCT: the CAN TX message struct + \arg CAN_RX_MESSAGE_STRUCT: the CAN RX message struct + \param[in] p_struct: the pointer of the specific struct + \param[out] none + \retval none +*/ +void can_struct_para_init(can_struct_type_enum type, void *p_struct) +{ + uint8_t i; + + if(NULL == p_struct) { + CAN_ERROR_HANDLE("struct parameter can not be NULL \r\n"); + } + /* get type of the struct */ + switch(type) { + /* used for can_init() */ + case CAN_INIT_STRUCT: + ((can_parameter_struct *)p_struct)->auto_bus_off_recovery = DISABLE; + ((can_parameter_struct *)p_struct)->auto_retrans = DISABLE; + ((can_parameter_struct *)p_struct)->auto_wake_up = DISABLE; + ((can_parameter_struct *)p_struct)->prescaler = 0x0400U; + ((can_parameter_struct *)p_struct)->rec_fifo_overwrite = DISABLE; + ((can_parameter_struct *)p_struct)->resync_jump_width = CAN_BT_SJW_1TQ; + ((can_parameter_struct *)p_struct)->time_segment_1 = CAN_BT_BS1_3TQ; + ((can_parameter_struct *)p_struct)->time_segment_2 = CAN_BT_BS2_1TQ; + ((can_parameter_struct *)p_struct)->time_triggered = DISABLE; + ((can_parameter_struct *)p_struct)->trans_fifo_order = DISABLE; + ((can_parameter_struct *)p_struct)->working_mode = CAN_NORMAL_MODE; + break; + /* used for can_filter_init() */ + case CAN_FILTER_STRUCT: + ((can_filter_parameter_struct *)p_struct)->filter_bits = CAN_FILTERBITS_32BIT; + ((can_filter_parameter_struct *)p_struct)->filter_enable = DISABLE; + ((can_filter_parameter_struct *)p_struct)->filter_fifo_number = CAN_FIFO0; + ((can_filter_parameter_struct *)p_struct)->filter_list_high = 0x0000U; + ((can_filter_parameter_struct *)p_struct)->filter_list_low = 0x0000U; + ((can_filter_parameter_struct *)p_struct)->filter_mask_high = 0x0000U; + ((can_filter_parameter_struct *)p_struct)->filter_mask_low = 0x0000U; + ((can_filter_parameter_struct *)p_struct)->filter_mode = CAN_FILTERMODE_MASK; + ((can_filter_parameter_struct *)p_struct)->filter_number = 0U; + break; +#ifdef GD32E508 + /* used for can_fd_init() */ + case CAN_FD_FRAME_STRUCT: + ((can_fdframe_struct *)p_struct)->data_prescaler = 0x0400U; + ((can_fdframe_struct *)p_struct)->data_resync_jump_width = 1U - 1U; + ((can_fdframe_struct *)p_struct)->data_time_segment_1 = 3U - 1U; + ((can_fdframe_struct *)p_struct)->data_time_segment_2 = 2U - 1U; + ((can_fdframe_struct *)p_struct)->delay_compensation = DISABLE; + ((can_fdframe_struct *)p_struct)->esi_mode = CAN_ESIMOD_HARDWARE; + ((can_fdframe_struct *)p_struct)->excp_event_detect = ENABLE; + ((can_fdframe_struct *)p_struct)->fd_frame = DISABLE; + ((can_fdframe_struct *)p_struct)->iso_bosch = CAN_FDMOD_ISO; + ((can_fdframe_struct *)p_struct)->p_delay_compensation = 0U; + break; + /* used for can_message_transmit() */ + case CAN_TX_MESSAGE_STRUCT: + ((can_trasnmit_message_struct *)p_struct)->fd_brs = CAN_BRS_DISABLE; + ((can_trasnmit_message_struct *)p_struct)->fd_esi = CAN_ESI_DOMINANT; + ((can_trasnmit_message_struct *)p_struct)->fd_flag = CAN_FDF_CLASSIC; + + for(i = 0U; i < 64U; i++) { + ((can_trasnmit_message_struct *)p_struct)->tx_data[i] = 0U; + } + + ((can_trasnmit_message_struct *)p_struct)->tx_dlen = 0u; + ((can_trasnmit_message_struct *)p_struct)->tx_efid = 0U; + ((can_trasnmit_message_struct *)p_struct)->tx_ff = (uint8_t)CAN_FF_STANDARD; + ((can_trasnmit_message_struct *)p_struct)->tx_ft = (uint8_t)CAN_FT_DATA; + ((can_trasnmit_message_struct *)p_struct)->tx_sfid = 0U; + break; + /* used for can_message_receive() */ + case CAN_RX_MESSAGE_STRUCT: + ((can_receive_message_struct *)p_struct)->fd_brs = CAN_BRS_DISABLE; + ((can_receive_message_struct *)p_struct)->fd_esi = CAN_ESI_DOMINANT; + ((can_receive_message_struct *)p_struct)->fd_flag = CAN_FDF_CLASSIC; + + for(i = 0U; i < 64U; i++) { + ((can_receive_message_struct *)p_struct)->rx_data[i] = 0U; + } + + ((can_receive_message_struct *)p_struct)->rx_dlen = 0U; + ((can_receive_message_struct *)p_struct)->rx_efid = 0U; + ((can_receive_message_struct *)p_struct)->rx_ff = (uint8_t)CAN_FF_STANDARD; + ((can_receive_message_struct *)p_struct)->rx_fi = 0U; + ((can_receive_message_struct *)p_struct)->rx_ft = (uint8_t)CAN_FT_DATA; + ((can_receive_message_struct *)p_struct)->rx_sfid = 0U; + break; +#else + /* used for can_message_transmit() */ + case CAN_TX_MESSAGE_STRUCT: + for(i = 0U; i < 8U; i++) { + ((can_trasnmit_message_struct *)p_struct)->tx_data[i] = 0U; + } + + ((can_trasnmit_message_struct *)p_struct)->tx_dlen = 0u; + ((can_trasnmit_message_struct *)p_struct)->tx_efid = 0U; + ((can_trasnmit_message_struct *)p_struct)->tx_ff = (uint8_t)CAN_FF_STANDARD; + ((can_trasnmit_message_struct *)p_struct)->tx_ft = (uint8_t)CAN_FT_DATA; + ((can_trasnmit_message_struct *)p_struct)->tx_sfid = 0U; + break; + /* used for can_message_receive() */ + case CAN_RX_MESSAGE_STRUCT: + for(i = 0U; i < 8U; i++) { + ((can_receive_message_struct *)p_struct)->rx_data[i] = 0U; + } + + ((can_receive_message_struct *)p_struct)->rx_dlen = 0U; + ((can_receive_message_struct *)p_struct)->rx_efid = 0U; + ((can_receive_message_struct *)p_struct)->rx_ff = (uint8_t)CAN_FF_STANDARD; + ((can_receive_message_struct *)p_struct)->rx_fi = 0U; + ((can_receive_message_struct *)p_struct)->rx_ft = (uint8_t)CAN_FT_DATA; + ((can_receive_message_struct *)p_struct)->rx_sfid = 0U; + break; + +#endif /* GD32E508 */ + default: + CAN_ERROR_HANDLE("parameter is invalid \r\n"); + break; + } +} + +/*! + \brief initialize CAN + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] can_parameter_init: parameters for CAN initializtion + \arg working_mode: CAN_NORMAL_MODE, CAN_LOOPBACK_MODE, CAN_SILENT_MODE, CAN_SILENT_LOOPBACK_MODE + \arg resync_jump_width: 0x00 - 0x07 + \arg time_segment_1: 0x00 - 0x7F + \arg time_segment_2: 0x00 - 0x1F + \arg time_triggered: ENABLE or DISABLE + \arg auto_bus_off_recovery: ENABLE or DISABLE + \arg auto_wake_up: ENABLE or DISABLE + \arg auto_retrans: ENABLE or DISABLE + \arg rec_fifo_overwrite: ENABLE or DISABLE + \arg trans_fifo_order: ENABLE or DISABLE + \arg prescaler: 0x0001 - 0x0400 + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus can_init(uint32_t can_periph, can_parameter_struct *can_parameter_init) +{ + uint32_t timeout = CAN_TIMEOUT; + ErrStatus flag = ERROR; +#ifdef GD32E508 + uint32_t fdctl_status; +#endif /* GD32E508 */ + + /* disable sleep mode */ + CAN_CTL(can_periph) &= ~CAN_CTL_SLPWMOD; + /* enable initialize mode */ + CAN_CTL(can_periph) |= CAN_CTL_IWMOD; + /* wait ACK */ + while((CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + /* check initialize working success */ + if(CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) { + flag = ERROR; + } else { +#ifdef GD32E508 + /* set the bit timing register */ + fdctl_status = CAN_FDCTL(can_periph); + if(CAN_FDCTL_FDEN != (fdctl_status & CAN_FDCTL_FDEN)) { + /* CAN FD disable, should frist enable, then write */ + fdctl_status = fdctl_status | CAN_FDCTL_FDEN; + CAN_FDCTL(can_periph) = fdctl_status; + CAN_BT(can_periph) = (BT_MODE((uint32_t)can_parameter_init->working_mode) | \ + BT_SJW((uint32_t)can_parameter_init->resync_jump_width) | \ + BT_BS1((uint32_t)can_parameter_init->time_segment_1) | \ + BT_BS2((uint32_t)can_parameter_init->time_segment_2) | \ + BT_BAUDPSC(((uint32_t)(can_parameter_init->prescaler) - 1U))); + fdctl_status = fdctl_status & (~CAN_FDCTL_FDEN); + CAN_FDCTL(can_periph) = fdctl_status; + } else { + /* CAN FD enable */ + CAN_BT(can_periph) = (BT_MODE((uint32_t)can_parameter_init->working_mode) | \ + BT_SJW((uint32_t)can_parameter_init->resync_jump_width) | \ + BT_BS1((uint32_t)can_parameter_init->time_segment_1) | \ + BT_BS2((uint32_t)can_parameter_init->time_segment_2) | \ + BT_BAUDPSC(((uint32_t)(can_parameter_init->prescaler) - 1U))); + } +#else + /* set the bit timing register */ + CAN_BT(can_periph) = (BT_MODE((uint32_t)can_parameter_init->working_mode) | \ + BT_SJW((uint32_t)can_parameter_init->resync_jump_width) | \ + BT_BS1((uint32_t)can_parameter_init->time_segment_1) | \ + BT_BS2((uint32_t)can_parameter_init->time_segment_2) | \ + BT_BAUDPSC(((uint32_t)(can_parameter_init->prescaler) - 1U))); + +#endif /* GD32E508 */ + /* time trigger communication mode */ + if(ENABLE == can_parameter_init->time_triggered) { + CAN_CTL(can_periph) |= CAN_CTL_TTC; + } else { + CAN_CTL(can_periph) &= ~CAN_CTL_TTC; + } + /* automatic bus-off managment */ + if(ENABLE == can_parameter_init->auto_bus_off_recovery) { + CAN_CTL(can_periph) |= CAN_CTL_ABOR; + } else { + CAN_CTL(can_periph) &= ~CAN_CTL_ABOR; + } + /* automatic wakeup mode */ + if(ENABLE == can_parameter_init->auto_wake_up) { + CAN_CTL(can_periph) |= CAN_CTL_AWU; + } else { + CAN_CTL(can_periph) &= ~CAN_CTL_AWU; + } + /* automatic retransmission mode */ + if(ENABLE == can_parameter_init->auto_retrans) { + CAN_CTL(can_periph) &= ~CAN_CTL_ARD; + } else { + CAN_CTL(can_periph) |= CAN_CTL_ARD; + } + /* receive fifo overwrite mode */ + if(ENABLE == can_parameter_init->rec_fifo_overwrite) { + CAN_CTL(can_periph) &= ~CAN_CTL_RFOD; + } else { + CAN_CTL(can_periph) |= CAN_CTL_RFOD; + } + /* transmit fifo order */ + if(ENABLE == can_parameter_init->trans_fifo_order) { + CAN_CTL(can_periph) |= CAN_CTL_TFO; + } else { + CAN_CTL(can_periph) &= ~CAN_CTL_TFO; + } + /* disable initialize mode */ + CAN_CTL(can_periph) &= ~CAN_CTL_IWMOD; + timeout = CAN_TIMEOUT; + /* wait the ACK */ + while((CAN_STAT_IWS == (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + /* check exit initialize mode */ + if(0U != timeout) { + flag = SUCCESS; + } + } + return flag; +} + +/*! + \brief initialize CAN filter + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] can_filter_parameter_init: struct for CAN filter initialization + \arg filter_list_high: 0x0000 - 0xFFFF + \arg filter_list_low: 0x0000 - 0xFFFF + \arg filter_mask_high: 0x0000 - 0xFFFF + \arg filter_mask_low: 0x0000 - 0xFFFF + \arg filter_fifo_number: CAN_FIFO0, CAN_FIFO1 + \arg filter_number: In CAN0(0 - 27), In CAN2(0 - 14) + \arg filter_mode: CAN_FILTERMODE_MASK, CAN_FILTERMODE_LIST + \arg filter_bits: CAN_FILTERBITS_32BIT, CAN_FILTERBITS_16BIT + \arg filter_enable: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void can_filter_init(uint32_t can_periph, can_filter_parameter_struct *can_filter_parameter_init) +{ + uint32_t val = 0U; + + val = ((uint32_t)1) << (can_filter_parameter_init->filter_number); + if(CAN1 == can_periph) { + can_periph = CAN0; + } + /* filter lock disable */ + CAN_FCTL(can_periph) |= CAN_FCTL_FLD; + /* disable filter */ + CAN_FW(can_periph) &= ~(uint32_t)val; + + /* filter 16 bits */ + if(CAN_FILTERBITS_16BIT == can_filter_parameter_init->filter_bits) { + /* set filter 16 bits */ + CAN_FSCFG(can_periph) &= ~(uint32_t)val; + /* first 16 bits list and first 16 bits mask or first 16 bits list and second 16 bits list */ + CAN_FDATA0(can_periph, can_filter_parameter_init->filter_number) = \ + FDATA_MASK_HIGH((can_filter_parameter_init->filter_mask_low) & CAN_FILTER_MASK_16BITS) | \ + FDATA_MASK_LOW((can_filter_parameter_init->filter_list_low) & CAN_FILTER_MASK_16BITS); + /* second 16 bits list and second 16 bits mask or third 16 bits list and fourth 16 bits list */ + CAN_FDATA1(can_periph, can_filter_parameter_init->filter_number) = \ + FDATA_MASK_HIGH((can_filter_parameter_init->filter_mask_high) & CAN_FILTER_MASK_16BITS) | \ + FDATA_MASK_LOW((can_filter_parameter_init->filter_list_high) & CAN_FILTER_MASK_16BITS); + } + /* filter 32 bits */ + if(CAN_FILTERBITS_32BIT == can_filter_parameter_init->filter_bits) { + /* set filter 32 bits */ + CAN_FSCFG(can_periph) |= (uint32_t)val; + /* 32 bits list or first 32 bits list */ + CAN_FDATA0(can_periph, can_filter_parameter_init->filter_number) = \ + FDATA_MASK_HIGH((can_filter_parameter_init->filter_list_high) & CAN_FILTER_MASK_16BITS) | + FDATA_MASK_LOW((can_filter_parameter_init->filter_list_low) & CAN_FILTER_MASK_16BITS); + /* 32 bits mask or second 32 bits list */ + CAN_FDATA1(can_periph, can_filter_parameter_init->filter_number) = \ + FDATA_MASK_HIGH((can_filter_parameter_init->filter_mask_high) & CAN_FILTER_MASK_16BITS) | + FDATA_MASK_LOW((can_filter_parameter_init->filter_mask_low) & CAN_FILTER_MASK_16BITS); + } + + /* filter mode */ + if(CAN_FILTERMODE_MASK == can_filter_parameter_init->filter_mode) { + /* mask mode */ + CAN_FMCFG(can_periph) &= ~(uint32_t)val; + } else { + /* list mode */ + CAN_FMCFG(can_periph) |= (uint32_t)val; + } + + /* filter FIFO */ + if(CAN_FIFO0 == (can_filter_parameter_init->filter_fifo_number)) { + /* FIFO0 */ + CAN_FAFIFO(can_periph) &= ~(uint32_t)val; + } else { + /* FIFO1 */ + CAN_FAFIFO(can_periph) |= (uint32_t)val; + } + + /* filter working */ + if(ENABLE == can_filter_parameter_init->filter_enable) { + + CAN_FW(can_periph) |= (uint32_t)val; + } + + /* filter lock enable */ + CAN_FCTL(can_periph) &= ~CAN_FCTL_FLD; +} + +/*! + \brief CAN filter mask mode initialization + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] id: extended(11-bits) or standard(29-bits) identifier + \arg 0x00000000 - 0x1FFFFFFF + \param[in] mask: extended(11-bits) or standard(29-bits) identifier mask + \arg 0x00000000 - 0x1FFFFFFF + \param[in] format_fifo: format and fifo states + only one parameter can be selected which is shown as below: + \arg CAN_STANDARD_FIFO0 + \arg CAN_STANDARD_FIFO1 + \arg CAN_EXTENDED_FIFO0 + \arg CAN_EXTENDED_FIFO1 + \param[in] filter_number: filter sequence number + \arg In CAN0:0 - 27 ,In CAN2:0 - 14 + \param[out] none + \retval none +*/ +void can_filter_mask_mode_init(uint32_t can_periph, uint32_t id, uint32_t mask, can_format_fifo_enum format_fifo, uint16_t filter_number) +{ + can_filter_parameter_struct can_filter; + + /* Initialize the filter structure */ + can_struct_para_init(CAN_FILTER_STRUCT, &can_filter); + if(CAN1 == can_periph) { + can_periph = CAN0; + } + /* filter config */ + can_filter.filter_number = filter_number; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_enable = ENABLE; + + switch(format_fifo) { + /* standard FIFO 0 */ + case CAN_STANDARD_FIFO0: + can_filter.filter_fifo_number = CAN_FIFO0; + /* configure SFID[10:0] */ + can_filter.filter_list_high = (uint16_t)id << 5; + can_filter.filter_list_low = 0x0000U; + /* configure SFID[10:0] mask */ + can_filter.filter_mask_high = (uint16_t)mask << 5; + /* both data and remote frames can be received */ + can_filter.filter_mask_low = (uint16_t)(1U << 2U); + + break; + /* standard FIFO 1 */ + case CAN_STANDARD_FIFO1: + can_filter.filter_fifo_number = CAN_FIFO1; + /* configure SFID[10:0] */ + can_filter.filter_list_high = (uint16_t)id << 5; + can_filter.filter_list_low = 0x0000U; + /* configure SFID[10:0] mask */ + can_filter.filter_mask_high = (uint16_t)mask << 5; + /* both data and remote frames can be received */ + can_filter.filter_mask_low = (uint16_t)(1U << 2U); + + break; + /* extended FIFO 0 */ + case CAN_EXTENDED_FIFO0: + can_filter.filter_fifo_number = CAN_FIFO0; + /* configure EFID[28:13] */ + can_filter.filter_list_high = (uint16_t)(id >> 13); + /* configure EFID[12:0] and frame format bit set */ + can_filter.filter_list_low = ((uint16_t)(id << 3)) | (1U << 2); + /* configure EFID[28:13] mask */ + can_filter.filter_mask_high = (uint16_t)(mask >> 13); + /* configure EFID[12:0] and frame format bit mask */ + /* both data and remote frames can be received */ + can_filter.filter_mask_low = ((uint16_t)(mask << 3)) | (1U << 2); + + break; + /* extended FIFO 1 */ + case CAN_EXTENDED_FIFO1: + can_filter.filter_fifo_number = CAN_FIFO1; + /* configure EFID[28:13] */ + can_filter.filter_list_high = (uint16_t)(id >> 13); + /* configure EFID[12:0] and frame format bit set */ + can_filter.filter_list_low = ((uint16_t)(id << 3)) | (1U << 2); + /* configure EFID[28:13] mask */ + can_filter.filter_mask_high = (uint16_t)(mask >> 13); + /* configure EFID[12:0] and frame format bit mask */ + /* both data and remote frames can be received */ + can_filter.filter_mask_low = ((uint16_t)(mask << 3)) | (1U << 2); + + break; + default: + CAN_ERROR_HANDLE("parameter is invalid \r\n"); + } + + can_filter_init(can_periph, &can_filter); +} + +/*! + \brief CAN communication mode configure + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] mode: communication mode + only one parameter can be selected which is shown as below: + \arg CAN_NORMAL_MODE + \arg CAN_LOOPBACK_MODE + \arg CAN_SILENT_MODE + \arg CAN_SILENT_LOOPBACK_MODE + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus can_monitor_mode_set(uint32_t can_periph, uint8_t mode) +{ + ErrStatus reval = SUCCESS; + uint32_t timeout = CAN_TIMEOUT; + + if(mode == (mode & CAN_SILENT_LOOPBACK_MODE)) { + /* disable sleep mode */ + CAN_CTL(can_periph) &= (~(uint32_t)CAN_CTL_SLPWMOD); + /* set initialize mode */ + CAN_CTL(can_periph) |= (uint8_t)CAN_CTL_IWMOD; + /* wait the acknowledge */ + timeout = CAN_TIMEOUT; + while((CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + + if(CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) { + reval = ERROR; + } else { + CAN_BT(can_periph) &= ~BT_MODE(3); + CAN_BT(can_periph) |= BT_MODE(mode); + + timeout = CAN_TIMEOUT; + /* enter normal mode */ + CAN_CTL(can_periph) &= ~(uint32_t)(CAN_CTL_SLPWMOD | CAN_CTL_IWMOD); + /* wait the acknowledge */ + while((0U != (CAN_STAT(can_periph) & (CAN_STAT_IWS | CAN_STAT_SLPWS))) && (0U != timeout)) { + timeout--; + } + if(0U != (CAN_STAT(can_periph) & (CAN_STAT_IWS | CAN_STAT_SLPWS))) { + reval = ERROR; + } + } + } else { + reval = ERROR; + } + + return reval; +} + +#ifdef GD32E508 +/*! + \brief initialize CAN FD function + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] can_fdframe_init: parameters for CAN FD initializtion + \arg fd_frame: ENABLE or DISABLE + \arg excp_event_detect: ENABLE or DISABLE + \arg delay_compensation: ENABLE or DISABLE + \arg p_delay_compensation: the pointer of tdc struct + can_fd_tdc_struct: + tdc_mode: CAN_TDCMOD_CALC_AND_OFFSET or CAN_TDCMOD_OFFSET + tdc_filter: 0x00 - 0x07 + tdc_offset: 0x00 - 0x07 + \arg iso_bosch: CAN_FDMOD_ISO or CAN_FDMOD_BOSCH + \arg esi_mode: CAN_ESIMOD_HARDWARE or CAN_ESIMOD_SOFTWARE + \arg data_resync_jump_width: 0x00 - 0x07 + \arg data_time_segment_1: 0x00 - 0x0F + \arg data_time_segment_2: 0x00 - 0x07 + \arg data_prescaler: 0x0001 - 0x0400 + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus can_fd_init(uint32_t can_periph, can_fdframe_struct *can_fdframe_init) +{ + uint32_t timeout = CAN_TIMEOUT; + uint32_t tempreg = 0U; + + /* check null pointer */ + if(0 == can_fdframe_init) { + return ERROR; + } + /* disable sleep mode */ + CAN_CTL(can_periph) &= ~CAN_CTL_SLPWMOD; + /* enable initialize mode */ + CAN_CTL(can_periph) |= CAN_CTL_IWMOD; + /* wait ACK */ + while((CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + /* check initialize working success */ + if(CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) { + return ERROR; + } else { + /* set the data bit timing register */ + CAN_DBT(can_periph) = (BT_DSJW((uint32_t)can_fdframe_init->data_resync_jump_width) | \ + BT_DBS1((uint32_t)can_fdframe_init->data_time_segment_1) | \ + BT_DBS2((uint32_t)can_fdframe_init->data_time_segment_2) | \ + BT_BAUDPSC(((uint32_t)can_fdframe_init->data_prescaler) - 1U)); + + tempreg = can_fdframe_init->esi_mode | can_fdframe_init->iso_bosch; + + /* Protocol exception event detection */ + if(ENABLE == can_fdframe_init->excp_event_detect) { + tempreg &= ~CAN_FDCTL_PRED; + } else { + tempreg |= CAN_FDCTL_PRED; + } + + /* Transmitter delay compensation mode */ + if(ENABLE == can_fdframe_init->delay_compensation) { + tempreg |= CAN_FDCTL_TDCEN; + /* p_delay_compensation pointer should be config when TDC mode is enabled */ + if(0 != can_fdframe_init->p_delay_compensation) { + tempreg |= (can_fdframe_init->p_delay_compensation->tdc_mode & CAN_FDCTL_TDCMOD); + CAN_FDTDC(can_periph) = (FDTDC_TDCF(can_fdframe_init->p_delay_compensation->tdc_filter) | FDTDC_TDCO( + can_fdframe_init->p_delay_compensation->tdc_offset)); + } else { + return ERROR; + } + } else { + /* Transmitter delay compensation mode is disabled */ + tempreg &= ~CAN_FDCTL_TDCEN; + } + + /* FD operation mode */ + if(ENABLE == can_fdframe_init->fd_frame) { + tempreg |= CAN_FDCTL_FDEN; + } else { + tempreg &= ~CAN_FDCTL_FDEN; + } + CAN_FDCTL(can_periph) = tempreg; + + /* disable initialize mode */ + CAN_CTL(can_periph) &= ~CAN_CTL_IWMOD; + timeout = CAN_TIMEOUT; + /* wait the ACK */ + while((CAN_STAT_IWS == (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + /* check exit initialize mode */ + if(0U == timeout) { + return ERROR; + } + } + + return SUCCESS; +} + +/*! + \brief CAN FD frame function enable + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_fd_function_enable(uint32_t can_periph) +{ + CAN_FDCTL(can_periph) |= CAN_FDCTL_FDEN; +} + +/*! + \brief CAN FD frame function disable + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL + \param[out] none + \retval none +*/ +void can_fd_function_disable(uint32_t can_periph) +{ + CAN_FDCTL(can_periph) &= ~CAN_FDCTL_FDEN; +} +#endif /* GD32E508 */ + +/*! + \brief set CAN1 fliter start bank number + \param[in] start_bank: CAN1 start bank number + only one parameter can be selected which is shown as below: + \arg (1..27) + \param[out] none + \retval none +*/ +void can1_filter_start_bank(uint8_t start_bank) +{ + /* filter lock disable */ + CAN_FCTL(CAN0) |= CAN_FCTL_FLD; + /* set CAN1 filter start number */ + CAN_FCTL(CAN0) &= ~(uint32_t)CAN_FCTL_HBC1F; + CAN_FCTL(CAN0) |= FCTL_HBC1F(start_bank); + /* filter lock enable */ + CAN_FCTL(CAN0) &= ~CAN_FCTL_FLD; +} + +/*! + \brief enable CAN debug freeze + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_debug_freeze_enable(uint32_t can_periph) +{ + /* set DFZ bit */ + CAN_CTL(can_periph) |= CAN_CTL_DFZ; +#ifdef GD32E50X_CL + switch(can_periph) { + case CAN0: + dbg_periph_enable(DBG_CAN0_HOLD); + break; + case CAN1: + dbg_periph_enable(DBG_CAN1_HOLD); + break; + case CAN2: + dbg_periph_enable(DBG_CAN2_HOLD); + break; + default: + break; + } +#else + switch(can_periph) { + case CAN0: + dbg_periph_enable(DBG_CAN0_HOLD); + break; + case CAN1: + dbg_periph_enable(DBG_CAN1_HOLD); + break; + default: + break; + } +#endif +} + +/*! + \brief disable CAN debug freeze + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_debug_freeze_disable(uint32_t can_periph) +{ + /* set DFZ bit */ + CAN_CTL(can_periph) &= ~CAN_CTL_DFZ; +#ifdef GD32E50X_CL + switch(can_periph) { + case CAN0: + dbg_periph_disable(DBG_CAN0_HOLD); + break; + case CAN1: + dbg_periph_disable(DBG_CAN1_HOLD); + break; + case CAN2: + dbg_periph_disable(DBG_CAN2_HOLD); + break; + default: + break; +#else + switch(can_periph) { + case CAN0: + dbg_periph_disable(DBG_CAN0_HOLD); + break; + case CAN1: + dbg_periph_disable(DBG_CAN1_HOLD); + break; + default: + break; +#endif + } +} + +/*! + \brief enable CAN time trigger mode + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_time_trigger_mode_enable(uint32_t can_periph) +{ + uint8_t mailbox_number; + + /* enable the tcc mode */ + CAN_CTL(can_periph) |= CAN_CTL_TTC; + /* enable time stamp */ + for(mailbox_number = 0U; mailbox_number < 3U; mailbox_number++) { + CAN_TMP(can_periph, mailbox_number) |= CAN_TMP_TSEN; + } +} + +/*! + \brief disable CAN time trigger mode + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval none +*/ +void can_time_trigger_mode_disable(uint32_t can_periph) +{ + uint8_t mailbox_number; + + /* disable the TCC mode */ + CAN_CTL(can_periph) &= ~CAN_CTL_TTC; + /* reset TSEN bits */ + for(mailbox_number = 0U; mailbox_number < 3U; mailbox_number++) { + CAN_TMP(can_periph, mailbox_number) &= ~CAN_TMP_TSEN; + } +} + +/*! + \brief transmit CAN message + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] transmit_message: struct for CAN transmit message + \arg tx_sfid: 0x00000000 - 0x000007FF + \arg tx_efid: 0x00000000 - 0x1FFFFFFF + \arg tx_ff: CAN_FF_STANDARD, CAN_FF_EXTENDED + \arg tx_ft: CAN_FT_DATA, CAN_FT_REMOTE + \arg tx_dlen: 0 - 8 (FD mode: 0 - 8, or 12, 16, 20, 24, 32, 48, 64) + \arg tx_data[]: 0x00 - 0xFF + \param[out] none + \retval mailbox_number +*/ +uint8_t can_message_transmit(uint32_t can_periph, can_trasnmit_message_struct *transmit_message) +{ + uint8_t mailbox_number = CAN_MAILBOX0; + volatile uint32_t p_temp; +#ifdef GD32E508 + uint8_t i = 0U; + uint8_t hit = 0U; + uint32_t canfd_en = 0U; + uint32_t reg_temp = 0U; +#endif /* GD32E508 */ + + /* select one empty mailbox */ + if(CAN_TSTAT_TME0 == (CAN_TSTAT(can_periph)&CAN_TSTAT_TME0)) { + mailbox_number = CAN_MAILBOX0; + } else if(CAN_TSTAT_TME1 == (CAN_TSTAT(can_periph)&CAN_TSTAT_TME1)) { + mailbox_number = CAN_MAILBOX1; + } else if(CAN_TSTAT_TME2 == (CAN_TSTAT(can_periph)&CAN_TSTAT_TME2)) { + mailbox_number = CAN_MAILBOX2; + } else { + mailbox_number = CAN_NOMAILBOX; + } + /* return no mailbox empty */ + if(CAN_NOMAILBOX == mailbox_number) { + return CAN_NOMAILBOX; + } + + CAN_TMI(can_periph, mailbox_number) &= CAN_TMI_TEN; + if(CAN_FF_STANDARD == transmit_message->tx_ff) { + /* set transmit mailbox standard identifier */ + CAN_TMI(can_periph, mailbox_number) |= (uint32_t)(TMI_SFID(transmit_message->tx_sfid) | \ + transmit_message->tx_ft); + } else { + /* set transmit mailbox extended identifier */ + CAN_TMI(can_periph, mailbox_number) |= (uint32_t)(TMI_EFID(transmit_message->tx_efid) | \ + transmit_message->tx_ff | \ + transmit_message->tx_ft); + } + +#ifdef GD32E508 + if(CAN_FDF_CLASSIC == transmit_message->fd_flag) { + /* set the data length */ + CAN_TMP(can_periph, mailbox_number) &= ~(CAN_TMP_DLENC | CAN_TMP_ESI | CAN_TMP_BRS | CAN_TMP_FDF); + /* Classic CAN frame data length does not exceed 8 */ + if (transmit_message->tx_dlen > 8U) { + transmit_message->tx_dlen = 8U; + } + CAN_TMP(can_periph, mailbox_number) |= transmit_message->tx_dlen; + /* set the data */ + CAN_TMDATA0(can_periph, mailbox_number) = TMDATA0_DB3(transmit_message->tx_data[3]) | \ + TMDATA0_DB2(transmit_message->tx_data[2]) | \ + TMDATA0_DB1(transmit_message->tx_data[1]) | \ + TMDATA0_DB0(transmit_message->tx_data[0]); + CAN_TMDATA1(can_periph, mailbox_number) = TMDATA1_DB7(transmit_message->tx_data[7]) | \ + TMDATA1_DB6(transmit_message->tx_data[6]) | \ + TMDATA1_DB5(transmit_message->tx_data[5]) | \ + TMDATA1_DB4(transmit_message->tx_data[4]); + } else { + canfd_en = CAN_FDCTL(can_periph) & CAN_FDCTL_FDEN; + /* check FD funciton has been enabled */ + if(canfd_en) { + if(transmit_message->tx_dlen <= 8U) { + /* set the data length */ + reg_temp |= transmit_message->tx_dlen; + } else { + /* data length greater than 8 */ + for(i = 0U; i < 7U; i++) { + if(transmit_message->tx_dlen == g_can_fdlength_table[i]) { + hit = 1U; + break; + } + } + /* data length is valid */ + if(1U == hit) { + reg_temp |= 9U + i; + } else { + CAN_ERROR_HANDLE("dlen is invalid \r\n"); + } + } + reg_temp |= (((uint32_t)transmit_message->fd_brs << 5U) | ((uint32_t)transmit_message->fd_esi << 4U) | ((uint32_t)transmit_message->fd_flag << 7U)); + CAN_TMP(can_periph, mailbox_number) = reg_temp; + + /* set the data */ + i = transmit_message->tx_dlen / 4U; + + /* data length is 5-7 need send 2 word */ + if((1U == i) && (4U != transmit_message->tx_dlen)) { + i++; + } + p_temp = (uint32_t)transmit_message->tx_data; + if((0U == i)) { + CAN_TMDATA0(can_periph, mailbox_number) = *(uint32_t *)p_temp; + } else { + for(; i > 0U; i--) { + CAN_TMDATA0(can_periph, mailbox_number) = *(uint32_t *)p_temp; + p_temp = ((uint32_t)((uint32_t)p_temp + 4U)); + } + } + + } else { + CAN_ERROR_HANDLE("CAN FD function disabled \r\n"); + } + } +#else + /* set the data length */ + CAN_TMP(can_periph, mailbox_number) &= ~CAN_TMP_DLENC; + CAN_TMP(can_periph, mailbox_number) |= transmit_message->tx_dlen; + /* set the data */ + CAN_TMDATA0(can_periph, mailbox_number) = TMDATA0_DB3(transmit_message->tx_data[3]) | \ + TMDATA0_DB2(transmit_message->tx_data[2]) | \ + TMDATA0_DB1(transmit_message->tx_data[1]) | \ + TMDATA0_DB0(transmit_message->tx_data[0]); + CAN_TMDATA1(can_periph, mailbox_number) = TMDATA1_DB7(transmit_message->tx_data[7]) | \ + TMDATA1_DB6(transmit_message->tx_data[6]) | \ + TMDATA1_DB5(transmit_message->tx_data[5]) | \ + TMDATA1_DB4(transmit_message->tx_data[4]); +#endif /* GD32E508 */ + /* enable transmission */ + CAN_TMI(can_periph, mailbox_number) |= CAN_TMI_TEN; + + return mailbox_number; +} + +/*! + \brief get CAN transmit state + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] mailbox_number + only one parameter can be selected which is shown as below: + \arg CAN_MAILBOX(x=0,1,2) + \param[out] none + \retval can_transmit_state_enum +*/ +can_transmit_state_enum can_transmit_states(uint32_t can_periph, uint8_t mailbox_number) +{ + can_transmit_state_enum state = CAN_TRANSMIT_FAILED; + uint32_t val = 0U; + + /* check selected mailbox state */ + switch(mailbox_number) { + /* mailbox0 */ + case CAN_MAILBOX0: + val = CAN_TSTAT(can_periph) & (CAN_TSTAT_MTF0 | CAN_TSTAT_MTFNERR0 | CAN_TSTAT_TME0); + break; + /* mailbox1 */ + case CAN_MAILBOX1: + val = CAN_TSTAT(can_periph) & (CAN_TSTAT_MTF1 | CAN_TSTAT_MTFNERR1 | CAN_TSTAT_TME1); + break; + /* mailbox2 */ + case CAN_MAILBOX2: + val = CAN_TSTAT(can_periph) & (CAN_TSTAT_MTF2 | CAN_TSTAT_MTFNERR2 | CAN_TSTAT_TME2); + break; + default: + val = CAN_TRANSMIT_FAILED; + break; + } + + switch(val) { + /* transmit pending */ + case(CAN_STATE_PENDING): + state = CAN_TRANSMIT_PENDING; + break; + /* mailbox0 transmit succeeded */ + case(CAN_TSTAT_MTF0 | CAN_TSTAT_MTFNERR0 | CAN_TSTAT_TME0): + state = CAN_TRANSMIT_OK; + break; + /* mailbox1 transmit succeeded */ + case(CAN_TSTAT_MTF1 | CAN_TSTAT_MTFNERR1 | CAN_TSTAT_TME1): + state = CAN_TRANSMIT_OK; + break; + /* mailbox2 transmit succeeded */ + case(CAN_TSTAT_MTF2 | CAN_TSTAT_MTFNERR2 | CAN_TSTAT_TME2): + state = CAN_TRANSMIT_OK; + break; + /* transmit failed */ + default: + state = CAN_TRANSMIT_FAILED; + break; + } + return state; +} + +/*! + \brief stop CAN transmission + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] mailbox_number + only one parameter can be selected which is shown as below: + \arg CAN_MAILBOXx(x=0,1,2) + \param[out] none + \retval ErrStatus: ERROR or SUCCESS +*/ +ErrStatus can_transmission_stop(uint32_t can_periph, uint8_t mailbox_number) +{ + ErrStatus reval = SUCCESS; + /* timeout for CAN_TSTAT_MSTx bits */ + uint32_t timeout = CAN_TIMEOUT; + uint32_t reg_value0 = 0U; + uint32_t reg_value1 = 0U; + + /* get the status of transmit FIFO order */ + reg_value0 = CAN_CTL(can_periph) & CAN_CTL_TFO; + + if(CAN_MAILBOX0 == mailbox_number) { + reg_value1 = CAN_TSTAT(can_periph) & (CAN_TSTAT_TMLS0 | CAN_ALL_MAILBOX_EMPTY); + if((CAN_CTL_TFO == reg_value0) && (CAN_TSTAT_TMLS0 == reg_value1)){ + reval = ERROR; + } else { + CAN_TSTAT(can_periph) |= CAN_TSTAT_MST0; + while((CAN_TSTAT_MST0 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST0)) && (0U != timeout)) { + timeout--; + } + if(CAN_TSTAT_MST0 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST0)){ + reval = ERROR; + } + } + } else if(CAN_MAILBOX1 == mailbox_number) { + reg_value1 = CAN_TSTAT(can_periph) & (CAN_TSTAT_TMLS1 | CAN_ALL_MAILBOX_EMPTY); + if((CAN_CTL_TFO == reg_value0) && (CAN_TSTAT_TMLS1 == reg_value1)){ + reval = ERROR; + }else{ + CAN_TSTAT(can_periph) |= CAN_TSTAT_MST1; + while((CAN_TSTAT_MST1 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST1)) && (0U != timeout)) { + timeout--; + } + if(CAN_TSTAT_MST1 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST1)){ + reval = ERROR; + } + } + + } else if(CAN_MAILBOX2 == mailbox_number) { + reg_value1 = CAN_TSTAT(can_periph) & (CAN_TSTAT_TMLS2 | CAN_ALL_MAILBOX_EMPTY); + if((CAN_CTL_TFO == reg_value0) && (CAN_TSTAT_TMLS2 == reg_value1)){ + reval = ERROR; + }else{ + CAN_TSTAT(can_periph) |= CAN_TSTAT_MST2; + while((CAN_TSTAT_MST2 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST2)) && (0U != timeout)) { + timeout--; + } + if(CAN_TSTAT_MST2 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST2)){ + reval = ERROR; + } + } + } else { + /* illegal parameters */ + } + return reval; +} + +/*! + \brief CAN receive message + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] fifo_number + \arg CAN_FIFOx(x=0,1) + \param[out] receive_message: struct for CAN receive message + \arg rx_sfid: 0x00000000 - 0x000007FF + \arg rx_efid: 0x00000000 - 0x1FFFFFFF + \arg rx_ff: CAN_FF_STANDARD, CAN_FF_EXTENDED + \arg rx_ft: CAN_FT_DATA, CAN_FT_REMOTE + \arg rx_dlen: 0 - 8 (FD mode: 0 - 8, or 12, 16, 20, 24, 32, 48, 64) + \arg rx_data[]: 0x00 - 0xFF + \arg rx_fi: 0 - 27 + \retval none +*/ +void can_message_receive(uint32_t can_periph, uint8_t fifo_number, can_receive_message_struct *receive_message) +{ + volatile uint32_t p_temp; +#ifdef GD32E508 + uint32_t canfd_en = 0U; + uint32_t data_temp; + uint8_t canfd_recv_cnt = 0U; + uint8_t i; +#endif /* GD32E508 */ + + /* get the frame format */ + receive_message->rx_ff = (uint8_t)(CAN_RFIFOMI_FF & CAN_RFIFOMI(can_periph, fifo_number)); + if(CAN_FF_STANDARD == receive_message->rx_ff) { + /* get standard identifier */ + receive_message->rx_sfid = (uint32_t)(GET_RFIFOMI_SFID(CAN_RFIFOMI(can_periph, fifo_number))); + } else { + /* get extended identifier */ + receive_message->rx_efid = (uint32_t)(GET_RFIFOMI_EFID(CAN_RFIFOMI(can_periph, fifo_number))); + } + + /* get frame type */ + receive_message->rx_ft = (uint8_t)(CAN_RFIFOMI_FT & CAN_RFIFOMI(can_periph, fifo_number)); + /* filtering index */ + receive_message->rx_fi = (uint8_t)(GET_RFIFOMP_FI(CAN_RFIFOMP(can_periph, fifo_number))); + +#ifdef GD32E508 + receive_message->fd_flag = (uint8_t)((CAN_RFIFOMP_FDF & CAN_RFIFOMP(can_periph, fifo_number)) >> 7); + + canfd_en = CAN_FDCTL(can_periph) & CAN_FDCTL_FDEN; + if(!canfd_en) { + if(CAN_FDF_CLASSIC == receive_message->fd_flag) { + /* get recevie data length */ + receive_message->rx_dlen = (uint8_t)(GET_RFIFOMP_DLENC(CAN_RFIFOMP(can_periph, fifo_number))); + /* receive data */ + receive_message->rx_data[0] = (uint8_t)(GET_RFIFOMDATA0_DB0(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message->rx_data[1] = (uint8_t)(GET_RFIFOMDATA0_DB1(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message->rx_data[2] = (uint8_t)(GET_RFIFOMDATA0_DB2(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message->rx_data[3] = (uint8_t)(GET_RFIFOMDATA0_DB3(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message->rx_data[4] = (uint8_t)(GET_RFIFOMDATA1_DB4(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message->rx_data[5] = (uint8_t)(GET_RFIFOMDATA1_DB5(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message->rx_data[6] = (uint8_t)(GET_RFIFOMDATA1_DB6(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message->rx_data[7] = (uint8_t)(GET_RFIFOMDATA1_DB7(CAN_RFIFOMDATA1(can_periph, fifo_number))); + } else { + CAN_ERROR_HANDLE("CAN FD function disabled \r\n"); + } + } else { + /* check FD funciton has been enabled */ + /* get recevie data length */ + canfd_recv_cnt = (uint8_t)(GET_RFIFOMP_DLENC(CAN_RFIFOMP(can_periph, fifo_number))); + + if(canfd_recv_cnt <= 8U) { + /* set the data length */ + receive_message->rx_dlen = canfd_recv_cnt; + } else { + receive_message->rx_dlen = g_can_fdlength_table[canfd_recv_cnt - 9U]; + } + + receive_message->fd_brs = (uint8_t)((CAN_RFIFOMP(can_periph, fifo_number) & CAN_RFIFOMP_BRS) >> 5); + receive_message->fd_esi = (uint8_t)((CAN_RFIFOMP(can_periph, fifo_number) & CAN_RFIFOMP_ESI) >> 4); + + /* get the data */ + i = receive_message->rx_dlen / 4U; + + /* data length is 5-7 need receive 2 word */ + if((1U == i) && (4U != receive_message->rx_dlen)) { + i++; + } + p_temp = (uint32_t)(uint32_t)receive_message->rx_data; + if(0U == i) { + data_temp = CAN_RFIFOMDATA0(can_periph, fifo_number); + *(uint32_t *)p_temp = data_temp; + } else { + /* get the data by reading from CAN_RFIFOMDATA0 register*/ + for(; i > 0U; i--) { + data_temp = CAN_RFIFOMDATA0(can_periph, fifo_number); + *(uint32_t *)p_temp = data_temp; + p_temp = ((uint32_t)((uint32_t)p_temp + 4U)); + } + } + } +#else + /* get recevie data length */ + receive_message->rx_dlen = (uint8_t)(GET_RFIFOMP_DLENC(CAN_RFIFOMP(can_periph, fifo_number))); + + /* receive data */ + receive_message -> rx_data[0] = (uint8_t)(GET_RFIFOMDATA0_DB0(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message -> rx_data[1] = (uint8_t)(GET_RFIFOMDATA0_DB1(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message -> rx_data[2] = (uint8_t)(GET_RFIFOMDATA0_DB2(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message -> rx_data[3] = (uint8_t)(GET_RFIFOMDATA0_DB3(CAN_RFIFOMDATA0(can_periph, fifo_number))); + receive_message -> rx_data[4] = (uint8_t)(GET_RFIFOMDATA1_DB4(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message -> rx_data[5] = (uint8_t)(GET_RFIFOMDATA1_DB5(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message -> rx_data[6] = (uint8_t)(GET_RFIFOMDATA1_DB6(CAN_RFIFOMDATA1(can_periph, fifo_number))); + receive_message -> rx_data[7] = (uint8_t)(GET_RFIFOMDATA1_DB7(CAN_RFIFOMDATA1(can_periph, fifo_number))); + +#endif /* GD32E508 */ + /* release FIFO */ + if(CAN_FIFO0 == fifo_number) { + CAN_RFIFO0(can_periph) |= CAN_RFIFO0_RFD0; + } else { + CAN_RFIFO1(can_periph) |= CAN_RFIFO1_RFD1; + } +} + +/*! + \brief release FIFO0 + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] fifo_number + only one parameter can be selected which is shown as below: + \arg CAN_FIFOx(x=0,1) + \param[out] none + \retval none +*/ +void can_fifo_release(uint32_t can_periph, uint8_t fifo_number) +{ + if(CAN_FIFO0 == fifo_number) { + CAN_RFIFO0(can_periph) |= CAN_RFIFO0_RFD0; + } else if(CAN_FIFO1 == fifo_number) { + CAN_RFIFO1(can_periph) |= CAN_RFIFO1_RFD1; + } else { + /* illegal parameters */ + CAN_ERROR_HANDLE("CAN FIFO NUM is invalid \r\n"); + } +} + +/*! + \brief CAN receive message length + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] fifo_number + only one parameter can be selected which is shown as below: + \arg CAN_FIFOx(x=0,1) + \param[out] none + \retval message length +*/ +uint8_t can_receive_message_length_get(uint32_t can_periph, uint8_t fifo_number) +{ + uint8_t val = 0U; + + if(CAN_FIFO0 == fifo_number) { + /* FIFO0 */ + val = (uint8_t)(CAN_RFIFO0(can_periph) & CAN_RFIF_RFL_MASK); + } else if(CAN_FIFO1 == fifo_number) { + /* FIFO1 */ + val = (uint8_t)(CAN_RFIFO1(can_periph) & CAN_RFIF_RFL_MASK); + } else { + /* illegal parameters */ + } + return val; +} + +/*! + \brief set CAN working mode + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] can_working_mode + only one parameter can be selected which is shown as below: + \arg CAN_MODE_INITIALIZE + \arg CAN_MODE_NORMAL + \arg CAN_MODE_SLEEP + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus can_working_mode_set(uint32_t can_periph, uint8_t working_mode) +{ + ErrStatus flag = ERROR; + /* timeout for IWS or also for SLPWS bits */ + uint32_t timeout = CAN_TIMEOUT; + + if(CAN_MODE_INITIALIZE == working_mode) { + /* disable sleep mode */ + CAN_CTL(can_periph) &= (~(uint32_t)CAN_CTL_SLPWMOD); + /* set initialize mode */ + CAN_CTL(can_periph) |= (uint8_t)CAN_CTL_IWMOD; + /* wait the acknowledge */ + while((CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) && (0U != timeout)) { + timeout--; + } + if(CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) { + flag = ERROR; + } else { + flag = SUCCESS; + } + } else if(CAN_MODE_NORMAL == working_mode) { + /* enter normal mode */ + CAN_CTL(can_periph) &= ~(uint32_t)(CAN_CTL_SLPWMOD | CAN_CTL_IWMOD); + /* wait the acknowledge */ + while((0U != (CAN_STAT(can_periph) & (CAN_STAT_IWS | CAN_STAT_SLPWS))) && (0U != timeout)) { + timeout--; + } + if(0U != (CAN_STAT(can_periph) & (CAN_STAT_IWS | CAN_STAT_SLPWS))) { + flag = ERROR; + } else { + flag = SUCCESS; + } + } else if(CAN_MODE_SLEEP == working_mode) { + /* disable initialize mode */ + CAN_CTL(can_periph) &= (~(uint32_t)CAN_CTL_IWMOD); + /* set sleep mode */ + CAN_CTL(can_periph) |= (uint8_t)CAN_CTL_SLPWMOD; + /* wait the acknowledge */ + while((CAN_STAT_SLPWS != (CAN_STAT(can_periph) & CAN_STAT_SLPWS)) && (0U != timeout)) { + timeout--; + } + if(CAN_STAT_SLPWS != (CAN_STAT(can_periph) & CAN_STAT_SLPWS)) { + flag = ERROR; + } else { + flag = SUCCESS; + } + } else { + flag = ERROR; + } + return flag; +} + +/*! + \brief wake up CAN + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus can_wakeup(uint32_t can_periph) +{ + ErrStatus flag = ERROR; + uint32_t timeout = CAN_TIMEOUT; + + /* wakeup */ + CAN_CTL(can_periph) &= ~CAN_CTL_SLPWMOD; + + while((0U != (CAN_STAT(can_periph) & CAN_STAT_SLPWS)) && (0x00U != timeout)) { + timeout--; + } + /* check state */ + if(0U != (CAN_STAT(can_periph) & CAN_STAT_SLPWS)) { + flag = ERROR; + } else { + flag = SUCCESS; + } + return flag; +} + +/*! + \brief get CAN error type + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval can_error_enum + \arg CAN_ERROR_NONE: no error + \arg CAN_ERROR_FILL: fill error + \arg CAN_ERROR_FORMATE: format error + \arg CAN_ERROR_ACK: ACK error + \arg CAN_ERROR_BITRECESSIVE: bit recessive + \arg CAN_ERROR_BITDOMINANTER: bit dominant error + \arg CAN_ERROR_CRC: CRC error + \arg CAN_ERROR_SOFTWARECFG: software configure +*/ +can_error_enum can_error_get(uint32_t can_periph) +{ + can_error_enum error; + error = CAN_ERROR_NONE; + + /* get error type */ + error = (can_error_enum)(GET_ERR_ERRN(CAN_ERR(can_periph))); + return error; +} + +/*! + \brief get CAN receive error number + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL + \param[out] none + \retval error number +*/ +uint8_t can_receive_error_number_get(uint32_t can_periph) +{ + uint8_t val; + + /* get error count */ + val = (uint8_t)(GET_ERR_RECNT(CAN_ERR(can_periph))); + return val; +} + +/*! + \brief get CAN transmit error number + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[out] none + \retval error number +*/ +uint8_t can_transmit_error_number_get(uint32_t can_periph) +{ + uint8_t val; + + val = (uint8_t)(GET_ERR_TECNT(CAN_ERR(can_periph))); + return val; +} + +/*! + \brief enable CAN interrupt + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] interrupt + one or more parameters can be selected which are shown as below: + \arg CAN_INT_TME: transmit mailbox empty interrupt enable + \arg CAN_INT_RFNE0: receive FIFO0 not empty interrupt enable + \arg CAN_INT_RFF0: receive FIFO0 full interrupt enable + \arg CAN_INT_RFO0: receive FIFO0 overfull interrupt enable + \arg CAN_INT_RFNE1: receive FIFO1 not empty interrupt enable + \arg CAN_INT_RFF1: receive FIFO1 full interrupt enable + \arg CAN_INT_RFO1: receive FIFO1 overfull interrupt enable + \arg CAN_INT_WERR: warning error interrupt enable + \arg CAN_INT_PERR: passive error interrupt enable + \arg CAN_INT_BO: bus-off interrupt enable + \arg CAN_INT_ERRN: error number interrupt enable + \arg CAN_INT_ERR: error interrupt enable + \arg CAN_INT_WU: wakeup interrupt enable + \arg CAN_INT_SLPW: sleep working interrupt enable + \param[out] none + \retval none +*/ +void can_interrupt_enable(uint32_t can_periph, uint32_t interrupt) +{ + CAN_INTEN(can_periph) |= interrupt; +} + +/*! + \brief disable CAN interrupt + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] interrupt + one or more parameters can be selected which are shown as below: + \arg CAN_INT_TME: transmit mailbox empty interrupt enable + \arg CAN_INT_RFNE0: receive FIFO0 not empty interrupt enable + \arg CAN_INT_RFF0: receive FIFO0 full interrupt enable + \arg CAN_INT_RFO0: receive FIFO0 overfull interrupt enable + \arg CAN_INT_RFNE1: receive FIFO1 not empty interrupt enable + \arg CAN_INT_RFF1: receive FIFO1 full interrupt enable + \arg CAN_INT_RFO1: receive FIFO1 overfull interrupt enable + \arg CAN_INT_WERR: warning error interrupt enable + \arg CAN_INT_PERR: passive error interrupt enable + \arg CAN_INT_BO: bus-off interrupt enable + \arg CAN_INT_ERRN: error number interrupt enable + \arg CAN_INT_ERR: error interrupt enable + \arg CAN_INT_WU: wakeup interrupt enable + \arg CAN_INT_SLPW: sleep working interrupt enable + \param[out] none + \retval none +*/ +void can_interrupt_disable(uint32_t can_periph, uint32_t interrupt) +{ + CAN_INTEN(can_periph) &= ~interrupt; +} + +/*! + \brief get CAN flag state + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] flag: CAN flags, refer to can_flag_enum + only one parameter can be selected which is shown as below: + \arg CAN_FLAG_RXL: RX level + \arg CAN_FLAG_LASTRX: last sample value of RX pin + \arg CAN_FLAG_RS: receiving state + \arg CAN_FLAG_TS: transmitting state + \arg CAN_FLAG_SLPIF: status change flag of entering sleep working mode + \arg CAN_FLAG_WUIF: status change flag of wakeup from sleep working mode + \arg CAN_FLAG_ERRIF: error flag + \arg CAN_FLAG_SLPWS: sleep working state + \arg CAN_FLAG_IWS: initial working state + \arg CAN_FLAG_TMLS2: transmit mailbox 2 last sending in Tx FIFO + \arg CAN_FLAG_TMLS1: transmit mailbox 1 last sending in Tx FIFO + \arg CAN_FLAG_TMLS0: transmit mailbox 0 last sending in Tx FIFO + \arg CAN_FLAG_TME2: transmit mailbox 2 empty + \arg CAN_FLAG_TME1: transmit mailbox 1 empty + \arg CAN_FLAG_TME0: transmit mailbox 0 empty + \arg CAN_FLAG_MTE2: mailbox 2 transmit error + \arg CAN_FLAG_MTE1: mailbox 1 transmit error + \arg CAN_FLAG_MTE0: mailbox 0 transmit error + \arg CAN_FLAG_MAL2: mailbox 2 arbitration lost + \arg CAN_FLAG_MAL1: mailbox 1 arbitration lost + \arg CAN_FLAG_MAL0: mailbox 0 arbitration lost + \arg CAN_FLAG_MTFNERR2: mailbox 2 transmit finished with no error + \arg CAN_FLAG_MTFNERR1: mailbox 1 transmit finished with no error + \arg CAN_FLAG_MTFNERR0: mailbox 0 transmit finished with no error + \arg CAN_FLAG_MTF2: mailbox 2 transmit finished + \arg CAN_FLAG_MTF1: mailbox 1 transmit finished + \arg CAN_FLAG_MTF0: mailbox 0 transmit finished + \arg CAN_FLAG_RFO0: receive FIFO0 overfull + \arg CAN_FLAG_RFF0: receive FIFO0 full + \arg CAN_FLAG_RFO1: receive FIFO1 overfull + \arg CAN_FLAG_RFF1: receive FIFO1 full + \arg CAN_FLAG_BOERR: bus-off error + \arg CAN_FLAG_PERR: passive error + \arg CAN_FLAG_WERR: warning error + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus can_flag_get(uint32_t can_periph, can_flag_enum flag) +{ + /* get flag and interrupt enable state */ + if(RESET != (CAN_REG_VAL(can_periph, flag) & BIT(CAN_BIT_POS(flag)))) { + return SET; + } else { + return RESET; + } +} + +/*! + \brief clear CAN flag state + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] flag: CAN flags, refer to can_flag_enum + only one parameter can be selected which is shown as below: + \arg CAN_FLAG_SLPIF: status change flag of entering sleep working mode + \arg CAN_FLAG_WUIF: status change flag of wakeup from sleep working mode + \arg CAN_FLAG_ERRIF: error flag + \arg CAN_FLAG_MTE2: mailbox 2 transmit error + \arg CAN_FLAG_MTE1: mailbox 1 transmit error + \arg CAN_FLAG_MTE0: mailbox 0 transmit error + \arg CAN_FLAG_MAL2: mailbox 2 arbitration lost + \arg CAN_FLAG_MAL1: mailbox 1 arbitration lost + \arg CAN_FLAG_MAL0: mailbox 0 arbitration lost + \arg CAN_FLAG_MTFNERR2: mailbox 2 transmit finished with no error + \arg CAN_FLAG_MTFNERR1: mailbox 1 transmit finished with no error + \arg CAN_FLAG_MTFNERR0: mailbox 0 transmit finished with no error + \arg CAN_FLAG_MTF2: mailbox 2 transmit finished + \arg CAN_FLAG_MTF1: mailbox 1 transmit finished + \arg CAN_FLAG_MTF0: mailbox 0 transmit finished + \arg CAN_FLAG_RFO0: receive FIFO0 overfull + \arg CAN_FLAG_RFF0: receive FIFO0 full + \arg CAN_FLAG_RFO1: receive FIFO1 overfull + \arg CAN_FLAG_RFF1: receive FIFO1 full + \param[out] none + \retval none +*/ +void can_flag_clear(uint32_t can_periph, can_flag_enum flag) +{ + CAN_REG_VAL(can_periph, flag) |= BIT(CAN_BIT_POS(flag)); +} + +/*! + \brief get CAN interrupt flag state + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] int_flag: CAN interrupt flags, refer to can_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg CAN_INT_FLAG_SLPIF: status change interrupt flag of sleep working mode entering + \arg CAN_INT_FLAG_WUIF: status change interrupt flag of wakeup from sleep working mode + \arg CAN_INT_FLAG_ERRIF: error interrupt flag + \arg CAN_INT_FLAG_MTF2: mailbox 2 transmit finished interrupt flag + \arg CAN_INT_FLAG_MTF1: mailbox 1 transmit finished interrupt flag + \arg CAN_INT_FLAG_MTF0: mailbox 0 transmit finished interrupt flag + \arg CAN_INT_FLAG_RFO0: receive FIFO0 overfull interrupt flag + \arg CAN_INT_FLAG_RFF0: receive FIFO0 full interrupt flag + \arg CAN_INT_FLAG_RFL0: receive FIFO0 not empty interrupt flag + \arg CAN_INT_FLAG_RFO1: receive FIFO1 overfull interrupt flag + \arg CAN_INT_FLAG_RFF1: receive FIFO1 full interrupt flag + \arg CAN_INT_FLAG_RFL1: receive FIFO1 not empty interrupt flag + \arg CAN_INT_FLAG_ERRN: error number interrupt flag + \arg CAN_INT_FLAG_BOERR: bus-off error interrupt flag + \arg CAN_INT_FLAG_PERR: passive error interrupt flag + \arg CAN_INT_FLAG_WERR: warning error interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus can_interrupt_flag_get(uint32_t can_periph, can_interrupt_flag_enum int_flag) +{ + uint32_t ret1 = RESET; + uint32_t ret2 = RESET; + + /* get the staus of interrupt flag */ + if(int_flag == CAN_INT_FLAG_RFL0) { + ret1 = can_receive_message_length_get(can_periph, CAN_FIFO0); + } else if(int_flag == CAN_INT_FLAG_RFL1) { + ret1 = can_receive_message_length_get(can_periph, CAN_FIFO1); + } else if(int_flag == CAN_INT_FLAG_ERRN) { + ret1 = can_error_get(can_periph); + } else { + ret1 = CAN_REG_VALS(can_periph, int_flag) & BIT(CAN_BIT_POS0(int_flag)); + } + /* get the staus of interrupt enale bit */ + ret2 = CAN_INTEN(can_periph) & BIT(CAN_BIT_POS1(int_flag)); + if(ret1 && ret2) { + return SET; + } else { + return RESET; + } +} + +/*! + \brief clear CAN interrupt flag state + \param[in] can_periph + \arg CANx(x=0,1,2),the CAN2 only for GD32E50X_CL and GD32E508 + \param[in] int_flag: CAN interrupt flags, refer to can_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg CAN_INT_FLAG_SLPIF: status change interrupt flag of sleep working mode entering + \arg CAN_INT_FLAG_WUIF: status change interrupt flag of wakeup from sleep working mode + \arg CAN_INT_FLAG_ERRIF: error interrupt flag + \arg CAN_INT_FLAG_MTF2: mailbox 2 transmit finished interrupt flag + \arg CAN_INT_FLAG_MTF1: mailbox 1 transmit finished interrupt flag + \arg CAN_INT_FLAG_MTF0: mailbox 0 transmit finished interrupt flag + \arg CAN_INT_FLAG_RFO0: receive FIFO0 overfull interrupt flag + \arg CAN_INT_FLAG_RFF0: receive FIFO0 full interrupt flag + \arg CAN_INT_FLAG_RFO1: receive FIFO1 overfull interrupt flag + \arg CAN_INT_FLAG_RFF1: receive FIFO1 full interrupt flag + \param[out] none + \retval none +*/ +void can_interrupt_flag_clear(uint32_t can_periph, can_interrupt_flag_enum int_flag) +{ + CAN_REG_VALS(can_periph, int_flag) |= BIT(CAN_BIT_POS0(int_flag)); +} +#endif /* GD32EPRT */ diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_crc.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_crc.c new file mode 100644 index 0000000..ea8a401 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_crc.c @@ -0,0 +1,239 @@ +/*! + \file gd32e50x_crc.c + \brief CRC driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_crc.h" + +/*! + \brief deinitialize CRC calculation unit + \param[in] none + \param[out] none + \retval none +*/ +void crc_deinit(void) +{ + CRC_IDATA = (uint32_t)0xFFFFFFFFU; + CRC_DATA = (uint32_t)0xFFFFFFFFU; + CRC_FDATA = (uint32_t)0x00000000U; + CRC_POLY = (uint32_t)0x04C11DB7U; + CRC_CTL = CRC_CTL_RST; +} + +/*! + \brief reset data register to the value of initializaiton data register + \param[in] none + \param[out] none + \retval none +*/ +void crc_data_register_reset(void) +{ + CRC_CTL |= (uint32_t)CRC_CTL_RST; +} + +/*! + \brief enable the reverse operation of output data + \param[in] none + \param[out] none + \retval none +*/ +void crc_reverse_output_data_enable(void) +{ + CRC_CTL &= (uint32_t)(~ CRC_CTL_REV_O); + CRC_CTL |= (uint32_t)CRC_CTL_REV_O; +} + +/*! + \brief disable the reverse operation of output data + \param[in] none + \param[out] none + \retval none +*/ +void crc_reverse_output_data_disable(void) +{ + CRC_CTL &= (uint32_t)(~ CRC_CTL_REV_O); +} + +/*! + \brief configure the CRC input data function + \param[in] data_reverse: specify input data reverse function + only one parameter can be selected which is shown as below: + \arg CRC_INPUT_DATA_NOT: input data is not reversed + \arg CRC_INPUT_DATA_BYTE: input data is reversed on 8 bits + \arg CRC_INPUT_DATA_HALFWORD: input data is reversed on 16 bits + \arg CRC_INPUT_DATA_WORD: input data is reversed on 32 bits + \param[out] none + \retval none +*/ +void crc_input_data_reverse_config(uint32_t data_reverse) +{ + CRC_CTL &= (uint32_t)(~CRC_CTL_REV_I); + CRC_CTL |= (uint32_t)data_reverse; +} + +/*! + \brief read the data register + \param[in] none + \param[out] none + \retval 32-bit value of the data register +*/ +uint32_t crc_data_register_read(void) +{ + uint32_t data; + data = CRC_DATA; + return (data); +} + +/*! + \brief read the free data register + \param[in] none + \param[out] none + \retval 8-bit value of the free data register +*/ +uint8_t crc_free_data_register_read(void) +{ + uint8_t fdata; + fdata = (uint8_t)CRC_FDATA; + return (fdata); +} + +/*! + \brief write the free data register + \param[in] free_data: specify 8-bit data + \param[out] none + \retval none +*/ +void crc_free_data_register_write(uint8_t free_data) +{ + CRC_FDATA = (uint32_t)free_data; +} + +/*! + \brief write the initializaiton data register + \param[in] init_data: specify 32-bit data + \param[out] none + \retval none +*/ +void crc_init_data_register_write(uint32_t init_data) +{ + CRC_IDATA = (uint32_t)init_data; +} + +/*! + \brief configure the CRC size of polynomial function + \param[in] poly_size: size of polynomial + only one parameter can be selected which is shown as below: + \arg CRC_CTL_PS_32: 32-bit polynomial for CRC calculation + \arg CRC_CTL_PS_16: 16-bit polynomial for CRC calculation + \arg CRC_CTL_PS_8: 8-bit polynomial for CRC calculation + \arg CRC_CTL_PS_7: 7-bit polynomial for CRC calculation + \param[out] none + \retval none +*/ +void crc_polynomial_size_set(uint32_t poly_size) +{ + CRC_CTL &= (uint32_t)(~(CRC_CTL_PS)); + CRC_CTL |= (uint32_t)poly_size; +} + +/*! + \brief configure the CRC polynomial value function + \param[in] poly: configurable polynomial value + \param[out] none + \retval none +*/ +void crc_polynomial_set(uint32_t poly) +{ + CRC_POLY &= (uint32_t)(~CRC_POLY_POLY); + CRC_POLY = poly; +} + +/*! + \brief CRC calculate single data + \param[in] sdata: specify input data data + \param[in] data_format: input data format + only one parameter can be selected which is shown as below: + \arg INPUT_FORMAT_WORD: input data in word format + \arg INPUT_FORMAT_HALFWORD: input data in half-word format + \arg INPUT_FORMAT_BYTE: input data in byte format + \param[out] none + \retval CRC calculate value +*/ +uint32_t crc_single_data_calculate(uint32_t sdata, uint8_t data_format) +{ + if(INPUT_FORMAT_WORD == data_format){ + REG32(CRC) = sdata; + }else if(INPUT_FORMAT_HALFWORD == data_format){ + REG16(CRC) = (uint16_t)sdata; + }else{ + REG8(CRC) = (uint8_t)sdata; + } + + return(CRC_DATA); +} + +/*! + \brief CRC calculate a data array + \param[in] array: pointer to the input data array + \param[in] size: size of the array + \param[in] data_format: input data format + only one parameter can be selected which is shown as below: + \arg INPUT_FORMAT_WORD: input data in word format + \arg INPUT_FORMAT_HALFWORD: input data in half-word format + \arg INPUT_FORMAT_BYTE: input data in byte format + \param[out] none + \retval CRC calculate value +*/ +uint32_t crc_block_data_calculate(void *array, uint32_t size, uint8_t data_format) +{ + uint32_t index; + uint32_t data = (uint32_t)array; + + if(INPUT_FORMAT_WORD == data_format) { + for(index = 0U; index < size; index++) { + REG32(CRC) = *(uint32_t *)data; + data += 4U; + } + } else if(INPUT_FORMAT_HALFWORD == data_format) { + for(index = 0U; index < size; index++) { + REG16(CRC) = *(uint16_t *)data; + data += 2U; + } + } else { + for(index = 0U; index < size; index++) { + REG8(CRC) = *(uint8_t *)data; + data += 1U; + } + } + + return (CRC_DATA); +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_crc.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_crc.c.base@1.5.0 new file mode 100644 index 0000000..ea8a401 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_crc.c.base@1.5.0 @@ -0,0 +1,239 @@ +/*! + \file gd32e50x_crc.c + \brief CRC driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_crc.h" + +/*! + \brief deinitialize CRC calculation unit + \param[in] none + \param[out] none + \retval none +*/ +void crc_deinit(void) +{ + CRC_IDATA = (uint32_t)0xFFFFFFFFU; + CRC_DATA = (uint32_t)0xFFFFFFFFU; + CRC_FDATA = (uint32_t)0x00000000U; + CRC_POLY = (uint32_t)0x04C11DB7U; + CRC_CTL = CRC_CTL_RST; +} + +/*! + \brief reset data register to the value of initializaiton data register + \param[in] none + \param[out] none + \retval none +*/ +void crc_data_register_reset(void) +{ + CRC_CTL |= (uint32_t)CRC_CTL_RST; +} + +/*! + \brief enable the reverse operation of output data + \param[in] none + \param[out] none + \retval none +*/ +void crc_reverse_output_data_enable(void) +{ + CRC_CTL &= (uint32_t)(~ CRC_CTL_REV_O); + CRC_CTL |= (uint32_t)CRC_CTL_REV_O; +} + +/*! + \brief disable the reverse operation of output data + \param[in] none + \param[out] none + \retval none +*/ +void crc_reverse_output_data_disable(void) +{ + CRC_CTL &= (uint32_t)(~ CRC_CTL_REV_O); +} + +/*! + \brief configure the CRC input data function + \param[in] data_reverse: specify input data reverse function + only one parameter can be selected which is shown as below: + \arg CRC_INPUT_DATA_NOT: input data is not reversed + \arg CRC_INPUT_DATA_BYTE: input data is reversed on 8 bits + \arg CRC_INPUT_DATA_HALFWORD: input data is reversed on 16 bits + \arg CRC_INPUT_DATA_WORD: input data is reversed on 32 bits + \param[out] none + \retval none +*/ +void crc_input_data_reverse_config(uint32_t data_reverse) +{ + CRC_CTL &= (uint32_t)(~CRC_CTL_REV_I); + CRC_CTL |= (uint32_t)data_reverse; +} + +/*! + \brief read the data register + \param[in] none + \param[out] none + \retval 32-bit value of the data register +*/ +uint32_t crc_data_register_read(void) +{ + uint32_t data; + data = CRC_DATA; + return (data); +} + +/*! + \brief read the free data register + \param[in] none + \param[out] none + \retval 8-bit value of the free data register +*/ +uint8_t crc_free_data_register_read(void) +{ + uint8_t fdata; + fdata = (uint8_t)CRC_FDATA; + return (fdata); +} + +/*! + \brief write the free data register + \param[in] free_data: specify 8-bit data + \param[out] none + \retval none +*/ +void crc_free_data_register_write(uint8_t free_data) +{ + CRC_FDATA = (uint32_t)free_data; +} + +/*! + \brief write the initializaiton data register + \param[in] init_data: specify 32-bit data + \param[out] none + \retval none +*/ +void crc_init_data_register_write(uint32_t init_data) +{ + CRC_IDATA = (uint32_t)init_data; +} + +/*! + \brief configure the CRC size of polynomial function + \param[in] poly_size: size of polynomial + only one parameter can be selected which is shown as below: + \arg CRC_CTL_PS_32: 32-bit polynomial for CRC calculation + \arg CRC_CTL_PS_16: 16-bit polynomial for CRC calculation + \arg CRC_CTL_PS_8: 8-bit polynomial for CRC calculation + \arg CRC_CTL_PS_7: 7-bit polynomial for CRC calculation + \param[out] none + \retval none +*/ +void crc_polynomial_size_set(uint32_t poly_size) +{ + CRC_CTL &= (uint32_t)(~(CRC_CTL_PS)); + CRC_CTL |= (uint32_t)poly_size; +} + +/*! + \brief configure the CRC polynomial value function + \param[in] poly: configurable polynomial value + \param[out] none + \retval none +*/ +void crc_polynomial_set(uint32_t poly) +{ + CRC_POLY &= (uint32_t)(~CRC_POLY_POLY); + CRC_POLY = poly; +} + +/*! + \brief CRC calculate single data + \param[in] sdata: specify input data data + \param[in] data_format: input data format + only one parameter can be selected which is shown as below: + \arg INPUT_FORMAT_WORD: input data in word format + \arg INPUT_FORMAT_HALFWORD: input data in half-word format + \arg INPUT_FORMAT_BYTE: input data in byte format + \param[out] none + \retval CRC calculate value +*/ +uint32_t crc_single_data_calculate(uint32_t sdata, uint8_t data_format) +{ + if(INPUT_FORMAT_WORD == data_format){ + REG32(CRC) = sdata; + }else if(INPUT_FORMAT_HALFWORD == data_format){ + REG16(CRC) = (uint16_t)sdata; + }else{ + REG8(CRC) = (uint8_t)sdata; + } + + return(CRC_DATA); +} + +/*! + \brief CRC calculate a data array + \param[in] array: pointer to the input data array + \param[in] size: size of the array + \param[in] data_format: input data format + only one parameter can be selected which is shown as below: + \arg INPUT_FORMAT_WORD: input data in word format + \arg INPUT_FORMAT_HALFWORD: input data in half-word format + \arg INPUT_FORMAT_BYTE: input data in byte format + \param[out] none + \retval CRC calculate value +*/ +uint32_t crc_block_data_calculate(void *array, uint32_t size, uint8_t data_format) +{ + uint32_t index; + uint32_t data = (uint32_t)array; + + if(INPUT_FORMAT_WORD == data_format) { + for(index = 0U; index < size; index++) { + REG32(CRC) = *(uint32_t *)data; + data += 4U; + } + } else if(INPUT_FORMAT_HALFWORD == data_format) { + for(index = 0U; index < size; index++) { + REG16(CRC) = *(uint16_t *)data; + data += 2U; + } + } else { + for(index = 0U; index < size; index++) { + REG8(CRC) = *(uint8_t *)data; + data += 1U; + } + } + + return (CRC_DATA); +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dbg.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dbg.c new file mode 100644 index 0000000..629de2e --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dbg.c @@ -0,0 +1,148 @@ +/*! + \file gd32e50x_dbg.c + \brief DBG driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_dbg.h" + +#define DBG_RESET_VAL ((uint32_t)0x00000000U) /*!< DBG reset value */ + +/*! + \brief deinitialize the DBG + \param[in] none + \param[out] none + \retval none +*/ +void dbg_deinit(void) +{ + DBG_CTL = DBG_RESET_VAL; +} + +/*! + \brief read DBG_ID code register + \param[in] none + \param[out] none + \retval DBG_ID code +*/ +uint32_t dbg_id_get(void) +{ + return DBG_ID; +} + +/*! + \brief enable low power behavior when the mcu is in debug mode + \param[in] dbg_low_power: + one or more parameters can be selected which are shown as below: + \arg DBG_LOW_POWER_SLEEP: keep debugger connection during sleep mode + \arg DBG_LOW_POWER_DEEPSLEEP: keep debugger connection during deepsleep mode + \arg DBG_LOW_POWER_STANDBY: keep debugger connection during standby mode + \param[out] none + \retval none +*/ +void dbg_low_power_enable(uint32_t dbg_low_power) +{ + DBG_CTL |= dbg_low_power; +} + +/*! + \brief disable low power behavior when the mcu is in debug mode + \param[in] dbg_low_power: + one or more parameters can be selected which are shown as below: + \arg DBG_LOW_POWER_SLEEP: do not keep debugger connection during sleep mode + \arg DBG_LOW_POWER_DEEPSLEEP: do not keep debugger connection during deepsleep mode + \arg DBG_LOW_POWER_STANDBY: do not keep debugger connection during standby mode + \param[out] none + \retval none +*/ +void dbg_low_power_disable(uint32_t dbg_low_power) +{ + DBG_CTL &= ~dbg_low_power; +} + +/*! + \brief enable peripheral behavior when the mcu is in debug mode + \param[in] dbg_periph: refer to dbg_periph_enum + only one parameter can be selected which is shown as below: + \arg DBG_FWDGT_HOLD : hold FWDGT counter when core is halted + \arg DBG_WWDGT_HOLD : hold WWDGT counter when core is halted + \arg DBG_CANx_HOLD (x=0,1,2 CAN2 is only available for CL series, CANx are not available for GD32EPRT series): hold CANx receive register counter when core is halted + \arg DBG_I2Cx_HOLD (x=0,1,2): hold I2Cx smbus timeout when core is halted + \arg DBG_TIMERx_HOLD (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13, TIMER8..13 are not available for GD32EPRT series): hold TIMERx counter when core is halted + \arg DBG_SHRTIMER_HOLD : hold SHRTIMER counter when core is halted, except for GD32EPRT series + \param[out] none + \retval none +*/ +void dbg_periph_enable(dbg_periph_enum dbg_periph) +{ + DBG_REG_VAL(dbg_periph) |= BIT(DBG_BIT_POS(dbg_periph)); +} + +/*! + \brief disable peripheral behavior when the mcu is in debug mode + \param[in] dbg_periph: refer to dbg_periph_enum + only one parameter can be selected which is shown as below: + \arg DBG_FWDGT_HOLD : hold FWDGT counter when core is halted + \arg DBG_WWDGT_HOLD : hold WWDGT counter when core is halted + \arg DBG_CANx_HOLD (x=0,1,2 CAN2 is only available for CL series, CANx are not available for GD32EPRT series): hold CANx receive register counter when core is halted + \arg DBG_I2Cx_HOLD (x=0,1,2): hold I2Cx smbus timeout when core is halted + \arg DBG_TIMERx_HOLD (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for GD32EPRT series): hold TIMERx counter when core is halted + \arg DBG_SHRTIMER_HOLD : hold SHRTIMER counter when core is halted, except for GD32EPRT series + \param[out] none + \retval none +*/ +void dbg_periph_disable(dbg_periph_enum dbg_periph) +{ + DBG_REG_VAL(dbg_periph) &= ~BIT(DBG_BIT_POS(dbg_periph)); +} + +/*! + \brief enable trace pin assignment + \param[in] none + \param[out] none + \retval none +*/ +void dbg_trace_pin_enable(void) +{ + DBG_CTL |= DBG_CTL_TRACE_IOEN; +} + +/*! + \brief disable trace pin assignment + \param[in] none + \param[out] none + \retval none +*/ +void dbg_trace_pin_disable(void) +{ + DBG_CTL &= ~DBG_CTL_TRACE_IOEN; +} + diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dbg.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dbg.c.base@1.5.0 new file mode 100644 index 0000000..629de2e --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dbg.c.base@1.5.0 @@ -0,0 +1,148 @@ +/*! + \file gd32e50x_dbg.c + \brief DBG driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_dbg.h" + +#define DBG_RESET_VAL ((uint32_t)0x00000000U) /*!< DBG reset value */ + +/*! + \brief deinitialize the DBG + \param[in] none + \param[out] none + \retval none +*/ +void dbg_deinit(void) +{ + DBG_CTL = DBG_RESET_VAL; +} + +/*! + \brief read DBG_ID code register + \param[in] none + \param[out] none + \retval DBG_ID code +*/ +uint32_t dbg_id_get(void) +{ + return DBG_ID; +} + +/*! + \brief enable low power behavior when the mcu is in debug mode + \param[in] dbg_low_power: + one or more parameters can be selected which are shown as below: + \arg DBG_LOW_POWER_SLEEP: keep debugger connection during sleep mode + \arg DBG_LOW_POWER_DEEPSLEEP: keep debugger connection during deepsleep mode + \arg DBG_LOW_POWER_STANDBY: keep debugger connection during standby mode + \param[out] none + \retval none +*/ +void dbg_low_power_enable(uint32_t dbg_low_power) +{ + DBG_CTL |= dbg_low_power; +} + +/*! + \brief disable low power behavior when the mcu is in debug mode + \param[in] dbg_low_power: + one or more parameters can be selected which are shown as below: + \arg DBG_LOW_POWER_SLEEP: do not keep debugger connection during sleep mode + \arg DBG_LOW_POWER_DEEPSLEEP: do not keep debugger connection during deepsleep mode + \arg DBG_LOW_POWER_STANDBY: do not keep debugger connection during standby mode + \param[out] none + \retval none +*/ +void dbg_low_power_disable(uint32_t dbg_low_power) +{ + DBG_CTL &= ~dbg_low_power; +} + +/*! + \brief enable peripheral behavior when the mcu is in debug mode + \param[in] dbg_periph: refer to dbg_periph_enum + only one parameter can be selected which is shown as below: + \arg DBG_FWDGT_HOLD : hold FWDGT counter when core is halted + \arg DBG_WWDGT_HOLD : hold WWDGT counter when core is halted + \arg DBG_CANx_HOLD (x=0,1,2 CAN2 is only available for CL series, CANx are not available for GD32EPRT series): hold CANx receive register counter when core is halted + \arg DBG_I2Cx_HOLD (x=0,1,2): hold I2Cx smbus timeout when core is halted + \arg DBG_TIMERx_HOLD (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13, TIMER8..13 are not available for GD32EPRT series): hold TIMERx counter when core is halted + \arg DBG_SHRTIMER_HOLD : hold SHRTIMER counter when core is halted, except for GD32EPRT series + \param[out] none + \retval none +*/ +void dbg_periph_enable(dbg_periph_enum dbg_periph) +{ + DBG_REG_VAL(dbg_periph) |= BIT(DBG_BIT_POS(dbg_periph)); +} + +/*! + \brief disable peripheral behavior when the mcu is in debug mode + \param[in] dbg_periph: refer to dbg_periph_enum + only one parameter can be selected which is shown as below: + \arg DBG_FWDGT_HOLD : hold FWDGT counter when core is halted + \arg DBG_WWDGT_HOLD : hold WWDGT counter when core is halted + \arg DBG_CANx_HOLD (x=0,1,2 CAN2 is only available for CL series, CANx are not available for GD32EPRT series): hold CANx receive register counter when core is halted + \arg DBG_I2Cx_HOLD (x=0,1,2): hold I2Cx smbus timeout when core is halted + \arg DBG_TIMERx_HOLD (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for GD32EPRT series): hold TIMERx counter when core is halted + \arg DBG_SHRTIMER_HOLD : hold SHRTIMER counter when core is halted, except for GD32EPRT series + \param[out] none + \retval none +*/ +void dbg_periph_disable(dbg_periph_enum dbg_periph) +{ + DBG_REG_VAL(dbg_periph) &= ~BIT(DBG_BIT_POS(dbg_periph)); +} + +/*! + \brief enable trace pin assignment + \param[in] none + \param[out] none + \retval none +*/ +void dbg_trace_pin_enable(void) +{ + DBG_CTL |= DBG_CTL_TRACE_IOEN; +} + +/*! + \brief disable trace pin assignment + \param[in] none + \param[out] none + \retval none +*/ +void dbg_trace_pin_disable(void) +{ + DBG_CTL &= ~DBG_CTL_TRACE_IOEN; +} + diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dma.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dma.c new file mode 100644 index 0000000..9b4b501 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dma.c @@ -0,0 +1,752 @@ +/*! + \file gd32e50x_dma.c + \brief DMA driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_dma.h" +#include + +#define DMA_WRONG_HANDLE while(1){} + +/* check whether peripheral matches channels or not */ +static ErrStatus dma_periph_and_channel_check(uint32_t dma_periph, dma_channel_enum channelx); + +/*! + \brief deinitialize DMA a channel registers + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel is deinitialized + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_deinit(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + /* disable DMA a channel */ + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_CHEN; + /* reset DMA channel registers */ + DMA_CHCTL(dma_periph, channelx) = DMA_CHCTL_RESET_VALUE; + DMA_CHCNT(dma_periph, channelx) = DMA_CHCNT_RESET_VALUE; + DMA_CHPADDR(dma_periph, channelx) = DMA_CHPADDR_RESET_VALUE; + DMA_CHMADDR(dma_periph, channelx) = DMA_CHMADDR_RESET_VALUE; + DMA_INTC(dma_periph) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, channelx); +} + +/*! + \brief initialize the parameters of DMA struct with the default values + \param[in] init_struct: the initialization data needed to initialize DMA channel + \param[out] none + \retval none +*/ +void dma_struct_para_init(dma_parameter_struct* init_struct) +{ + if(NULL == init_struct){ + DMA_WRONG_HANDLE + } + + /* set the DMA struct with the default values */ + init_struct->periph_addr = 0U; + init_struct->periph_width = 0U; + init_struct->periph_inc = DMA_PERIPH_INCREASE_DISABLE; + init_struct->memory_addr = 0U; + init_struct->memory_width = 0U; + init_struct->memory_inc = DMA_MEMORY_INCREASE_DISABLE; + init_struct->number = 0U; + init_struct->direction = DMA_PERIPHERAL_TO_MEMORY; + init_struct->priority = DMA_PRIORITY_LOW; +} + +/*! + \brief initialize DMA channel + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel is initialized + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] init_struct: the data needed to initialize DMA channel + periph_addr: peripheral base address + periph_width: DMA_PERIPHERAL_WIDTH_8BIT, DMA_PERIPHERAL_WIDTH_16BIT, DMA_PERIPHERAL_WIDTH_32BIT + periph_inc: DMA_PERIPH_INCREASE_ENABLE, DMA_PERIPH_INCREASE_DISABLE + memory_addr: memory base address + memory_width: DMA_MEMORY_WIDTH_8BIT, DMA_MEMORY_WIDTH_16BIT, DMA_MEMORY_WIDTH_32BIT + memory_inc: DMA_MEMORY_INCREASE_ENABLE, DMA_MEMORY_INCREASE_DISABLE + direction: DMA_PERIPHERAL_TO_MEMORY, DMA_MEMORY_TO_PERIPHERAL + number: the number of remaining data to be transferred by the DMA + priority: DMA_PRIORITY_LOW, DMA_PRIORITY_MEDIUM, DMA_PRIORITY_HIGH, DMA_PRIORITY_ULTRA_HIGH + \param[out] none + \retval none +*/ +void dma_init(uint32_t dma_periph, dma_channel_enum channelx, dma_parameter_struct* init_struct) +{ + uint32_t ctl; + + if(NULL == init_struct){ + DMA_WRONG_HANDLE + } + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + /* configure peripheral base address */ + DMA_CHPADDR(dma_periph, channelx) = init_struct->periph_addr; + + /* configure memory base address */ + DMA_CHMADDR(dma_periph, channelx) = init_struct->memory_addr; + + /* configure the number of remaining data to be transferred */ + DMA_CHCNT(dma_periph, channelx) = (init_struct->number & DMA_CHANNEL_CNT_MASK); + + /* configure peripheral transfer width,memory transfer width and priority */ + ctl = DMA_CHCTL(dma_periph, channelx); + ctl &= ~(DMA_CHXCTL_PWIDTH | DMA_CHXCTL_MWIDTH | DMA_CHXCTL_PRIO); + ctl |= (init_struct->periph_width | init_struct->memory_width | init_struct->priority); + DMA_CHCTL(dma_periph, channelx) = ctl; + + /* configure peripheral increasing mode */ + if(DMA_PERIPH_INCREASE_ENABLE == init_struct->periph_inc){ + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_PNAGA; + }else{ + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_PNAGA; + } + + /* configure memory increasing mode */ + if(DMA_MEMORY_INCREASE_ENABLE == init_struct->memory_inc){ + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_MNAGA; + }else{ + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_MNAGA; + } + + /* configure the direction of data transfer */ + if(DMA_PERIPHERAL_TO_MEMORY == init_struct->direction){ + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_DIR; + }else{ + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_DIR; + } +} + +/*! + \brief enable DMA circulation mode + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_circulation_enable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_CMEN; +} + +/*! + \brief disable DMA circulation mode + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_circulation_disable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_CMEN; +} + +/*! + \brief enable memory to memory mode + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_memory_to_memory_enable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_M2M; +} + +/*! + \brief disable memory to memory mode + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_memory_to_memory_disable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_M2M; +} + +/*! + \brief enable DMA channel + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_channel_enable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_CHEN; +} + +/*! + \brief disable DMA channel + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_channel_disable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_CHEN; +} + +/*! + \brief set DMA peripheral base address + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to set peripheral base address + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] address: peripheral base address + \param[out] none + \retval none +*/ +void dma_periph_address_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t address) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHPADDR(dma_periph, channelx) = address; +} + +/*! + \brief set DMA memory base address + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to set memory base address + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] address: memory base address + \param[out] none + \retval none +*/ +void dma_memory_address_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t address) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHMADDR(dma_periph, channelx) = address; +} + +/*! + \brief set the number of remaining data to be transferred by the DMA + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to set number + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] number: the number of remaining data to be transferred by the DMA, 0x00000000-0x0000FFFF + \param[out] none + \retval none +*/ +void dma_transfer_number_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t number) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCNT(dma_periph, channelx) = (number & DMA_CHANNEL_CNT_MASK); +} + +/*! + \brief get the number of remaining data to be transferred by the DMA + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to set number + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval uint32_t: the number of remaining data to be transferred by the DMA, 0x00000000-0x0000FFFF +*/ +uint32_t dma_transfer_number_get(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + return (uint32_t)DMA_CHCNT(dma_periph, channelx); +} + +/*! + \brief configure priority level of DMA channel + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] priority: priority Level of this channel + only one parameter can be selected which is shown as below: + \arg DMA_PRIORITY_LOW: low priority + \arg DMA_PRIORITY_MEDIUM: medium priority + \arg DMA_PRIORITY_HIGH: high priority + \arg DMA_PRIORITY_ULTRA_HIGH: ultra high priority + \param[out] none + \retval none +*/ +void dma_priority_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t priority) +{ + uint32_t ctl; + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + /* acquire DMA_CHxCTL register */ + ctl = DMA_CHCTL(dma_periph, channelx); + /* assign regiser */ + ctl &= ~DMA_CHXCTL_PRIO; + ctl |= priority; + DMA_CHCTL(dma_periph, channelx) = ctl; +} + +/*! + \brief configure transfer data size of memory + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] mwidth: transfer data width of memory + only one parameter can be selected which is shown as below: + \arg DMA_MEMORY_WIDTH_8BIT: transfer data width of memory is 8-bit + \arg DMA_MEMORY_WIDTH_16BIT: transfer data width of memory is 16-bit + \arg DMA_MEMORY_WIDTH_32BIT: transfer data width of memory is 32-bit + \param[out] none + \retval none +*/ +void dma_memory_width_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t mwidth) +{ + uint32_t ctl; + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + /* acquire DMA_CHxCTL register */ + ctl = DMA_CHCTL(dma_periph, channelx); + /* assign regiser */ + ctl &= ~DMA_CHXCTL_MWIDTH; + ctl |= mwidth; + DMA_CHCTL(dma_periph, channelx) = ctl; +} + +/*! + \brief configure transfer data size of peripheral + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] pwidth: transfer data width of peripheral + only one parameter can be selected which is shown as below: + \arg DMA_PERIPHERAL_WIDTH_8BIT: transfer data width of peripheral is 8-bit + \arg DMA_PERIPHERAL_WIDTH_16BIT: transfer data width of peripheral is 16-bit + \arg DMA_PERIPHERAL_WIDTH_32BIT: transfer data width of peripheral is 32-bit + \param[out] none + \retval none +*/ +void dma_periph_width_config (uint32_t dma_periph, dma_channel_enum channelx, uint32_t pwidth) +{ + uint32_t ctl; + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + /* acquire DMA_CHxCTL register */ + ctl = DMA_CHCTL(dma_periph, channelx); + /* assign regiser */ + ctl &= ~DMA_CHXCTL_PWIDTH; + ctl |= pwidth; + DMA_CHCTL(dma_periph, channelx) = ctl; +} + +/*! + \brief enable next address increasement algorithm of memory + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_memory_increase_enable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_MNAGA; +} + +/*! + \brief disable next address increasement algorithm of memory + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_memory_increase_disable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_MNAGA; +} + +/*! + \brief enable next address increasement algorithm of peripheral + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_periph_increase_enable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_PNAGA; +} + +/*! + \brief disable next address increasement algorithm of peripheral + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_periph_increase_disable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_PNAGA; +} + +/*! + \brief configure the direction of data transfer on the channel + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] direction: specify the direction of data transfer + only one parameter can be selected which is shown as below: + \arg DMA_PERIPHERAL_TO_MEMORY: read from peripheral and write to memory + \arg DMA_MEMORY_TO_PERIPHERAL: read from memory and write to peripheral + \param[out] none + \retval none +*/ +void dma_transfer_direction_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t direction) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + if(DMA_PERIPHERAL_TO_MEMORY == direction){ + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_DIR; + }else { + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_DIR; + } +} + +/*! + \brief check DMA flag is set or not + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to get flag + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] flag: specify get which flag + only one parameter can be selected which is shown as below: + \arg DMA_FLAG_G: global interrupt flag of channel + \arg DMA_FLAG_FTF: full transfer finish flag of channel + \arg DMA_FLAG_HTF: half transfer finish flag of channel + \arg DMA_FLAG_ERR: error flag of channel + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus dma_flag_get(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag) +{ + FlagStatus reval; + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + if(RESET != (DMA_INTF(dma_periph) & DMA_FLAG_ADD(flag, channelx))){ + reval = SET; + }else{ + reval = RESET; + } + + return reval; +} + +/*! + \brief clear a DMA channel flag + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to clear flag + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] flag: specify get which flag + only one parameter can be selected which is shown as below: + \arg DMA_FLAG_G: global interrupt flag of channel + \arg DMA_FLAG_FTF: full transfer finish flag of channel + \arg DMA_FLAG_HTF: half transfer finish flag of channel + \arg DMA_FLAG_ERR: error flag of channel + \param[out] none + \retval none +*/ +void dma_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_INTC(dma_periph) |= DMA_FLAG_ADD(flag, channelx); +} + +/*! + \brief enable DMA interrupt + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] source: specify which interrupt to enbale + one or more parameters can be selected which are shown as below + \arg DMA_INT_FTF: channel full transfer finish interrupt + \arg DMA_INT_HTF: channel half transfer finish interrupt + \arg DMA_INT_ERR: channel error interrupt + \param[out] none + \retval none +*/ +void dma_interrupt_enable(uint32_t dma_periph, dma_channel_enum channelx, uint32_t source) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= source; +} + +/*! + \brief disable DMA interrupt + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] source: specify which interrupt to disbale + one or more parameters can be selected which are shown as below + \arg DMA_INT_FTF: channel full transfer finish interrupt + \arg DMA_INT_HTF: channel half transfer finish interrupt + \arg DMA_INT_ERR: channel error interrupt + \param[out] none + \retval none +*/ +void dma_interrupt_disable(uint32_t dma_periph, dma_channel_enum channelx, uint32_t source) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~source; +} + +/*! + \brief check DMA flag and interrupt enable bit is set or not + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to get flag + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] flag: specify get which flag + only one parameter can be selected which is shown as below: + \arg DMA_INT_FLAG_FTF: full transfer finish interrupt flag of channel + \arg DMA_INT_FLAG_HTF: half transfer finish interrupt flag of channel + \arg DMA_INT_FLAG_ERR: error interrupt flag of channel + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus dma_interrupt_flag_get(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag) +{ + uint32_t interrupt_enable = 0U, interrupt_flag = 0U; + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + switch(flag){ + case DMA_INT_FLAG_FTF: + interrupt_flag = DMA_INTF(dma_periph) & DMA_FLAG_ADD(flag, channelx); + interrupt_enable = DMA_CHCTL(dma_periph, channelx) & DMA_CHXCTL_FTFIE; + break; + case DMA_INT_FLAG_HTF: + interrupt_flag = DMA_INTF(dma_periph) & DMA_FLAG_ADD(flag, channelx); + interrupt_enable = DMA_CHCTL(dma_periph, channelx) & DMA_CHXCTL_HTFIE; + break; + case DMA_INT_FLAG_ERR: + interrupt_flag = DMA_INTF(dma_periph) & DMA_FLAG_ADD(flag, channelx); + interrupt_enable = DMA_CHCTL(dma_periph, channelx) & DMA_CHXCTL_ERRIE; + break; + default: + DMA_WRONG_HANDLE + } + + if(interrupt_flag && interrupt_enable){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear a DMA channel flag + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to clear flag + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] flag: specify get which flag + only one parameter can be selected which is shown as below: + \arg DMA_INT_FLAG_G: global interrupt flag of channel + \arg DMA_INT_FLAG_FTF: full transfer finish interrupt flag of channel + \arg DMA_INT_FLAG_HTF: half transfer finish interrupt flag of channel + \arg DMA_INT_FLAG_ERR: error interrupt flag of channel + \param[out] none + \retval none +*/ +void dma_interrupt_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_INTC(dma_periph) |= DMA_FLAG_ADD(flag, channelx); +} + +/*! + \brief check whether peripheral and channels match + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA_CHx(x=0..6) + \param[out] none + \retval none +*/ +static ErrStatus dma_periph_and_channel_check(uint32_t dma_periph, dma_channel_enum channelx) +{ + ErrStatus val = SUCCESS; + + if(DMA1 == dma_periph){ + /* for DMA1, the channel is from DMA_CH0 to DMA_CH4 */ + if(channelx > DMA_CH4){ + val = ERROR; + } + } + + return val; +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dma.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dma.c.base@1.5.0 new file mode 100644 index 0000000..9b4b501 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_dma.c.base@1.5.0 @@ -0,0 +1,752 @@ +/*! + \file gd32e50x_dma.c + \brief DMA driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_dma.h" +#include + +#define DMA_WRONG_HANDLE while(1){} + +/* check whether peripheral matches channels or not */ +static ErrStatus dma_periph_and_channel_check(uint32_t dma_periph, dma_channel_enum channelx); + +/*! + \brief deinitialize DMA a channel registers + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel is deinitialized + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_deinit(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + /* disable DMA a channel */ + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_CHEN; + /* reset DMA channel registers */ + DMA_CHCTL(dma_periph, channelx) = DMA_CHCTL_RESET_VALUE; + DMA_CHCNT(dma_periph, channelx) = DMA_CHCNT_RESET_VALUE; + DMA_CHPADDR(dma_periph, channelx) = DMA_CHPADDR_RESET_VALUE; + DMA_CHMADDR(dma_periph, channelx) = DMA_CHMADDR_RESET_VALUE; + DMA_INTC(dma_periph) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, channelx); +} + +/*! + \brief initialize the parameters of DMA struct with the default values + \param[in] init_struct: the initialization data needed to initialize DMA channel + \param[out] none + \retval none +*/ +void dma_struct_para_init(dma_parameter_struct* init_struct) +{ + if(NULL == init_struct){ + DMA_WRONG_HANDLE + } + + /* set the DMA struct with the default values */ + init_struct->periph_addr = 0U; + init_struct->periph_width = 0U; + init_struct->periph_inc = DMA_PERIPH_INCREASE_DISABLE; + init_struct->memory_addr = 0U; + init_struct->memory_width = 0U; + init_struct->memory_inc = DMA_MEMORY_INCREASE_DISABLE; + init_struct->number = 0U; + init_struct->direction = DMA_PERIPHERAL_TO_MEMORY; + init_struct->priority = DMA_PRIORITY_LOW; +} + +/*! + \brief initialize DMA channel + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel is initialized + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] init_struct: the data needed to initialize DMA channel + periph_addr: peripheral base address + periph_width: DMA_PERIPHERAL_WIDTH_8BIT, DMA_PERIPHERAL_WIDTH_16BIT, DMA_PERIPHERAL_WIDTH_32BIT + periph_inc: DMA_PERIPH_INCREASE_ENABLE, DMA_PERIPH_INCREASE_DISABLE + memory_addr: memory base address + memory_width: DMA_MEMORY_WIDTH_8BIT, DMA_MEMORY_WIDTH_16BIT, DMA_MEMORY_WIDTH_32BIT + memory_inc: DMA_MEMORY_INCREASE_ENABLE, DMA_MEMORY_INCREASE_DISABLE + direction: DMA_PERIPHERAL_TO_MEMORY, DMA_MEMORY_TO_PERIPHERAL + number: the number of remaining data to be transferred by the DMA + priority: DMA_PRIORITY_LOW, DMA_PRIORITY_MEDIUM, DMA_PRIORITY_HIGH, DMA_PRIORITY_ULTRA_HIGH + \param[out] none + \retval none +*/ +void dma_init(uint32_t dma_periph, dma_channel_enum channelx, dma_parameter_struct* init_struct) +{ + uint32_t ctl; + + if(NULL == init_struct){ + DMA_WRONG_HANDLE + } + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + /* configure peripheral base address */ + DMA_CHPADDR(dma_periph, channelx) = init_struct->periph_addr; + + /* configure memory base address */ + DMA_CHMADDR(dma_periph, channelx) = init_struct->memory_addr; + + /* configure the number of remaining data to be transferred */ + DMA_CHCNT(dma_periph, channelx) = (init_struct->number & DMA_CHANNEL_CNT_MASK); + + /* configure peripheral transfer width,memory transfer width and priority */ + ctl = DMA_CHCTL(dma_periph, channelx); + ctl &= ~(DMA_CHXCTL_PWIDTH | DMA_CHXCTL_MWIDTH | DMA_CHXCTL_PRIO); + ctl |= (init_struct->periph_width | init_struct->memory_width | init_struct->priority); + DMA_CHCTL(dma_periph, channelx) = ctl; + + /* configure peripheral increasing mode */ + if(DMA_PERIPH_INCREASE_ENABLE == init_struct->periph_inc){ + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_PNAGA; + }else{ + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_PNAGA; + } + + /* configure memory increasing mode */ + if(DMA_MEMORY_INCREASE_ENABLE == init_struct->memory_inc){ + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_MNAGA; + }else{ + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_MNAGA; + } + + /* configure the direction of data transfer */ + if(DMA_PERIPHERAL_TO_MEMORY == init_struct->direction){ + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_DIR; + }else{ + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_DIR; + } +} + +/*! + \brief enable DMA circulation mode + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_circulation_enable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_CMEN; +} + +/*! + \brief disable DMA circulation mode + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_circulation_disable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_CMEN; +} + +/*! + \brief enable memory to memory mode + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_memory_to_memory_enable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_M2M; +} + +/*! + \brief disable memory to memory mode + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_memory_to_memory_disable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_M2M; +} + +/*! + \brief enable DMA channel + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_channel_enable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_CHEN; +} + +/*! + \brief disable DMA channel + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_channel_disable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_CHEN; +} + +/*! + \brief set DMA peripheral base address + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to set peripheral base address + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] address: peripheral base address + \param[out] none + \retval none +*/ +void dma_periph_address_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t address) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHPADDR(dma_periph, channelx) = address; +} + +/*! + \brief set DMA memory base address + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to set memory base address + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] address: memory base address + \param[out] none + \retval none +*/ +void dma_memory_address_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t address) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHMADDR(dma_periph, channelx) = address; +} + +/*! + \brief set the number of remaining data to be transferred by the DMA + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to set number + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] number: the number of remaining data to be transferred by the DMA, 0x00000000-0x0000FFFF + \param[out] none + \retval none +*/ +void dma_transfer_number_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t number) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCNT(dma_periph, channelx) = (number & DMA_CHANNEL_CNT_MASK); +} + +/*! + \brief get the number of remaining data to be transferred by the DMA + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to set number + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval uint32_t: the number of remaining data to be transferred by the DMA, 0x00000000-0x0000FFFF +*/ +uint32_t dma_transfer_number_get(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + return (uint32_t)DMA_CHCNT(dma_periph, channelx); +} + +/*! + \brief configure priority level of DMA channel + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] priority: priority Level of this channel + only one parameter can be selected which is shown as below: + \arg DMA_PRIORITY_LOW: low priority + \arg DMA_PRIORITY_MEDIUM: medium priority + \arg DMA_PRIORITY_HIGH: high priority + \arg DMA_PRIORITY_ULTRA_HIGH: ultra high priority + \param[out] none + \retval none +*/ +void dma_priority_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t priority) +{ + uint32_t ctl; + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + /* acquire DMA_CHxCTL register */ + ctl = DMA_CHCTL(dma_periph, channelx); + /* assign regiser */ + ctl &= ~DMA_CHXCTL_PRIO; + ctl |= priority; + DMA_CHCTL(dma_periph, channelx) = ctl; +} + +/*! + \brief configure transfer data size of memory + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] mwidth: transfer data width of memory + only one parameter can be selected which is shown as below: + \arg DMA_MEMORY_WIDTH_8BIT: transfer data width of memory is 8-bit + \arg DMA_MEMORY_WIDTH_16BIT: transfer data width of memory is 16-bit + \arg DMA_MEMORY_WIDTH_32BIT: transfer data width of memory is 32-bit + \param[out] none + \retval none +*/ +void dma_memory_width_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t mwidth) +{ + uint32_t ctl; + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + /* acquire DMA_CHxCTL register */ + ctl = DMA_CHCTL(dma_periph, channelx); + /* assign regiser */ + ctl &= ~DMA_CHXCTL_MWIDTH; + ctl |= mwidth; + DMA_CHCTL(dma_periph, channelx) = ctl; +} + +/*! + \brief configure transfer data size of peripheral + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] pwidth: transfer data width of peripheral + only one parameter can be selected which is shown as below: + \arg DMA_PERIPHERAL_WIDTH_8BIT: transfer data width of peripheral is 8-bit + \arg DMA_PERIPHERAL_WIDTH_16BIT: transfer data width of peripheral is 16-bit + \arg DMA_PERIPHERAL_WIDTH_32BIT: transfer data width of peripheral is 32-bit + \param[out] none + \retval none +*/ +void dma_periph_width_config (uint32_t dma_periph, dma_channel_enum channelx, uint32_t pwidth) +{ + uint32_t ctl; + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + /* acquire DMA_CHxCTL register */ + ctl = DMA_CHCTL(dma_periph, channelx); + /* assign regiser */ + ctl &= ~DMA_CHXCTL_PWIDTH; + ctl |= pwidth; + DMA_CHCTL(dma_periph, channelx) = ctl; +} + +/*! + \brief enable next address increasement algorithm of memory + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_memory_increase_enable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_MNAGA; +} + +/*! + \brief disable next address increasement algorithm of memory + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_memory_increase_disable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_MNAGA; +} + +/*! + \brief enable next address increasement algorithm of peripheral + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_periph_increase_enable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_PNAGA; +} + +/*! + \brief disable next address increasement algorithm of peripheral + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[out] none + \retval none +*/ +void dma_periph_increase_disable(uint32_t dma_periph, dma_channel_enum channelx) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_PNAGA; +} + +/*! + \brief configure the direction of data transfer on the channel + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] direction: specify the direction of data transfer + only one parameter can be selected which is shown as below: + \arg DMA_PERIPHERAL_TO_MEMORY: read from peripheral and write to memory + \arg DMA_MEMORY_TO_PERIPHERAL: read from memory and write to peripheral + \param[out] none + \retval none +*/ +void dma_transfer_direction_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t direction) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + if(DMA_PERIPHERAL_TO_MEMORY == direction){ + DMA_CHCTL(dma_periph, channelx) &= ~DMA_CHXCTL_DIR; + }else { + DMA_CHCTL(dma_periph, channelx) |= DMA_CHXCTL_DIR; + } +} + +/*! + \brief check DMA flag is set or not + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to get flag + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] flag: specify get which flag + only one parameter can be selected which is shown as below: + \arg DMA_FLAG_G: global interrupt flag of channel + \arg DMA_FLAG_FTF: full transfer finish flag of channel + \arg DMA_FLAG_HTF: half transfer finish flag of channel + \arg DMA_FLAG_ERR: error flag of channel + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus dma_flag_get(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag) +{ + FlagStatus reval; + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + if(RESET != (DMA_INTF(dma_periph) & DMA_FLAG_ADD(flag, channelx))){ + reval = SET; + }else{ + reval = RESET; + } + + return reval; +} + +/*! + \brief clear a DMA channel flag + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to clear flag + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] flag: specify get which flag + only one parameter can be selected which is shown as below: + \arg DMA_FLAG_G: global interrupt flag of channel + \arg DMA_FLAG_FTF: full transfer finish flag of channel + \arg DMA_FLAG_HTF: half transfer finish flag of channel + \arg DMA_FLAG_ERR: error flag of channel + \param[out] none + \retval none +*/ +void dma_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_INTC(dma_periph) |= DMA_FLAG_ADD(flag, channelx); +} + +/*! + \brief enable DMA interrupt + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] source: specify which interrupt to enbale + one or more parameters can be selected which are shown as below + \arg DMA_INT_FTF: channel full transfer finish interrupt + \arg DMA_INT_HTF: channel half transfer finish interrupt + \arg DMA_INT_ERR: channel error interrupt + \param[out] none + \retval none +*/ +void dma_interrupt_enable(uint32_t dma_periph, dma_channel_enum channelx, uint32_t source) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) |= source; +} + +/*! + \brief disable DMA interrupt + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] source: specify which interrupt to disbale + one or more parameters can be selected which are shown as below + \arg DMA_INT_FTF: channel full transfer finish interrupt + \arg DMA_INT_HTF: channel half transfer finish interrupt + \arg DMA_INT_ERR: channel error interrupt + \param[out] none + \retval none +*/ +void dma_interrupt_disable(uint32_t dma_periph, dma_channel_enum channelx, uint32_t source) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_CHCTL(dma_periph, channelx) &= ~source; +} + +/*! + \brief check DMA flag and interrupt enable bit is set or not + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to get flag + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] flag: specify get which flag + only one parameter can be selected which is shown as below: + \arg DMA_INT_FLAG_FTF: full transfer finish interrupt flag of channel + \arg DMA_INT_FLAG_HTF: half transfer finish interrupt flag of channel + \arg DMA_INT_FLAG_ERR: error interrupt flag of channel + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus dma_interrupt_flag_get(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag) +{ + uint32_t interrupt_enable = 0U, interrupt_flag = 0U; + + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + switch(flag){ + case DMA_INT_FLAG_FTF: + interrupt_flag = DMA_INTF(dma_periph) & DMA_FLAG_ADD(flag, channelx); + interrupt_enable = DMA_CHCTL(dma_periph, channelx) & DMA_CHXCTL_FTFIE; + break; + case DMA_INT_FLAG_HTF: + interrupt_flag = DMA_INTF(dma_periph) & DMA_FLAG_ADD(flag, channelx); + interrupt_enable = DMA_CHCTL(dma_periph, channelx) & DMA_CHXCTL_HTFIE; + break; + case DMA_INT_FLAG_ERR: + interrupt_flag = DMA_INTF(dma_periph) & DMA_FLAG_ADD(flag, channelx); + interrupt_enable = DMA_CHCTL(dma_periph, channelx) & DMA_CHXCTL_ERRIE; + break; + default: + DMA_WRONG_HANDLE + } + + if(interrupt_flag && interrupt_enable){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear a DMA channel flag + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel to clear flag + only one parameter can be selected which is shown as below: + \arg DMA0: DMA_CHx(x=0..6), DMA1: DMA_CHx(x=0..4) + \param[in] flag: specify get which flag + only one parameter can be selected which is shown as below: + \arg DMA_INT_FLAG_G: global interrupt flag of channel + \arg DMA_INT_FLAG_FTF: full transfer finish interrupt flag of channel + \arg DMA_INT_FLAG_HTF: half transfer finish interrupt flag of channel + \arg DMA_INT_FLAG_ERR: error interrupt flag of channel + \param[out] none + \retval none +*/ +void dma_interrupt_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag) +{ + if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){ + DMA_WRONG_HANDLE + } + + DMA_INTC(dma_periph) |= DMA_FLAG_ADD(flag, channelx); +} + +/*! + \brief check whether peripheral and channels match + \param[in] dma_periph: DMAx(x=0,1) + \arg DMAx(x=0,1) + \param[in] channelx: specify which DMA channel + only one parameter can be selected which is shown as below: + \arg DMA_CHx(x=0..6) + \param[out] none + \retval none +*/ +static ErrStatus dma_periph_and_channel_check(uint32_t dma_periph, dma_channel_enum channelx) +{ + ErrStatus val = SUCCESS; + + if(DMA1 == dma_periph){ + /* for DMA1, the channel is from DMA_CH0 to DMA_CH4 */ + if(channelx > DMA_CH4){ + val = ERROR; + } + } + + return val; +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exti.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exti.c new file mode 100644 index 0000000..99cfa62 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exti.c @@ -0,0 +1,249 @@ +/*! + \file gd32e50x_exti.c + \brief EXTI driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_exti.h" + +#define EXTI_REG_RESET_VALUE ((uint32_t)0x00000000U) + +/*! + \brief deinitialize the EXTI + \param[in] none + \param[out] none + \retval none +*/ +void exti_deinit(void) +{ + /* reset the value of all the EXTI registers */ + EXTI_INTEN = EXTI_REG_RESET_VALUE; + EXTI_EVEN = EXTI_REG_RESET_VALUE; + EXTI_RTEN = EXTI_REG_RESET_VALUE; + EXTI_FTEN = EXTI_REG_RESET_VALUE; + EXTI_SWIEV = EXTI_REG_RESET_VALUE; +} + +/*! + \brief initialize the EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[in] mode: interrupt or event mode, refer to exti_mode_enum + only one parameter can be selected which is shown as below: + \arg EXTI_INTERRUPT: interrupt mode + \arg EXTI_EVENT: event mode + \param[in] trig_type: interrupt trigger type, refer to exti_trig_type_enum + only one parameter can be selected which is shown as below: + \arg EXTI_TRIG_RISING: rising edge trigger + \arg EXTI_TRIG_FALLING: falling trigger + \arg EXTI_TRIG_BOTH: rising and falling trigger + \arg EXTI_TRIG_NONE: without rising edge or falling edge trigger + \param[out] none + \retval none +*/ +void exti_init(exti_line_enum linex, exti_mode_enum mode, exti_trig_type_enum trig_type) +{ + /* reset the EXTI line x */ + EXTI_INTEN &= ~(uint32_t)linex; + EXTI_EVEN &= ~(uint32_t)linex; + EXTI_RTEN &= ~(uint32_t)linex; + EXTI_FTEN &= ~(uint32_t)linex; + + /* set the EXTI mode and enable the interrupts or events from EXTI line x */ + switch(mode){ + case EXTI_INTERRUPT: + EXTI_INTEN |= (uint32_t)linex; + break; + case EXTI_EVENT: + EXTI_EVEN |= (uint32_t)linex; + break; + default: + break; + } + + /* set the EXTI trigger type */ + switch(trig_type){ + case EXTI_TRIG_RISING: + EXTI_RTEN |= (uint32_t)linex; + EXTI_FTEN &= ~(uint32_t)linex; + break; + case EXTI_TRIG_FALLING: + EXTI_RTEN &= ~(uint32_t)linex; + EXTI_FTEN |= (uint32_t)linex; + break; + case EXTI_TRIG_BOTH: + EXTI_RTEN |= (uint32_t)linex; + EXTI_FTEN |= (uint32_t)linex; + break; + case EXTI_TRIG_NONE: + default: + break; + } +} + +/*! + \brief enable the interrupts from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_enable(exti_line_enum linex) +{ + EXTI_INTEN |= (uint32_t)linex; +} + +/*! + \brief disable the interrupt from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_disable(exti_line_enum linex) +{ + EXTI_INTEN &= ~(uint32_t)linex; +} + +/*! + \brief enable the events from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_event_enable(exti_line_enum linex) +{ + EXTI_EVEN |= (uint32_t)linex; +} + +/*! + \brief disable the events from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_event_disable(exti_line_enum linex) +{ + EXTI_EVEN &= ~(uint32_t)linex; +} + +/*! + \brief enable the software interrupt event from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_software_interrupt_enable(exti_line_enum linex) +{ + EXTI_SWIEV |= (uint32_t)linex; +} + +/*! + \brief disable the software interrupt event from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_software_interrupt_disable(exti_line_enum linex) +{ + EXTI_SWIEV &= ~(uint32_t)linex; +} + +/*! + \brief get EXTI line x interrupt pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval FlagStatus: status of flag (RESET or SET) +*/ +FlagStatus exti_flag_get(exti_line_enum linex) +{ + if(RESET != (EXTI_PD & (uint32_t)linex)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear EXTI line x interrupt pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_flag_clear(exti_line_enum linex) +{ + EXTI_PD = (uint32_t)linex; +} + +/*! + \brief get EXTI line x interrupt pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval FlagStatus: status of flag (RESET or SET) +*/ +FlagStatus exti_interrupt_flag_get(exti_line_enum linex) +{ + if(RESET != (EXTI_PD & (uint32_t)linex)) { + return SET; + } else { + return RESET; + } +} + +/*! + \brief clear EXTI line x interrupt pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_flag_clear(exti_line_enum linex) +{ + EXTI_PD = (uint32_t)linex; +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exti.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exti.c.base@1.5.0 new file mode 100644 index 0000000..99cfa62 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exti.c.base@1.5.0 @@ -0,0 +1,249 @@ +/*! + \file gd32e50x_exti.c + \brief EXTI driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_exti.h" + +#define EXTI_REG_RESET_VALUE ((uint32_t)0x00000000U) + +/*! + \brief deinitialize the EXTI + \param[in] none + \param[out] none + \retval none +*/ +void exti_deinit(void) +{ + /* reset the value of all the EXTI registers */ + EXTI_INTEN = EXTI_REG_RESET_VALUE; + EXTI_EVEN = EXTI_REG_RESET_VALUE; + EXTI_RTEN = EXTI_REG_RESET_VALUE; + EXTI_FTEN = EXTI_REG_RESET_VALUE; + EXTI_SWIEV = EXTI_REG_RESET_VALUE; +} + +/*! + \brief initialize the EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[in] mode: interrupt or event mode, refer to exti_mode_enum + only one parameter can be selected which is shown as below: + \arg EXTI_INTERRUPT: interrupt mode + \arg EXTI_EVENT: event mode + \param[in] trig_type: interrupt trigger type, refer to exti_trig_type_enum + only one parameter can be selected which is shown as below: + \arg EXTI_TRIG_RISING: rising edge trigger + \arg EXTI_TRIG_FALLING: falling trigger + \arg EXTI_TRIG_BOTH: rising and falling trigger + \arg EXTI_TRIG_NONE: without rising edge or falling edge trigger + \param[out] none + \retval none +*/ +void exti_init(exti_line_enum linex, exti_mode_enum mode, exti_trig_type_enum trig_type) +{ + /* reset the EXTI line x */ + EXTI_INTEN &= ~(uint32_t)linex; + EXTI_EVEN &= ~(uint32_t)linex; + EXTI_RTEN &= ~(uint32_t)linex; + EXTI_FTEN &= ~(uint32_t)linex; + + /* set the EXTI mode and enable the interrupts or events from EXTI line x */ + switch(mode){ + case EXTI_INTERRUPT: + EXTI_INTEN |= (uint32_t)linex; + break; + case EXTI_EVENT: + EXTI_EVEN |= (uint32_t)linex; + break; + default: + break; + } + + /* set the EXTI trigger type */ + switch(trig_type){ + case EXTI_TRIG_RISING: + EXTI_RTEN |= (uint32_t)linex; + EXTI_FTEN &= ~(uint32_t)linex; + break; + case EXTI_TRIG_FALLING: + EXTI_RTEN &= ~(uint32_t)linex; + EXTI_FTEN |= (uint32_t)linex; + break; + case EXTI_TRIG_BOTH: + EXTI_RTEN |= (uint32_t)linex; + EXTI_FTEN |= (uint32_t)linex; + break; + case EXTI_TRIG_NONE: + default: + break; + } +} + +/*! + \brief enable the interrupts from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_enable(exti_line_enum linex) +{ + EXTI_INTEN |= (uint32_t)linex; +} + +/*! + \brief disable the interrupt from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_disable(exti_line_enum linex) +{ + EXTI_INTEN &= ~(uint32_t)linex; +} + +/*! + \brief enable the events from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_event_enable(exti_line_enum linex) +{ + EXTI_EVEN |= (uint32_t)linex; +} + +/*! + \brief disable the events from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_event_disable(exti_line_enum linex) +{ + EXTI_EVEN &= ~(uint32_t)linex; +} + +/*! + \brief enable the software interrupt event from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_software_interrupt_enable(exti_line_enum linex) +{ + EXTI_SWIEV |= (uint32_t)linex; +} + +/*! + \brief disable the software interrupt event from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_software_interrupt_disable(exti_line_enum linex) +{ + EXTI_SWIEV &= ~(uint32_t)linex; +} + +/*! + \brief get EXTI line x interrupt pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval FlagStatus: status of flag (RESET or SET) +*/ +FlagStatus exti_flag_get(exti_line_enum linex) +{ + if(RESET != (EXTI_PD & (uint32_t)linex)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear EXTI line x interrupt pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_flag_clear(exti_line_enum linex) +{ + EXTI_PD = (uint32_t)linex; +} + +/*! + \brief get EXTI line x interrupt pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval FlagStatus: status of flag (RESET or SET) +*/ +FlagStatus exti_interrupt_flag_get(exti_line_enum linex) +{ + if(RESET != (EXTI_PD & (uint32_t)linex)) { + return SET; + } else { + return RESET; + } +} + +/*! + \brief clear EXTI line x interrupt pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..21): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_flag_clear(exti_line_enum linex) +{ + EXTI_PD = (uint32_t)linex; +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_fmc.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_fmc.c new file mode 100644 index 0000000..a220f53 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_fmc.c @@ -0,0 +1,875 @@ +/*! + \file gd32e50x_fmc.c + \brief FMC driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_fmc.h" + +/* FMC mask */ +#define LOW_8BITS_MASK ((uint32_t)0x000000FFU) +#define HIGH_8BITS_MASK ((uint32_t)0x0000FF00U) +#define LOW_8BITS_MASK1 ((uint32_t)0x00FF0000U) +#define HIGH_8BITS_MASK1 ((uint32_t)0xFF000000U) +#define LOW_16BITS_MASK ((uint32_t)0x0000FFFFU) +#define HIGH_16BITS_MASK ((uint32_t)0xFFFF0000U) + +/* USER of option bytes mask */ +#define OB_USER_MASK ((uint8_t)0x38U) + +/* default offset */ +#define FMC_OBSTAT_USER_OFFSET 2U +#define FMC_OBSTAT_DATA_OFFSET 10U + +/* return the FMC state */ +static fmc_state_enum fmc_state_get(void); +/* check FMC ready or not */ +static fmc_state_enum fmc_ready_wait(uint32_t timeout); + +/*! + \brief unlock the main FMC operation + it is better to used in pairs with fmc_lock + \param[in] none + \param[out] none + \retval none +*/ +void fmc_unlock(void) +{ + if(RESET != (FMC_CTL & FMC_CTL_LK)){ + /* write the FMC unlock key */ + FMC_KEY = UNLOCK_KEY0; + FMC_KEY = UNLOCK_KEY1; + } +} + +/*! + \brief lock the main FMC operation + it is better to used in pairs with fmc_unlock after an operation + \param[in] none + \param[out] none + \retval none +*/ +void fmc_lock(void) +{ + /* set the LK bit */ + FMC_CTL |= FMC_CTL_LK; +} + +/*! + \brief set the wait state + \param[in] wscnt: wait state + only one parameter can be selected which is shown as below: + \arg FMC_WAIT_STATE_0: 0 wait state added + \arg FMC_WAIT_STATE_1: 1 wait state added + \arg FMC_WAIT_STATE_2: 2 wait state added + \arg FMC_WAIT_STATE_3: 3 wait state added + \arg FMC_WAIT_STATE_4: 4 wait state added + \param[out] none + \retval none +*/ +void fmc_wscnt_set(uint32_t wscnt) +{ + uint32_t ws; + + ws = FMC_WS; + + /* set the wait state counter value */ + ws &= ~FMC_WS_WSCNT; + FMC_WS = (ws | wscnt); +} + +/*! + \brief enable pre-fetch + \param[in] none + \param[out] none + \retval none +*/ +void fmc_prefetch_enable(void) +{ + FMC_WS |= FMC_WS_PFEN; +} + +/*! + \brief disable pre-fetch + \param[in] none + \param[out] none + \retval none +*/ +void fmc_prefetch_disable(void) +{ + FMC_WS &= ~FMC_WS_PFEN; +} + +/*! + \brief enable IBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_ibus_enable(void) +{ + FMC_WS |= FMC_WS_ICEN; +} + +/*! + \brief disable IBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_ibus_disable(void) +{ + FMC_WS &= ~FMC_WS_ICEN; +} + +/*! + \brief reset IBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_ibus_reset(void) +{ + FMC_WS |= FMC_WS_ICRST; +} + +/*! + \brief enable DBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_dbus_enable(void) +{ + FMC_WS |= FMC_WS_DCEN; +} + +/*! + \brief disable DBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_dbus_disable(void) +{ + FMC_WS &= ~FMC_WS_DCEN; +} + +/*! + \brief reset DBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_dbus_reset(void) +{ + FMC_WS |= FMC_WS_DCRST; +} + +/*! + \brief FMC erase page + \param[in] page_address: target page address + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error +*/ +fmc_state_enum fmc_page_erase(uint32_t page_address) +{ + fmc_state_enum fmc_state; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* start page erase */ + FMC_CTL |= FMC_CTL_PER; + FMC_ADDR = page_address; + FMC_CTL |= FMC_CTL_START; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + /* reset the PER bit */ + FMC_CTL &= ~FMC_CTL_PER; + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief FMC erase whole chip + \param[in] none + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error +*/ +fmc_state_enum fmc_mass_erase(void) +{ + fmc_state_enum fmc_state; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* start chip erase */ + FMC_CTL |= FMC_CTL_MER; + FMC_CTL |= FMC_CTL_START; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + /* reset the MER bit */ + FMC_CTL &= ~FMC_CTL_MER; + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief FMC program a word at the corresponding address + \param[in] address: address to program + \param[in] data: word to program + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error +*/ +fmc_state_enum fmc_word_program(uint32_t address, uint32_t data) +{ + fmc_state_enum fmc_state; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* set the PG bit to start program */ + FMC_CTL |= FMC_CTL_PG; + REG32(address) = data; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + /* reset the PG bit */ + FMC_CTL &= ~FMC_CTL_PG; + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief unlock the option bytes operation + it is better to used in pairs with ob_lock + \param[in] none + \param[out] none + \retval none +*/ +void ob_unlock(void) +{ + if(RESET == (FMC_CTL & FMC_CTL_OBWEN)){ + /* write the FMC ob unlock key */ + FMC_OBKEY = UNLOCK_KEY0; + FMC_OBKEY = UNLOCK_KEY1; + } +} + +/*! + \brief lock the option bytes operation + it is better to used in pairs with ob_unlock after an operation + \param[in] none + \param[out] none + \retval none +*/ +void ob_lock(void) +{ + /* reset the OBWEN bit */ + FMC_CTL &= ~FMC_CTL_OBWEN; +} + +/*! + \brief erase the option bytes + programmer must ensure FMC & option bytes are both unlocked before calling this function + \param[in] none + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error + \arg FMC_OB_HSPC: high security protection +*/ +fmc_state_enum ob_erase(void) +{ + uint32_t temp_spc; + fmc_state_enum fmc_state; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + temp_spc = OB_SPC_USER; + + /* check the option bytes security protection value */ + if((RESET != ob_security_protection_flag_get()) && (FMC_HSPC == (temp_spc & LOW_8BITS_MASK))){ + fmc_state = FMC_OB_HSPC; + } + + if(FMC_READY == fmc_state){ + /* start erase the option bytes */ + FMC_CTL |= FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_START; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* reset the OBER bit and enable the option bytes programming */ + FMC_CTL &= ~FMC_CTL_OBER; + /* set the OBPG bit */ + FMC_CTL |= FMC_CTL_OBPG; + /* restore the last get option bytes security protection code */ + OB_SPC_USER = (temp_spc & LOW_8BITS_MASK) | LOW_8BITS_MASK1; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + /* reset the OBPG bit */ + FMC_CTL &= ~FMC_CTL_OBPG; + }else{ + /* reset the OBER bit */ + FMC_CTL &= ~FMC_CTL_OBER; + } + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief enable write protection + programmer must ensure FMC & option bytes are both unlocked before calling this function + \param[in] ob_wp: specify sector to be write protected + only one parameter can be selected which are shown as below: + \arg OB_WP_NONE: disable all write protection + \arg OB_WP_x(x=0..31): write protect specify sector + \arg OB_WP_ALL: write protect all sector + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error + \arg FMC_OB_HSPC: high security protection +*/ +fmc_state_enum ob_write_protection_enable(uint32_t ob_wp) +{ + uint32_t temp_spc; + uint8_t i; + uint32_t op_byte[4]; + + fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + temp_spc = OB_SPC_USER; + + /* check the option bytes security protection value */ + if((RESET != ob_security_protection_flag_get()) && (FMC_HSPC == (temp_spc & LOW_8BITS_MASK))){ + fmc_state = FMC_OB_HSPC; + } + + for(i = 0U; i < 4U; i++){ + op_byte[i] = OP_BYTE(i); + } + + ob_wp = (uint32_t)(~ob_wp); + op_byte[2] = (ob_wp & LOW_8BITS_MASK) | ((ob_wp & HIGH_8BITS_MASK) << 8); + op_byte[3] = ((ob_wp & LOW_8BITS_MASK1) >> 16) | ((ob_wp & HIGH_8BITS_MASK1) >> 8); + + if(FMC_READY == fmc_state){ + /* start erase the option byte */ + FMC_CTL |= FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_START; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* reset the OBER bit and enable the option bytes programming */ + FMC_CTL &= ~FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_OBPG; + + for(i = 0U; i < 4U; i++){ + OP_BYTE(i) = op_byte[i]; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + if(FMC_READY != fmc_state){ + break; + } + } + /* reset the OBPG bit */ + FMC_CTL &= ~FMC_CTL_OBPG; + }else{ + /* reset the OBER bit */ + FMC_CTL &= ~FMC_CTL_OBER; + } + } + + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief configure security protection + programmer must ensure FMC & option bytes are both unlocked before calling this function + \param[in] ob_spc: specify security protection + only one parameter can be selected which is shown as below: + \arg FMC_NSPC: no security protection + \arg FMC_LSPC: low security protection + \arg FMC_HSPC: high security protection + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error + \arg FMC_OB_HSPC: high security protection +*/ +fmc_state_enum ob_security_protection_config(uint8_t ob_spc) +{ + uint32_t temp_spc; + uint8_t i; + uint32_t op_byte[4]; + + fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + temp_spc = OB_SPC_USER & LOW_16BITS_MASK; + + /* check the option bytes security protection value */ + if((RESET != ob_security_protection_flag_get()) && (FMC_HSPC == (temp_spc & LOW_8BITS_MASK))){ + fmc_state = FMC_OB_HSPC; + } + + for(i = 0U; i < 4U; i++){ + op_byte[i] = OP_BYTE(i); + } + op_byte[0] = ((uint32_t)(ob_spc)) | ((op_byte[0] & HIGH_16BITS_MASK)); + + if(FMC_READY == fmc_state){ + /* start erase the option bytes */ + FMC_CTL |= FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_START; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* reset the OBER bit and enable the option bytes programming */ + FMC_CTL &= ~FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_OBPG; + + for(i = 0U; i < 4U; i++){ + OP_BYTE(i) = op_byte[i]; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + if(FMC_READY != fmc_state){ + break; + } + } + + /* reset the OBPG bit */ + FMC_CTL &= ~FMC_CTL_OBPG; + }else{ + /* reset the OBER bit */ + FMC_CTL &= ~FMC_CTL_OBER; + } + } + + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief program option bytes USER + programmer must ensure FMC & option bytes are both unlocked before calling this function + \param[in] ob_fwdgt: option bytes free watchdog value + only one parameter can be selected which is shown as below: + \arg OB_FWDGT_SW: software free watchdog + \arg OB_FWDGT_HW: hardware free watchdog + \param[in] ob_deepsleep: option bytes deepsleep reset value + only one parameter can be selected which is shown as below: + \arg OB_DEEPSLEEP_NRST: no reset when entering deepsleep mode + \arg OB_DEEPSLEEP_RST: generate a reset instead of entering deepsleep mode + \param[in] ob_stdby:option bytes standby reset value + only one parameter can be selected which is shown as below: + \arg OB_STDBY_NRST: no reset when entering standby mode + \arg OB_STDBY_RST: generate a reset instead of entering standby mode + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error + \arg FMC_OB_HSPC: high security protection +*/ +fmc_state_enum ob_user_write(uint8_t ob_fwdgt, uint8_t ob_deepsleep, uint8_t ob_stdby) +{ + uint32_t temp_spc; + uint8_t temp; + uint8_t i; + uint32_t op_byte[4]; + + fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + temp_spc = OB_SPC_USER; + + /* check the option bytes security protection value */ + if((RESET != ob_security_protection_flag_get()) && (FMC_HSPC == (temp_spc & LOW_8BITS_MASK))){ + fmc_state = FMC_OB_HSPC; + } + + for(i = 0U; i < 4U; i++){ + op_byte[i] = OP_BYTE(i); + } + temp = ((uint8_t)((uint8_t)(((uint8_t)(ob_fwdgt) | ob_deepsleep) | ob_stdby)) | (OB_USER_MASK)); + op_byte[0] = ((uint32_t)(temp) << 16U) | ((op_byte[0] & LOW_16BITS_MASK)); + + if(FMC_READY == fmc_state){ + /* start erase the option bytes */ + FMC_CTL |= FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_START; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + if(FMC_READY == fmc_state){ + /* reset the OBER bit and enable the option bytes programming */ + FMC_CTL &= ~FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_OBPG; + + for(i = 0U; i < 4U; i++){ + OP_BYTE(i) = op_byte[i]; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + if(FMC_READY != fmc_state){ + break; + } + } + + /* reset the OBPG bit */ + FMC_CTL &= ~FMC_CTL_OBPG; + }else{ + /* reset the OBER bit */ + FMC_CTL &= ~FMC_CTL_OBER; + } + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief program option bytes DATA + \param[in] data: the byte to be programmed + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error + \arg FMC_OB_HSPC: high security protection +*/ +fmc_state_enum ob_data_program(uint16_t ob_data) +{ + uint32_t temp_spc; + uint8_t i; + uint32_t op_byte[4]; + + fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + temp_spc = OB_SPC_USER; + + /* check the option bytes security protection value */ + if((RESET != ob_security_protection_flag_get()) && (FMC_HSPC == (temp_spc & LOW_8BITS_MASK))){ + fmc_state = FMC_OB_HSPC; + } + + for(i = 0U; i < 4U; i++){ + op_byte[i] = OP_BYTE(i); + } + op_byte[1] = (uint32_t)((ob_data & LOW_8BITS_MASK) | ((ob_data & HIGH_8BITS_MASK) << 8)); + + if(FMC_READY == fmc_state){ + /* start erase the option bytes */ + FMC_CTL |= FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_START; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* reset the OBER bit and enable the option bytes programming */ + FMC_CTL &= ~FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_OBPG; + + for(i = 0U; i < 4U; i++){ + OP_BYTE(i) = op_byte[i]; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + if(FMC_READY != fmc_state){ + break; + } + } + /* reset the OBPG bit */ + FMC_CTL &= ~FMC_CTL_OBPG; + }else{ + /* reset the OBER bit */ + FMC_CTL &= ~FMC_CTL_OBER; + } + } + + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief get the value of option bytes USER + \param[in] none + \param[out] none + \retval the option bytes USER value +*/ +uint8_t ob_user_get(void) +{ + /* return the FMC user option bytes value */ + return (uint8_t)(FMC_OBSTAT >> FMC_OBSTAT_USER_OFFSET); +} + +/*! + \brief get the value of option bytes DATA + \param[in] none + \param[out] none + \retval the option bytes DATA value +*/ +uint16_t ob_data_get(void) +{ + return (uint16_t)(FMC_OBSTAT >> FMC_OBSTAT_DATA_OFFSET); +} + +/*! + \brief get the value of option bytes write protection + \param[in] none + \param[out] none + \retval the write protection option bytes value +*/ +uint32_t ob_write_protection_get(void) +{ + /* return the FMC write protection option bytes value */ + return FMC_WP; +} + +/*! + \brief get the FMC option bytes security protection state + \param[in] none + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus ob_security_protection_flag_get(void) +{ + FlagStatus spc_state = RESET; + + if(RESET != (FMC_OBSTAT & FMC_OBSTAT_SPC)){ + spc_state = SET; + }else{ + spc_state = RESET; + } + return spc_state; +} + +/*! + \brief get FMC flag status + \param[in] flag: FMC flag + only one parameter can be selected which is shown as below: + \arg FMC_FLAG_BUSY: FMC busy flag + \arg FMC_FLAG_PGERR: FMC program error flag + \arg FMC_FLAG_PGAERR: FMC program alignment error flag + \arg FMC_FLAG_WPERR: FMC erase/program protection error flag + \arg FMC_FLAG_END: FMC end of operation flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus fmc_flag_get(uint32_t flag) +{ + FlagStatus status = RESET; + + if(FMC_STAT & flag){ + status = SET; + } + /* return the state of corresponding FMC flag */ + return status; +} + +/*! + \brief clear the FMC flag + \param[in] flag: FMC flag + only one parameter can be selected which is shown as below: + \arg FMC_FLAG_PGERR: FMC program error flag + \arg FMC_FLAG_PGAERR: FMC program alignment error flag + \arg FMC_FLAG_WPERR: FMC erase/program protection error flag + \arg FMC_FLAG_END: FMC end of operation flag + \param[out] none + \retval none +*/ +void fmc_flag_clear(uint32_t flag) +{ + /* clear the flags */ + FMC_STAT = flag; +} + +/*! + \brief enable FMC interrupt + \param[in] interrupt: the FMC interrupt + only one parameter can be selected which is shown as below: + \arg FMC_INT_END: FMC end of operation interrupt + \arg FMC_INT_ERR: FMC error interrupt + \param[out] none + \retval none +*/ +void fmc_interrupt_enable(uint32_t interrupt) +{ + FMC_CTL |= interrupt; +} + +/*! + \brief disable FMC interrupt + \param[in] interrupt: the FMC interrupt + only one parameter can be selected which is shown as below: + \arg FMC_INT_END: FMC end of operation interrupt + \arg FMC_INT_ERR: FMC error interrupt + \param[out] none + \retval none +*/ +void fmc_interrupt_disable(uint32_t interrupt) +{ + FMC_CTL &= ~(uint32_t)interrupt; +} + +/*! + \brief get FMC interrupt flag + \param[in] flag: FMC interrupt flag + only one parameter can be selected which is shown as below: + \arg FMC_INT_FLAG_PGERR: FMC operation error interrupt flag + \arg FMC_INT_FLAG_PGAERR: FMC program alignment error interrupt flag + \arg FMC_INT_FLAG_WPERR: FMC erase/program protection error interrupt flag + \arg FMC_INT_FLAG_END: FMC end of operation interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus fmc_interrupt_flag_get(uint32_t flag) +{ + FlagStatus status = RESET; + + if(FMC_STAT & flag){ + status = SET; + } + /* return the state of corresponding FMC flag */ + return status; +} + +/*! + \brief clear FMC interrupt flag + \param[in] flag: FMC interrupt flag + one or more parameters can be selected which is shown as below: + \arg FMC_INT_FLAG_PGERR: FMC operation error interrupt flag + \arg FMC_INT_FLAG_PGAERR: FMC program alignment error interrupt flag + \arg FMC_INT_FLAG_WPERR: FMC erase/program protection error interrupt flag + \arg FMC_INT_FLAG_END: FMC end of operation interrupt flag + \param[out] none + \retval none +*/ +void fmc_interrupt_flag_clear(uint32_t flag) +{ + /* clear the flag */ + FMC_STAT = flag; +} + +/*! + \brief get the FMC state + \param[in] none + \param[out] none + \retval state of FMC, refer to fmc_state_enum +*/ +static fmc_state_enum fmc_state_get(void) +{ + fmc_state_enum fmc_state = FMC_READY; + + if((uint32_t)0x00U != (FMC_STAT & FMC_STAT_BUSY)){ + fmc_state = FMC_BUSY; + }else{ + if((uint32_t)0x00U != (FMC_STAT & FMC_STAT_WPERR)){ + fmc_state = FMC_WPERR; + }else{ + if((uint32_t)0x00U != (FMC_STAT & (FMC_STAT_PGERR))){ + fmc_state = FMC_PGERR; + }else{ + if((uint32_t)0x00U != (FMC_STAT & (FMC_STAT_PGAERR))){ + fmc_state = FMC_PGAERR; + } + } + } + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief check whether FMC is ready or not + \param[in] timeout: timeout count + \param[out] none + \retval state of FMC, refer to fmc_state_enum +*/ +static fmc_state_enum fmc_ready_wait(uint32_t timeout) +{ + fmc_state_enum fmc_state = FMC_BUSY; + + /* wait for FMC ready */ + do{ + /* get FMC state */ + fmc_state = fmc_state_get(); + timeout--; + }while((FMC_BUSY == fmc_state) && (0x00U != timeout)); + + if(FMC_BUSY == fmc_state){ + fmc_state = FMC_TOERR; + } + /* return the FMC state */ + return fmc_state; +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_fmc.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_fmc.c.base@1.5.0 new file mode 100644 index 0000000..a220f53 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_fmc.c.base@1.5.0 @@ -0,0 +1,875 @@ +/*! + \file gd32e50x_fmc.c + \brief FMC driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_fmc.h" + +/* FMC mask */ +#define LOW_8BITS_MASK ((uint32_t)0x000000FFU) +#define HIGH_8BITS_MASK ((uint32_t)0x0000FF00U) +#define LOW_8BITS_MASK1 ((uint32_t)0x00FF0000U) +#define HIGH_8BITS_MASK1 ((uint32_t)0xFF000000U) +#define LOW_16BITS_MASK ((uint32_t)0x0000FFFFU) +#define HIGH_16BITS_MASK ((uint32_t)0xFFFF0000U) + +/* USER of option bytes mask */ +#define OB_USER_MASK ((uint8_t)0x38U) + +/* default offset */ +#define FMC_OBSTAT_USER_OFFSET 2U +#define FMC_OBSTAT_DATA_OFFSET 10U + +/* return the FMC state */ +static fmc_state_enum fmc_state_get(void); +/* check FMC ready or not */ +static fmc_state_enum fmc_ready_wait(uint32_t timeout); + +/*! + \brief unlock the main FMC operation + it is better to used in pairs with fmc_lock + \param[in] none + \param[out] none + \retval none +*/ +void fmc_unlock(void) +{ + if(RESET != (FMC_CTL & FMC_CTL_LK)){ + /* write the FMC unlock key */ + FMC_KEY = UNLOCK_KEY0; + FMC_KEY = UNLOCK_KEY1; + } +} + +/*! + \brief lock the main FMC operation + it is better to used in pairs with fmc_unlock after an operation + \param[in] none + \param[out] none + \retval none +*/ +void fmc_lock(void) +{ + /* set the LK bit */ + FMC_CTL |= FMC_CTL_LK; +} + +/*! + \brief set the wait state + \param[in] wscnt: wait state + only one parameter can be selected which is shown as below: + \arg FMC_WAIT_STATE_0: 0 wait state added + \arg FMC_WAIT_STATE_1: 1 wait state added + \arg FMC_WAIT_STATE_2: 2 wait state added + \arg FMC_WAIT_STATE_3: 3 wait state added + \arg FMC_WAIT_STATE_4: 4 wait state added + \param[out] none + \retval none +*/ +void fmc_wscnt_set(uint32_t wscnt) +{ + uint32_t ws; + + ws = FMC_WS; + + /* set the wait state counter value */ + ws &= ~FMC_WS_WSCNT; + FMC_WS = (ws | wscnt); +} + +/*! + \brief enable pre-fetch + \param[in] none + \param[out] none + \retval none +*/ +void fmc_prefetch_enable(void) +{ + FMC_WS |= FMC_WS_PFEN; +} + +/*! + \brief disable pre-fetch + \param[in] none + \param[out] none + \retval none +*/ +void fmc_prefetch_disable(void) +{ + FMC_WS &= ~FMC_WS_PFEN; +} + +/*! + \brief enable IBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_ibus_enable(void) +{ + FMC_WS |= FMC_WS_ICEN; +} + +/*! + \brief disable IBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_ibus_disable(void) +{ + FMC_WS &= ~FMC_WS_ICEN; +} + +/*! + \brief reset IBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_ibus_reset(void) +{ + FMC_WS |= FMC_WS_ICRST; +} + +/*! + \brief enable DBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_dbus_enable(void) +{ + FMC_WS |= FMC_WS_DCEN; +} + +/*! + \brief disable DBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_dbus_disable(void) +{ + FMC_WS &= ~FMC_WS_DCEN; +} + +/*! + \brief reset DBUS cache + \param[in] none + \param[out] none + \retval none +*/ +void fmc_dbus_reset(void) +{ + FMC_WS |= FMC_WS_DCRST; +} + +/*! + \brief FMC erase page + \param[in] page_address: target page address + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error +*/ +fmc_state_enum fmc_page_erase(uint32_t page_address) +{ + fmc_state_enum fmc_state; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* start page erase */ + FMC_CTL |= FMC_CTL_PER; + FMC_ADDR = page_address; + FMC_CTL |= FMC_CTL_START; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + /* reset the PER bit */ + FMC_CTL &= ~FMC_CTL_PER; + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief FMC erase whole chip + \param[in] none + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error +*/ +fmc_state_enum fmc_mass_erase(void) +{ + fmc_state_enum fmc_state; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* start chip erase */ + FMC_CTL |= FMC_CTL_MER; + FMC_CTL |= FMC_CTL_START; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + /* reset the MER bit */ + FMC_CTL &= ~FMC_CTL_MER; + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief FMC program a word at the corresponding address + \param[in] address: address to program + \param[in] data: word to program + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error +*/ +fmc_state_enum fmc_word_program(uint32_t address, uint32_t data) +{ + fmc_state_enum fmc_state; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* set the PG bit to start program */ + FMC_CTL |= FMC_CTL_PG; + REG32(address) = data; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + /* reset the PG bit */ + FMC_CTL &= ~FMC_CTL_PG; + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief unlock the option bytes operation + it is better to used in pairs with ob_lock + \param[in] none + \param[out] none + \retval none +*/ +void ob_unlock(void) +{ + if(RESET == (FMC_CTL & FMC_CTL_OBWEN)){ + /* write the FMC ob unlock key */ + FMC_OBKEY = UNLOCK_KEY0; + FMC_OBKEY = UNLOCK_KEY1; + } +} + +/*! + \brief lock the option bytes operation + it is better to used in pairs with ob_unlock after an operation + \param[in] none + \param[out] none + \retval none +*/ +void ob_lock(void) +{ + /* reset the OBWEN bit */ + FMC_CTL &= ~FMC_CTL_OBWEN; +} + +/*! + \brief erase the option bytes + programmer must ensure FMC & option bytes are both unlocked before calling this function + \param[in] none + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error + \arg FMC_OB_HSPC: high security protection +*/ +fmc_state_enum ob_erase(void) +{ + uint32_t temp_spc; + fmc_state_enum fmc_state; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + temp_spc = OB_SPC_USER; + + /* check the option bytes security protection value */ + if((RESET != ob_security_protection_flag_get()) && (FMC_HSPC == (temp_spc & LOW_8BITS_MASK))){ + fmc_state = FMC_OB_HSPC; + } + + if(FMC_READY == fmc_state){ + /* start erase the option bytes */ + FMC_CTL |= FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_START; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* reset the OBER bit and enable the option bytes programming */ + FMC_CTL &= ~FMC_CTL_OBER; + /* set the OBPG bit */ + FMC_CTL |= FMC_CTL_OBPG; + /* restore the last get option bytes security protection code */ + OB_SPC_USER = (temp_spc & LOW_8BITS_MASK) | LOW_8BITS_MASK1; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + /* reset the OBPG bit */ + FMC_CTL &= ~FMC_CTL_OBPG; + }else{ + /* reset the OBER bit */ + FMC_CTL &= ~FMC_CTL_OBER; + } + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief enable write protection + programmer must ensure FMC & option bytes are both unlocked before calling this function + \param[in] ob_wp: specify sector to be write protected + only one parameter can be selected which are shown as below: + \arg OB_WP_NONE: disable all write protection + \arg OB_WP_x(x=0..31): write protect specify sector + \arg OB_WP_ALL: write protect all sector + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error + \arg FMC_OB_HSPC: high security protection +*/ +fmc_state_enum ob_write_protection_enable(uint32_t ob_wp) +{ + uint32_t temp_spc; + uint8_t i; + uint32_t op_byte[4]; + + fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + temp_spc = OB_SPC_USER; + + /* check the option bytes security protection value */ + if((RESET != ob_security_protection_flag_get()) && (FMC_HSPC == (temp_spc & LOW_8BITS_MASK))){ + fmc_state = FMC_OB_HSPC; + } + + for(i = 0U; i < 4U; i++){ + op_byte[i] = OP_BYTE(i); + } + + ob_wp = (uint32_t)(~ob_wp); + op_byte[2] = (ob_wp & LOW_8BITS_MASK) | ((ob_wp & HIGH_8BITS_MASK) << 8); + op_byte[3] = ((ob_wp & LOW_8BITS_MASK1) >> 16) | ((ob_wp & HIGH_8BITS_MASK1) >> 8); + + if(FMC_READY == fmc_state){ + /* start erase the option byte */ + FMC_CTL |= FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_START; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* reset the OBER bit and enable the option bytes programming */ + FMC_CTL &= ~FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_OBPG; + + for(i = 0U; i < 4U; i++){ + OP_BYTE(i) = op_byte[i]; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + if(FMC_READY != fmc_state){ + break; + } + } + /* reset the OBPG bit */ + FMC_CTL &= ~FMC_CTL_OBPG; + }else{ + /* reset the OBER bit */ + FMC_CTL &= ~FMC_CTL_OBER; + } + } + + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief configure security protection + programmer must ensure FMC & option bytes are both unlocked before calling this function + \param[in] ob_spc: specify security protection + only one parameter can be selected which is shown as below: + \arg FMC_NSPC: no security protection + \arg FMC_LSPC: low security protection + \arg FMC_HSPC: high security protection + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error + \arg FMC_OB_HSPC: high security protection +*/ +fmc_state_enum ob_security_protection_config(uint8_t ob_spc) +{ + uint32_t temp_spc; + uint8_t i; + uint32_t op_byte[4]; + + fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + temp_spc = OB_SPC_USER & LOW_16BITS_MASK; + + /* check the option bytes security protection value */ + if((RESET != ob_security_protection_flag_get()) && (FMC_HSPC == (temp_spc & LOW_8BITS_MASK))){ + fmc_state = FMC_OB_HSPC; + } + + for(i = 0U; i < 4U; i++){ + op_byte[i] = OP_BYTE(i); + } + op_byte[0] = ((uint32_t)(ob_spc)) | ((op_byte[0] & HIGH_16BITS_MASK)); + + if(FMC_READY == fmc_state){ + /* start erase the option bytes */ + FMC_CTL |= FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_START; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* reset the OBER bit and enable the option bytes programming */ + FMC_CTL &= ~FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_OBPG; + + for(i = 0U; i < 4U; i++){ + OP_BYTE(i) = op_byte[i]; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + if(FMC_READY != fmc_state){ + break; + } + } + + /* reset the OBPG bit */ + FMC_CTL &= ~FMC_CTL_OBPG; + }else{ + /* reset the OBER bit */ + FMC_CTL &= ~FMC_CTL_OBER; + } + } + + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief program option bytes USER + programmer must ensure FMC & option bytes are both unlocked before calling this function + \param[in] ob_fwdgt: option bytes free watchdog value + only one parameter can be selected which is shown as below: + \arg OB_FWDGT_SW: software free watchdog + \arg OB_FWDGT_HW: hardware free watchdog + \param[in] ob_deepsleep: option bytes deepsleep reset value + only one parameter can be selected which is shown as below: + \arg OB_DEEPSLEEP_NRST: no reset when entering deepsleep mode + \arg OB_DEEPSLEEP_RST: generate a reset instead of entering deepsleep mode + \param[in] ob_stdby:option bytes standby reset value + only one parameter can be selected which is shown as below: + \arg OB_STDBY_NRST: no reset when entering standby mode + \arg OB_STDBY_RST: generate a reset instead of entering standby mode + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error + \arg FMC_OB_HSPC: high security protection +*/ +fmc_state_enum ob_user_write(uint8_t ob_fwdgt, uint8_t ob_deepsleep, uint8_t ob_stdby) +{ + uint32_t temp_spc; + uint8_t temp; + uint8_t i; + uint32_t op_byte[4]; + + fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + temp_spc = OB_SPC_USER; + + /* check the option bytes security protection value */ + if((RESET != ob_security_protection_flag_get()) && (FMC_HSPC == (temp_spc & LOW_8BITS_MASK))){ + fmc_state = FMC_OB_HSPC; + } + + for(i = 0U; i < 4U; i++){ + op_byte[i] = OP_BYTE(i); + } + temp = ((uint8_t)((uint8_t)(((uint8_t)(ob_fwdgt) | ob_deepsleep) | ob_stdby)) | (OB_USER_MASK)); + op_byte[0] = ((uint32_t)(temp) << 16U) | ((op_byte[0] & LOW_16BITS_MASK)); + + if(FMC_READY == fmc_state){ + /* start erase the option bytes */ + FMC_CTL |= FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_START; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + if(FMC_READY == fmc_state){ + /* reset the OBER bit and enable the option bytes programming */ + FMC_CTL &= ~FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_OBPG; + + for(i = 0U; i < 4U; i++){ + OP_BYTE(i) = op_byte[i]; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + if(FMC_READY != fmc_state){ + break; + } + } + + /* reset the OBPG bit */ + FMC_CTL &= ~FMC_CTL_OBPG; + }else{ + /* reset the OBER bit */ + FMC_CTL &= ~FMC_CTL_OBER; + } + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief program option bytes DATA + \param[in] data: the byte to be programmed + \param[out] none + \retval state of FMC + \arg FMC_READY: the operation has been completed + \arg FMC_PGERR: program error + \arg FMC_PGAERR: program alignment error + \arg FMC_WPERR: erase/program protection error + \arg FMC_TOERR: timeout error + \arg FMC_OB_HSPC: high security protection +*/ +fmc_state_enum ob_data_program(uint16_t ob_data) +{ + uint32_t temp_spc; + uint8_t i; + uint32_t op_byte[4]; + + fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + temp_spc = OB_SPC_USER; + + /* check the option bytes security protection value */ + if((RESET != ob_security_protection_flag_get()) && (FMC_HSPC == (temp_spc & LOW_8BITS_MASK))){ + fmc_state = FMC_OB_HSPC; + } + + for(i = 0U; i < 4U; i++){ + op_byte[i] = OP_BYTE(i); + } + op_byte[1] = (uint32_t)((ob_data & LOW_8BITS_MASK) | ((ob_data & HIGH_8BITS_MASK) << 8)); + + if(FMC_READY == fmc_state){ + /* start erase the option bytes */ + FMC_CTL |= FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_START; + + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + + if(FMC_READY == fmc_state){ + /* reset the OBER bit and enable the option bytes programming */ + FMC_CTL &= ~FMC_CTL_OBER; + FMC_CTL |= FMC_CTL_OBPG; + + for(i = 0U; i < 4U; i++){ + OP_BYTE(i) = op_byte[i]; + /* wait for the FMC ready */ + fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); + if(FMC_READY != fmc_state){ + break; + } + } + /* reset the OBPG bit */ + FMC_CTL &= ~FMC_CTL_OBPG; + }else{ + /* reset the OBER bit */ + FMC_CTL &= ~FMC_CTL_OBER; + } + } + + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief get the value of option bytes USER + \param[in] none + \param[out] none + \retval the option bytes USER value +*/ +uint8_t ob_user_get(void) +{ + /* return the FMC user option bytes value */ + return (uint8_t)(FMC_OBSTAT >> FMC_OBSTAT_USER_OFFSET); +} + +/*! + \brief get the value of option bytes DATA + \param[in] none + \param[out] none + \retval the option bytes DATA value +*/ +uint16_t ob_data_get(void) +{ + return (uint16_t)(FMC_OBSTAT >> FMC_OBSTAT_DATA_OFFSET); +} + +/*! + \brief get the value of option bytes write protection + \param[in] none + \param[out] none + \retval the write protection option bytes value +*/ +uint32_t ob_write_protection_get(void) +{ + /* return the FMC write protection option bytes value */ + return FMC_WP; +} + +/*! + \brief get the FMC option bytes security protection state + \param[in] none + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus ob_security_protection_flag_get(void) +{ + FlagStatus spc_state = RESET; + + if(RESET != (FMC_OBSTAT & FMC_OBSTAT_SPC)){ + spc_state = SET; + }else{ + spc_state = RESET; + } + return spc_state; +} + +/*! + \brief get FMC flag status + \param[in] flag: FMC flag + only one parameter can be selected which is shown as below: + \arg FMC_FLAG_BUSY: FMC busy flag + \arg FMC_FLAG_PGERR: FMC program error flag + \arg FMC_FLAG_PGAERR: FMC program alignment error flag + \arg FMC_FLAG_WPERR: FMC erase/program protection error flag + \arg FMC_FLAG_END: FMC end of operation flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus fmc_flag_get(uint32_t flag) +{ + FlagStatus status = RESET; + + if(FMC_STAT & flag){ + status = SET; + } + /* return the state of corresponding FMC flag */ + return status; +} + +/*! + \brief clear the FMC flag + \param[in] flag: FMC flag + only one parameter can be selected which is shown as below: + \arg FMC_FLAG_PGERR: FMC program error flag + \arg FMC_FLAG_PGAERR: FMC program alignment error flag + \arg FMC_FLAG_WPERR: FMC erase/program protection error flag + \arg FMC_FLAG_END: FMC end of operation flag + \param[out] none + \retval none +*/ +void fmc_flag_clear(uint32_t flag) +{ + /* clear the flags */ + FMC_STAT = flag; +} + +/*! + \brief enable FMC interrupt + \param[in] interrupt: the FMC interrupt + only one parameter can be selected which is shown as below: + \arg FMC_INT_END: FMC end of operation interrupt + \arg FMC_INT_ERR: FMC error interrupt + \param[out] none + \retval none +*/ +void fmc_interrupt_enable(uint32_t interrupt) +{ + FMC_CTL |= interrupt; +} + +/*! + \brief disable FMC interrupt + \param[in] interrupt: the FMC interrupt + only one parameter can be selected which is shown as below: + \arg FMC_INT_END: FMC end of operation interrupt + \arg FMC_INT_ERR: FMC error interrupt + \param[out] none + \retval none +*/ +void fmc_interrupt_disable(uint32_t interrupt) +{ + FMC_CTL &= ~(uint32_t)interrupt; +} + +/*! + \brief get FMC interrupt flag + \param[in] flag: FMC interrupt flag + only one parameter can be selected which is shown as below: + \arg FMC_INT_FLAG_PGERR: FMC operation error interrupt flag + \arg FMC_INT_FLAG_PGAERR: FMC program alignment error interrupt flag + \arg FMC_INT_FLAG_WPERR: FMC erase/program protection error interrupt flag + \arg FMC_INT_FLAG_END: FMC end of operation interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus fmc_interrupt_flag_get(uint32_t flag) +{ + FlagStatus status = RESET; + + if(FMC_STAT & flag){ + status = SET; + } + /* return the state of corresponding FMC flag */ + return status; +} + +/*! + \brief clear FMC interrupt flag + \param[in] flag: FMC interrupt flag + one or more parameters can be selected which is shown as below: + \arg FMC_INT_FLAG_PGERR: FMC operation error interrupt flag + \arg FMC_INT_FLAG_PGAERR: FMC program alignment error interrupt flag + \arg FMC_INT_FLAG_WPERR: FMC erase/program protection error interrupt flag + \arg FMC_INT_FLAG_END: FMC end of operation interrupt flag + \param[out] none + \retval none +*/ +void fmc_interrupt_flag_clear(uint32_t flag) +{ + /* clear the flag */ + FMC_STAT = flag; +} + +/*! + \brief get the FMC state + \param[in] none + \param[out] none + \retval state of FMC, refer to fmc_state_enum +*/ +static fmc_state_enum fmc_state_get(void) +{ + fmc_state_enum fmc_state = FMC_READY; + + if((uint32_t)0x00U != (FMC_STAT & FMC_STAT_BUSY)){ + fmc_state = FMC_BUSY; + }else{ + if((uint32_t)0x00U != (FMC_STAT & FMC_STAT_WPERR)){ + fmc_state = FMC_WPERR; + }else{ + if((uint32_t)0x00U != (FMC_STAT & (FMC_STAT_PGERR))){ + fmc_state = FMC_PGERR; + }else{ + if((uint32_t)0x00U != (FMC_STAT & (FMC_STAT_PGAERR))){ + fmc_state = FMC_PGAERR; + } + } + } + } + /* return the FMC state */ + return fmc_state; +} + +/*! + \brief check whether FMC is ready or not + \param[in] timeout: timeout count + \param[out] none + \retval state of FMC, refer to fmc_state_enum +*/ +static fmc_state_enum fmc_ready_wait(uint32_t timeout) +{ + fmc_state_enum fmc_state = FMC_BUSY; + + /* wait for FMC ready */ + do{ + /* get FMC state */ + fmc_state = fmc_state_get(); + timeout--; + }while((FMC_BUSY == fmc_state) && (0x00U != timeout)); + + if(FMC_BUSY == fmc_state){ + fmc_state = FMC_TOERR; + } + /* return the FMC state */ + return fmc_state; +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_gpio.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_gpio.c new file mode 100644 index 0000000..fcff5ba --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_gpio.c @@ -0,0 +1,699 @@ +/*! + \file gd32e50x_gpio.c + \brief GPIO driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_gpio.h" + +#define AFIO_EXTI_SOURCE_MASK ((uint8_t)0x03U) /*!< AFIO exti source selection mask*/ +#define AFIO_EXTI_SOURCE_FIELDS ((uint8_t)0x04U) /*!< select AFIO exti source registers */ +#define LSB_16BIT_MASK ((uint16_t)0xFFFFU) /*!< LSB 16-bit mask */ +#define PCF_POSITION_MASK ((uint32_t)0x000F0000U) /*!< AFIO_PCF register position mask */ +#define PCF_SWJCFG_MASK ((uint32_t)0xF8FFFFFFU) /*!< AFIO_PCF register SWJCFG mask */ +#define PCF_LOCATION1_MASK ((uint32_t)0x00200000U) /*!< AFIO_PCF register location1 mask */ +#define PCF_LOCATION2_MASK ((uint32_t)0x00100000U) /*!< AFIO_PCF register location2 mask */ +#define AFIO_PCF1_FIELDS ((uint32_t)0x80000000U) /*!< select AFIO_PCF1 register */ +#define GPIO_OUTPUT_PORT_OFFSET ((uint32_t)4U) /*!< GPIO event output port offset*/ + +/*! + \brief reset GPIO port + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval none +*/ +void gpio_deinit(uint32_t gpio_periph) +{ + switch(gpio_periph){ + case GPIOA: + /* reset GPIOA */ + rcu_periph_reset_enable(RCU_GPIOARST); + rcu_periph_reset_disable(RCU_GPIOARST); + break; + case GPIOB: + /* reset GPIOB */ + rcu_periph_reset_enable(RCU_GPIOBRST); + rcu_periph_reset_disable(RCU_GPIOBRST); + break; + case GPIOC: + /* reset GPIOC */ + rcu_periph_reset_enable(RCU_GPIOCRST); + rcu_periph_reset_disable(RCU_GPIOCRST); + break; + case GPIOD: + /* reset GPIOD */ + rcu_periph_reset_enable(RCU_GPIODRST); + rcu_periph_reset_disable(RCU_GPIODRST); + break; + case GPIOE: + /* reset GPIOE */ + rcu_periph_reset_enable(RCU_GPIOERST); + rcu_periph_reset_disable(RCU_GPIOERST); + break; + case GPIOF: + /* reset GPIOF */ + rcu_periph_reset_enable(RCU_GPIOFRST); + rcu_periph_reset_disable(RCU_GPIOFRST); + break; + case GPIOG: + /* reset GPIOG */ + rcu_periph_reset_enable(RCU_GPIOGRST); + rcu_periph_reset_disable(RCU_GPIOGRST); + break; + default: + break; + } +} + +/*! + \brief reset alternate function I/O(AFIO) + \param[in] none + \param[out] none + \retval none +*/ +void gpio_afio_deinit(void) +{ + rcu_periph_reset_enable(RCU_AFRST); + rcu_periph_reset_disable(RCU_AFRST); +} + +/*! + \brief GPIO parameter initialization + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] mode: gpio pin mode + only one parameter can be selected which is shown as below: + \arg GPIO_MODE_AIN: analog input mode + \arg GPIO_MODE_IN_FLOATING: floating input mode + \arg GPIO_MODE_IPD: pull-down input mode + \arg GPIO_MODE_IPU: pull-up input mode + \arg GPIO_MODE_OUT_OD: GPIO output with open-drain + \arg GPIO_MODE_OUT_PP: GPIO output with push-pull + \arg GPIO_MODE_AF_OD: AFIO output with open-drain + \arg GPIO_MODE_AF_PP: AFIO output with push-pull + \param[in] speed: gpio output max speed value + only one parameter can be selected which is shown as below: + \arg GPIO_OSPEED_10MHZ: output max speed 10MHz + \arg GPIO_OSPEED_2MHZ: output max speed 2MHz + \arg GPIO_OSPEED_50MHZ: output max speed 50MHz + \arg GPIO_OSPEED_MAX: output max speed more than 50MHz + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_init(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin) +{ + uint16_t i; + uint32_t temp_mode = 0U; + uint32_t reg = 0U; + + /* GPIO mode configuration */ + temp_mode = (uint32_t)(mode & ((uint32_t)0x0FU)); + + /* GPIO speed configuration */ + if(((uint32_t)0x00U) != ((uint32_t)mode & ((uint32_t)0x10U))){ + /* output mode max speed */ + if(GPIO_OSPEED_MAX == (uint32_t)speed){ + temp_mode |= (uint32_t)0x03U; + /* set the corresponding SPD bit */ + GPIOx_SPD(gpio_periph) |= (uint32_t)pin ; + }else{ + /* output mode max speed:10MHz,2MHz,50MHz */ + temp_mode |= (uint32_t)speed; + } + } + + /* configure the eight low port pins with GPIO_CTL0 */ + for(i = 0U;i < 8U;i++){ + if((1U << i) & pin){ + reg = GPIO_CTL0(gpio_periph); + + /* clear the specified pin mode bits */ + reg &= ~GPIO_MODE_MASK(i); + /* set the specified pin mode bits */ + reg |= GPIO_MODE_SET(i, temp_mode); + + /* set IPD or IPU */ + if(GPIO_MODE_IPD == mode){ + /* reset the corresponding OCTL bit */ + GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin); + }else{ + /* set the corresponding OCTL bit */ + if(GPIO_MODE_IPU == mode){ + GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin); + } + } + /* set GPIO_CTL0 register */ + GPIO_CTL0(gpio_periph) = reg; + } + } + /* configure the eight high port pins with GPIO_CTL1 */ + for(i = 8U;i < 16U;i++){ + if((1U << i) & pin){ + reg = GPIO_CTL1(gpio_periph); + + /* clear the specified pin mode bits */ + reg &= ~GPIO_MODE_MASK(i - 8U); + /* set the specified pin mode bits */ + reg |= GPIO_MODE_SET(i - 8U, temp_mode); + + /* set IPD or IPU */ + if(GPIO_MODE_IPD == mode){ + /* reset the corresponding OCTL bit */ + GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin); + }else{ + /* set the corresponding OCTL bit */ + if(GPIO_MODE_IPU == mode){ + GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin); + } + } + /* set GPIO_CTL1 register */ + GPIO_CTL1(gpio_periph) = reg; + } + } +} + +/*! + \brief set GPIO pin bit + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_bit_set(uint32_t gpio_periph,uint32_t pin) +{ + GPIO_BOP(gpio_periph) = (uint32_t)pin; +} + +/*! + \brief reset GPIO pin bit + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_bit_reset(uint32_t gpio_periph,uint32_t pin) +{ + GPIO_BC(gpio_periph) = (uint32_t)pin; +} + +/*! + \brief write data to the specified GPIO pin + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[in] bit_value: SET or RESET + \arg RESET: clear the port pin + \arg SET: set the port pin + \param[out] none + \retval none +*/ +void gpio_bit_write(uint32_t gpio_periph,uint32_t pin,bit_status bit_value) +{ + if(RESET != bit_value){ + GPIO_BOP(gpio_periph) = (uint32_t)pin; + }else{ + GPIO_BC(gpio_periph) = (uint32_t)pin; + } +} + +/*! + \brief write data to the specified GPIO port + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] data: specify the value to be written to the port output data register + \param[out] none + \retval none +*/ +void gpio_port_write(uint32_t gpio_periph,uint16_t data) +{ + GPIO_OCTL(gpio_periph) = (uint32_t)data; +} + +/*! + \brief get GPIO pin input status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + only one parameter can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval input status of gpio pin: SET or RESET +*/ +FlagStatus gpio_input_bit_get(uint32_t gpio_periph,uint32_t pin) +{ + if((uint32_t)RESET != (GPIO_ISTAT(gpio_periph)&(pin))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief get GPIO port input status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval input status of gpio all pins +*/ +uint16_t gpio_input_port_get(uint32_t gpio_periph) +{ + return (uint16_t)(GPIO_ISTAT(gpio_periph)); +} + +/*! + \brief get GPIO pin output status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + only one parameter can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval output status of gpio pin: SET or RESET +*/ +FlagStatus gpio_output_bit_get(uint32_t gpio_periph,uint32_t pin) +{ + if((uint32_t)RESET !=(GPIO_OCTL(gpio_periph)&(pin))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief get GPIO port output status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval output status of gpio all pins +*/ +uint16_t gpio_output_port_get(uint32_t gpio_periph) +{ + return ((uint16_t)GPIO_OCTL(gpio_periph)); +} + +/*! + \brief configure GPIO pin remap + \param[in] gpio_remap: select the pin to remap + only one parameter can be selected which are shown as below: + \arg GPIO_SPI0_REMAP: SPI0 remapping + \arg GPIO_I2C0_REMAP: I2C0 remapping + \arg GPIO_USART0_REMAP: USART0 remapping + \arg GPIO_USART1_REMAP: USART1 remapping + \arg GPIO_USART2_PARTIAL_REMAP: USART2 partial remapping + \arg GPIO_USART2_FULL_REMAP: USART2 full remapping + \arg GPIO_TIMER0_PARTIAL_REMAP: TIMER0 partial remapping + \arg GPIO_TIMER0_FULL_REMAP: TIMER0 full remapping + \arg GPIO_TIMER1_PARTIAL_REMAP0: TIMER1 partial remapping + \arg GPIO_TIMER1_PARTIAL_REMAP1: TIMER1 partial remapping + \arg GPIO_TIMER1_FULL_REMAP: TIMER1 full remapping + \arg GPIO_TIMER2_PARTIAL_REMAP: TIMER2 partial remapping + \arg GPIO_TIMER2_FULL_REMAP: TIMER2 full remapping + \arg GPIO_TIMER3_REMAP: TIMER3 remapping + \arg GPIO_CAN0_PARTIAL_REMAP: CAN0 partial remapping(not support on GD32EPRT devices) + \arg GPIO_CAN0_FULL_REMAP: CAN0 full remapping(not support on GD32EPRT devices) + \arg GPIO_PD01_REMAP: PD01 remapping + \arg GPIO_TIMER4CH3_IREMAP: TIMER4 channel3 internal remapping + \arg GPIO_ADC0_ETRGRT_REMAP: ADC0 external trigger routine conversion remapping(only for GD32E50X_HD devices) + \arg GPIO_ADC1_ETRGRT_REMAP: ADC1 external trigger routine conversion remapping(only for GD32E50X_HD devices) + \arg GPIO_ENET_REMAP: ENET remapping(only for GD32E50X_CL and GD32E508 devices) + \arg GPIO_CAN1_REMAP: CAN1 remapping(not support on GD32EPRT devices) + \arg GPIO_SWJ_NONJTRST_REMAP: full SWJ(JTAG-DP + SW-DP),but without NJTRST + \arg GPIO_SWJ_SWDPENABLE_REMAP: JTAG-DP disabled and SW-DP enabled + \arg GPIO_SWJ_DISABLE_REMAP: JTAG-DP disabled and SW-DP disabled + \arg GPIO_SPI2_REMAP: SPI2 remapping + \arg GPIO_TIMER1ITR1_REMAP: TIMER1 internal trigger 1 remapping(only for GD32E50X_CL and GD32E508 devices) + \arg GPIO_PTP_PPS_REMAP: ethernet PTP PPS remapping(only for GD32E50X_CL and GD32E508 devices) + \arg GPIO_TIMER8_REMAP: TIMER8 remapping(not support on GD32EPRT devices) + \arg GPIO_TIMER9_REMAP: TIMER9 remapping(not support on GD32EPRT devices) + \arg GPIO_TIMER10_REMAP: TIMER10 remapping(not support on GD32EPRT devices) + \arg GPIO_TIMER12_REMAP: TIMER12 remapping(not support on GD32EPRT devices) + \arg GPIO_TIMER13_REMAP: TIMER13 remapping(not support on GD32EPRT devices) + \arg GPIO_EXMC_NADV_REMAP: EXMC_NADV connect/disconnect + \arg GPIO_CTC_REMAP0: CTC remapping(PD15) + \arg GPIO_CTC_REMAP1: CTC remapping(PF0) + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void gpio_pin_remap_config(uint32_t remap, ControlStatus newvalue) +{ + uint32_t remap1 = 0U, remap2 = 0U, temp_reg = 0U, temp_mask = 0U; + + if(((uint32_t)0x80000000U) == (remap & 0x80000000U)){ + /* get AFIO_PCF1 regiter value */ + temp_reg = AFIO_PCF1; + }else{ + /* get AFIO_PCF0 regiter value */ + temp_reg = AFIO_PCF0; + } + + temp_mask = (remap & PCF_POSITION_MASK) >> 0x10U; + remap1 = remap & LSB_16BIT_MASK; + + /* judge pin remap type */ + if((PCF_LOCATION1_MASK | PCF_LOCATION2_MASK) == (remap & (PCF_LOCATION1_MASK | PCF_LOCATION2_MASK))){ + temp_reg &= PCF_SWJCFG_MASK; + AFIO_PCF0 &= PCF_SWJCFG_MASK; + }else if(PCF_LOCATION2_MASK == (remap & PCF_LOCATION2_MASK)){ + remap2 = ((uint32_t)0x03U) << temp_mask; + temp_reg &= ~remap2; + temp_reg |= ~PCF_SWJCFG_MASK; + }else{ + temp_reg &= ~(remap1 << ((remap >> 0x15U)*0x10U)); + temp_reg |= ~PCF_SWJCFG_MASK; + } + + /* set pin remap value */ + if(DISABLE != newvalue){ + temp_reg |= (remap1 << ((remap >> 0x15U)*0x10U)); + } + + if(AFIO_PCF1_FIELDS == (remap & AFIO_PCF1_FIELDS)){ + /* set AFIO_PCF1 regiter value */ + AFIO_PCF1 = temp_reg; + }else{ + /* set AFIO_PCF0 regiter value */ + AFIO_PCF0 = temp_reg; + } +} + +/*! + \brief configure AFIO port alternate function + \param[in] afio_function: select the port AFIO function(SHRTIMER not support on GD32EPRT devices) + only one parameter can be selected which are shown as below: + \arg AFIO_PA2_CMP1_CFG: configure PA2 alternate function to CMP1 + \arg AFIO_PA3_USBHS_CFG: configure PA3 alternate function to USBHS + \arg AFIO_PA5_USBHS_CFG: configure PA5 alternate function to USBHS + \arg AFIO_PA8_I2C2_CFG: configure PA8 alternate function to I2C2 + \arg AFIO_PA8_SHRTIMER_CFG: configure PA8 alternate function to SHRTIMER + \arg AFIO_PA9_CAN2_CFG: configure PA9 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PA9_I2C2_CFG: configure PA9 alternate function to I2C2 + \arg AFIO_PA9_SHRTIMER_CFG: configure PA9 alternate function to SHRTIMER + \arg AFIO_PA10_CAN2_CFG: configure PA10 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PA10_CMP5_CFG: configure PA10 alternate function to CMP5 + \arg AFIO_PA10_SHRTIMER_CFG: configure PA10 alternate function to SHRTIMER + \arg AFIO_PA11_USART5_CFG: configure PA11 alternate function to USART5 + \arg AFIO_PA11_SHRTIMER_CFG: configure PA11 alternate function to SHRTIMER + \arg AFIO_PA12_CMP1_CFG: configure PA12 alternate function to CMP1 + \arg AFIO_PA12_USART5_CFG: configure PA12 alternate function to USART5 + \arg AFIO_PA12_SHRTIMER_CFG: configure PA12 alternate function to SHRTIMER + \arg AFIO_PA15_SHRTIMER_CFG: configure PA15 alternate function to SHRTIMER + \arg AFIO_PB0_USBHS_CFG: configure PB0 alternate function to USBHS + \arg AFIO_PB1_CMP3_CFG: configure PB1 alternate function to CMP3 + \arg AFIO_PB1_USBHS_CFG: configure PB1 alternate function to USBHS + \arg AFIO_PB1_SHRTIMER_CFG: configure PB1 alternate function to SHRTIMER + \arg AFIO_PB2_USBHS_CFG: configure PB2 alternate function to USBHS + \arg AFIO_PB2_SHRTIMER_CFG: configure PB2 alternate function to SHRTIMER + \arg AFIO_PB3_SHRTIMER_CFG: configure PB3 alternate function to SHRTIMER + \arg AFIO_PB4_I2S2_CFG: configure PB4 alternate function to I2S2 + \arg AFIO_PB4_I2C2_CFG: configure PB4 alternate function to I2C2 + \arg AFIO_PB4_SHRTIMER_CFG: configure PB4 alternate function to SHRTIMER + \arg AFIO_PB5_I2C2_CFG: configure PB5 alternate function to I2C2 + \arg AFIO_PB5_USBHS_CFG: configure PB5 alternate function to USBHS + \arg AFIO_PB5_SHRTIMER_CFG: configure PB5 alternate function to SHRTIMER + \arg AFIO_PB6_SHRTIMER_CFG: configure PB6 alternate function to SHRTIMER + \arg AFIO_PB7_SHRTIMER_CFG: configure PB7 alternate function to SHRTIMER + \arg AFIO_PB8_I2C2_CFG: configure PB8 alternate function to I2C2 + \arg AFIO_PB8_SHRTIMER_CFG: configure PB8 alternate function to SHRTIMER + \arg AFIO_PB9_CMP1_CFG: configure PB9 alternate function to CMP1 + \arg AFIO_PB9_SHRTIMER_CFG: configure PB9 alternate function to SHRTIMER + \arg AFIO_PB10_CAN2_CFG: configure PB10 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PB10_USBHS_CFG: configure PB10 alternate function to USBHS + \arg AFIO_PB10_SHRTIMER_CFG: configure PB10 alternate function to SHRTIMER + \arg AFIO_PB11_CAN2_CFG: configure PB11 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PB11_USBHS_CFG: configure PB11 alternate function to USBHS + \arg AFIO_PB11_SHRTIMER_CFG: configure PB11 alternate function to SHRTIMER + \arg AFIO_PB12_USBHS_CFG: configure PB12 alternate function to USBHS + \arg AFIO_PB12_SHRTIMER_CFG: configure PB12 alternate function to SHRTIMER + \arg AFIO_PB13_USBHS_CFG: configure PB13 alternate function to USBHS + \arg AFIO_PB13_SHRTIMER_CFG: configure PB13 alternate function to SHRTIMER + \arg AFIO_PB14_I2S1_CFG: configure PB14 alternate function to I2S1 + \arg AFIO_PB14_SHRTIMER_CFG: configure PB14 alternate function to SHRTIMER + \arg AFIO_PB15_SHRTIMER_CFG: configure PB15 alternate function to SHRTIMER + \arg AFIO_PC0_USBHS_CFG: configure PC0 alternate function to USBHS + \arg AFIO_PC2_I2S1_CFG: configure PC2 alternate function to I2S1 + \arg AFIO_PC2_USBHS_CFG: configure PC2 alternate function to USBHS + \arg AFIO_PC3_USBHS_CFG: configure PC3 alternate function to USBHS + \arg AFIO_PC6_CMP5_CFG: configure PC6 alternate function to CMP5 + \arg AFIO_PC6_USART5_CFG: configure PC6 alternate function to USART5 + \arg AFIO_PC6_SHRTIMER_CFG: configure PC6 alternate function to SHRTIMER + \arg AFIO_PC7_USART5_CFG: configure PC7 alternate function to USART5 + \arg AFIO_PC7_SHRTIMER_CFG: configure PC7 alternate function to SHRTIMER + \arg AFIO_PC8_USART5_CFG: configure PC8 alternate function to USART5 + \arg AFIO_PC8_SHRTIMER_CFG: configure PC8 alternate function to SHRTIMER + \arg AFIO_PC9_I2C2_CFG: configure PC9 alternate function to I2C2 + \arg AFIO_PC9_SHRTIMER_CFG: configure PC9 alternate function to SHRTIMER + \arg AFIO_PC10_I2C2_CFG: configure PC10 alternate function to I2C2 + \arg AFIO_PC11_I2S2_CFG: configure PC11 alternate function to I2S2 + \arg AFIO_PC11_SHRTIMER_CFG: configure PC11 alternate function to SHRTIMER + \arg AFIO_PC12_SHRTIMER_CFG: configure PC12 alternate function to SHRTIMER + \arg AFIO_PD4_SHRTIMER_CFG: configure PD4 alternate function to SHRTIMER + \arg AFIO_PD5_SHRTIMER_CFG: configure PD5 alternate function to SHRTIMER + \arg AFIO_PE0_CAN2_CFG: configure PE0 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PE0_SHRTIMER_CFG: configure PE0 alternate function to SHRTIMER + \arg AFIO_PE1_CAN2_CFG: configure PE1 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PE1_SHRTIMER_CFG: configure PE1 alternate function to SHRTIMER + \arg AFIO_PE8_CMP1_CFG: configure PE8 alternate function to CMP1 + \arg AFIO_PE9_CMP3_CFG: configure PE9 alternate function to CMP3 + \arg AFIO_PE10_CMP5_CFG: configure PE10 alternate function to CMP5 + \arg AFIO_PE11_CMP5_CFG: configure PE11 alternate function to CMP5 + \arg AFIO_PE12_CMP3_CFG: configure PE12 alternate function to CMP3 + \arg AFIO_PE13_CMP1_CFG: configure PE13 alternate function to CMP1 + \arg AFIO_PG6_SHRTIMER_CFG: configure PG6 alternate function to SHRTIMER + \arg AFIO_PG7_USART5_CFG: configure PG7 alternate function to USART5 + \arg AFIO_PG7_SHRTIMER_CFG: configure PG7 alternate function to SHRTIMER + \arg AFIO_PG9_USART5_CFG: configure PG9 alternate function to USART5 + \arg AFIO_PG10_SHRTIMER_CFG: configure PG10 alternate function to SHRTIMER + \arg AFIO_PG11_SHRTIMER_CFG: configure PG11 alternate function to SHRTIMER + \arg AFIO_PG12_SHRTIMER_CFG: configure PG12 alternate function to SHRTIMER + \arg AFIO_PG13_SHRTIMER_CFG: configure PG13 alternate function to SHRTIMER + \arg AFIO_PG14_USART5_CFG: configure PG14 alternate function to USART5 + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void gpio_afio_port_config(uint32_t afio_function, ControlStatus newvalue) +{ + uint32_t remap1 = 0U, remap2 = 0U, temp_reg = 0U, temp_mask = 0U; + + /* get AFIO_PCFx(x=A,B,C,D,E,G) regiter value */ + temp_reg = REG32(AFIO+0x0000003CU+((afio_function>>24)<<2)); + + temp_mask = (afio_function & PCF_POSITION_MASK) >> 0x10U; + remap1 = afio_function & LSB_16BIT_MASK; + + /* judge port function select type */ + if(afio_function & PCF_LOCATION2_MASK){ + remap2 = ((uint32_t)0x03U) << temp_mask; + remap2 = (remap2 << (((afio_function & PCF_LOCATION1_MASK) >> 0x15U)*0x10U)); + temp_reg &= ~remap2; + }else{ + temp_reg &= ~(remap1 << (((afio_function & PCF_LOCATION1_MASK) >> 0x15U)*0x10U)); + } + + /* set pin remap value */ + if(DISABLE != newvalue){ + temp_reg |= (remap1 << (((afio_function & PCF_LOCATION1_MASK) >> 0x15U)*0x10U)); + } + + /* set AFIO_PCFx(x=A,B,C,D,E,G) regiter value */ + REG32(AFIO+0x0000003CU+((afio_function>>24)<<2)) = temp_reg; +} + +#if (defined(GD32E50X_CL) || defined(GD32E508) || defined(GD32EPRT)) +/*! + \brief select ethernet MII or RMII PHY + \param[in] enet_sel: ethernet MII or RMII PHY selection + \arg GPIO_ENET_PHY_MII: configure ethernet MAC for connection with an MII PHY + \arg GPIO_ENET_PHY_RMII: configure ethernet MAC for connection with an RMII PHY + \param[out] none + \retval none +*/ +void gpio_ethernet_phy_select(uint32_t enet_sel) +{ + /* clear AFIO_PCF0_ENET_PHY_SEL bit */ + AFIO_PCF0 &= (uint32_t)(~AFIO_PCF0_ENET_PHY_SEL); + + /* select MII or RMII PHY */ + AFIO_PCF0 |= (uint32_t)enet_sel; +} +#endif /* GD32E50X_CL||GD32E508||GD32EPRT */ + +/*! + \brief select GPIO pin exti sources + \param[in] output_port: gpio event output port + only one parameter can be selected which are shown as below: + \arg GPIO_PORT_SOURCE_GPIOA: output port source A + \arg GPIO_PORT_SOURCE_GPIOB: output port source B + \arg GPIO_PORT_SOURCE_GPIOC: output port source C + \arg GPIO_PORT_SOURCE_GPIOD: output port source D + \arg GPIO_PORT_SOURCE_GPIOE: output port source E + \arg GPIO_PORT_SOURCE_GPIOF: output port source F + \arg GPIO_PORT_SOURCE_GPIOG: output port source G + \param[in] output_pin: GPIO output pin source + only one parameter can be selected which are shown as below: + \arg GPIO_PIN_SOURCE_x(x=0..15) + \param[out] none + \retval none +*/ +void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin) +{ + uint32_t source = 0U; + source = ((uint32_t)0x0FU) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK)); + + /* select EXTI sources */ + if(GPIO_PIN_SOURCE_4 > output_pin){ + /* select EXTI0/EXTI1/EXTI2/EXTI3 */ + AFIO_EXTISS0 &= ~source; + AFIO_EXTISS0 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else if(GPIO_PIN_SOURCE_8 > output_pin){ + /* select EXTI4/EXTI5/EXTI6/EXTI7 */ + AFIO_EXTISS1 &= ~source; + AFIO_EXTISS1 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else if(GPIO_PIN_SOURCE_12 > output_pin){ + /* select EXTI8/EXTI9/EXTI10/EXTI11 */ + AFIO_EXTISS2 &= ~source; + AFIO_EXTISS2 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else{ + /* select EXTI12/EXTI13/EXTI14/EXTI15 */ + AFIO_EXTISS3 &= ~source; + AFIO_EXTISS3 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + } +} + +/*! + \brief configure GPIO pin event output + \param[in] output_port: gpio event output port + only one parameter can be selected which are shown as below: + \arg GPIO_EVENT_PORT_GPIOA: event output port A + \arg GPIO_EVENT_PORT_GPIOB: event output port B + \arg GPIO_EVENT_PORT_GPIOC: event output port C + \arg GPIO_EVENT_PORT_GPIOD: event output port D + \arg GPIO_EVENT_PORT_GPIOE: event output port E + \param[in] output_pin: GPIO event output pin + only one parameter can be selected which are shown as below: + \arg GPIO_EVENT_PIN_x(x=0..15) + \param[out] none + \retval none +*/ +void gpio_event_output_config(uint8_t output_port, uint8_t output_pin) +{ + uint32_t reg = 0U; + reg = AFIO_EC; + + /* clear AFIO_EC_PORT and AFIO_EC_PIN bits */ + reg &= (uint32_t)(~(AFIO_EC_PORT|AFIO_EC_PIN)); + + reg |= (uint32_t)((uint32_t)output_port << GPIO_OUTPUT_PORT_OFFSET); + reg |= (uint32_t)output_pin; + + AFIO_EC = reg; +} + +/*! + \brief enable GPIO pin event output + \param[in] none + \param[out] none + \retval none +*/ +void gpio_event_output_enable(void) +{ + AFIO_EC |= AFIO_EC_EOE; +} + +/*! + \brief disable GPIO pin event output + \param[in] none + \param[out] none + \retval none +*/ +void gpio_event_output_disable(void) +{ + AFIO_EC &= (uint32_t)(~AFIO_EC_EOE); +} + +/*! + \brief lock GPIO pin bit + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_pin_lock(uint32_t gpio_periph,uint32_t pin) +{ + uint32_t lock = 0x00010000U; + lock |= pin; + + /* lock key writing sequence: write 1 -> write 0 -> write 1 -> read 0 -> read 1 */ + GPIO_LOCK(gpio_periph) = (uint32_t)lock; + GPIO_LOCK(gpio_periph) = (uint32_t)pin; + GPIO_LOCK(gpio_periph) = (uint32_t)lock; + lock = GPIO_LOCK(gpio_periph); + lock = GPIO_LOCK(gpio_periph); +} + +/*! + \brief configure the I/O compensation cell + \param[in] compensation: specifies the I/O compensation cell mode + only one parameter can be selected which are shown as below: + \arg GPIO_COMPENSATION_ENABLE: I/O compensation cell is enabled + \arg GPIO_COMPENSATION_DISABLE: I/O compensation cell is disabled + \param[out] none + \retval none +*/ +void gpio_compensation_config(uint32_t compensation) +{ + uint32_t reg; + reg = AFIO_CPSCTL; + + /* reset the AFIO_CPSCTL_CPS_EN bit and set according to gpio_compensation */ + reg &= ~AFIO_CPSCTL_CPS_EN; + AFIO_CPSCTL = (reg | compensation); +} + +/*! + \brief check the I/O compensation cell is ready or not + \param[in] none + \param[out] none + \retval FlagStatus: SET or RESET + */ +FlagStatus gpio_compensation_flag_get(void) +{ + if(((uint32_t)RESET) != (AFIO_CPSCTL & AFIO_CPSCTL_CPS_RDY)){ + return SET; + }else{ + return RESET; + } +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_gpio.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_gpio.c.base@1.5.0 new file mode 100644 index 0000000..fcff5ba --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_gpio.c.base@1.5.0 @@ -0,0 +1,699 @@ +/*! + \file gd32e50x_gpio.c + \brief GPIO driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_gpio.h" + +#define AFIO_EXTI_SOURCE_MASK ((uint8_t)0x03U) /*!< AFIO exti source selection mask*/ +#define AFIO_EXTI_SOURCE_FIELDS ((uint8_t)0x04U) /*!< select AFIO exti source registers */ +#define LSB_16BIT_MASK ((uint16_t)0xFFFFU) /*!< LSB 16-bit mask */ +#define PCF_POSITION_MASK ((uint32_t)0x000F0000U) /*!< AFIO_PCF register position mask */ +#define PCF_SWJCFG_MASK ((uint32_t)0xF8FFFFFFU) /*!< AFIO_PCF register SWJCFG mask */ +#define PCF_LOCATION1_MASK ((uint32_t)0x00200000U) /*!< AFIO_PCF register location1 mask */ +#define PCF_LOCATION2_MASK ((uint32_t)0x00100000U) /*!< AFIO_PCF register location2 mask */ +#define AFIO_PCF1_FIELDS ((uint32_t)0x80000000U) /*!< select AFIO_PCF1 register */ +#define GPIO_OUTPUT_PORT_OFFSET ((uint32_t)4U) /*!< GPIO event output port offset*/ + +/*! + \brief reset GPIO port + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval none +*/ +void gpio_deinit(uint32_t gpio_periph) +{ + switch(gpio_periph){ + case GPIOA: + /* reset GPIOA */ + rcu_periph_reset_enable(RCU_GPIOARST); + rcu_periph_reset_disable(RCU_GPIOARST); + break; + case GPIOB: + /* reset GPIOB */ + rcu_periph_reset_enable(RCU_GPIOBRST); + rcu_periph_reset_disable(RCU_GPIOBRST); + break; + case GPIOC: + /* reset GPIOC */ + rcu_periph_reset_enable(RCU_GPIOCRST); + rcu_periph_reset_disable(RCU_GPIOCRST); + break; + case GPIOD: + /* reset GPIOD */ + rcu_periph_reset_enable(RCU_GPIODRST); + rcu_periph_reset_disable(RCU_GPIODRST); + break; + case GPIOE: + /* reset GPIOE */ + rcu_periph_reset_enable(RCU_GPIOERST); + rcu_periph_reset_disable(RCU_GPIOERST); + break; + case GPIOF: + /* reset GPIOF */ + rcu_periph_reset_enable(RCU_GPIOFRST); + rcu_periph_reset_disable(RCU_GPIOFRST); + break; + case GPIOG: + /* reset GPIOG */ + rcu_periph_reset_enable(RCU_GPIOGRST); + rcu_periph_reset_disable(RCU_GPIOGRST); + break; + default: + break; + } +} + +/*! + \brief reset alternate function I/O(AFIO) + \param[in] none + \param[out] none + \retval none +*/ +void gpio_afio_deinit(void) +{ + rcu_periph_reset_enable(RCU_AFRST); + rcu_periph_reset_disable(RCU_AFRST); +} + +/*! + \brief GPIO parameter initialization + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] mode: gpio pin mode + only one parameter can be selected which is shown as below: + \arg GPIO_MODE_AIN: analog input mode + \arg GPIO_MODE_IN_FLOATING: floating input mode + \arg GPIO_MODE_IPD: pull-down input mode + \arg GPIO_MODE_IPU: pull-up input mode + \arg GPIO_MODE_OUT_OD: GPIO output with open-drain + \arg GPIO_MODE_OUT_PP: GPIO output with push-pull + \arg GPIO_MODE_AF_OD: AFIO output with open-drain + \arg GPIO_MODE_AF_PP: AFIO output with push-pull + \param[in] speed: gpio output max speed value + only one parameter can be selected which is shown as below: + \arg GPIO_OSPEED_10MHZ: output max speed 10MHz + \arg GPIO_OSPEED_2MHZ: output max speed 2MHz + \arg GPIO_OSPEED_50MHZ: output max speed 50MHz + \arg GPIO_OSPEED_MAX: output max speed more than 50MHz + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_init(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin) +{ + uint16_t i; + uint32_t temp_mode = 0U; + uint32_t reg = 0U; + + /* GPIO mode configuration */ + temp_mode = (uint32_t)(mode & ((uint32_t)0x0FU)); + + /* GPIO speed configuration */ + if(((uint32_t)0x00U) != ((uint32_t)mode & ((uint32_t)0x10U))){ + /* output mode max speed */ + if(GPIO_OSPEED_MAX == (uint32_t)speed){ + temp_mode |= (uint32_t)0x03U; + /* set the corresponding SPD bit */ + GPIOx_SPD(gpio_periph) |= (uint32_t)pin ; + }else{ + /* output mode max speed:10MHz,2MHz,50MHz */ + temp_mode |= (uint32_t)speed; + } + } + + /* configure the eight low port pins with GPIO_CTL0 */ + for(i = 0U;i < 8U;i++){ + if((1U << i) & pin){ + reg = GPIO_CTL0(gpio_periph); + + /* clear the specified pin mode bits */ + reg &= ~GPIO_MODE_MASK(i); + /* set the specified pin mode bits */ + reg |= GPIO_MODE_SET(i, temp_mode); + + /* set IPD or IPU */ + if(GPIO_MODE_IPD == mode){ + /* reset the corresponding OCTL bit */ + GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin); + }else{ + /* set the corresponding OCTL bit */ + if(GPIO_MODE_IPU == mode){ + GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin); + } + } + /* set GPIO_CTL0 register */ + GPIO_CTL0(gpio_periph) = reg; + } + } + /* configure the eight high port pins with GPIO_CTL1 */ + for(i = 8U;i < 16U;i++){ + if((1U << i) & pin){ + reg = GPIO_CTL1(gpio_periph); + + /* clear the specified pin mode bits */ + reg &= ~GPIO_MODE_MASK(i - 8U); + /* set the specified pin mode bits */ + reg |= GPIO_MODE_SET(i - 8U, temp_mode); + + /* set IPD or IPU */ + if(GPIO_MODE_IPD == mode){ + /* reset the corresponding OCTL bit */ + GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin); + }else{ + /* set the corresponding OCTL bit */ + if(GPIO_MODE_IPU == mode){ + GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin); + } + } + /* set GPIO_CTL1 register */ + GPIO_CTL1(gpio_periph) = reg; + } + } +} + +/*! + \brief set GPIO pin bit + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_bit_set(uint32_t gpio_periph,uint32_t pin) +{ + GPIO_BOP(gpio_periph) = (uint32_t)pin; +} + +/*! + \brief reset GPIO pin bit + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_bit_reset(uint32_t gpio_periph,uint32_t pin) +{ + GPIO_BC(gpio_periph) = (uint32_t)pin; +} + +/*! + \brief write data to the specified GPIO pin + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[in] bit_value: SET or RESET + \arg RESET: clear the port pin + \arg SET: set the port pin + \param[out] none + \retval none +*/ +void gpio_bit_write(uint32_t gpio_periph,uint32_t pin,bit_status bit_value) +{ + if(RESET != bit_value){ + GPIO_BOP(gpio_periph) = (uint32_t)pin; + }else{ + GPIO_BC(gpio_periph) = (uint32_t)pin; + } +} + +/*! + \brief write data to the specified GPIO port + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] data: specify the value to be written to the port output data register + \param[out] none + \retval none +*/ +void gpio_port_write(uint32_t gpio_periph,uint16_t data) +{ + GPIO_OCTL(gpio_periph) = (uint32_t)data; +} + +/*! + \brief get GPIO pin input status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + only one parameter can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval input status of gpio pin: SET or RESET +*/ +FlagStatus gpio_input_bit_get(uint32_t gpio_periph,uint32_t pin) +{ + if((uint32_t)RESET != (GPIO_ISTAT(gpio_periph)&(pin))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief get GPIO port input status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval input status of gpio all pins +*/ +uint16_t gpio_input_port_get(uint32_t gpio_periph) +{ + return (uint16_t)(GPIO_ISTAT(gpio_periph)); +} + +/*! + \brief get GPIO pin output status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + only one parameter can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval output status of gpio pin: SET or RESET +*/ +FlagStatus gpio_output_bit_get(uint32_t gpio_periph,uint32_t pin) +{ + if((uint32_t)RESET !=(GPIO_OCTL(gpio_periph)&(pin))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief get GPIO port output status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval output status of gpio all pins +*/ +uint16_t gpio_output_port_get(uint32_t gpio_periph) +{ + return ((uint16_t)GPIO_OCTL(gpio_periph)); +} + +/*! + \brief configure GPIO pin remap + \param[in] gpio_remap: select the pin to remap + only one parameter can be selected which are shown as below: + \arg GPIO_SPI0_REMAP: SPI0 remapping + \arg GPIO_I2C0_REMAP: I2C0 remapping + \arg GPIO_USART0_REMAP: USART0 remapping + \arg GPIO_USART1_REMAP: USART1 remapping + \arg GPIO_USART2_PARTIAL_REMAP: USART2 partial remapping + \arg GPIO_USART2_FULL_REMAP: USART2 full remapping + \arg GPIO_TIMER0_PARTIAL_REMAP: TIMER0 partial remapping + \arg GPIO_TIMER0_FULL_REMAP: TIMER0 full remapping + \arg GPIO_TIMER1_PARTIAL_REMAP0: TIMER1 partial remapping + \arg GPIO_TIMER1_PARTIAL_REMAP1: TIMER1 partial remapping + \arg GPIO_TIMER1_FULL_REMAP: TIMER1 full remapping + \arg GPIO_TIMER2_PARTIAL_REMAP: TIMER2 partial remapping + \arg GPIO_TIMER2_FULL_REMAP: TIMER2 full remapping + \arg GPIO_TIMER3_REMAP: TIMER3 remapping + \arg GPIO_CAN0_PARTIAL_REMAP: CAN0 partial remapping(not support on GD32EPRT devices) + \arg GPIO_CAN0_FULL_REMAP: CAN0 full remapping(not support on GD32EPRT devices) + \arg GPIO_PD01_REMAP: PD01 remapping + \arg GPIO_TIMER4CH3_IREMAP: TIMER4 channel3 internal remapping + \arg GPIO_ADC0_ETRGRT_REMAP: ADC0 external trigger routine conversion remapping(only for GD32E50X_HD devices) + \arg GPIO_ADC1_ETRGRT_REMAP: ADC1 external trigger routine conversion remapping(only for GD32E50X_HD devices) + \arg GPIO_ENET_REMAP: ENET remapping(only for GD32E50X_CL and GD32E508 devices) + \arg GPIO_CAN1_REMAP: CAN1 remapping(not support on GD32EPRT devices) + \arg GPIO_SWJ_NONJTRST_REMAP: full SWJ(JTAG-DP + SW-DP),but without NJTRST + \arg GPIO_SWJ_SWDPENABLE_REMAP: JTAG-DP disabled and SW-DP enabled + \arg GPIO_SWJ_DISABLE_REMAP: JTAG-DP disabled and SW-DP disabled + \arg GPIO_SPI2_REMAP: SPI2 remapping + \arg GPIO_TIMER1ITR1_REMAP: TIMER1 internal trigger 1 remapping(only for GD32E50X_CL and GD32E508 devices) + \arg GPIO_PTP_PPS_REMAP: ethernet PTP PPS remapping(only for GD32E50X_CL and GD32E508 devices) + \arg GPIO_TIMER8_REMAP: TIMER8 remapping(not support on GD32EPRT devices) + \arg GPIO_TIMER9_REMAP: TIMER9 remapping(not support on GD32EPRT devices) + \arg GPIO_TIMER10_REMAP: TIMER10 remapping(not support on GD32EPRT devices) + \arg GPIO_TIMER12_REMAP: TIMER12 remapping(not support on GD32EPRT devices) + \arg GPIO_TIMER13_REMAP: TIMER13 remapping(not support on GD32EPRT devices) + \arg GPIO_EXMC_NADV_REMAP: EXMC_NADV connect/disconnect + \arg GPIO_CTC_REMAP0: CTC remapping(PD15) + \arg GPIO_CTC_REMAP1: CTC remapping(PF0) + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void gpio_pin_remap_config(uint32_t remap, ControlStatus newvalue) +{ + uint32_t remap1 = 0U, remap2 = 0U, temp_reg = 0U, temp_mask = 0U; + + if(((uint32_t)0x80000000U) == (remap & 0x80000000U)){ + /* get AFIO_PCF1 regiter value */ + temp_reg = AFIO_PCF1; + }else{ + /* get AFIO_PCF0 regiter value */ + temp_reg = AFIO_PCF0; + } + + temp_mask = (remap & PCF_POSITION_MASK) >> 0x10U; + remap1 = remap & LSB_16BIT_MASK; + + /* judge pin remap type */ + if((PCF_LOCATION1_MASK | PCF_LOCATION2_MASK) == (remap & (PCF_LOCATION1_MASK | PCF_LOCATION2_MASK))){ + temp_reg &= PCF_SWJCFG_MASK; + AFIO_PCF0 &= PCF_SWJCFG_MASK; + }else if(PCF_LOCATION2_MASK == (remap & PCF_LOCATION2_MASK)){ + remap2 = ((uint32_t)0x03U) << temp_mask; + temp_reg &= ~remap2; + temp_reg |= ~PCF_SWJCFG_MASK; + }else{ + temp_reg &= ~(remap1 << ((remap >> 0x15U)*0x10U)); + temp_reg |= ~PCF_SWJCFG_MASK; + } + + /* set pin remap value */ + if(DISABLE != newvalue){ + temp_reg |= (remap1 << ((remap >> 0x15U)*0x10U)); + } + + if(AFIO_PCF1_FIELDS == (remap & AFIO_PCF1_FIELDS)){ + /* set AFIO_PCF1 regiter value */ + AFIO_PCF1 = temp_reg; + }else{ + /* set AFIO_PCF0 regiter value */ + AFIO_PCF0 = temp_reg; + } +} + +/*! + \brief configure AFIO port alternate function + \param[in] afio_function: select the port AFIO function(SHRTIMER not support on GD32EPRT devices) + only one parameter can be selected which are shown as below: + \arg AFIO_PA2_CMP1_CFG: configure PA2 alternate function to CMP1 + \arg AFIO_PA3_USBHS_CFG: configure PA3 alternate function to USBHS + \arg AFIO_PA5_USBHS_CFG: configure PA5 alternate function to USBHS + \arg AFIO_PA8_I2C2_CFG: configure PA8 alternate function to I2C2 + \arg AFIO_PA8_SHRTIMER_CFG: configure PA8 alternate function to SHRTIMER + \arg AFIO_PA9_CAN2_CFG: configure PA9 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PA9_I2C2_CFG: configure PA9 alternate function to I2C2 + \arg AFIO_PA9_SHRTIMER_CFG: configure PA9 alternate function to SHRTIMER + \arg AFIO_PA10_CAN2_CFG: configure PA10 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PA10_CMP5_CFG: configure PA10 alternate function to CMP5 + \arg AFIO_PA10_SHRTIMER_CFG: configure PA10 alternate function to SHRTIMER + \arg AFIO_PA11_USART5_CFG: configure PA11 alternate function to USART5 + \arg AFIO_PA11_SHRTIMER_CFG: configure PA11 alternate function to SHRTIMER + \arg AFIO_PA12_CMP1_CFG: configure PA12 alternate function to CMP1 + \arg AFIO_PA12_USART5_CFG: configure PA12 alternate function to USART5 + \arg AFIO_PA12_SHRTIMER_CFG: configure PA12 alternate function to SHRTIMER + \arg AFIO_PA15_SHRTIMER_CFG: configure PA15 alternate function to SHRTIMER + \arg AFIO_PB0_USBHS_CFG: configure PB0 alternate function to USBHS + \arg AFIO_PB1_CMP3_CFG: configure PB1 alternate function to CMP3 + \arg AFIO_PB1_USBHS_CFG: configure PB1 alternate function to USBHS + \arg AFIO_PB1_SHRTIMER_CFG: configure PB1 alternate function to SHRTIMER + \arg AFIO_PB2_USBHS_CFG: configure PB2 alternate function to USBHS + \arg AFIO_PB2_SHRTIMER_CFG: configure PB2 alternate function to SHRTIMER + \arg AFIO_PB3_SHRTIMER_CFG: configure PB3 alternate function to SHRTIMER + \arg AFIO_PB4_I2S2_CFG: configure PB4 alternate function to I2S2 + \arg AFIO_PB4_I2C2_CFG: configure PB4 alternate function to I2C2 + \arg AFIO_PB4_SHRTIMER_CFG: configure PB4 alternate function to SHRTIMER + \arg AFIO_PB5_I2C2_CFG: configure PB5 alternate function to I2C2 + \arg AFIO_PB5_USBHS_CFG: configure PB5 alternate function to USBHS + \arg AFIO_PB5_SHRTIMER_CFG: configure PB5 alternate function to SHRTIMER + \arg AFIO_PB6_SHRTIMER_CFG: configure PB6 alternate function to SHRTIMER + \arg AFIO_PB7_SHRTIMER_CFG: configure PB7 alternate function to SHRTIMER + \arg AFIO_PB8_I2C2_CFG: configure PB8 alternate function to I2C2 + \arg AFIO_PB8_SHRTIMER_CFG: configure PB8 alternate function to SHRTIMER + \arg AFIO_PB9_CMP1_CFG: configure PB9 alternate function to CMP1 + \arg AFIO_PB9_SHRTIMER_CFG: configure PB9 alternate function to SHRTIMER + \arg AFIO_PB10_CAN2_CFG: configure PB10 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PB10_USBHS_CFG: configure PB10 alternate function to USBHS + \arg AFIO_PB10_SHRTIMER_CFG: configure PB10 alternate function to SHRTIMER + \arg AFIO_PB11_CAN2_CFG: configure PB11 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PB11_USBHS_CFG: configure PB11 alternate function to USBHS + \arg AFIO_PB11_SHRTIMER_CFG: configure PB11 alternate function to SHRTIMER + \arg AFIO_PB12_USBHS_CFG: configure PB12 alternate function to USBHS + \arg AFIO_PB12_SHRTIMER_CFG: configure PB12 alternate function to SHRTIMER + \arg AFIO_PB13_USBHS_CFG: configure PB13 alternate function to USBHS + \arg AFIO_PB13_SHRTIMER_CFG: configure PB13 alternate function to SHRTIMER + \arg AFIO_PB14_I2S1_CFG: configure PB14 alternate function to I2S1 + \arg AFIO_PB14_SHRTIMER_CFG: configure PB14 alternate function to SHRTIMER + \arg AFIO_PB15_SHRTIMER_CFG: configure PB15 alternate function to SHRTIMER + \arg AFIO_PC0_USBHS_CFG: configure PC0 alternate function to USBHS + \arg AFIO_PC2_I2S1_CFG: configure PC2 alternate function to I2S1 + \arg AFIO_PC2_USBHS_CFG: configure PC2 alternate function to USBHS + \arg AFIO_PC3_USBHS_CFG: configure PC3 alternate function to USBHS + \arg AFIO_PC6_CMP5_CFG: configure PC6 alternate function to CMP5 + \arg AFIO_PC6_USART5_CFG: configure PC6 alternate function to USART5 + \arg AFIO_PC6_SHRTIMER_CFG: configure PC6 alternate function to SHRTIMER + \arg AFIO_PC7_USART5_CFG: configure PC7 alternate function to USART5 + \arg AFIO_PC7_SHRTIMER_CFG: configure PC7 alternate function to SHRTIMER + \arg AFIO_PC8_USART5_CFG: configure PC8 alternate function to USART5 + \arg AFIO_PC8_SHRTIMER_CFG: configure PC8 alternate function to SHRTIMER + \arg AFIO_PC9_I2C2_CFG: configure PC9 alternate function to I2C2 + \arg AFIO_PC9_SHRTIMER_CFG: configure PC9 alternate function to SHRTIMER + \arg AFIO_PC10_I2C2_CFG: configure PC10 alternate function to I2C2 + \arg AFIO_PC11_I2S2_CFG: configure PC11 alternate function to I2S2 + \arg AFIO_PC11_SHRTIMER_CFG: configure PC11 alternate function to SHRTIMER + \arg AFIO_PC12_SHRTIMER_CFG: configure PC12 alternate function to SHRTIMER + \arg AFIO_PD4_SHRTIMER_CFG: configure PD4 alternate function to SHRTIMER + \arg AFIO_PD5_SHRTIMER_CFG: configure PD5 alternate function to SHRTIMER + \arg AFIO_PE0_CAN2_CFG: configure PE0 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PE0_SHRTIMER_CFG: configure PE0 alternate function to SHRTIMER + \arg AFIO_PE1_CAN2_CFG: configure PE1 alternate function to CAN2(not support on GD32EPRT devices) + \arg AFIO_PE1_SHRTIMER_CFG: configure PE1 alternate function to SHRTIMER + \arg AFIO_PE8_CMP1_CFG: configure PE8 alternate function to CMP1 + \arg AFIO_PE9_CMP3_CFG: configure PE9 alternate function to CMP3 + \arg AFIO_PE10_CMP5_CFG: configure PE10 alternate function to CMP5 + \arg AFIO_PE11_CMP5_CFG: configure PE11 alternate function to CMP5 + \arg AFIO_PE12_CMP3_CFG: configure PE12 alternate function to CMP3 + \arg AFIO_PE13_CMP1_CFG: configure PE13 alternate function to CMP1 + \arg AFIO_PG6_SHRTIMER_CFG: configure PG6 alternate function to SHRTIMER + \arg AFIO_PG7_USART5_CFG: configure PG7 alternate function to USART5 + \arg AFIO_PG7_SHRTIMER_CFG: configure PG7 alternate function to SHRTIMER + \arg AFIO_PG9_USART5_CFG: configure PG9 alternate function to USART5 + \arg AFIO_PG10_SHRTIMER_CFG: configure PG10 alternate function to SHRTIMER + \arg AFIO_PG11_SHRTIMER_CFG: configure PG11 alternate function to SHRTIMER + \arg AFIO_PG12_SHRTIMER_CFG: configure PG12 alternate function to SHRTIMER + \arg AFIO_PG13_SHRTIMER_CFG: configure PG13 alternate function to SHRTIMER + \arg AFIO_PG14_USART5_CFG: configure PG14 alternate function to USART5 + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void gpio_afio_port_config(uint32_t afio_function, ControlStatus newvalue) +{ + uint32_t remap1 = 0U, remap2 = 0U, temp_reg = 0U, temp_mask = 0U; + + /* get AFIO_PCFx(x=A,B,C,D,E,G) regiter value */ + temp_reg = REG32(AFIO+0x0000003CU+((afio_function>>24)<<2)); + + temp_mask = (afio_function & PCF_POSITION_MASK) >> 0x10U; + remap1 = afio_function & LSB_16BIT_MASK; + + /* judge port function select type */ + if(afio_function & PCF_LOCATION2_MASK){ + remap2 = ((uint32_t)0x03U) << temp_mask; + remap2 = (remap2 << (((afio_function & PCF_LOCATION1_MASK) >> 0x15U)*0x10U)); + temp_reg &= ~remap2; + }else{ + temp_reg &= ~(remap1 << (((afio_function & PCF_LOCATION1_MASK) >> 0x15U)*0x10U)); + } + + /* set pin remap value */ + if(DISABLE != newvalue){ + temp_reg |= (remap1 << (((afio_function & PCF_LOCATION1_MASK) >> 0x15U)*0x10U)); + } + + /* set AFIO_PCFx(x=A,B,C,D,E,G) regiter value */ + REG32(AFIO+0x0000003CU+((afio_function>>24)<<2)) = temp_reg; +} + +#if (defined(GD32E50X_CL) || defined(GD32E508) || defined(GD32EPRT)) +/*! + \brief select ethernet MII or RMII PHY + \param[in] enet_sel: ethernet MII or RMII PHY selection + \arg GPIO_ENET_PHY_MII: configure ethernet MAC for connection with an MII PHY + \arg GPIO_ENET_PHY_RMII: configure ethernet MAC for connection with an RMII PHY + \param[out] none + \retval none +*/ +void gpio_ethernet_phy_select(uint32_t enet_sel) +{ + /* clear AFIO_PCF0_ENET_PHY_SEL bit */ + AFIO_PCF0 &= (uint32_t)(~AFIO_PCF0_ENET_PHY_SEL); + + /* select MII or RMII PHY */ + AFIO_PCF0 |= (uint32_t)enet_sel; +} +#endif /* GD32E50X_CL||GD32E508||GD32EPRT */ + +/*! + \brief select GPIO pin exti sources + \param[in] output_port: gpio event output port + only one parameter can be selected which are shown as below: + \arg GPIO_PORT_SOURCE_GPIOA: output port source A + \arg GPIO_PORT_SOURCE_GPIOB: output port source B + \arg GPIO_PORT_SOURCE_GPIOC: output port source C + \arg GPIO_PORT_SOURCE_GPIOD: output port source D + \arg GPIO_PORT_SOURCE_GPIOE: output port source E + \arg GPIO_PORT_SOURCE_GPIOF: output port source F + \arg GPIO_PORT_SOURCE_GPIOG: output port source G + \param[in] output_pin: GPIO output pin source + only one parameter can be selected which are shown as below: + \arg GPIO_PIN_SOURCE_x(x=0..15) + \param[out] none + \retval none +*/ +void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin) +{ + uint32_t source = 0U; + source = ((uint32_t)0x0FU) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK)); + + /* select EXTI sources */ + if(GPIO_PIN_SOURCE_4 > output_pin){ + /* select EXTI0/EXTI1/EXTI2/EXTI3 */ + AFIO_EXTISS0 &= ~source; + AFIO_EXTISS0 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else if(GPIO_PIN_SOURCE_8 > output_pin){ + /* select EXTI4/EXTI5/EXTI6/EXTI7 */ + AFIO_EXTISS1 &= ~source; + AFIO_EXTISS1 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else if(GPIO_PIN_SOURCE_12 > output_pin){ + /* select EXTI8/EXTI9/EXTI10/EXTI11 */ + AFIO_EXTISS2 &= ~source; + AFIO_EXTISS2 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else{ + /* select EXTI12/EXTI13/EXTI14/EXTI15 */ + AFIO_EXTISS3 &= ~source; + AFIO_EXTISS3 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + } +} + +/*! + \brief configure GPIO pin event output + \param[in] output_port: gpio event output port + only one parameter can be selected which are shown as below: + \arg GPIO_EVENT_PORT_GPIOA: event output port A + \arg GPIO_EVENT_PORT_GPIOB: event output port B + \arg GPIO_EVENT_PORT_GPIOC: event output port C + \arg GPIO_EVENT_PORT_GPIOD: event output port D + \arg GPIO_EVENT_PORT_GPIOE: event output port E + \param[in] output_pin: GPIO event output pin + only one parameter can be selected which are shown as below: + \arg GPIO_EVENT_PIN_x(x=0..15) + \param[out] none + \retval none +*/ +void gpio_event_output_config(uint8_t output_port, uint8_t output_pin) +{ + uint32_t reg = 0U; + reg = AFIO_EC; + + /* clear AFIO_EC_PORT and AFIO_EC_PIN bits */ + reg &= (uint32_t)(~(AFIO_EC_PORT|AFIO_EC_PIN)); + + reg |= (uint32_t)((uint32_t)output_port << GPIO_OUTPUT_PORT_OFFSET); + reg |= (uint32_t)output_pin; + + AFIO_EC = reg; +} + +/*! + \brief enable GPIO pin event output + \param[in] none + \param[out] none + \retval none +*/ +void gpio_event_output_enable(void) +{ + AFIO_EC |= AFIO_EC_EOE; +} + +/*! + \brief disable GPIO pin event output + \param[in] none + \param[out] none + \retval none +*/ +void gpio_event_output_disable(void) +{ + AFIO_EC &= (uint32_t)(~AFIO_EC_EOE); +} + +/*! + \brief lock GPIO pin bit + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_pin_lock(uint32_t gpio_periph,uint32_t pin) +{ + uint32_t lock = 0x00010000U; + lock |= pin; + + /* lock key writing sequence: write 1 -> write 0 -> write 1 -> read 0 -> read 1 */ + GPIO_LOCK(gpio_periph) = (uint32_t)lock; + GPIO_LOCK(gpio_periph) = (uint32_t)pin; + GPIO_LOCK(gpio_periph) = (uint32_t)lock; + lock = GPIO_LOCK(gpio_periph); + lock = GPIO_LOCK(gpio_periph); +} + +/*! + \brief configure the I/O compensation cell + \param[in] compensation: specifies the I/O compensation cell mode + only one parameter can be selected which are shown as below: + \arg GPIO_COMPENSATION_ENABLE: I/O compensation cell is enabled + \arg GPIO_COMPENSATION_DISABLE: I/O compensation cell is disabled + \param[out] none + \retval none +*/ +void gpio_compensation_config(uint32_t compensation) +{ + uint32_t reg; + reg = AFIO_CPSCTL; + + /* reset the AFIO_CPSCTL_CPS_EN bit and set according to gpio_compensation */ + reg &= ~AFIO_CPSCTL_CPS_EN; + AFIO_CPSCTL = (reg | compensation); +} + +/*! + \brief check the I/O compensation cell is ready or not + \param[in] none + \param[out] none + \retval FlagStatus: SET or RESET + */ +FlagStatus gpio_compensation_flag_get(void) +{ + if(((uint32_t)RESET) != (AFIO_CPSCTL & AFIO_CPSCTL_CPS_RDY)){ + return SET; + }else{ + return RESET; + } +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_misc.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_misc.c new file mode 100644 index 0000000..8d58fb7 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_misc.c @@ -0,0 +1,180 @@ +/*! + \file gd32e50x_misc.c + \brief MISC driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_misc.h" + +/*! + \brief set the priority group + \param[in] nvic_prigroup: the NVIC priority group + \arg NVIC_PRIGROUP_PRE0_SUB4:0 bits for pre-emption priority 4 bits for subpriority + \arg NVIC_PRIGROUP_PRE1_SUB3:1 bits for pre-emption priority 3 bits for subpriority + \arg NVIC_PRIGROUP_PRE2_SUB2:2 bits for pre-emption priority 2 bits for subpriority + \arg NVIC_PRIGROUP_PRE3_SUB1:3 bits for pre-emption priority 1 bits for subpriority + \arg NVIC_PRIGROUP_PRE4_SUB0:4 bits for pre-emption priority 0 bits for subpriority + \param[out] none + \retval none +*/ +void nvic_priority_group_set(uint32_t nvic_prigroup) +{ + /* set the priority group value */ + SCB->AIRCR = NVIC_AIRCR_VECTKEY_MASK | nvic_prigroup; +} + +/*! + \brief enable NVIC request + \param[in] nvic_irq: the NVIC interrupt request, detailed in IRQn_Type + \param[in] nvic_irq_pre_priority: the pre-emption priority needed to set + \param[in] nvic_irq_sub_priority: the subpriority needed to set + \param[out] none + \retval none +*/ +void nvic_irq_enable(IRQn_Type nvic_irq, uint8_t nvic_irq_pre_priority, + uint8_t nvic_irq_sub_priority) +{ + uint32_t nvic_prigroup, nvic_priority; + + /* check current priority group */ + switch(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) { + case NVIC_PRIGROUP_PRE0_SUB4: + case NVIC_PRIGROUP_PRE1_SUB3: + case NVIC_PRIGROUP_PRE2_SUB2: + case NVIC_PRIGROUP_PRE3_SUB1: + case NVIC_PRIGROUP_PRE4_SUB0: + break; + default: + nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); + break; + } + + /* get the priority group value */ + nvic_prigroup = NVIC_GetPriorityGrouping(); + + /* encoding the pre-emption, subpriority priority */ + nvic_priority = NVIC_EncodePriority(nvic_prigroup, (uint32_t)nvic_irq_pre_priority, (uint32_t)nvic_irq_sub_priority); + /* set priority */ + NVIC_SetPriority(nvic_irq, nvic_priority); + + /* enable the selected IRQ */ + NVIC_EnableIRQ(nvic_irq); +} + +/*! + \brief disable NVIC request + \param[in] nvic_irq: the NVIC interrupt request, detailed in IRQn_Type + \param[out] none + \retval none +*/ +void nvic_irq_disable(IRQn_Type nvic_irq) +{ + /* disable the selected IRQ.*/ + NVIC_DisableIRQ((IRQn_Type)nvic_irq); +} + +/*! + \brief initiates a system reset request to reset the MCU + \param[in] none + \param[out] none + \retval none +*/ +void nvic_system_reset(void) +{ + NVIC_SystemReset(); +} + +/*! + \brief set the NVIC vector table base address + \param[in] nvic_vict_tab: the RAM or FLASH base address + \arg NVIC_VECTTAB_RAM: RAM base address + \are NVIC_VECTTAB_FLASH: Flash base address + \param[in] offset: Vector Table offset + \param[out] none + \retval none +*/ +void nvic_vector_table_set(uint32_t nvic_vict_tab, uint32_t offset) +{ + SCB->VTOR = nvic_vict_tab | (offset & NVIC_VECTTAB_OFFSET_MASK); + __DSB(); +} + +/*! + \brief set the state of the low power mode + \param[in] lowpower_mode: the low power mode state + \arg SCB_LPM_SLEEP_EXIT_ISR: if chose this para, the system always enter low power + mode by exiting from ISR + \arg SCB_LPM_DEEPSLEEP: if chose this para, the system will enter the DEEPSLEEP mode + \arg SCB_LPM_WAKE_BY_ALL_INT: if chose this para, the lowpower mode can be woke up + by all the enable and disable interrupts + \param[out] none + \retval none +*/ +void system_lowpower_set(uint8_t lowpower_mode) +{ + SCB->SCR |= (uint32_t)lowpower_mode; +} + +/*! + \brief reset the state of the low power mode + \param[in] lowpower_mode: the low power mode state + \arg SCB_LPM_SLEEP_EXIT_ISR: if chose this para, the system will exit low power + mode by exiting from ISR + \arg SCB_LPM_DEEPSLEEP: if chose this para, the system will enter the SLEEP mode + \arg SCB_LPM_WAKE_BY_ALL_INT: if chose this para, the lowpower mode only can be + woke up by the enable interrupts + \param[out] none + \retval none +*/ +void system_lowpower_reset(uint8_t lowpower_mode) +{ + SCB->SCR &= (~(uint32_t)lowpower_mode); +} + +/*! + \brief set the systick clock source + \param[in] systick_clksource: the systick clock source needed to choose + \arg SYSTICK_CLKSOURCE_HCLK: systick clock source is from HCLK + \arg SYSTICK_CLKSOURCE_HCLK_DIV8: systick clock source is from HCLK/8 + \param[out] none + \retval none +*/ + +void systick_clksource_set(uint32_t systick_clksource) +{ + if(SYSTICK_CLKSOURCE_HCLK == systick_clksource ){ + /* set the systick clock source from HCLK */ + SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK; + }else{ + /* set the systick clock source from HCLK/8 */ + SysTick->CTRL &= SYSTICK_CLKSOURCE_HCLK_DIV8; + } +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_misc.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_misc.c.base@1.5.0 new file mode 100644 index 0000000..8d58fb7 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_misc.c.base@1.5.0 @@ -0,0 +1,180 @@ +/*! + \file gd32e50x_misc.c + \brief MISC driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_misc.h" + +/*! + \brief set the priority group + \param[in] nvic_prigroup: the NVIC priority group + \arg NVIC_PRIGROUP_PRE0_SUB4:0 bits for pre-emption priority 4 bits for subpriority + \arg NVIC_PRIGROUP_PRE1_SUB3:1 bits for pre-emption priority 3 bits for subpriority + \arg NVIC_PRIGROUP_PRE2_SUB2:2 bits for pre-emption priority 2 bits for subpriority + \arg NVIC_PRIGROUP_PRE3_SUB1:3 bits for pre-emption priority 1 bits for subpriority + \arg NVIC_PRIGROUP_PRE4_SUB0:4 bits for pre-emption priority 0 bits for subpriority + \param[out] none + \retval none +*/ +void nvic_priority_group_set(uint32_t nvic_prigroup) +{ + /* set the priority group value */ + SCB->AIRCR = NVIC_AIRCR_VECTKEY_MASK | nvic_prigroup; +} + +/*! + \brief enable NVIC request + \param[in] nvic_irq: the NVIC interrupt request, detailed in IRQn_Type + \param[in] nvic_irq_pre_priority: the pre-emption priority needed to set + \param[in] nvic_irq_sub_priority: the subpriority needed to set + \param[out] none + \retval none +*/ +void nvic_irq_enable(IRQn_Type nvic_irq, uint8_t nvic_irq_pre_priority, + uint8_t nvic_irq_sub_priority) +{ + uint32_t nvic_prigroup, nvic_priority; + + /* check current priority group */ + switch(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) { + case NVIC_PRIGROUP_PRE0_SUB4: + case NVIC_PRIGROUP_PRE1_SUB3: + case NVIC_PRIGROUP_PRE2_SUB2: + case NVIC_PRIGROUP_PRE3_SUB1: + case NVIC_PRIGROUP_PRE4_SUB0: + break; + default: + nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); + break; + } + + /* get the priority group value */ + nvic_prigroup = NVIC_GetPriorityGrouping(); + + /* encoding the pre-emption, subpriority priority */ + nvic_priority = NVIC_EncodePriority(nvic_prigroup, (uint32_t)nvic_irq_pre_priority, (uint32_t)nvic_irq_sub_priority); + /* set priority */ + NVIC_SetPriority(nvic_irq, nvic_priority); + + /* enable the selected IRQ */ + NVIC_EnableIRQ(nvic_irq); +} + +/*! + \brief disable NVIC request + \param[in] nvic_irq: the NVIC interrupt request, detailed in IRQn_Type + \param[out] none + \retval none +*/ +void nvic_irq_disable(IRQn_Type nvic_irq) +{ + /* disable the selected IRQ.*/ + NVIC_DisableIRQ((IRQn_Type)nvic_irq); +} + +/*! + \brief initiates a system reset request to reset the MCU + \param[in] none + \param[out] none + \retval none +*/ +void nvic_system_reset(void) +{ + NVIC_SystemReset(); +} + +/*! + \brief set the NVIC vector table base address + \param[in] nvic_vict_tab: the RAM or FLASH base address + \arg NVIC_VECTTAB_RAM: RAM base address + \are NVIC_VECTTAB_FLASH: Flash base address + \param[in] offset: Vector Table offset + \param[out] none + \retval none +*/ +void nvic_vector_table_set(uint32_t nvic_vict_tab, uint32_t offset) +{ + SCB->VTOR = nvic_vict_tab | (offset & NVIC_VECTTAB_OFFSET_MASK); + __DSB(); +} + +/*! + \brief set the state of the low power mode + \param[in] lowpower_mode: the low power mode state + \arg SCB_LPM_SLEEP_EXIT_ISR: if chose this para, the system always enter low power + mode by exiting from ISR + \arg SCB_LPM_DEEPSLEEP: if chose this para, the system will enter the DEEPSLEEP mode + \arg SCB_LPM_WAKE_BY_ALL_INT: if chose this para, the lowpower mode can be woke up + by all the enable and disable interrupts + \param[out] none + \retval none +*/ +void system_lowpower_set(uint8_t lowpower_mode) +{ + SCB->SCR |= (uint32_t)lowpower_mode; +} + +/*! + \brief reset the state of the low power mode + \param[in] lowpower_mode: the low power mode state + \arg SCB_LPM_SLEEP_EXIT_ISR: if chose this para, the system will exit low power + mode by exiting from ISR + \arg SCB_LPM_DEEPSLEEP: if chose this para, the system will enter the SLEEP mode + \arg SCB_LPM_WAKE_BY_ALL_INT: if chose this para, the lowpower mode only can be + woke up by the enable interrupts + \param[out] none + \retval none +*/ +void system_lowpower_reset(uint8_t lowpower_mode) +{ + SCB->SCR &= (~(uint32_t)lowpower_mode); +} + +/*! + \brief set the systick clock source + \param[in] systick_clksource: the systick clock source needed to choose + \arg SYSTICK_CLKSOURCE_HCLK: systick clock source is from HCLK + \arg SYSTICK_CLKSOURCE_HCLK_DIV8: systick clock source is from HCLK/8 + \param[out] none + \retval none +*/ + +void systick_clksource_set(uint32_t systick_clksource) +{ + if(SYSTICK_CLKSOURCE_HCLK == systick_clksource ){ + /* set the systick clock source from HCLK */ + SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK; + }else{ + /* set the systick clock source from HCLK/8 */ + SysTick->CTRL &= SYSTICK_CLKSOURCE_HCLK_DIV8; + } +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_pmu.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_pmu.c new file mode 100644 index 0000000..853f0d6 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_pmu.c @@ -0,0 +1,522 @@ +/*! + \file gd32e50x_pmu.c + \brief PMU driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_pmu.h" + +/*! + \brief reset PMU registers + \param[in] none + \param[out] none + \retval none +*/ +void pmu_deinit(void) +{ + /* reset PMU */ + rcu_periph_reset_enable(RCU_PMURST); + rcu_periph_reset_disable(RCU_PMURST); +} + +/*! + \brief select low voltage detector threshold + \param[in] lvdt_n: + only one parameter can be selected which is shown as below: + \arg PMU_LVDT_0: voltage threshold is 2.1V + \arg PMU_LVDT_1: voltage threshold is 2.3V + \arg PMU_LVDT_2: voltage threshold is 2.4V + \arg PMU_LVDT_3: voltage threshold is 2.6V + \arg PMU_LVDT_4: voltage threshold is 2.7V + \arg PMU_LVDT_5: voltage threshold is 2.9V + \arg PMU_LVDT_6: voltage threshold is 3.0V + \arg PMU_LVDT_7: voltage threshold is 3.1V + \param[out] none + \retval none +*/ +void pmu_lvd_select(uint32_t lvdt_n) +{ + /* disable LVD */ + PMU_CTL0 &= ~PMU_CTL0_LVDEN; + /* clear LVDT bits */ + PMU_CTL0 &= ~PMU_CTL0_LVDT; + /* set LVDT bits according to lvdt_n */ + PMU_CTL0 |= lvdt_n; + /* enable LVD */ + PMU_CTL0 |= PMU_CTL0_LVDEN; +} + +/*! + \brief disable PMU LVD + \param[in] none + \param[out] none + \retval none +*/ +void pmu_lvd_disable(void) +{ + /* disable LVD */ + PMU_CTL0 &= ~PMU_CTL0_LVDEN; +} + +/*! + \brief enable high-driver mode + this bit set by software only when IRC8M or HXTAL used as system clock + \param[in] none + \param[out] none + \retval none +*/ +void pmu_highdriver_mode_enable(void) +{ + PMU_CTL0 |= PMU_CTL0_HDEN; +} + +/*! + \brief disable high-driver mode + \param[in] none + \param[out] none + \retval none +*/ +void pmu_highdriver_mode_disable(void) +{ + PMU_CTL0 &= ~PMU_CTL0_HDEN; +} + +/*! + \brief switch high-driver mode + this bit set by software only when IRC8M or HXTAL used as system clock + \param[in] highdr_switch: + only one parameter can be selected which is shown as below: + \arg PMU_HIGHDR_SWITCH_NONE: disable high-driver mode switch + \arg PMU_HIGHDR_SWITCH_EN: enable high-driver mode switch + \param[out] none + \retval none +*/ +void pmu_highdriver_switch_select(uint32_t highdr_switch) +{ + /* wait for HDRF flag set */ + while(SET != pmu_flag_get(PMU_FLAG_HDRF)){ + } + PMU_CTL0 &= ~PMU_CTL0_HDS; + PMU_CTL0 |= highdr_switch; +} + +/*! + \brief enable low-driver mode in deep-sleep/deep-sleep 1/deep-sleep 2 mode + \param[in] none + \param[out] none + \retval none +*/ +void pmu_lowdriver_mode_enable(void) +{ + PMU_CTL0 |= PMU_CTL0_LDEN; +} + +/*! + \brief disable low-driver mode in deep-sleep/deep-sleep 1/deep-sleep 2 mode + \param[in] none + \param[out] none + \retval none +*/ +void pmu_lowdriver_mode_disable(void) +{ + PMU_CTL0 &= ~PMU_CTL0_LDEN; +} + +/*! + \brief in deep-sleep/deep-sleep 1/deep-sleep 2 mode, driver mode when use low power LDO + \param[in] mode: + only one parameter can be selected which is shown as below: + \arg PMU_NORMALDR_LOWPWR: normal driver when use low power LDO + \arg PMU_LOWDR_LOWPWR: low-driver mode enabled when LDEN is 11 and use low power LDO + \param[out] none + \retval none +*/ +void pmu_lowpower_driver_config(uint32_t mode) +{ + PMU_CTL0 &= ~PMU_CTL0_LDLP; + PMU_CTL0 |= mode; +} + +/*! + \brief in deep-sleep/deep-sleep 1/deep-sleep 2 mode, driver mode when use normal power LDO + \param[in] mode: + only one parameter can be selected which is shown as below: + \arg PMU_NORMALDR_NORMALPWR: normal driver when use normal power LDO + \arg PMU_LOWDR_NORMALPWR: low-driver mode enabled when LDEN is 11 and use normal power LDO + \param[out] none + \retval none +*/ +void pmu_normalpower_driver_config(uint32_t mode) +{ + PMU_CTL0 &= ~PMU_CTL0_LDNP; + PMU_CTL0 |= mode; +} + +/*! + \brief PMU work at sleep mode + \param[in] sleepmodecmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_sleepmode(uint8_t sleepmodecmd) +{ + /* clear sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); + + /* select WFI or WFE command to enter sleep mode */ + if(WFI_CMD == sleepmodecmd){ + __WFI(); + }else{ + __SEV(); + __WFE(); + __WFE(); + } +} + +/*! + \brief PMU work at deepsleep mode + \param[in] ldo: + only one parameter can be selected which is shown as below: + \arg PMU_LDO_NORMAL: LDO normal work when pmu enter deepsleep mode + \arg PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode + \param[in] lowdrive: + only one parameter can be selected which is shown as below: + \arg PMU_LOWDRIVER_ENABLE: low-driver mode enable in deep-sleep mode + \arg PMU_LOWDRIVER_DISABLE: low-driver mode disable in deep-sleep mode + \param[in] deepsleepmodecmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_deepsleepmode(uint32_t ldo, uint32_t lowdrive, uint8_t deepsleepmodecmd) +{ + /* clear stbmod and ldolp bits and low drive bits */ + PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_STBMOD | PMU_CTL0_LDOLP | PMU_CTL0_LDEN | PMU_CTL0_LDNP | PMU_CTL0_LDLP)); + /* clear deep-sleep 1/2 mode enable bits */ + PMU_CTL1 &= ~(PMU_CTL1_DPMOD1 | PMU_CTL1_DPMOD2); + + /* set ldolp bit according to pmu_ldo */ + PMU_CTL0 |= ldo; + + /* low drive mode config in deep-sleep mode */ + if(PMU_LOWDRIVER_ENABLE == lowdrive){ + if(PMU_LDO_NORMAL == ldo){ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDNP); + }else{ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDLP); + } + } + + /* set sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + /* select WFI or WFE command to enter deepsleep mode */ + if(WFI_CMD == deepsleepmodecmd){ + __WFI(); + }else{ + __SEV(); + __WFE(); + __WFE(); + } + + /* reset sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); +} + +/*! + \brief PMU work at deepsleep mode 1 + \param[in] ldo: + only one parameter can be selected which is shown as below: + \arg PMU_LDO_NORMAL: LDO normal work when pmu enter deepsleep mode 1 + \arg PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode 1 + \param[in] lowdrive: + only one parameter can be selected which is shown as below: + \arg PMU_LOWDRIVER_ENABLE: low-driver mode enable in deep-sleep 1 mode + \arg PMU_LOWDRIVER_DISABLE: low-driver mode disable in deep-sleep 1 mode + \param[in] deepsleepmode1cmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_deepsleepmode_1(uint32_t ldo, uint32_t lowdrive, uint8_t deepsleepmode1cmd) +{ + /* clear stbmod and ldolp bits and low drive bits */ + PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_STBMOD | PMU_CTL0_LDOLP | PMU_CTL0_LDEN | PMU_CTL0_LDNP | PMU_CTL0_LDLP)); + /* clear deep-sleep 2 mode enable bit */ + PMU_CTL1 &= ~PMU_CTL1_DPMOD2; + /* enable deep-sleep 1 mode */ + PMU_CTL1 |= PMU_CTL1_DPMOD1; + + /* set ldolp bit according to pmu_ldo */ + PMU_CTL0 |= ldo; + + /* low drive mode config in deep-sleep 1 mode */ + if(PMU_LOWDRIVER_ENABLE == lowdrive){ + if(PMU_LDO_NORMAL == ldo){ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDNP); + }else{ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDLP); + } + } + + /* set sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + /* select WFI or WFE command to enter deepsleep mode 1 */ + if(WFI_CMD == deepsleepmode1cmd){ + __WFI(); + }else{ + __SEV(); + __WFE(); + __WFE(); + } + + /* reset sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); + PMU_CTL1 &= ~PMU_CTL1_DPMOD1; +} + +/*! + \brief PMU work at deepsleep mode 2 + \param[in] ldo: + only one parameter can be selected which is shown as below: + \arg PMU_LDO_NORMAL: LDO normal work when pmu enter deepsleep mode 2 + \arg PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode 2 + \param[in] lowdrive: + only one parameter can be selected which is shown as below: + \arg PMU_LOWDRIVER_ENABLE: low-driver mode enable in deep-sleep 2 mode + \arg PMU_LOWDRIVER_DISABLE: low-driver mode disable in deep-sleep 2 mode + \param[in] deepsleepmode2cmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_deepsleepmode_2(uint32_t ldo, uint32_t lowdrive, uint8_t deepsleepmode2cmd) +{ + /* clear stbmod and ldolp bits and low drive bits */ + PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_STBMOD | PMU_CTL0_LDOLP | PMU_CTL0_LDEN | PMU_CTL0_LDNP | PMU_CTL0_LDLP)); + /* clear deep-sleep 1 mode enable bit */ + PMU_CTL1 &= ~PMU_CTL1_DPMOD1; + /* enable deep-sleep 2 mode */ + PMU_CTL1 |= PMU_CTL1_DPMOD2; + + /* set ldolp bit according to pmu_ldo */ + PMU_CTL0 |= ldo; + + /* low drive mode config in deep-sleep 2 mode */ + if(PMU_LOWDRIVER_ENABLE == lowdrive){ + if(PMU_LDO_NORMAL == ldo){ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDNP); + }else{ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDLP); + } + } + + /* set sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + /* select WFI or WFE command to enter deepsleep mode 2 */ + if(WFI_CMD == deepsleepmode2cmd){ + __WFI(); + }else{ + __SEV(); + __WFE(); + __WFE(); + } + + /* reset sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); + PMU_CTL1 &= ~PMU_CTL1_DPMOD2; +} + +/*! + \brief pmu work at standby mode + \param[in] none + \param[out] none + \retval none +*/ +void pmu_to_standbymode(void) +{ + /* set stbmod bit */ + PMU_CTL0 |= PMU_CTL0_STBMOD; + + /* reset wakeup flag */ + PMU_CTL0 |= PMU_CTL0_WURST; + + /* set sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + REG32( 0xE000E010U ) &= 0x00010004U; + REG32( 0xE000E180U ) = 0XFFFFFFF7U; + REG32( 0xE000E184U ) = 0XFFFFFDFFU; + REG32( 0xE000E188U ) = 0xFFFFFFFFU; + + /* select WFI command to enter standby mode */ + __WFI(); +} + +/*! + \brief enable PMU wakeup pin + \param[in] wakeup_pin: + one or more parameters can be selected which are shown as below: + \arg PMU_WAKEUP_PIN0: WKUP Pin 0 (PA0) + \arg PMU_WAKEUP_PIN1: WKUP Pin 1 (PC13) + \arg PMU_WAKEUP_PIN2: WKUP Pin 2 (PE6) + \arg PMU_WAKEUP_PIN3: WKUP Pin 3 (PA2) + \arg PMU_WAKEUP_PIN4: WKUP Pin 4 (PC5) + \arg PMU_WAKEUP_PIN5: WKUP Pin 5 (PB5) + \arg PMU_WAKEUP_PIN6: WKUP Pin 6 (PB15) + \arg PMU_WAKEUP_PIN7: WKUP Pin 7 (PF8) + \param[out] none + \retval none +*/ +void pmu_wakeup_pin_enable(uint32_t wakeup_pin) +{ + PMU_CS0 |= wakeup_pin; +} + +/*! + \brief disable PMU wakeup pin + \param[in] wakeup_pin: + one or more parameters can be selected which are shown as below: + \arg PMU_WAKEUP_PIN0: WKUP Pin 0 (PA0) + \arg PMU_WAKEUP_PIN1: WKUP Pin 1 (PC13) + \arg PMU_WAKEUP_PIN2: WKUP Pin 2 (PE6) + \arg PMU_WAKEUP_PIN3: WKUP Pin 3 (PA2) + \arg PMU_WAKEUP_PIN4: WKUP Pin 4 (PC5) + \arg PMU_WAKEUP_PIN5: WKUP Pin 5 (PB5) + \arg PMU_WAKEUP_PIN6: WKUP Pin 6 (PB15) + \arg PMU_WAKEUP_PIN7: WKUP Pin 7 (PF8) + \param[out] none + \retval none +*/ +void pmu_wakeup_pin_disable(uint32_t wakeup_pin) +{ + PMU_CS0 &= ~(wakeup_pin); +} + +/*! + \brief enable backup domain write + \param[in] none + \param[out] none + \retval none +*/ +void pmu_backup_write_enable(void) +{ + PMU_CTL0 |= PMU_CTL0_BKPWEN; +} + +/*! + \brief disable backup domain write + \param[in] none + \param[out] none + \retval none +*/ +void pmu_backup_write_disable(void) +{ + PMU_CTL0 &= ~PMU_CTL0_BKPWEN; +} + +/*! + \brief get flag state + \param[in] flag: + only one parameter can be selected which is shown as below: + \arg PMU_FLAG_WAKEUP: wakeup flag + \arg PMU_FLAG_STANDBY: standby flag + \arg PMU_FLAG_LVD: lvd flag + \arg PMU_FLAG_HDRF: high-driver ready flag + \arg PMU_FLAG_HDSRF: high-driver switch ready flag + \arg PMU_FLAG_LDRF: low-driver mode ready flag + \arg PMU_FLAG_DEEPSLEEP_1: deep-sleep 1 mode status flag + \arg PMU_FLAG_DEEPSLEEP_2: deep-sleep 2 mode status flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus pmu_flag_get(uint32_t flag) +{ + FlagStatus ret = RESET; + + if(RESET != (flag & BIT(31))){ + flag &= ~BIT(31); + if(PMU_CS1 & flag){ + ret = SET; + } + }else{ + if(PMU_CS0 & flag){ + ret = SET; + } + } + return ret; +} + +/*! + \brief clear flag bit + \param[in] flag: + only one parameter can be selected which is shown as below: + \arg PMU_FLAG_RESET_WAKEUP: reset wakeup flag + \arg PMU_FLAG_RESET_STANDBY: reset standby flag + \arg PMU_FLAG_RESET_DEEPSLEEP_1: reset deep-sleep 1 mode status flag + \arg PMU_FLAG_RESET_DEEPSLEEP_2: reset deep-sleep 2 mode status flag + \param[out] none + \retval none +*/ +void pmu_flag_clear(uint32_t flag) +{ + switch(flag){ + case PMU_FLAG_RESET_WAKEUP: + /* reset wakeup flag */ + PMU_CTL0 |= PMU_CTL0_WURST; + break; + case PMU_FLAG_RESET_STANDBY: + /* reset standby flag */ + PMU_CTL0 |= PMU_CTL0_STBRST; + break; + case PMU_FLAG_RESET_DEEPSLEEP_1: + /* reset deep-sleep 1 mode status flag */ + PMU_CS1 &= ~PMU_CS1_DPF1; + break; + case PMU_FLAG_RESET_DEEPSLEEP_2: + /* reset deep-sleep 2 mode status flag */ + PMU_CS1 &= ~PMU_CS1_DPF2; + break; + default : + break; + } +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_pmu.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_pmu.c.base@1.5.0 new file mode 100644 index 0000000..853f0d6 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_pmu.c.base@1.5.0 @@ -0,0 +1,522 @@ +/*! + \file gd32e50x_pmu.c + \brief PMU driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_pmu.h" + +/*! + \brief reset PMU registers + \param[in] none + \param[out] none + \retval none +*/ +void pmu_deinit(void) +{ + /* reset PMU */ + rcu_periph_reset_enable(RCU_PMURST); + rcu_periph_reset_disable(RCU_PMURST); +} + +/*! + \brief select low voltage detector threshold + \param[in] lvdt_n: + only one parameter can be selected which is shown as below: + \arg PMU_LVDT_0: voltage threshold is 2.1V + \arg PMU_LVDT_1: voltage threshold is 2.3V + \arg PMU_LVDT_2: voltage threshold is 2.4V + \arg PMU_LVDT_3: voltage threshold is 2.6V + \arg PMU_LVDT_4: voltage threshold is 2.7V + \arg PMU_LVDT_5: voltage threshold is 2.9V + \arg PMU_LVDT_6: voltage threshold is 3.0V + \arg PMU_LVDT_7: voltage threshold is 3.1V + \param[out] none + \retval none +*/ +void pmu_lvd_select(uint32_t lvdt_n) +{ + /* disable LVD */ + PMU_CTL0 &= ~PMU_CTL0_LVDEN; + /* clear LVDT bits */ + PMU_CTL0 &= ~PMU_CTL0_LVDT; + /* set LVDT bits according to lvdt_n */ + PMU_CTL0 |= lvdt_n; + /* enable LVD */ + PMU_CTL0 |= PMU_CTL0_LVDEN; +} + +/*! + \brief disable PMU LVD + \param[in] none + \param[out] none + \retval none +*/ +void pmu_lvd_disable(void) +{ + /* disable LVD */ + PMU_CTL0 &= ~PMU_CTL0_LVDEN; +} + +/*! + \brief enable high-driver mode + this bit set by software only when IRC8M or HXTAL used as system clock + \param[in] none + \param[out] none + \retval none +*/ +void pmu_highdriver_mode_enable(void) +{ + PMU_CTL0 |= PMU_CTL0_HDEN; +} + +/*! + \brief disable high-driver mode + \param[in] none + \param[out] none + \retval none +*/ +void pmu_highdriver_mode_disable(void) +{ + PMU_CTL0 &= ~PMU_CTL0_HDEN; +} + +/*! + \brief switch high-driver mode + this bit set by software only when IRC8M or HXTAL used as system clock + \param[in] highdr_switch: + only one parameter can be selected which is shown as below: + \arg PMU_HIGHDR_SWITCH_NONE: disable high-driver mode switch + \arg PMU_HIGHDR_SWITCH_EN: enable high-driver mode switch + \param[out] none + \retval none +*/ +void pmu_highdriver_switch_select(uint32_t highdr_switch) +{ + /* wait for HDRF flag set */ + while(SET != pmu_flag_get(PMU_FLAG_HDRF)){ + } + PMU_CTL0 &= ~PMU_CTL0_HDS; + PMU_CTL0 |= highdr_switch; +} + +/*! + \brief enable low-driver mode in deep-sleep/deep-sleep 1/deep-sleep 2 mode + \param[in] none + \param[out] none + \retval none +*/ +void pmu_lowdriver_mode_enable(void) +{ + PMU_CTL0 |= PMU_CTL0_LDEN; +} + +/*! + \brief disable low-driver mode in deep-sleep/deep-sleep 1/deep-sleep 2 mode + \param[in] none + \param[out] none + \retval none +*/ +void pmu_lowdriver_mode_disable(void) +{ + PMU_CTL0 &= ~PMU_CTL0_LDEN; +} + +/*! + \brief in deep-sleep/deep-sleep 1/deep-sleep 2 mode, driver mode when use low power LDO + \param[in] mode: + only one parameter can be selected which is shown as below: + \arg PMU_NORMALDR_LOWPWR: normal driver when use low power LDO + \arg PMU_LOWDR_LOWPWR: low-driver mode enabled when LDEN is 11 and use low power LDO + \param[out] none + \retval none +*/ +void pmu_lowpower_driver_config(uint32_t mode) +{ + PMU_CTL0 &= ~PMU_CTL0_LDLP; + PMU_CTL0 |= mode; +} + +/*! + \brief in deep-sleep/deep-sleep 1/deep-sleep 2 mode, driver mode when use normal power LDO + \param[in] mode: + only one parameter can be selected which is shown as below: + \arg PMU_NORMALDR_NORMALPWR: normal driver when use normal power LDO + \arg PMU_LOWDR_NORMALPWR: low-driver mode enabled when LDEN is 11 and use normal power LDO + \param[out] none + \retval none +*/ +void pmu_normalpower_driver_config(uint32_t mode) +{ + PMU_CTL0 &= ~PMU_CTL0_LDNP; + PMU_CTL0 |= mode; +} + +/*! + \brief PMU work at sleep mode + \param[in] sleepmodecmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_sleepmode(uint8_t sleepmodecmd) +{ + /* clear sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); + + /* select WFI or WFE command to enter sleep mode */ + if(WFI_CMD == sleepmodecmd){ + __WFI(); + }else{ + __SEV(); + __WFE(); + __WFE(); + } +} + +/*! + \brief PMU work at deepsleep mode + \param[in] ldo: + only one parameter can be selected which is shown as below: + \arg PMU_LDO_NORMAL: LDO normal work when pmu enter deepsleep mode + \arg PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode + \param[in] lowdrive: + only one parameter can be selected which is shown as below: + \arg PMU_LOWDRIVER_ENABLE: low-driver mode enable in deep-sleep mode + \arg PMU_LOWDRIVER_DISABLE: low-driver mode disable in deep-sleep mode + \param[in] deepsleepmodecmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_deepsleepmode(uint32_t ldo, uint32_t lowdrive, uint8_t deepsleepmodecmd) +{ + /* clear stbmod and ldolp bits and low drive bits */ + PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_STBMOD | PMU_CTL0_LDOLP | PMU_CTL0_LDEN | PMU_CTL0_LDNP | PMU_CTL0_LDLP)); + /* clear deep-sleep 1/2 mode enable bits */ + PMU_CTL1 &= ~(PMU_CTL1_DPMOD1 | PMU_CTL1_DPMOD2); + + /* set ldolp bit according to pmu_ldo */ + PMU_CTL0 |= ldo; + + /* low drive mode config in deep-sleep mode */ + if(PMU_LOWDRIVER_ENABLE == lowdrive){ + if(PMU_LDO_NORMAL == ldo){ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDNP); + }else{ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDLP); + } + } + + /* set sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + /* select WFI or WFE command to enter deepsleep mode */ + if(WFI_CMD == deepsleepmodecmd){ + __WFI(); + }else{ + __SEV(); + __WFE(); + __WFE(); + } + + /* reset sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); +} + +/*! + \brief PMU work at deepsleep mode 1 + \param[in] ldo: + only one parameter can be selected which is shown as below: + \arg PMU_LDO_NORMAL: LDO normal work when pmu enter deepsleep mode 1 + \arg PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode 1 + \param[in] lowdrive: + only one parameter can be selected which is shown as below: + \arg PMU_LOWDRIVER_ENABLE: low-driver mode enable in deep-sleep 1 mode + \arg PMU_LOWDRIVER_DISABLE: low-driver mode disable in deep-sleep 1 mode + \param[in] deepsleepmode1cmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_deepsleepmode_1(uint32_t ldo, uint32_t lowdrive, uint8_t deepsleepmode1cmd) +{ + /* clear stbmod and ldolp bits and low drive bits */ + PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_STBMOD | PMU_CTL0_LDOLP | PMU_CTL0_LDEN | PMU_CTL0_LDNP | PMU_CTL0_LDLP)); + /* clear deep-sleep 2 mode enable bit */ + PMU_CTL1 &= ~PMU_CTL1_DPMOD2; + /* enable deep-sleep 1 mode */ + PMU_CTL1 |= PMU_CTL1_DPMOD1; + + /* set ldolp bit according to pmu_ldo */ + PMU_CTL0 |= ldo; + + /* low drive mode config in deep-sleep 1 mode */ + if(PMU_LOWDRIVER_ENABLE == lowdrive){ + if(PMU_LDO_NORMAL == ldo){ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDNP); + }else{ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDLP); + } + } + + /* set sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + /* select WFI or WFE command to enter deepsleep mode 1 */ + if(WFI_CMD == deepsleepmode1cmd){ + __WFI(); + }else{ + __SEV(); + __WFE(); + __WFE(); + } + + /* reset sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); + PMU_CTL1 &= ~PMU_CTL1_DPMOD1; +} + +/*! + \brief PMU work at deepsleep mode 2 + \param[in] ldo: + only one parameter can be selected which is shown as below: + \arg PMU_LDO_NORMAL: LDO normal work when pmu enter deepsleep mode 2 + \arg PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode 2 + \param[in] lowdrive: + only one parameter can be selected which is shown as below: + \arg PMU_LOWDRIVER_ENABLE: low-driver mode enable in deep-sleep 2 mode + \arg PMU_LOWDRIVER_DISABLE: low-driver mode disable in deep-sleep 2 mode + \param[in] deepsleepmode2cmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_deepsleepmode_2(uint32_t ldo, uint32_t lowdrive, uint8_t deepsleepmode2cmd) +{ + /* clear stbmod and ldolp bits and low drive bits */ + PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_STBMOD | PMU_CTL0_LDOLP | PMU_CTL0_LDEN | PMU_CTL0_LDNP | PMU_CTL0_LDLP)); + /* clear deep-sleep 1 mode enable bit */ + PMU_CTL1 &= ~PMU_CTL1_DPMOD1; + /* enable deep-sleep 2 mode */ + PMU_CTL1 |= PMU_CTL1_DPMOD2; + + /* set ldolp bit according to pmu_ldo */ + PMU_CTL0 |= ldo; + + /* low drive mode config in deep-sleep 2 mode */ + if(PMU_LOWDRIVER_ENABLE == lowdrive){ + if(PMU_LDO_NORMAL == ldo){ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDNP); + }else{ + PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDLP); + } + } + + /* set sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + /* select WFI or WFE command to enter deepsleep mode 2 */ + if(WFI_CMD == deepsleepmode2cmd){ + __WFI(); + }else{ + __SEV(); + __WFE(); + __WFE(); + } + + /* reset sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); + PMU_CTL1 &= ~PMU_CTL1_DPMOD2; +} + +/*! + \brief pmu work at standby mode + \param[in] none + \param[out] none + \retval none +*/ +void pmu_to_standbymode(void) +{ + /* set stbmod bit */ + PMU_CTL0 |= PMU_CTL0_STBMOD; + + /* reset wakeup flag */ + PMU_CTL0 |= PMU_CTL0_WURST; + + /* set sleepdeep bit of Cortex-M33 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + REG32( 0xE000E010U ) &= 0x00010004U; + REG32( 0xE000E180U ) = 0XFFFFFFF7U; + REG32( 0xE000E184U ) = 0XFFFFFDFFU; + REG32( 0xE000E188U ) = 0xFFFFFFFFU; + + /* select WFI command to enter standby mode */ + __WFI(); +} + +/*! + \brief enable PMU wakeup pin + \param[in] wakeup_pin: + one or more parameters can be selected which are shown as below: + \arg PMU_WAKEUP_PIN0: WKUP Pin 0 (PA0) + \arg PMU_WAKEUP_PIN1: WKUP Pin 1 (PC13) + \arg PMU_WAKEUP_PIN2: WKUP Pin 2 (PE6) + \arg PMU_WAKEUP_PIN3: WKUP Pin 3 (PA2) + \arg PMU_WAKEUP_PIN4: WKUP Pin 4 (PC5) + \arg PMU_WAKEUP_PIN5: WKUP Pin 5 (PB5) + \arg PMU_WAKEUP_PIN6: WKUP Pin 6 (PB15) + \arg PMU_WAKEUP_PIN7: WKUP Pin 7 (PF8) + \param[out] none + \retval none +*/ +void pmu_wakeup_pin_enable(uint32_t wakeup_pin) +{ + PMU_CS0 |= wakeup_pin; +} + +/*! + \brief disable PMU wakeup pin + \param[in] wakeup_pin: + one or more parameters can be selected which are shown as below: + \arg PMU_WAKEUP_PIN0: WKUP Pin 0 (PA0) + \arg PMU_WAKEUP_PIN1: WKUP Pin 1 (PC13) + \arg PMU_WAKEUP_PIN2: WKUP Pin 2 (PE6) + \arg PMU_WAKEUP_PIN3: WKUP Pin 3 (PA2) + \arg PMU_WAKEUP_PIN4: WKUP Pin 4 (PC5) + \arg PMU_WAKEUP_PIN5: WKUP Pin 5 (PB5) + \arg PMU_WAKEUP_PIN6: WKUP Pin 6 (PB15) + \arg PMU_WAKEUP_PIN7: WKUP Pin 7 (PF8) + \param[out] none + \retval none +*/ +void pmu_wakeup_pin_disable(uint32_t wakeup_pin) +{ + PMU_CS0 &= ~(wakeup_pin); +} + +/*! + \brief enable backup domain write + \param[in] none + \param[out] none + \retval none +*/ +void pmu_backup_write_enable(void) +{ + PMU_CTL0 |= PMU_CTL0_BKPWEN; +} + +/*! + \brief disable backup domain write + \param[in] none + \param[out] none + \retval none +*/ +void pmu_backup_write_disable(void) +{ + PMU_CTL0 &= ~PMU_CTL0_BKPWEN; +} + +/*! + \brief get flag state + \param[in] flag: + only one parameter can be selected which is shown as below: + \arg PMU_FLAG_WAKEUP: wakeup flag + \arg PMU_FLAG_STANDBY: standby flag + \arg PMU_FLAG_LVD: lvd flag + \arg PMU_FLAG_HDRF: high-driver ready flag + \arg PMU_FLAG_HDSRF: high-driver switch ready flag + \arg PMU_FLAG_LDRF: low-driver mode ready flag + \arg PMU_FLAG_DEEPSLEEP_1: deep-sleep 1 mode status flag + \arg PMU_FLAG_DEEPSLEEP_2: deep-sleep 2 mode status flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus pmu_flag_get(uint32_t flag) +{ + FlagStatus ret = RESET; + + if(RESET != (flag & BIT(31))){ + flag &= ~BIT(31); + if(PMU_CS1 & flag){ + ret = SET; + } + }else{ + if(PMU_CS0 & flag){ + ret = SET; + } + } + return ret; +} + +/*! + \brief clear flag bit + \param[in] flag: + only one parameter can be selected which is shown as below: + \arg PMU_FLAG_RESET_WAKEUP: reset wakeup flag + \arg PMU_FLAG_RESET_STANDBY: reset standby flag + \arg PMU_FLAG_RESET_DEEPSLEEP_1: reset deep-sleep 1 mode status flag + \arg PMU_FLAG_RESET_DEEPSLEEP_2: reset deep-sleep 2 mode status flag + \param[out] none + \retval none +*/ +void pmu_flag_clear(uint32_t flag) +{ + switch(flag){ + case PMU_FLAG_RESET_WAKEUP: + /* reset wakeup flag */ + PMU_CTL0 |= PMU_CTL0_WURST; + break; + case PMU_FLAG_RESET_STANDBY: + /* reset standby flag */ + PMU_CTL0 |= PMU_CTL0_STBRST; + break; + case PMU_FLAG_RESET_DEEPSLEEP_1: + /* reset deep-sleep 1 mode status flag */ + PMU_CS1 &= ~PMU_CS1_DPF1; + break; + case PMU_FLAG_RESET_DEEPSLEEP_2: + /* reset deep-sleep 2 mode status flag */ + PMU_CS1 &= ~PMU_CS1_DPF2; + break; + default : + break; + } +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_rcu.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_rcu.c new file mode 100644 index 0000000..41a5251 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_rcu.c @@ -0,0 +1,1621 @@ +/*! + \file gd32e50x_rcu.c + \brief RCU driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_rcu.h" + +/* define clock source */ +#define SEL_IRC8M ((uint16_t)0U) /* IRC8M is selected as CK_SYS */ +#define SEL_HXTAL ((uint16_t)1U) /* HXTAL is selected as CK_SYS */ +#define SEL_PLL ((uint16_t)2U) /* PLL is selected as CK_SYS */ + +/* define startup timeout count */ +#define OSC_STARTUP_TIMEOUT ((uint32_t)0x000FFFFFU) +#define LXTAL_STARTUP_TIMEOUT ((uint32_t)0x03FFFFFFU) + +/* ADC clock prescaler offset */ +#define RCU_ADC_PSC_OFFSET ((uint32_t)14U) + +/* RCU IRC8M adjust value mask and offset*/ +#define RCU_IRC8M_ADJUST_MASK ((uint8_t)0x1FU) +#define RCU_IRC8M_ADJUST_OFFSET ((uint32_t)3U) + +/* RCU PLL1 clock multiplication factor offset */ +#define RCU_CFG1_PLL1MF_OFFSET ((uint32_t)8U) +/* RCU PREDV1 division factor offset*/ +#define RCU_CFG1_PREDV1_OFFSET ((uint32_t)4U) + +/*! + \brief deinitialize the RCU + \param[in] none + \param[out] none + \retval none +*/ +void rcu_deinit(void) +{ + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + while(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + } + RCU_CFG0 &= ~RCU_CFG0_SCS; + /* reset CTL register */ + RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN); + RCU_CTL &= ~RCU_CTL_HXTALBPS; +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + RCU_CTL &= ~(RCU_CTL_PLL1EN | RCU_CTL_PLL2EN); +#endif /* GD32E50X_CL and GD32EPRT and GD32E508 */ + /* reset CFG0 register */ +#if defined(GD32E50X_HD) + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0 | RCU_CFG0_PLLMF | + RCU_CFG0_USBDPSC | RCU_CFG0_CKOUT0SEL | RCU_CFG0_PLLMF_4 | RCU_CFG0_ADCPSC_2 | RCU_CFG0_PLLMF_5 | RCU_CFG0_USBDPSC_2); +#elif defined(GD32E50X_CL) || defined(GD32E508) + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF | + RCU_CFG0_USBHSPSC | RCU_CFG0_CKOUT0SEL | RCU_CFG0_ADCPSC_2 | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5 | RCU_CFG0_USBHSPSC_2); +#elif defined(GD32EPRT) + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF | + RCU_CFG0_USBDPSC | RCU_CFG0_CKOUT0SEL | RCU_CFG0_ADCPSC_2 | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5 | RCU_CFG0_USBDPSC_2); +#endif /* GD32E50X_HD */ + + /* reset INT and CFG1 register */ +#if defined(GD32E50X_HD) + RCU_INT = 0x009f0000U; + RCU_CFG1 &= ~(RCU_CFG1_ADCPSC_3 | RCU_CFG1_PLLPRESEL); + RCU_CFG2 &= ~(RCU_CFG2_USART5SEL | RCU_CFG2_I2C2SEL); +#elif defined(GD32E50X_CL) || defined(GD32E508) + RCU_INT = 0x00ff0000U; + RCU_ADDINT = 0x00C00000U; + RCU_ADDCTL &= ~(RCU_ADDCTL_CK48MSEL | RCU_ADDCTL_USBHSSEL | RCU_ADDCTL_USBHSDV | RCU_ADDCTL_USBSWEN | RCU_ADDCTL_PLLUSBEN | RCU_ADDCTL_IRC48MEN); + RCU_ADDCFG &= ~(RCU_ADDCFG_PLLUSBPREDV | RCU_ADDCFG_PLLUSBPRESEL | RCU_ADDCFG_PLLUSBPREDVSEL | RCU_ADDCFG_PLLUSBMF); + + RCU_CFG1 &= ~(RCU_CFG1_PREDV0 | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PLL2MF | + RCU_CFG1_PREDV0SEL | RCU_CFG1_I2S1SEL | RCU_CFG1_I2S2SEL | RCU_CFG1_SHRTIMERSEL | RCU_CFG1_PLL2MF_5 | RCU_CFG1_ADCPSC_3 | + RCU_CFG1_PLLPRESEL | RCU_CFG1_PLL2MF_4); + RCU_CFG2 &= ~(RCU_CFG2_USART5SEL | RCU_CFG2_I2C2SEL); +#elif defined(GD32EPRT) + RCU_INT = 0x00ff0000U; + RCU_ADDINT &= ~ (RCU_ADDINT_IRC48MSTBIC); + RCU_ADDCTL &= ~(RCU_ADDCTL_CK48MSEL | RCU_ADDCTL_IRC48MEN); + + RCU_CFG1 &= ~(RCU_CFG1_PREDV0 | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PLL2MF | + RCU_CFG1_PREDV0SEL | RCU_CFG1_I2S1SEL | RCU_CFG1_I2S2SEL | RCU_CFG1_PLL2MF_5 | RCU_CFG1_ADCPSC_3 | + RCU_CFG1_PLLPRESEL | RCU_CFG1_PLL2MF_4); + RCU_CFG2 &= ~(RCU_CFG2_USART5SEL | RCU_CFG2_I2C2SEL); + +#endif /* GD32E50X_HD */ +} + +/*! + \brief enable the peripherals clock + \param[in] periph: RCU peripherals, refer to rcu_periph_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOx (x = A,B,C,D,E,F,G): GPIO ports clock + \arg RCU_AF : alternate function clock + \arg RCU_CRC: CRC clock + \arg RCU_DMAx (x = 0,1): DMA clock + \arg RCU_ENET: ENET clock(EPRT and CL series available) + \arg RCU_ENETTX: ENETTX clock(EPRT and CL series available) + \arg RCU_ENETRX: ENETRX clock(EPRT and CL series available) + \arg RCU_USBD: USBD clock(HD and EPRT series available) + \arg RCU_USBHS: USBHS clock(CL series available) + \arg RCU_TMU: TMU clock + \arg RCU_SQPI: SQPI clock + \arg RCU_EXMC: EXMC clock + \arg RCU_TIMERx (x = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for EPRT series): TIMER clock + \arg RCU_WWDGT: WWDGT clock + \arg RCU_SPIx (x = 0,1,2): SPI clock + \arg RCU_USARTx (x = 0,1,2,5): USART clock + \arg RCU_UARTx (x = 3,4): UART clock + \arg RCU_I2Cx (x = 0,1,2): I2C clock + \arg RCU_CANx (x = 0,1,2,CAN2 is only available for CL series,CANx is not avaliable for GD32EPRT): CAN clock + \arg RCU_PMU: PMU clock + \arg RCU_DAC: DAC clock + \arg RCU_RTC: RTC clock + \arg RCU_ADCx (x = 0,1,2, ADC2 is not available for CL series): ADC clock + \arg RCU_SDIO: SDIO clock(not available for CL and EPRT series) + \arg RCU_CTC: CTC clock + \arg RCU_BKPI: BKP interface clock + \arg RCU_SHRTIMER: (not available for EPRT series):SHRTIMER clock + \arg RCU_CMP(CMP is only available for CL series):CMP clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_enable(rcu_periph_enum periph) +{ + RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief disable the peripherals clock + \param[in] periph: RCU peripherals, refer to rcu_periph_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOx (x = A,B,C,D,E,F,G): GPIO ports clock + \arg RCU_AF : alternate function clock + \arg RCU_CRC: CRC clock + \arg RCU_DMAx (x =0,1): DMA clock + \arg RCU_ENET: ENET clock(EPRT and CL series available) + \arg RCU_ENETTX: ENETTX clock(EPRT and CL series available) + \arg RCU_ENETRX: ENETRX clock(EPRT and CL series available) + \arg RCU_USBD: USBD clock(HD and EPRT series available) + \arg RCU_USBHS: USBHS clock(CL series available) + \arg RCU_TMU: TMU clock + \arg RCU_SQPI: SQPI clock + \arg RCU_EXMC: EXMC clock + \arg RCU_TIMERx (x = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for EPRT series): TIMER clock + \arg RCU_WWDGT: WWDGT clock + \arg RCU_SPIx (x = 0,1,2): SPI clock + \arg RCU_USARTx (x = 0,1,2,5): USART clock + \arg RCU_UARTx (x = 3,4): UART clock + \arg RCU_I2Cx (x = 0,1,2): I2C clock + \arg RCU_CANx (x = 0,1,2,CAN2 is only available for CL series,CANx is not avaliable for GD32EPRT): CAN clock + \arg RCU_PMU: PMU clock + \arg RCU_DAC: DAC clock + \arg RCU_RTC: RTC clock + \arg RCU_ADCx (x = 0,1,2,ADC2 is not available for CL series): ADC clock + \arg RCU_SDIO: SDIO clock(not available for CL and EPRT series) + \arg RCU_CTC: CTC clock + \arg RCU_BKPI: BKP interface clock + \arg RCU_SHRTIMER: (not available for EPRT series): SHRTIMER clock + \arg RCU_CMP(CMP is only available for CL series):CMP clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_disable(rcu_periph_enum periph) +{ + RCU_REG_VAL(periph) &= ~BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief enable the peripherals clock when sleep mode + \param[in] periph: RCU peripherals, refer to rcu_periph_sleep_enum + only one parameter can be selected which is shown as below: + \arg RCU_FMC_SLP: FMC clock + \arg RCU_SRAM_SLP: SRAM clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_sleep_enable(rcu_periph_sleep_enum periph) +{ + RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief disable the peripherals clock when sleep mode + \param[in] periph: RCU peripherals, refer to rcu_periph_sleep_enum + only one parameter can be selected which is shown as below: + \arg RCU_SRAM_SLP: SRAM clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_sleep_disable(rcu_periph_sleep_enum periph) +{ + RCU_REG_VAL(periph) &= ~BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief reset the peripherals + \param[in] periph_reset: RCU peripherals reset, refer to rcu_periph_reset_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOxRST (x = A,B,C,D,E,F,G): reset GPIO ports + \arg RCU_AFRST : reset alternate function clock + \arg RCU_ENETRST: reset ENET(EPRT and CL series available) + \arg RCU_USBDRST: reset USBD(HD and EPRT series available) + \arg RCU_USBHSRST: reset USBHS(CL series available) + \arg RCU_TMURST: reset TMU + \arg RCU_SQPIRST: reset SQPI + \arg RCU_TIMERxRST (x = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for EPRT series): reset TIMER + \arg RCU_WWDGTRST: reset WWDGT + \arg RCU_SPIxRST (x = 0,1,2): reset SPI + \arg RCU_USARTxRST (x = 0,1,2,5): reset USART + \arg RCU_UARTxRST (x = 3,4): reset UART + \arg RCU_I2CxRST (x = 0,1): reset I2C + \arg RCU_PMURST: reset PMU + \arg RCU_DACRST: reset DAC + \arg RCU_ADCRST (x = 0,1,2,ADC2 is not available for CL series): reset ADC + \arg RCU_CTCRST: reset CTC + \arg RCU_BKPIRST: reset BKPI + \arg RCU_SHRTIMERRST: (not available for EPRT series):reset SHRTIMERRST + \arg RCU_CMPRST(CMP is only available for CL series): reset CMP + \param[out] none + \retval none +*/ +void rcu_periph_reset_enable(rcu_periph_reset_enum periph_reset) +{ + RCU_REG_VAL(periph_reset) |= BIT(RCU_BIT_POS(periph_reset)); +} + +/*! + \brief disable reset the peripheral + \param[in] periph_reset: RCU peripherals reset, refer to rcu_periph_reset_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOxRST (x = A,B,C,D,E,F,G): reset GPIO ports + \arg RCU_AFRST : reset alternate function clock + \arg RCU_ENETRST: reset ENET(CL series available) + \arg RCU_USBDRST: reset USBD(HD and EPRT series available) + \arg RCU_USBHSRST: reset USBHS(CL series available) + \arg RCU_TMURST: reset TMU + \arg RCU_SQPIRST: reset SQPI + \arg RCU_TIMERxRST (x = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for EPRT series): reset TIMER + \arg RCU_WWDGTRST: reset WWDGT + \arg RCU_SPIxRST (x = 0,1,2): reset SPI + \arg RCU_USARTxRST (x = 0,1,2,5): reset USART + \arg RCU_UARTxRST (x = 3,4): reset UART + \arg RCU_I2CxRST (x = 0,1): reset I2C + \arg RCU_PMURST: reset PMU + \arg RCU_DACRST: reset DAC + \arg RCU_ADCRST (x = 0,1,2,ADC2 is not available for CL series): reset ADC + \arg RCU_CTCRST: reset CTC + \arg RCU_BKPIRST: reset BKPI + \arg RCU_SHRTIMERRST: (not available for EPRT series):reset SHRTIMERRST + \arg RCU_CMPRST(CMP is only available for CL series): reset CMP + \param[out] none + \retval none +*/ +void rcu_periph_reset_disable(rcu_periph_reset_enum periph_reset) +{ + RCU_REG_VAL(periph_reset) &= ~BIT(RCU_BIT_POS(periph_reset)); +} + +/*! + \brief reset the BKP domain + \param[in] none + \param[out] none + \retval none +*/ +void rcu_bkp_reset_enable(void) +{ + RCU_BDCTL |= RCU_BDCTL_BKPRST; +} + +/*! + \brief disable the BKP domain reset + \param[in] none + \param[out] none + \retval none +*/ +void rcu_bkp_reset_disable(void) +{ + RCU_BDCTL &= ~RCU_BDCTL_BKPRST; +} + +/*! + \brief configure the system clock source + \param[in] ck_sys: system clock source select + only one parameter can be selected which is shown as below: + \arg RCU_CKSYSSRC_IRC8M: select CK_IRC8M as the CK_SYS source + \arg RCU_CKSYSSRC_HXTAL: select CK_HXTAL as the CK_SYS source + \arg RCU_CKSYSSRC_PLL: select CK_PLL as the CK_SYS source + \param[out] none + \retval none +*/ +void rcu_system_clock_source_config(uint32_t ck_sys) +{ + uint32_t reg; + + reg = RCU_CFG0; + /* reset the SCS bits and set according to ck_sys */ + reg &= ~RCU_CFG0_SCS; + RCU_CFG0 = (reg | ck_sys); +} + +/*! + \brief get the system clock source + \param[in] none + \param[out] none + \retval which clock is selected as CK_SYS source + \arg RCU_SCSS_IRC8M: CK_IRC8M is selected as the CK_SYS source + \arg RCU_SCSS_HXTAL: CK_HXTAL is selected as the CK_SYS source + \arg RCU_SCSS_PLL: CK_PLL is selected as the CK_SYS source +*/ +uint32_t rcu_system_clock_source_get(void) +{ + return (RCU_CFG0 & RCU_CFG0_SCSS); +} + +/*! + \brief configure the AHB clock prescaler selection + \param[in] ck_ahb: AHB clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_AHB_CKSYS_DIVx (x = 1, 2, 4, 8, 16, 64, 128, 256, 512): select CK_SYS / x as CK_AHB + \param[out] none + \retval none +*/ +void rcu_ahb_clock_config(uint32_t ck_ahb) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the AHBPSC bits and set according to ck_ahb */ + reg &= ~RCU_CFG0_AHBPSC; + RCU_CFG0 = (reg | ck_ahb); +} + +/*! + \brief configure the APB1 clock prescaler selection + \param[in] ck_apb1: APB1 clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_APB1_CKAHB_DIV1: select CK_AHB as CK_APB1 + \arg RCU_APB1_CKAHB_DIV2: select CK_AHB / 2 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV4: select CK_AHB / 4 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV8: select CK_AHB / 8 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV16: select CK_AHB / 16 as CK_APB1 + \param[out] none + \retval none +*/ +void rcu_apb1_clock_config(uint32_t ck_apb1) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the APB1PSC and set according to ck_apb1 */ + reg &= ~RCU_CFG0_APB1PSC; + RCU_CFG0 = (reg | ck_apb1); +} + +/*! + \brief configure the APB2 clock prescaler selection + \param[in] ck_apb2: APB2 clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_APB2_CKAHB_DIV1: select CK_AHB as CK_APB2 + \arg RCU_APB2_CKAHB_DIV2: select CK_AHB/2 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV4: select CK_AHB/4 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV8: select CK_AHB/8 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV16: select CK_AHB/16 as CK_APB2 + \param[out] none + \retval none +*/ +void rcu_apb2_clock_config(uint32_t ck_apb2) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the APB2PSC and set according to ck_apb2 */ + reg &= ~RCU_CFG0_APB2PSC; + RCU_CFG0 = (reg | ck_apb2); +} + +/*! + \brief configure the CK_OUT0 clock source + \param[in] ckout0_src: CK_OUT0 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_CKOUT0SRC_NONE: no clock selected + \arg RCU_CKOUT0SRC_CKSYS: system clock selected + \arg RCU_CKOUT0SRC_IRC8M: high speed 8M internal oscillator clock selected + \arg RCU_CKOUT0SRC_HXTAL: HXTAL selected + \arg RCU_CKOUT0SRC_CKPLL_DIV2: CK_PLL/2 selected + \arg RCU_CKOUT0SRC_CKPLL1: CK_PLL1 selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_CKPLL2_DIV2: CK_PLL2/2 selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_EXT1: EXT1 selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_CKPLL2: CK_PLL2 clock selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_CKIRC48M: CK_IRC48M clock selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_CKIRC48M_DIV8: CK_IRC48M/8 clock selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_CKPLLUSB_DIV32: CK_PLLUSB/32 clock selected (available for CL and E508 series) + \param[out] none + \retval none +*/ +void rcu_ckout0_config(uint32_t ckout0_src) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the CKOUT0SRC, set according to ckout0_src */ + reg &= ~RCU_CFG0_CKOUT0SEL; + RCU_CFG0 = (reg | ckout0_src); +} + +/*! + \brief configure the main PLL clock + \param[in] pll_src: PLL clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_PLLSRC_IRC8M_DIV2: IRC8M/2 clock selected as source clock of PLL + \arg RCU_PLLSRC_HXTAL_IRC48M: HXTAL or IRC48M selected as source clock of PLL + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL_MULx (HD series x = 2..63, CL series x = 2..14, 16..64, 6.5): PLL clock * x + \param[out] none + \retval none +*/ +void rcu_pll_config(uint32_t pll_src, uint32_t pll_mul) +{ + uint32_t reg = 0U; + + reg = RCU_CFG0; + + /* PLL clock source and multiplication factor configuration */ + reg &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + reg |= (pll_src | pll_mul); + + RCU_CFG0 = reg; +} + +/*! + \brief configure the PLL clock source preselection + \param[in] pll_presel: PLL clock source preselection + only one parameter can be selected which is shown as below: + \arg RCU_PLLPRESRC_HXTAL: HXTAL selected as PREDV0 input source clock + \arg RCU_PLLPRESRC_IRC48M: CK_PLL selected as PREDV0 input source clock + \param[out] none + \retval none +*/ +void rcu_pllpresel_config(uint32_t pll_presel) +{ + uint32_t reg = 0U; + + reg = RCU_CFG1; + + /* PLL clock source preselection */ + reg &= ~RCU_CFG1_PLLPRESEL; + reg |= pll_presel; + + RCU_CFG1 = reg; +} + +#if defined(GD32E50X_HD) +/*! + \brief configure the PREDV0 division factor + \param[in] predv0_div: PREDV0 division factor + \arg RCU_PREDV0_DIVx (x = 1, 2): PREDV0 input source clock is divided x + \param[out] none + \retval none +*/ +void rcu_predv0_config(uint32_t predv0_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG0; + /* reset PREDV0 bit */ + reg &= ~RCU_CFG0_PREDV0; + if(RCU_PREDV0_DIV2 == predv0_div){ + /* set the PREDV0 bit */ + reg |= RCU_CFG0_PREDV0; + } + + RCU_CFG0 = reg; +} +#elif defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) +/*! + \brief configure the PREDV0 division factor and clock source + \param[in] predv0_source: PREDV0 input clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_PREDV0SRC_HXTAL_IRC48M: HXTAL or IRC48M selected as PREDV0 input source clock + \arg RCU_PREDV0SRC_CKPLL1: CK_PLL1 selected as PREDV0 input source clock + \param[in] predv0_div: PREDV0 division factor + only one parameter can be selected which is shown as below: + \arg RCU_PREDV0_DIVx (x = 1..16): PREDV0 input source clock is divided x + \param[out] none + \retval none +*/ +void rcu_predv0_config(uint32_t predv0_source, uint32_t predv0_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG1; + /* reset PREDV0SEL and PREDV0 bits */ + reg &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV0); + /* set the PREDV0SEL and PREDV0 division factor */ + reg |= (predv0_source | predv0_div); + + RCU_CFG1 = reg; +} + +/*! + \brief configure the PREDV1 division factor + \param[in] predv1_div: PREDV1 division factor + only one parameter can be selected which is shown as below: + \arg RCU_PREDV1_DIVx (x = 1..16): PREDV1 input source clock is divided x + \param[out] none + \retval none +*/ +void rcu_predv1_config(uint32_t predv1_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG1; + /* reset the PREDV1 bits */ + reg &= ~RCU_CFG1_PREDV1; + /* set the PREDV1 division factor */ + reg |= predv1_div; + + RCU_CFG1 = reg; +} + +/*! + \brief configure the PLL1 clock + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL1_MULx (x = 8..14,16,20): PLL1 clock * x + \param[out] none + \retval none +*/ +void rcu_pll1_config(uint32_t pll_mul) +{ + RCU_CFG1 &= ~RCU_CFG1_PLL1MF; + RCU_CFG1 |= pll_mul; +} + +/*! + \brief configure the PLL2 clock + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL2_MULx (x = 8..14,16,20,18..32,40,34..64,80): PLL2 clock * x + \param[out] none + \retval none +*/ +void rcu_pll2_config(uint32_t pll_mul) +{ + RCU_CFG1 &= ~RCU_CFG1_PLL2MF; + RCU_CFG1 |= pll_mul; +} +#endif /* GD32E50X_HD */ + +#if defined(GD32E50X_CL) || defined(GD32E508) +/*! + \brief configure the PLLUSB clock source preselection + \param[in] pllusb_presel: PLLUSB clock source preselection + only one parameter can be selected which is shown as below: + \arg RCU_PLLUSBPRESRC_HXTAL: HXTAL selected as PLLUSB source clock + \arg RCU_PLLUSBPRESRC_IRC48M: IRC48M clock selected as PLLUSB source clock + \param[out] none + \retval none +*/ +void rcu_pllusbpresel_config(uint32_t pllusb_presel) +{ + uint32_t reg = 0U; + + reg = RCU_ADDCFG; + + /* PLLUSB clock source preselection */ + reg &= ~RCU_ADDCFG_PLLUSBPRESEL; + reg |= pllusb_presel; + + RCU_ADDCFG = reg; +} + +/*! + \brief configure the PLLUSBPREDV division factor and clock source + \param[in] pllusbpredv_source: PLLUSBPREDV input clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_PLLUSBPREDVSRC_HXTAL_IRC48M: HXTAL or IRC48M selected as PLLUSBPREDV input source clock + \arg RCU_PLLUSBPREDVSRC_CKPLL1: CK_PLL1 selected as PLLUSBPREDV input source clock + \param[in] pllusbpredv_div: PLLUSBPREDV division factor + only one parameter can be selected which is shown as below: + \arg RCU_PLLUSBPREDV_DIVx (x = 1..15): PLLUSBPREDV input source clock divided by x + \param[out] none + \retval none +*/ +void rcu_pllusbpredv_config(uint32_t pllusbpredv_source, uint32_t pllusbpredv_div) +{ + uint32_t reg = 0U; + + reg = RCU_ADDCFG; + /* reset PLLUSBPRESEL and PLLUSBPREDV bits */ + reg &= ~(RCU_ADDCFG_PLLUSBPREDVSEL | RCU_ADDCFG_PLLUSBPREDV); + /* set the PLLUSBPRESEL and PLLUSBPREDV division factor */ + reg |= (pllusbpredv_source | pllusbpredv_div); + + RCU_ADDCFG = reg; +} + +/*! + \brief configure the PLLUSB clock + \param[in] pllusb_mul: PLLUSB clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLLUSB_MULx (x = 16,17..127): PLLUSB source clock multiply by x + \param[out] none + \retval none +*/ +void rcu_pllusb_config(uint32_t pllusb_mul) +{ + RCU_ADDCFG &= ~RCU_ADDCFG_PLLUSBMF; + RCU_ADDCFG |= pllusb_mul; +} + +#endif /* GD32E50X_CL and GD32E508 */ + +/*! + \brief configure the ADC prescaler factor + \param[in] adc_psc: ADC prescaler factor + only one parameter can be selected which is shown as below: + \arg RCU_CKADC_CKAPB2_DIV2: ADC prescaler select CK_APB2 / 2 + \arg RCU_CKADC_CKAPB2_DIV4: ADC prescaler select CK_APB2 / 4 + \arg RCU_CKADC_CKAPB2_DIV6: ADC prescaler select CK_APB2 / 6 + \arg RCU_CKADC_CKAPB2_DIV8: ADC prescaler select CK_APB2 / 8 + \arg RCU_CKADC_CKAPB2_DIV12: ADC prescaler select CK_APB2 / 12 + \arg RCU_CKADC_CKAPB2_DIV16: ADC prescaler select CK_APB2 / 16 + \arg RCU_CKADC_CKAHB_DIV5: ADC prescaler select CK_AHB / 5 + \arg RCU_CKADC_CKAHB_DIV6: ADC prescaler select CK_AHB / 6 + \arg RCU_CKADC_CKAHB_DIV10: ADC prescaler select CK_AHB / 10 + \arg RCU_CKADC_CKAHB_DIV20: ADC prescaler select CK_AHB / 20 + \param[out] none + \retval none +*/ +void rcu_adc_clock_config(uint32_t adc_psc) +{ + uint32_t reg0,reg1; + + /* reset the ADCPSC bits */ + reg0 = RCU_CFG0; + reg0 &= ~(RCU_CFG0_ADCPSC_2 | RCU_CFG0_ADCPSC); + reg1 = RCU_CFG1; + reg1 &= ~RCU_CFG1_ADCPSC_3; + + /* set the ADC prescaler factor */ + switch(adc_psc){ + case RCU_CKADC_CKAPB2_DIV2: + case RCU_CKADC_CKAPB2_DIV4: + case RCU_CKADC_CKAPB2_DIV6: + case RCU_CKADC_CKAPB2_DIV8: + reg0 |= (adc_psc << RCU_ADC_PSC_OFFSET); + break; + + case RCU_CKADC_CKAPB2_DIV12: + case RCU_CKADC_CKAPB2_DIV16: + adc_psc &= ~BIT(2); + reg0 |= ((adc_psc << RCU_ADC_PSC_OFFSET) | RCU_CFG0_ADCPSC_2); + break; + + case RCU_CKADC_CKAHB_DIV5: + case RCU_CKADC_CKAHB_DIV6: + case RCU_CKADC_CKAHB_DIV10: + case RCU_CKADC_CKAHB_DIV20: + adc_psc &= ~BITS(2,3); + reg0 |= (adc_psc << RCU_ADC_PSC_OFFSET); + reg1 |= RCU_CFG1_ADCPSC_3; + break; + + default: + break; + } + + /* set the register */ + RCU_CFG0 = reg0; + RCU_CFG1 = reg1; +} + +/*! + \brief configure the USBD/USBHS prescaler factor + \param[in] usb_psc: USB prescaler factor + only one parameter can be selected which is shown as below: + \arg RCU_CKUSB_CKPLL_DIV1_5: USBD / USBHS prescaler select CK_PLL / 1.5 + \arg RCU_CKUSB_CKPLL_DIV1: USBD / USBHS prescaler select CK_PLL / 1 + \arg RCU_CKUSB_CKPLL_DIV2_5: USBD / USBHS prescaler select CK_PLL / 2.5 + \arg RCU_CKUSB_CKPLL_DIV2: USBD / USBHS prescaler select CK_PLL / 2 + \arg RCU_CKUSB_CKPLL_DIV3: USBD / USBHS prescaler select CK_PLL / 3 + \arg RCU_CKUSB_CKPLL_DIV3_5: USBD / USBHS prescaler select CK_PLL / 3.5 + \arg RCU_CKUSB_CKPLL_DIV4: USBD / USBHS prescaler select CK_PLL / 4 + \param[out] none + \retval none +*/ +void rcu_usb_clock_config(uint32_t usb_psc) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* configure the USBD/USBHS prescaler factor */ +#if (defined(GD32E50X_HD) || defined(GD32EPRT)) + reg &= ~RCU_CFG0_USBDPSC; +#elif defined(GD32E50X_CL) || defined(GD32E508) + reg &= ~RCU_CFG0_USBHSPSC; +#endif /* GD32E50X_HD and GD32EPRT */ + + RCU_CFG0 = (reg | usb_psc); +} + +/*! + \brief configure the RTC clock source selection + \param[in] rtc_clock_source: RTC clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_RTCSRC_NONE: no clock selected + \arg RCU_RTCSRC_LXTAL: CK_LXTAL selected as RTC source clock + \arg RCU_RTCSRC_IRC40K: CK_IRC40K selected as RTC source clock + \arg RCU_RTCSRC_HXTAL_DIV_128: CK_HXTAL/128 selected as RTC source clock + \param[out] none + \retval none +*/ +void rcu_rtc_clock_config(uint32_t rtc_clock_source) +{ + uint32_t reg; + + reg = RCU_BDCTL; + /* reset the RTCSRC bits and set according to rtc_clock_source */ + reg &= ~RCU_BDCTL_RTCSRC; + RCU_BDCTL = (reg | rtc_clock_source); +} + +#ifndef GD32EPRT +/*! + \brief configure the SHRTIMER clock source selection + \param[in] shrtimer_clock_source: SHRTIMER clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_SHRTIMERSRC_CKAPB2: APB2 clock selected as SHRTIMER source clock + \arg RCU_SHRTIMERSRC_CKSYS: system clock selected as SHRTIMER source clock + \param[out] none + \retval none +*/ +void rcu_shrtimer_clock_config(uint32_t shrtimer_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG1; + /* reset the I2S1SEL bit and set according to shrtimer_clock_source */ + reg &= ~RCU_CFG1_SHRTIMERSEL; + RCU_CFG1 = (reg | shrtimer_clock_source); +} +#endif /* GD32EPRT */ + +/*! + \brief configure the USART5 clock source selection + \param[in] usart5_clock_source: USART5 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_USART5SRC_CKAPB2: APB2 clock selected as USART5 source clock + \arg RCU_USART5SRC_CKSYS: system clock selected as USART5 source clock + \arg RCU_USART5SRC_LXTAL: LXTAL clock selected as USART5 source clock + \arg RCU_USART5SRC_IRC8M: IRC8M clock selected as USART5 source clock + \param[out] none + \retval none +*/ +void rcu_usart5_clock_config(uint32_t usart5_clock_source) +{ + uint32_t reg; + reg = RCU_CFG2; + /* reset the I2S1SEL bit and set according to usart5_clock_source */ + reg &= ~RCU_CFG2_USART5SEL; + RCU_CFG2 = (reg | usart5_clock_source); +} + +/*! + \brief configure the I2C2 clock source selection + \param[in] i2c2_clock_source: I2C2 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_I2C2SRC_CKAPB1: APB1 clock selected as I2C2 source clock + \arg RCU_I2C2SRC_CKSYS: System clock selected as I2C2 source clock + \arg RCU_I2C2SRCSRC_CKIRC8M: CK_IRC8M clock selected as I2C2 source clock + \param[out] none + \retval none +*/ +void rcu_i2c2_clock_config(uint32_t i2c2_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG2; + /* reset the I2S1SEL bit and set according to i2c2_clock_source */ + reg &= ~RCU_CFG2_I2C2SEL; + RCU_CFG2 = (reg | i2c2_clock_source); +} + +/*! + \brief configure the CK48M clock source selection + \param[in] ck48m_clock_source: CK48M clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_CK48MSRC_CKPLL: CK_PLL selected as CK48M source clock + \arg RCU_CK48MSRC_IRC48M: CK_IRC48M selected as CK48M source clock + \arg RCU_CK48MSRC_CKPLLUSB: (not available for EPRT series): CKPLLUSB selected as CK48M source clock + \arg RCU_CK48MSRC_CKPLL2: CKPLL2 selected as CK48M source clock + \param[out] none + \retval none +*/ +void rcu_ck48m_clock_config(uint32_t ck48m_clock_source) +{ + uint32_t reg; + + reg = RCU_ADDCTL; + /* reset the CK48MSEL bit and set according to ck48m_clock_source */ + reg &= ~RCU_ADDCTL_CK48MSEL; + RCU_ADDCTL = (reg | ck48m_clock_source); +} + +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) +/*! + \brief configure the I2S1 clock source selection + \param[in] i2s_clock_source: I2S1 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_I2S1SRC_CKSYS: system clock selected as I2S1 source clock + \arg RCU_I2S1SRC_CKPLL2_MUL2: CK_PLL2 * 2 selected as I2S1 source clock + \param[out] none + \retval none +*/ +void rcu_i2s1_clock_config(uint32_t i2s_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG1; + /* reset the I2S1SEL bit and set according to i2s_clock_source */ + reg &= ~RCU_CFG1_I2S1SEL; + RCU_CFG1 = (reg | i2s_clock_source); +} + +/*! + \brief configure the I2S2 clock source selection + \param[in] i2s_clock_source: I2S2 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_I2S2SRC_CKSYS: system clock selected as I2S2 source clock + \arg RCU_I2S2SRC_CKPLL2_MUL2: CK_PLL2 * 2 selected as I2S2 source clock + \param[out] none + \retval none +*/ +void rcu_i2s2_clock_config(uint32_t i2s_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG1; + /* reset the I2S2SEL bit and set according to i2s_clock_source */ + reg &= ~RCU_CFG1_I2S2SEL; + RCU_CFG1 = (reg | i2s_clock_source); +} +#endif /* GD32E50X_CL and GD32E50X_EPRT and GD32E508 */ + +#if defined(GD32E50X_CL) || defined(GD32E508) +/*! + \brief configure the USBHSSEL source clock selection + \param[in] usbhssel_clock_source: USBHSSEL clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_USBHSSRC_48M: 48M clock selected as USBHS source clock + \arg RCU_CK48MSRC_60M: 60M clock selected as USBHS source clock + \param[out] none + \retval none +*/ +void rcu_usbhssel_config(uint32_t usbhssel_clock_source) +{ + uint32_t reg = 0U; + + reg = RCU_ADDCTL; + + /* USBHS clock source selection */ + reg &= ~RCU_ADDCTL_USBHSSEL; + reg |= usbhssel_clock_source; + + RCU_ADDCTL = reg; +} + +/*! + \brief configure the USBHSDV division factor + \param[in] usbhs_dv: USBHSDV division factor + only one parameter can be selected which is shown as below: + \arg RCU_USBHSDV_DIV2: USBHSDV input source clock divided by 2 + \arg RCU_USBHSDV_DIV4: USBHSDV input source clock divided by 4 + \arg RCU_USBHSDV_DIV6: USBHSDV input source clock divided by 6 + \arg RCU_USBHSDV_DIV8: USBHSDV input source clock divided by 8 + \arg RCU_USBHSDV_DIV10: USBHSDV input source clock divided by 10 + \arg RCU_USBHSDV_DIV12: USBHSDV input source clock divided by 12 + \arg RCU_USBHSDV_DIV14: USBHSDV input source clock divided by 14 + \arg RCU_USBHSDV_DIV16: USBHSDV input source clock divided by 16 + \param[out] none + \retval none +*/ +void rcu_usbdv_config(uint32_t usbhs_dv) +{ + uint32_t reg = 0U; + + reg = RCU_ADDCTL; + + /* usbhs input clock source division factor */ + reg &= ~RCU_ADDCTL_USBHSDV; + reg |= usbhs_dv; + + RCU_ADDCTL = reg; +} +#endif /* GD32E50X_CL and GD32E508 */ + +/*! + \brief configure the LXTAL drive capability + \param[in] lxtal_dricap: drive capability of LXTAL + only one parameter can be selected which is shown as below: + \arg RCU_LXTAL_LOWDRI: lower driving capability + \arg RCU_LXTAL_MED_LOWDRI: medium low driving capability + \arg RCU_LXTAL_MED_HIGHDRI: medium high driving capability + \arg RCU_LXTAL_HIGHDRI: higher driving capability + \param[out] none + \retval none +*/ +void rcu_lxtal_drive_capability_config(uint32_t lxtal_dricap) +{ + uint32_t reg; + + reg = RCU_BDCTL; + + /* reset the LXTALDRI bits and set according to lxtal_dricap */ + reg &= ~RCU_BDCTL_LXTALDRI; + RCU_BDCTL = (reg | lxtal_dricap); +} + +/*! + \brief wait for oscillator stabilization flags is SET or oscillator startup is timeout + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC48M: internal 48M RC oscillators(IRC48M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL, EPRT and E508 series available) + \arg RCU_PLL2_CK: phase locked loop 2(CL, EPRT and E508 series available) + \arg RCU_PLLUSB_CK: phase locked loop USB(CL and E508 series available) + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus rcu_osci_stab_wait(rcu_osci_type_enum osci) +{ + uint32_t stb_cnt = 0U; + ErrStatus reval = ERROR; + FlagStatus osci_stat = RESET; + + switch(osci){ + /* wait HXTAL stable */ + case RCU_HXTAL: + while((RESET == osci_stat) && (HXTAL_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_HXTALSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_HXTALSTB)){ + reval = SUCCESS; + } + break; + + /* wait LXTAL stable */ + case RCU_LXTAL: + while((RESET == osci_stat) && (LXTAL_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_LXTALSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_LXTALSTB)){ + reval = SUCCESS; + } + break; + + /* wait IRC8M stable */ + case RCU_IRC8M: + while((RESET == osci_stat) && (IRC8M_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_IRC8MSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_IRC8MSTB)){ + reval = SUCCESS; + } + break; + + /* wait IRC48M stable */ + case RCU_IRC48M: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_IRC48MSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if (RESET != rcu_flag_get(RCU_FLAG_IRC48MSTB)){ + reval = SUCCESS; + } + break; + + /* wait IRC40K stable */ + case RCU_IRC40K: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_IRC40KSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_IRC40KSTB)){ + reval = SUCCESS; + } + break; + + /* wait PLL stable */ + case RCU_PLL_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLLSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLLSTB)){ + reval = SUCCESS; + } + break; + +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + /* wait PLL1 stable */ + case RCU_PLL1_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLL1STB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLL1STB)){ + reval = SUCCESS; + } + break; + /* wait PLL2 stable */ + case RCU_PLL2_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLL2STB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLL2STB)){ + reval = SUCCESS; + } + break; +#endif /* GD32E50X_CL and GD32E50X_EPRT and GD32E508 */ +#if defined(GD32E50X_CL) || defined(GD32E508) + case RCU_PLLUSB_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLLUSBSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLLUSBSTB)){ + reval = SUCCESS; + } + break; +#endif /* GD32E50X_CL and GD32E508 */ + + default: + break; + } + + /* return value */ + return reval; +} + +/*! + \brief turn on the oscillator + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC48M: internal 48M RC oscillators(IRC48M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL, EPRT and E508 series available) + \arg RCU_PLL2_CK: phase locked loop 2(CL, EPRT and E508 series available) + \arg RCU_PLLUSB_CK: phase locked loop USB(CL and E508 series available) + \param[out] none + \retval none +*/ +void rcu_osci_on(rcu_osci_type_enum osci) +{ + RCU_REG_VAL(osci) |= BIT(RCU_BIT_POS(osci)); +} + +/*! + \brief turn off the oscillator + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC48M: internal 48M RC oscillators(IRC48M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL, EPRT and E508 series available) + \arg RCU_PLL2_CK: phase locked loop 2(CL, EPRT and E508 series available) + \arg RCU_PLLUSB_CK: phase locked loop USB(CL and E508 series available) + \param[out] none + \retval none +*/ +void rcu_osci_off(rcu_osci_type_enum osci) +{ + RCU_REG_VAL(osci) &= ~BIT(RCU_BIT_POS(osci)); +} + +/*! + \brief enable the oscillator bypass mode, HXTALEN or LXTALEN must be reset before it + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \param[out] none + \retval none +*/ +void rcu_osci_bypass_mode_enable(rcu_osci_type_enum osci) +{ + uint32_t reg; + + switch(osci){ + /* enable HXTAL to bypass mode */ + case RCU_HXTAL: + reg = RCU_CTL; + RCU_CTL &= ~RCU_CTL_HXTALEN; + RCU_CTL = (reg | RCU_CTL_HXTALBPS); + break; + /* enable LXTAL to bypass mode */ + case RCU_LXTAL: + reg = RCU_BDCTL; + RCU_BDCTL &= ~RCU_BDCTL_LXTALEN; + RCU_BDCTL = (reg | RCU_BDCTL_LXTALBPS); + break; + case RCU_IRC8M: + case RCU_IRC48M: + case RCU_IRC40K: + case RCU_PLL_CK: +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + case RCU_PLL1_CK: + case RCU_PLL2_CK: +#endif /* GD32E50X_CL and GD32EPRT and GD32E508 */ + break; + default: + break; + } +} + +/*! + \brief disable the oscillator bypass mode, HXTALEN or LXTALEN must be reset before it + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \param[out] none + \retval none +*/ +void rcu_osci_bypass_mode_disable(rcu_osci_type_enum osci) +{ + uint32_t reg; + + switch(osci){ + /* disable HXTAL to bypass mode */ + case RCU_HXTAL: + reg = RCU_CTL; + RCU_CTL &= ~RCU_CTL_HXTALEN; + RCU_CTL = (reg & ~RCU_CTL_HXTALBPS); + break; + /* disable LXTAL to bypass mode */ + case RCU_LXTAL: + reg = RCU_BDCTL; + RCU_BDCTL &= ~RCU_BDCTL_LXTALEN; + RCU_BDCTL = (reg & ~RCU_BDCTL_LXTALBPS); + break; + case RCU_IRC8M: + case RCU_IRC48M: + case RCU_IRC40K: + case RCU_PLL_CK: +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + case RCU_PLL1_CK: + case RCU_PLL2_CK: +#endif /* GD32E50X_CL and GD32EPRT and GD32E508 */ + break; + default: + break; + } +} + +/*! + \brief set the IRC8M adjust value + \param[in] irc8m_adjval: IRC8M adjust value, must be between 0 and 0x1F + \arg 0x00 - 0x1F + \param[out] none + \retval none +*/ +void rcu_irc8m_adjust_value_set(uint32_t irc8m_adjval) +{ + uint32_t reg; + + reg = RCU_CTL; + /* reset the IRC8MADJ bits and set according to irc8m_adjval */ + reg &= ~RCU_CTL_IRC8MADJ; + RCU_CTL = (reg | ((irc8m_adjval & RCU_IRC8M_ADJUST_MASK) << RCU_IRC8M_ADJUST_OFFSET)); +} + +/*! + \brief configure the spread spectrum modulation for the main PLL clock + \param[in] spread_spectrum_type: PLL spread spectrum modulation type select + \arg RCU_SS_TYPE_CENTER: center spread type is selected + \arg RCU_SS_TYPE_DOWN: down spread type is selected + \param[in] modstep: configure PLL spread spectrum modulation profile amplitude and frequency + \arg This parameter should be selected between 0 and 7FFF.The following criteria must be met: MODSTEP*MODCNT <=2^15-1 + \param[in] modcnt: configure PLL spread spectrum modulation profile amplitude and frequency + \arg This parameter should be selected between 0 and 1FFF.The following criteria must be met: MODSTEP*MODCNT <=2^15-1 + \param[out] none + \retval none +*/ +void rcu_spread_spectrum_config(uint32_t spread_spectrum_type, uint32_t modstep, uint32_t modcnt) +{ + uint32_t reg; + + reg = RCU_PLLSSCTL; + /* reset the RCU_PLLSSCTL register bits */ + reg &= ~(RCU_PLLSSCTL_MODCNT | RCU_PLLSSCTL_MODSTEP | RCU_PLLSSCTL_SS_TYPE); + RCU_PLLSSCTL = (reg | spread_spectrum_type | modstep << 13 | modcnt); +} + +/*! + \brief enable the PLL spread spectrum modulation + \param[in] none + \param[out] none + \retval none +*/ +void rcu_spread_spectrum_enable(void) +{ + RCU_PLLSSCTL |= RCU_PLLSSCTL_SSCGON; +} + +/*! + \brief disable the PLL spread spectrum modulation + \param[in] none + \param[out] none + \retval none +*/ +void rcu_spread_spectrum_disable(void) +{ + RCU_PLLSSCTL &= ~RCU_PLLSSCTL_SSCGON; +} + +/*! + \brief enable the HXTAL clock monitor + \param[in] none + \param[out] none + \retval none +*/ + +void rcu_hxtal_clock_monitor_enable(void) +{ + RCU_CTL |= RCU_CTL_CKMEN; +} + +/*! + \brief disable the HXTAL clock monitor + \param[in] none + \param[out] none + \retval none +*/ +void rcu_hxtal_clock_monitor_disable(void) +{ + RCU_CTL &= ~RCU_CTL_CKMEN; +} + + +/*! + \brief deep-sleep mode voltage select + \param[in] dsvol: deep sleep mode voltage + only one parameter can be selected which is shown as below: + \arg RCU_DEEPSLEEP_V_1_0: the core voltage is 1.0V + \arg RCU_DEEPSLEEP_V_0_9: the core voltage is 0.9V + \arg RCU_DEEPSLEEP_V_0_8: the core voltage is 0.8V + \arg RCU_DEEPSLEEP_V_0_7: the core voltage is 0.7V + \param[out] none + \retval none +*/ +void rcu_deepsleep_voltage_set(uint32_t dsvol) +{ + dsvol &= RCU_DSV_DSLPVS; + RCU_DSV = dsvol; +} + +/*! + \brief get the system clock, bus and peripheral clock frequency + \param[in] clock: the clock frequency which to get + only one parameter can be selected which is shown as below: + \arg CK_SYS: system clock frequency + \arg CK_AHB: AHB clock frequency + \arg CK_APB1: APB1 clock frequency + \arg CK_APB2: APB2 clock frequency + \arg CK_USART: USART5 clock frequency + \param[out] none + \retval clock frequency of system, AHB, APB1, APB2 +*/ +uint32_t rcu_clock_freq_get(rcu_clock_freq_enum clock) +{ + uint32_t sws, ck_freq = 0U; + uint32_t cksys_freq, ahb_freq, apb1_freq, apb2_freq; + uint32_t usart_freq = 0U; + uint32_t pllsel, pllpresel, predv0sel, pllmf,ck_src, idx, clk_exp; +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + uint32_t predv0, predv1, pll1mf; +#endif /* GD32E50X_CL and GD32EPRT and GD32E508*/ + + /* exponent of AHB, APB1 and APB2 clock divider */ + uint8_t ahb_exp[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + uint8_t apb1_exp[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + uint8_t apb2_exp[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + + sws = GET_BITS(RCU_CFG0, 2, 3); + switch(sws){ + /* IRC8M is selected as CK_SYS */ + case SEL_IRC8M: + cksys_freq = IRC8M_VALUE; + break; + /* HXTAL is selected as CK_SYS */ + case SEL_HXTAL: + cksys_freq = HXTAL_VALUE; + break; + /* PLL is selected as CK_SYS */ + case SEL_PLL: + /* PLL clock source selection, HXTAL, IRC48M or IRC8M/2 */ + pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL); + + if(RCU_PLLSRC_HXTAL_IRC48M == pllsel) { + /* PLL clock source is HXTAL or IRC48M */ + pllpresel = (RCU_CFG1 & RCU_CFG1_PLLPRESEL); + + if(RCU_PLLPRESRC_HXTAL == pllpresel){ + /* PLL clock source is HXTAL */ + ck_src = HXTAL_VALUE; + }else{ + /* PLL clock source is IRC48 */ + ck_src = IRC48M_VALUE; + } + +#if defined(GD32E50X_HD) + predv0sel = (RCU_CFG0 & RCU_CFG0_PREDV0); + /* PREDV0 input source clock divided by 2 */ + if(RCU_CFG0_PREDV0 == predv0sel){ + ck_src = HXTAL_VALUE/2U; + } +#elif defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + predv0sel = (RCU_CFG1 & RCU_CFG1_PREDV0SEL); + /* source clock use PLL1 */ + if(RCU_PREDV0SRC_CKPLL1 == predv0sel){ + predv1 = ((RCU_CFG1 & RCU_CFG1_PREDV1) >> RCU_CFG1_PREDV1_OFFSET) + 1U; + pll1mf = (uint32_t)((RCU_CFG1 & RCU_CFG1_PLL1MF) >> RCU_CFG1_PLL1MF_OFFSET) + 2U; + if(17U == pll1mf){ + pll1mf = 20U; + } + ck_src = (ck_src/predv1)*pll1mf; + } + predv0 = (RCU_CFG1 & RCU_CFG1_PREDV0) + 1U; + ck_src /= predv0; +#endif /* GD32E50X_HD */ + }else{ + /* PLL clock source is IRC8M/2 */ + ck_src = IRC8M_VALUE/2U; + } + + /* PLL multiplication factor */ + pllmf = GET_BITS(RCU_CFG0, 18, 21); + if((RCU_CFG0 & RCU_CFG0_PLLMF_4)){ + pllmf |= 0x10U; + } + if((RCU_CFG0 & RCU_CFG0_PLLMF_5)){ + pllmf |= 0x20U; + } + if(pllmf < 15U){ + pllmf += 2U; + }else if((pllmf >= 15U) && (pllmf <= 64U)){ + pllmf += 1U; + }else{ + } + cksys_freq = ck_src*pllmf; +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + if(15U == pllmf){ + cksys_freq = ck_src*6U + ck_src/2U; + } +#endif /* GD32E50X_CL and GD32EPRT and GD32E508 */ + + break; + /* IRC8M is selected as CK_SYS */ + default: + cksys_freq = IRC8M_VALUE; + break; + } + + /* calculate AHB clock frequency */ + idx = GET_BITS(RCU_CFG0, 4, 7); + clk_exp = ahb_exp[idx]; + ahb_freq = cksys_freq >> clk_exp; + + /* calculate APB1 clock frequency */ + idx = GET_BITS(RCU_CFG0, 8, 10); + clk_exp = apb1_exp[idx]; + apb1_freq = ahb_freq >> clk_exp; + + /* calculate APB2 clock frequency */ + idx = GET_BITS(RCU_CFG0, 11, 13); + clk_exp = apb2_exp[idx]; + apb2_freq = ahb_freq >> clk_exp; + + /* return the clocks frequency */ + switch(clock){ + case CK_SYS: + ck_freq = cksys_freq; + break; + case CK_AHB: + ck_freq = ahb_freq; + break; + case CK_APB1: + ck_freq = apb1_freq; + break; + case CK_APB2: + ck_freq = apb2_freq; + break; + case CK_USART: + /* calculate USART5 clock frequency */ + if(RCU_USART5SRC_CKAPB2 == (RCU_CFG2 & RCU_CFG2_USART5SEL)){ + usart_freq = apb2_freq; + }else if(RCU_USART5SRC_CKSYS == (RCU_CFG2 & RCU_CFG2_USART5SEL)){ + usart_freq = cksys_freq; + }else if(RCU_USART5SRC_LXTAL == (RCU_CFG2 & RCU_CFG2_USART5SEL)){ + usart_freq = LXTAL_VALUE; + }else if(RCU_USART5SRC_IRC8M == (RCU_CFG2 & RCU_CFG2_USART5SEL)){ + usart_freq = IRC8M_VALUE; + }else{ + } + ck_freq = usart_freq; + break; + default: + break; + } + return ck_freq; +} + +/*! + \brief get the clock stabilization and periphral reset flags + \param[in] flag: the clock stabilization and periphral reset flags, refer to rcu_flag_enum + only one parameter can be selected which is shown as below: + \arg RCU_FLAG_IRC8MSTB: IRC8M stabilization flag + \arg RCU_FLAG_HXTALSTB: HXTAL stabilization flag + \arg RCU_FLAG_PLLSTB: PLL stabilization flag + \arg RCU_FLAG_PLLUSBSTB: PLLUSB stabilization flag(CL and E508 series available) + \arg RCU_FLAG_PLL1STB: PLL1 stabilization flag(CL, EPRT and E508 series available) + \arg RCU_FLAG_PLL2STB: PLL2 stabilization flag(CL, EPRT and E508 series available) + \arg RCU_FLAG_LXTALSTB: LXTAL stabilization flag + \arg RCU_FLAG_IRC40KSTB: IRC40K stabilization flag + \arg RCU_FLAG_IRC48MSTB: IRC48M stabilization flag + \arg RCU_FLAG_EPRST: external PIN reset flag + \arg RCU_FLAG_PORRST: power reset flag + \arg RCU_FLAG_SWRST: software reset flag + \arg RCU_FLAG_FWDGTRST: free watchdog timer reset flag + \arg RCU_FLAG_WWDGTRST: window watchdog timer reset flag + \arg RCU_FLAG_LPRST: low-power reset flag + \param[out] none + \retval none +*/ +FlagStatus rcu_flag_get(rcu_flag_enum flag) +{ + /* get the rcu flag */ + if(RESET != (RCU_REG_VAL(flag) & BIT(RCU_BIT_POS(flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear all the reset flag + \param[in] none + \param[out] none + \retval none +*/ +void rcu_all_reset_flag_clear(void) +{ + RCU_RSTSCK |= RCU_RSTSCK_RSTFC; +} + +/*! + \brief get the clock stabilization interrupt and ckm flags + \param[in] int_flag: interrupt and ckm flags, refer to rcu_int_flag_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_FLAG_IRC40KSTB: IRC40K stabilization interrupt flag + \arg RCU_INT_FLAG_LXTALSTB: LXTAL stabilization interrupt flag + \arg RCU_INT_FLAG_IRC8MSTB: IRC8M stabilization interrupt flag + \arg RCU_INT_FLAG_HXTALSTB: HXTAL stabilization interrupt flag + \arg RCU_INT_FLAG_PLLSTB: PLL stabilization interrupt flag + \arg RCU_INT_FLAG_PLL1STB: PLL1 stabilization interrupt flag(CL, EPRT and E508 series available) + \arg RCU_INT_FLAG_PLL2STB: PLL2 stabilization interrupt flag(CL, EPRT and E508 series available) + \arg RCU_INT_FLAG_PLLUSBSTB: PLLUSB stabilization interrupt flag(CL and E508 series available) + \arg RCU_INT_FLAG_CKM: HXTAL clock stuck interrupt flag + \arg RCU_INT_FLAG_IRC48MSTB: IRC48M stabilization interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus rcu_interrupt_flag_get(rcu_int_flag_enum int_flag) +{ + /* get the rcu interrupt flag */ + if(RESET != (RCU_REG_VAL(int_flag) & BIT(RCU_BIT_POS(int_flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear the interrupt flags + \param[in] int_flag: clock stabilization and stuck interrupt flags clear, refer to rcu_int_flag_clear_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_FLAG_IRC40KSTB_CLR: IRC40K stabilization interrupt flag clear + \arg RCU_INT_FLAG_LXTALSTB_CLR: LXTAL stabilization interrupt flag clear + \arg RCU_INT_FLAG_IRC8MSTB_CLR: IRC8M stabilization interrupt flag clear + \arg RCU_INT_FLAG_HXTALSTB_CLR: HXTAL stabilization interrupt flag clear + \arg RCU_INT_FLAG_PLLSTB_CLR: PLL stabilization interrupt flag clear + \arg RCU_INT_FLAG_PLL1STB_CLR: PLL1 stabilization interrupt flag clear(CL, EPRT and E508 series available) + \arg RCU_INT_FLAG_PLL2STB_CLR: PLL2 stabilization interrupt flag clear(CL, EPRT and E508 series available) + \arg RCU_INT_FLAG_PLLUSBSTB_CLR: PLLUS stabilization interrupt flag clear(CL and E508 series available) + \arg RCU_INT_FLAG_CKM_CLR: clock stuck interrupt flag clear + \arg RCU_INT_FLAG_IRC48MSTB_CLR: IRC48M stabilization interrupt flag clear + \param[out] none + \retval none +*/ +void rcu_interrupt_flag_clear(rcu_int_flag_clear_enum int_flag) +{ + RCU_REG_VAL(int_flag) |= BIT(RCU_BIT_POS(int_flag)); +} + +/*! + \brief enable the stabilization interrupt + \param[in] interrupt clock stabilization interrupt, refer to rcu_int_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_IRC40KSTB: IRC40K stabilization interrupt enable + \arg RCU_INT_LXTALSTB: LXTAL stabilization interrupt enable + \arg RCU_INT_IRC8MSTB: IRC8M stabilization interrupt enable + \arg RCU_INT_HXTALSTB: HXTAL stabilization interrupt enable + \arg RCU_INT_PLLSTB: PLL stabilization interrupt enable + \arg RCU_INT_PLL1STB: PLL1 stabilization interrupt enable(CL, EPRT and E508 series available) + \arg RCU_INT_PLL2STB: PLL2 stabilization interrupt enable(CL, EPRT and E508 series available) + \arg RCU_INT_PLLUSBSTB: PLLUSB stabilization interrupt enable(CL and E508 series available) + \arg RCU_INT_IRC48MSTB: IRC48M stabilization interrupt enable + \param[out] none + \retval none +*/ +void rcu_interrupt_enable(rcu_int_enum interrupt) +{ + RCU_REG_VAL(interrupt) |= BIT(RCU_BIT_POS(interrupt)); +} + +/*! + \brief disable the stabilization interrupt + \param[in] interrupt clock stabilization interrupt, refer to rcu_int_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_IRC40KSTB: IRC40K stabilization interrupt enable + \arg RCU_INT_LXTALSTB: LXTAL stabilization interrupt enable + \arg RCU_INT_IRC8MSTB: IRC8M stabilization interrupt enable + \arg RCU_INT_HXTALSTB: HXTAL stabilization interrupt enable + \arg RCU_INT_PLLSTB: PLL stabilization interrupt enable + \arg RCU_INT_PLL1STB: PLL1 stabilization interrupt enable(CL, EPRT and E508 series available) + \arg RCU_INT_PLL2STB: PLL2 stabilization interrupt enable(CL, EPRT and E508 series available) + \arg RCU_INT_PLLUSBSTB: PLLUSB stabilization interrupt enable(CL and E508 series available) + \arg RCU_INT_IRC48MSTB: IRC48M stabilization interrupt enable + \param[out] none + \retval none +*/ +void rcu_interrupt_disable(rcu_int_enum interrupt) +{ + RCU_REG_VAL(interrupt) &= ~BIT(RCU_BIT_POS(interrupt)); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_rcu.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_rcu.c.base@1.5.0 new file mode 100644 index 0000000..41a5251 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_rcu.c.base@1.5.0 @@ -0,0 +1,1621 @@ +/*! + \file gd32e50x_rcu.c + \brief RCU driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_rcu.h" + +/* define clock source */ +#define SEL_IRC8M ((uint16_t)0U) /* IRC8M is selected as CK_SYS */ +#define SEL_HXTAL ((uint16_t)1U) /* HXTAL is selected as CK_SYS */ +#define SEL_PLL ((uint16_t)2U) /* PLL is selected as CK_SYS */ + +/* define startup timeout count */ +#define OSC_STARTUP_TIMEOUT ((uint32_t)0x000FFFFFU) +#define LXTAL_STARTUP_TIMEOUT ((uint32_t)0x03FFFFFFU) + +/* ADC clock prescaler offset */ +#define RCU_ADC_PSC_OFFSET ((uint32_t)14U) + +/* RCU IRC8M adjust value mask and offset*/ +#define RCU_IRC8M_ADJUST_MASK ((uint8_t)0x1FU) +#define RCU_IRC8M_ADJUST_OFFSET ((uint32_t)3U) + +/* RCU PLL1 clock multiplication factor offset */ +#define RCU_CFG1_PLL1MF_OFFSET ((uint32_t)8U) +/* RCU PREDV1 division factor offset*/ +#define RCU_CFG1_PREDV1_OFFSET ((uint32_t)4U) + +/*! + \brief deinitialize the RCU + \param[in] none + \param[out] none + \retval none +*/ +void rcu_deinit(void) +{ + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + while(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + } + RCU_CFG0 &= ~RCU_CFG0_SCS; + /* reset CTL register */ + RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN); + RCU_CTL &= ~RCU_CTL_HXTALBPS; +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + RCU_CTL &= ~(RCU_CTL_PLL1EN | RCU_CTL_PLL2EN); +#endif /* GD32E50X_CL and GD32EPRT and GD32E508 */ + /* reset CFG0 register */ +#if defined(GD32E50X_HD) + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0 | RCU_CFG0_PLLMF | + RCU_CFG0_USBDPSC | RCU_CFG0_CKOUT0SEL | RCU_CFG0_PLLMF_4 | RCU_CFG0_ADCPSC_2 | RCU_CFG0_PLLMF_5 | RCU_CFG0_USBDPSC_2); +#elif defined(GD32E50X_CL) || defined(GD32E508) + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF | + RCU_CFG0_USBHSPSC | RCU_CFG0_CKOUT0SEL | RCU_CFG0_ADCPSC_2 | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5 | RCU_CFG0_USBHSPSC_2); +#elif defined(GD32EPRT) + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF | + RCU_CFG0_USBDPSC | RCU_CFG0_CKOUT0SEL | RCU_CFG0_ADCPSC_2 | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5 | RCU_CFG0_USBDPSC_2); +#endif /* GD32E50X_HD */ + + /* reset INT and CFG1 register */ +#if defined(GD32E50X_HD) + RCU_INT = 0x009f0000U; + RCU_CFG1 &= ~(RCU_CFG1_ADCPSC_3 | RCU_CFG1_PLLPRESEL); + RCU_CFG2 &= ~(RCU_CFG2_USART5SEL | RCU_CFG2_I2C2SEL); +#elif defined(GD32E50X_CL) || defined(GD32E508) + RCU_INT = 0x00ff0000U; + RCU_ADDINT = 0x00C00000U; + RCU_ADDCTL &= ~(RCU_ADDCTL_CK48MSEL | RCU_ADDCTL_USBHSSEL | RCU_ADDCTL_USBHSDV | RCU_ADDCTL_USBSWEN | RCU_ADDCTL_PLLUSBEN | RCU_ADDCTL_IRC48MEN); + RCU_ADDCFG &= ~(RCU_ADDCFG_PLLUSBPREDV | RCU_ADDCFG_PLLUSBPRESEL | RCU_ADDCFG_PLLUSBPREDVSEL | RCU_ADDCFG_PLLUSBMF); + + RCU_CFG1 &= ~(RCU_CFG1_PREDV0 | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PLL2MF | + RCU_CFG1_PREDV0SEL | RCU_CFG1_I2S1SEL | RCU_CFG1_I2S2SEL | RCU_CFG1_SHRTIMERSEL | RCU_CFG1_PLL2MF_5 | RCU_CFG1_ADCPSC_3 | + RCU_CFG1_PLLPRESEL | RCU_CFG1_PLL2MF_4); + RCU_CFG2 &= ~(RCU_CFG2_USART5SEL | RCU_CFG2_I2C2SEL); +#elif defined(GD32EPRT) + RCU_INT = 0x00ff0000U; + RCU_ADDINT &= ~ (RCU_ADDINT_IRC48MSTBIC); + RCU_ADDCTL &= ~(RCU_ADDCTL_CK48MSEL | RCU_ADDCTL_IRC48MEN); + + RCU_CFG1 &= ~(RCU_CFG1_PREDV0 | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PLL2MF | + RCU_CFG1_PREDV0SEL | RCU_CFG1_I2S1SEL | RCU_CFG1_I2S2SEL | RCU_CFG1_PLL2MF_5 | RCU_CFG1_ADCPSC_3 | + RCU_CFG1_PLLPRESEL | RCU_CFG1_PLL2MF_4); + RCU_CFG2 &= ~(RCU_CFG2_USART5SEL | RCU_CFG2_I2C2SEL); + +#endif /* GD32E50X_HD */ +} + +/*! + \brief enable the peripherals clock + \param[in] periph: RCU peripherals, refer to rcu_periph_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOx (x = A,B,C,D,E,F,G): GPIO ports clock + \arg RCU_AF : alternate function clock + \arg RCU_CRC: CRC clock + \arg RCU_DMAx (x = 0,1): DMA clock + \arg RCU_ENET: ENET clock(EPRT and CL series available) + \arg RCU_ENETTX: ENETTX clock(EPRT and CL series available) + \arg RCU_ENETRX: ENETRX clock(EPRT and CL series available) + \arg RCU_USBD: USBD clock(HD and EPRT series available) + \arg RCU_USBHS: USBHS clock(CL series available) + \arg RCU_TMU: TMU clock + \arg RCU_SQPI: SQPI clock + \arg RCU_EXMC: EXMC clock + \arg RCU_TIMERx (x = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for EPRT series): TIMER clock + \arg RCU_WWDGT: WWDGT clock + \arg RCU_SPIx (x = 0,1,2): SPI clock + \arg RCU_USARTx (x = 0,1,2,5): USART clock + \arg RCU_UARTx (x = 3,4): UART clock + \arg RCU_I2Cx (x = 0,1,2): I2C clock + \arg RCU_CANx (x = 0,1,2,CAN2 is only available for CL series,CANx is not avaliable for GD32EPRT): CAN clock + \arg RCU_PMU: PMU clock + \arg RCU_DAC: DAC clock + \arg RCU_RTC: RTC clock + \arg RCU_ADCx (x = 0,1,2, ADC2 is not available for CL series): ADC clock + \arg RCU_SDIO: SDIO clock(not available for CL and EPRT series) + \arg RCU_CTC: CTC clock + \arg RCU_BKPI: BKP interface clock + \arg RCU_SHRTIMER: (not available for EPRT series):SHRTIMER clock + \arg RCU_CMP(CMP is only available for CL series):CMP clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_enable(rcu_periph_enum periph) +{ + RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief disable the peripherals clock + \param[in] periph: RCU peripherals, refer to rcu_periph_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOx (x = A,B,C,D,E,F,G): GPIO ports clock + \arg RCU_AF : alternate function clock + \arg RCU_CRC: CRC clock + \arg RCU_DMAx (x =0,1): DMA clock + \arg RCU_ENET: ENET clock(EPRT and CL series available) + \arg RCU_ENETTX: ENETTX clock(EPRT and CL series available) + \arg RCU_ENETRX: ENETRX clock(EPRT and CL series available) + \arg RCU_USBD: USBD clock(HD and EPRT series available) + \arg RCU_USBHS: USBHS clock(CL series available) + \arg RCU_TMU: TMU clock + \arg RCU_SQPI: SQPI clock + \arg RCU_EXMC: EXMC clock + \arg RCU_TIMERx (x = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for EPRT series): TIMER clock + \arg RCU_WWDGT: WWDGT clock + \arg RCU_SPIx (x = 0,1,2): SPI clock + \arg RCU_USARTx (x = 0,1,2,5): USART clock + \arg RCU_UARTx (x = 3,4): UART clock + \arg RCU_I2Cx (x = 0,1,2): I2C clock + \arg RCU_CANx (x = 0,1,2,CAN2 is only available for CL series,CANx is not avaliable for GD32EPRT): CAN clock + \arg RCU_PMU: PMU clock + \arg RCU_DAC: DAC clock + \arg RCU_RTC: RTC clock + \arg RCU_ADCx (x = 0,1,2,ADC2 is not available for CL series): ADC clock + \arg RCU_SDIO: SDIO clock(not available for CL and EPRT series) + \arg RCU_CTC: CTC clock + \arg RCU_BKPI: BKP interface clock + \arg RCU_SHRTIMER: (not available for EPRT series): SHRTIMER clock + \arg RCU_CMP(CMP is only available for CL series):CMP clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_disable(rcu_periph_enum periph) +{ + RCU_REG_VAL(periph) &= ~BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief enable the peripherals clock when sleep mode + \param[in] periph: RCU peripherals, refer to rcu_periph_sleep_enum + only one parameter can be selected which is shown as below: + \arg RCU_FMC_SLP: FMC clock + \arg RCU_SRAM_SLP: SRAM clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_sleep_enable(rcu_periph_sleep_enum periph) +{ + RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief disable the peripherals clock when sleep mode + \param[in] periph: RCU peripherals, refer to rcu_periph_sleep_enum + only one parameter can be selected which is shown as below: + \arg RCU_SRAM_SLP: SRAM clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_sleep_disable(rcu_periph_sleep_enum periph) +{ + RCU_REG_VAL(periph) &= ~BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief reset the peripherals + \param[in] periph_reset: RCU peripherals reset, refer to rcu_periph_reset_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOxRST (x = A,B,C,D,E,F,G): reset GPIO ports + \arg RCU_AFRST : reset alternate function clock + \arg RCU_ENETRST: reset ENET(EPRT and CL series available) + \arg RCU_USBDRST: reset USBD(HD and EPRT series available) + \arg RCU_USBHSRST: reset USBHS(CL series available) + \arg RCU_TMURST: reset TMU + \arg RCU_SQPIRST: reset SQPI + \arg RCU_TIMERxRST (x = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for EPRT series): reset TIMER + \arg RCU_WWDGTRST: reset WWDGT + \arg RCU_SPIxRST (x = 0,1,2): reset SPI + \arg RCU_USARTxRST (x = 0,1,2,5): reset USART + \arg RCU_UARTxRST (x = 3,4): reset UART + \arg RCU_I2CxRST (x = 0,1): reset I2C + \arg RCU_PMURST: reset PMU + \arg RCU_DACRST: reset DAC + \arg RCU_ADCRST (x = 0,1,2,ADC2 is not available for CL series): reset ADC + \arg RCU_CTCRST: reset CTC + \arg RCU_BKPIRST: reset BKPI + \arg RCU_SHRTIMERRST: (not available for EPRT series):reset SHRTIMERRST + \arg RCU_CMPRST(CMP is only available for CL series): reset CMP + \param[out] none + \retval none +*/ +void rcu_periph_reset_enable(rcu_periph_reset_enum periph_reset) +{ + RCU_REG_VAL(periph_reset) |= BIT(RCU_BIT_POS(periph_reset)); +} + +/*! + \brief disable reset the peripheral + \param[in] periph_reset: RCU peripherals reset, refer to rcu_periph_reset_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOxRST (x = A,B,C,D,E,F,G): reset GPIO ports + \arg RCU_AFRST : reset alternate function clock + \arg RCU_ENETRST: reset ENET(CL series available) + \arg RCU_USBDRST: reset USBD(HD and EPRT series available) + \arg RCU_USBHSRST: reset USBHS(CL series available) + \arg RCU_TMURST: reset TMU + \arg RCU_SQPIRST: reset SQPI + \arg RCU_TIMERxRST (x = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for EPRT series): reset TIMER + \arg RCU_WWDGTRST: reset WWDGT + \arg RCU_SPIxRST (x = 0,1,2): reset SPI + \arg RCU_USARTxRST (x = 0,1,2,5): reset USART + \arg RCU_UARTxRST (x = 3,4): reset UART + \arg RCU_I2CxRST (x = 0,1): reset I2C + \arg RCU_PMURST: reset PMU + \arg RCU_DACRST: reset DAC + \arg RCU_ADCRST (x = 0,1,2,ADC2 is not available for CL series): reset ADC + \arg RCU_CTCRST: reset CTC + \arg RCU_BKPIRST: reset BKPI + \arg RCU_SHRTIMERRST: (not available for EPRT series):reset SHRTIMERRST + \arg RCU_CMPRST(CMP is only available for CL series): reset CMP + \param[out] none + \retval none +*/ +void rcu_periph_reset_disable(rcu_periph_reset_enum periph_reset) +{ + RCU_REG_VAL(periph_reset) &= ~BIT(RCU_BIT_POS(periph_reset)); +} + +/*! + \brief reset the BKP domain + \param[in] none + \param[out] none + \retval none +*/ +void rcu_bkp_reset_enable(void) +{ + RCU_BDCTL |= RCU_BDCTL_BKPRST; +} + +/*! + \brief disable the BKP domain reset + \param[in] none + \param[out] none + \retval none +*/ +void rcu_bkp_reset_disable(void) +{ + RCU_BDCTL &= ~RCU_BDCTL_BKPRST; +} + +/*! + \brief configure the system clock source + \param[in] ck_sys: system clock source select + only one parameter can be selected which is shown as below: + \arg RCU_CKSYSSRC_IRC8M: select CK_IRC8M as the CK_SYS source + \arg RCU_CKSYSSRC_HXTAL: select CK_HXTAL as the CK_SYS source + \arg RCU_CKSYSSRC_PLL: select CK_PLL as the CK_SYS source + \param[out] none + \retval none +*/ +void rcu_system_clock_source_config(uint32_t ck_sys) +{ + uint32_t reg; + + reg = RCU_CFG0; + /* reset the SCS bits and set according to ck_sys */ + reg &= ~RCU_CFG0_SCS; + RCU_CFG0 = (reg | ck_sys); +} + +/*! + \brief get the system clock source + \param[in] none + \param[out] none + \retval which clock is selected as CK_SYS source + \arg RCU_SCSS_IRC8M: CK_IRC8M is selected as the CK_SYS source + \arg RCU_SCSS_HXTAL: CK_HXTAL is selected as the CK_SYS source + \arg RCU_SCSS_PLL: CK_PLL is selected as the CK_SYS source +*/ +uint32_t rcu_system_clock_source_get(void) +{ + return (RCU_CFG0 & RCU_CFG0_SCSS); +} + +/*! + \brief configure the AHB clock prescaler selection + \param[in] ck_ahb: AHB clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_AHB_CKSYS_DIVx (x = 1, 2, 4, 8, 16, 64, 128, 256, 512): select CK_SYS / x as CK_AHB + \param[out] none + \retval none +*/ +void rcu_ahb_clock_config(uint32_t ck_ahb) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the AHBPSC bits and set according to ck_ahb */ + reg &= ~RCU_CFG0_AHBPSC; + RCU_CFG0 = (reg | ck_ahb); +} + +/*! + \brief configure the APB1 clock prescaler selection + \param[in] ck_apb1: APB1 clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_APB1_CKAHB_DIV1: select CK_AHB as CK_APB1 + \arg RCU_APB1_CKAHB_DIV2: select CK_AHB / 2 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV4: select CK_AHB / 4 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV8: select CK_AHB / 8 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV16: select CK_AHB / 16 as CK_APB1 + \param[out] none + \retval none +*/ +void rcu_apb1_clock_config(uint32_t ck_apb1) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the APB1PSC and set according to ck_apb1 */ + reg &= ~RCU_CFG0_APB1PSC; + RCU_CFG0 = (reg | ck_apb1); +} + +/*! + \brief configure the APB2 clock prescaler selection + \param[in] ck_apb2: APB2 clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_APB2_CKAHB_DIV1: select CK_AHB as CK_APB2 + \arg RCU_APB2_CKAHB_DIV2: select CK_AHB/2 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV4: select CK_AHB/4 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV8: select CK_AHB/8 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV16: select CK_AHB/16 as CK_APB2 + \param[out] none + \retval none +*/ +void rcu_apb2_clock_config(uint32_t ck_apb2) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the APB2PSC and set according to ck_apb2 */ + reg &= ~RCU_CFG0_APB2PSC; + RCU_CFG0 = (reg | ck_apb2); +} + +/*! + \brief configure the CK_OUT0 clock source + \param[in] ckout0_src: CK_OUT0 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_CKOUT0SRC_NONE: no clock selected + \arg RCU_CKOUT0SRC_CKSYS: system clock selected + \arg RCU_CKOUT0SRC_IRC8M: high speed 8M internal oscillator clock selected + \arg RCU_CKOUT0SRC_HXTAL: HXTAL selected + \arg RCU_CKOUT0SRC_CKPLL_DIV2: CK_PLL/2 selected + \arg RCU_CKOUT0SRC_CKPLL1: CK_PLL1 selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_CKPLL2_DIV2: CK_PLL2/2 selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_EXT1: EXT1 selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_CKPLL2: CK_PLL2 clock selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_CKIRC48M: CK_IRC48M clock selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_CKIRC48M_DIV8: CK_IRC48M/8 clock selected (available for CL、EPRT and E508 series) + \arg RCU_CKOUT0SRC_CKPLLUSB_DIV32: CK_PLLUSB/32 clock selected (available for CL and E508 series) + \param[out] none + \retval none +*/ +void rcu_ckout0_config(uint32_t ckout0_src) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the CKOUT0SRC, set according to ckout0_src */ + reg &= ~RCU_CFG0_CKOUT0SEL; + RCU_CFG0 = (reg | ckout0_src); +} + +/*! + \brief configure the main PLL clock + \param[in] pll_src: PLL clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_PLLSRC_IRC8M_DIV2: IRC8M/2 clock selected as source clock of PLL + \arg RCU_PLLSRC_HXTAL_IRC48M: HXTAL or IRC48M selected as source clock of PLL + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL_MULx (HD series x = 2..63, CL series x = 2..14, 16..64, 6.5): PLL clock * x + \param[out] none + \retval none +*/ +void rcu_pll_config(uint32_t pll_src, uint32_t pll_mul) +{ + uint32_t reg = 0U; + + reg = RCU_CFG0; + + /* PLL clock source and multiplication factor configuration */ + reg &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + reg |= (pll_src | pll_mul); + + RCU_CFG0 = reg; +} + +/*! + \brief configure the PLL clock source preselection + \param[in] pll_presel: PLL clock source preselection + only one parameter can be selected which is shown as below: + \arg RCU_PLLPRESRC_HXTAL: HXTAL selected as PREDV0 input source clock + \arg RCU_PLLPRESRC_IRC48M: CK_PLL selected as PREDV0 input source clock + \param[out] none + \retval none +*/ +void rcu_pllpresel_config(uint32_t pll_presel) +{ + uint32_t reg = 0U; + + reg = RCU_CFG1; + + /* PLL clock source preselection */ + reg &= ~RCU_CFG1_PLLPRESEL; + reg |= pll_presel; + + RCU_CFG1 = reg; +} + +#if defined(GD32E50X_HD) +/*! + \brief configure the PREDV0 division factor + \param[in] predv0_div: PREDV0 division factor + \arg RCU_PREDV0_DIVx (x = 1, 2): PREDV0 input source clock is divided x + \param[out] none + \retval none +*/ +void rcu_predv0_config(uint32_t predv0_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG0; + /* reset PREDV0 bit */ + reg &= ~RCU_CFG0_PREDV0; + if(RCU_PREDV0_DIV2 == predv0_div){ + /* set the PREDV0 bit */ + reg |= RCU_CFG0_PREDV0; + } + + RCU_CFG0 = reg; +} +#elif defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) +/*! + \brief configure the PREDV0 division factor and clock source + \param[in] predv0_source: PREDV0 input clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_PREDV0SRC_HXTAL_IRC48M: HXTAL or IRC48M selected as PREDV0 input source clock + \arg RCU_PREDV0SRC_CKPLL1: CK_PLL1 selected as PREDV0 input source clock + \param[in] predv0_div: PREDV0 division factor + only one parameter can be selected which is shown as below: + \arg RCU_PREDV0_DIVx (x = 1..16): PREDV0 input source clock is divided x + \param[out] none + \retval none +*/ +void rcu_predv0_config(uint32_t predv0_source, uint32_t predv0_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG1; + /* reset PREDV0SEL and PREDV0 bits */ + reg &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV0); + /* set the PREDV0SEL and PREDV0 division factor */ + reg |= (predv0_source | predv0_div); + + RCU_CFG1 = reg; +} + +/*! + \brief configure the PREDV1 division factor + \param[in] predv1_div: PREDV1 division factor + only one parameter can be selected which is shown as below: + \arg RCU_PREDV1_DIVx (x = 1..16): PREDV1 input source clock is divided x + \param[out] none + \retval none +*/ +void rcu_predv1_config(uint32_t predv1_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG1; + /* reset the PREDV1 bits */ + reg &= ~RCU_CFG1_PREDV1; + /* set the PREDV1 division factor */ + reg |= predv1_div; + + RCU_CFG1 = reg; +} + +/*! + \brief configure the PLL1 clock + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL1_MULx (x = 8..14,16,20): PLL1 clock * x + \param[out] none + \retval none +*/ +void rcu_pll1_config(uint32_t pll_mul) +{ + RCU_CFG1 &= ~RCU_CFG1_PLL1MF; + RCU_CFG1 |= pll_mul; +} + +/*! + \brief configure the PLL2 clock + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL2_MULx (x = 8..14,16,20,18..32,40,34..64,80): PLL2 clock * x + \param[out] none + \retval none +*/ +void rcu_pll2_config(uint32_t pll_mul) +{ + RCU_CFG1 &= ~RCU_CFG1_PLL2MF; + RCU_CFG1 |= pll_mul; +} +#endif /* GD32E50X_HD */ + +#if defined(GD32E50X_CL) || defined(GD32E508) +/*! + \brief configure the PLLUSB clock source preselection + \param[in] pllusb_presel: PLLUSB clock source preselection + only one parameter can be selected which is shown as below: + \arg RCU_PLLUSBPRESRC_HXTAL: HXTAL selected as PLLUSB source clock + \arg RCU_PLLUSBPRESRC_IRC48M: IRC48M clock selected as PLLUSB source clock + \param[out] none + \retval none +*/ +void rcu_pllusbpresel_config(uint32_t pllusb_presel) +{ + uint32_t reg = 0U; + + reg = RCU_ADDCFG; + + /* PLLUSB clock source preselection */ + reg &= ~RCU_ADDCFG_PLLUSBPRESEL; + reg |= pllusb_presel; + + RCU_ADDCFG = reg; +} + +/*! + \brief configure the PLLUSBPREDV division factor and clock source + \param[in] pllusbpredv_source: PLLUSBPREDV input clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_PLLUSBPREDVSRC_HXTAL_IRC48M: HXTAL or IRC48M selected as PLLUSBPREDV input source clock + \arg RCU_PLLUSBPREDVSRC_CKPLL1: CK_PLL1 selected as PLLUSBPREDV input source clock + \param[in] pllusbpredv_div: PLLUSBPREDV division factor + only one parameter can be selected which is shown as below: + \arg RCU_PLLUSBPREDV_DIVx (x = 1..15): PLLUSBPREDV input source clock divided by x + \param[out] none + \retval none +*/ +void rcu_pllusbpredv_config(uint32_t pllusbpredv_source, uint32_t pllusbpredv_div) +{ + uint32_t reg = 0U; + + reg = RCU_ADDCFG; + /* reset PLLUSBPRESEL and PLLUSBPREDV bits */ + reg &= ~(RCU_ADDCFG_PLLUSBPREDVSEL | RCU_ADDCFG_PLLUSBPREDV); + /* set the PLLUSBPRESEL and PLLUSBPREDV division factor */ + reg |= (pllusbpredv_source | pllusbpredv_div); + + RCU_ADDCFG = reg; +} + +/*! + \brief configure the PLLUSB clock + \param[in] pllusb_mul: PLLUSB clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLLUSB_MULx (x = 16,17..127): PLLUSB source clock multiply by x + \param[out] none + \retval none +*/ +void rcu_pllusb_config(uint32_t pllusb_mul) +{ + RCU_ADDCFG &= ~RCU_ADDCFG_PLLUSBMF; + RCU_ADDCFG |= pllusb_mul; +} + +#endif /* GD32E50X_CL and GD32E508 */ + +/*! + \brief configure the ADC prescaler factor + \param[in] adc_psc: ADC prescaler factor + only one parameter can be selected which is shown as below: + \arg RCU_CKADC_CKAPB2_DIV2: ADC prescaler select CK_APB2 / 2 + \arg RCU_CKADC_CKAPB2_DIV4: ADC prescaler select CK_APB2 / 4 + \arg RCU_CKADC_CKAPB2_DIV6: ADC prescaler select CK_APB2 / 6 + \arg RCU_CKADC_CKAPB2_DIV8: ADC prescaler select CK_APB2 / 8 + \arg RCU_CKADC_CKAPB2_DIV12: ADC prescaler select CK_APB2 / 12 + \arg RCU_CKADC_CKAPB2_DIV16: ADC prescaler select CK_APB2 / 16 + \arg RCU_CKADC_CKAHB_DIV5: ADC prescaler select CK_AHB / 5 + \arg RCU_CKADC_CKAHB_DIV6: ADC prescaler select CK_AHB / 6 + \arg RCU_CKADC_CKAHB_DIV10: ADC prescaler select CK_AHB / 10 + \arg RCU_CKADC_CKAHB_DIV20: ADC prescaler select CK_AHB / 20 + \param[out] none + \retval none +*/ +void rcu_adc_clock_config(uint32_t adc_psc) +{ + uint32_t reg0,reg1; + + /* reset the ADCPSC bits */ + reg0 = RCU_CFG0; + reg0 &= ~(RCU_CFG0_ADCPSC_2 | RCU_CFG0_ADCPSC); + reg1 = RCU_CFG1; + reg1 &= ~RCU_CFG1_ADCPSC_3; + + /* set the ADC prescaler factor */ + switch(adc_psc){ + case RCU_CKADC_CKAPB2_DIV2: + case RCU_CKADC_CKAPB2_DIV4: + case RCU_CKADC_CKAPB2_DIV6: + case RCU_CKADC_CKAPB2_DIV8: + reg0 |= (adc_psc << RCU_ADC_PSC_OFFSET); + break; + + case RCU_CKADC_CKAPB2_DIV12: + case RCU_CKADC_CKAPB2_DIV16: + adc_psc &= ~BIT(2); + reg0 |= ((adc_psc << RCU_ADC_PSC_OFFSET) | RCU_CFG0_ADCPSC_2); + break; + + case RCU_CKADC_CKAHB_DIV5: + case RCU_CKADC_CKAHB_DIV6: + case RCU_CKADC_CKAHB_DIV10: + case RCU_CKADC_CKAHB_DIV20: + adc_psc &= ~BITS(2,3); + reg0 |= (adc_psc << RCU_ADC_PSC_OFFSET); + reg1 |= RCU_CFG1_ADCPSC_3; + break; + + default: + break; + } + + /* set the register */ + RCU_CFG0 = reg0; + RCU_CFG1 = reg1; +} + +/*! + \brief configure the USBD/USBHS prescaler factor + \param[in] usb_psc: USB prescaler factor + only one parameter can be selected which is shown as below: + \arg RCU_CKUSB_CKPLL_DIV1_5: USBD / USBHS prescaler select CK_PLL / 1.5 + \arg RCU_CKUSB_CKPLL_DIV1: USBD / USBHS prescaler select CK_PLL / 1 + \arg RCU_CKUSB_CKPLL_DIV2_5: USBD / USBHS prescaler select CK_PLL / 2.5 + \arg RCU_CKUSB_CKPLL_DIV2: USBD / USBHS prescaler select CK_PLL / 2 + \arg RCU_CKUSB_CKPLL_DIV3: USBD / USBHS prescaler select CK_PLL / 3 + \arg RCU_CKUSB_CKPLL_DIV3_5: USBD / USBHS prescaler select CK_PLL / 3.5 + \arg RCU_CKUSB_CKPLL_DIV4: USBD / USBHS prescaler select CK_PLL / 4 + \param[out] none + \retval none +*/ +void rcu_usb_clock_config(uint32_t usb_psc) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* configure the USBD/USBHS prescaler factor */ +#if (defined(GD32E50X_HD) || defined(GD32EPRT)) + reg &= ~RCU_CFG0_USBDPSC; +#elif defined(GD32E50X_CL) || defined(GD32E508) + reg &= ~RCU_CFG0_USBHSPSC; +#endif /* GD32E50X_HD and GD32EPRT */ + + RCU_CFG0 = (reg | usb_psc); +} + +/*! + \brief configure the RTC clock source selection + \param[in] rtc_clock_source: RTC clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_RTCSRC_NONE: no clock selected + \arg RCU_RTCSRC_LXTAL: CK_LXTAL selected as RTC source clock + \arg RCU_RTCSRC_IRC40K: CK_IRC40K selected as RTC source clock + \arg RCU_RTCSRC_HXTAL_DIV_128: CK_HXTAL/128 selected as RTC source clock + \param[out] none + \retval none +*/ +void rcu_rtc_clock_config(uint32_t rtc_clock_source) +{ + uint32_t reg; + + reg = RCU_BDCTL; + /* reset the RTCSRC bits and set according to rtc_clock_source */ + reg &= ~RCU_BDCTL_RTCSRC; + RCU_BDCTL = (reg | rtc_clock_source); +} + +#ifndef GD32EPRT +/*! + \brief configure the SHRTIMER clock source selection + \param[in] shrtimer_clock_source: SHRTIMER clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_SHRTIMERSRC_CKAPB2: APB2 clock selected as SHRTIMER source clock + \arg RCU_SHRTIMERSRC_CKSYS: system clock selected as SHRTIMER source clock + \param[out] none + \retval none +*/ +void rcu_shrtimer_clock_config(uint32_t shrtimer_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG1; + /* reset the I2S1SEL bit and set according to shrtimer_clock_source */ + reg &= ~RCU_CFG1_SHRTIMERSEL; + RCU_CFG1 = (reg | shrtimer_clock_source); +} +#endif /* GD32EPRT */ + +/*! + \brief configure the USART5 clock source selection + \param[in] usart5_clock_source: USART5 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_USART5SRC_CKAPB2: APB2 clock selected as USART5 source clock + \arg RCU_USART5SRC_CKSYS: system clock selected as USART5 source clock + \arg RCU_USART5SRC_LXTAL: LXTAL clock selected as USART5 source clock + \arg RCU_USART5SRC_IRC8M: IRC8M clock selected as USART5 source clock + \param[out] none + \retval none +*/ +void rcu_usart5_clock_config(uint32_t usart5_clock_source) +{ + uint32_t reg; + reg = RCU_CFG2; + /* reset the I2S1SEL bit and set according to usart5_clock_source */ + reg &= ~RCU_CFG2_USART5SEL; + RCU_CFG2 = (reg | usart5_clock_source); +} + +/*! + \brief configure the I2C2 clock source selection + \param[in] i2c2_clock_source: I2C2 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_I2C2SRC_CKAPB1: APB1 clock selected as I2C2 source clock + \arg RCU_I2C2SRC_CKSYS: System clock selected as I2C2 source clock + \arg RCU_I2C2SRCSRC_CKIRC8M: CK_IRC8M clock selected as I2C2 source clock + \param[out] none + \retval none +*/ +void rcu_i2c2_clock_config(uint32_t i2c2_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG2; + /* reset the I2S1SEL bit and set according to i2c2_clock_source */ + reg &= ~RCU_CFG2_I2C2SEL; + RCU_CFG2 = (reg | i2c2_clock_source); +} + +/*! + \brief configure the CK48M clock source selection + \param[in] ck48m_clock_source: CK48M clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_CK48MSRC_CKPLL: CK_PLL selected as CK48M source clock + \arg RCU_CK48MSRC_IRC48M: CK_IRC48M selected as CK48M source clock + \arg RCU_CK48MSRC_CKPLLUSB: (not available for EPRT series): CKPLLUSB selected as CK48M source clock + \arg RCU_CK48MSRC_CKPLL2: CKPLL2 selected as CK48M source clock + \param[out] none + \retval none +*/ +void rcu_ck48m_clock_config(uint32_t ck48m_clock_source) +{ + uint32_t reg; + + reg = RCU_ADDCTL; + /* reset the CK48MSEL bit and set according to ck48m_clock_source */ + reg &= ~RCU_ADDCTL_CK48MSEL; + RCU_ADDCTL = (reg | ck48m_clock_source); +} + +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) +/*! + \brief configure the I2S1 clock source selection + \param[in] i2s_clock_source: I2S1 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_I2S1SRC_CKSYS: system clock selected as I2S1 source clock + \arg RCU_I2S1SRC_CKPLL2_MUL2: CK_PLL2 * 2 selected as I2S1 source clock + \param[out] none + \retval none +*/ +void rcu_i2s1_clock_config(uint32_t i2s_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG1; + /* reset the I2S1SEL bit and set according to i2s_clock_source */ + reg &= ~RCU_CFG1_I2S1SEL; + RCU_CFG1 = (reg | i2s_clock_source); +} + +/*! + \brief configure the I2S2 clock source selection + \param[in] i2s_clock_source: I2S2 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_I2S2SRC_CKSYS: system clock selected as I2S2 source clock + \arg RCU_I2S2SRC_CKPLL2_MUL2: CK_PLL2 * 2 selected as I2S2 source clock + \param[out] none + \retval none +*/ +void rcu_i2s2_clock_config(uint32_t i2s_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG1; + /* reset the I2S2SEL bit and set according to i2s_clock_source */ + reg &= ~RCU_CFG1_I2S2SEL; + RCU_CFG1 = (reg | i2s_clock_source); +} +#endif /* GD32E50X_CL and GD32E50X_EPRT and GD32E508 */ + +#if defined(GD32E50X_CL) || defined(GD32E508) +/*! + \brief configure the USBHSSEL source clock selection + \param[in] usbhssel_clock_source: USBHSSEL clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_USBHSSRC_48M: 48M clock selected as USBHS source clock + \arg RCU_CK48MSRC_60M: 60M clock selected as USBHS source clock + \param[out] none + \retval none +*/ +void rcu_usbhssel_config(uint32_t usbhssel_clock_source) +{ + uint32_t reg = 0U; + + reg = RCU_ADDCTL; + + /* USBHS clock source selection */ + reg &= ~RCU_ADDCTL_USBHSSEL; + reg |= usbhssel_clock_source; + + RCU_ADDCTL = reg; +} + +/*! + \brief configure the USBHSDV division factor + \param[in] usbhs_dv: USBHSDV division factor + only one parameter can be selected which is shown as below: + \arg RCU_USBHSDV_DIV2: USBHSDV input source clock divided by 2 + \arg RCU_USBHSDV_DIV4: USBHSDV input source clock divided by 4 + \arg RCU_USBHSDV_DIV6: USBHSDV input source clock divided by 6 + \arg RCU_USBHSDV_DIV8: USBHSDV input source clock divided by 8 + \arg RCU_USBHSDV_DIV10: USBHSDV input source clock divided by 10 + \arg RCU_USBHSDV_DIV12: USBHSDV input source clock divided by 12 + \arg RCU_USBHSDV_DIV14: USBHSDV input source clock divided by 14 + \arg RCU_USBHSDV_DIV16: USBHSDV input source clock divided by 16 + \param[out] none + \retval none +*/ +void rcu_usbdv_config(uint32_t usbhs_dv) +{ + uint32_t reg = 0U; + + reg = RCU_ADDCTL; + + /* usbhs input clock source division factor */ + reg &= ~RCU_ADDCTL_USBHSDV; + reg |= usbhs_dv; + + RCU_ADDCTL = reg; +} +#endif /* GD32E50X_CL and GD32E508 */ + +/*! + \brief configure the LXTAL drive capability + \param[in] lxtal_dricap: drive capability of LXTAL + only one parameter can be selected which is shown as below: + \arg RCU_LXTAL_LOWDRI: lower driving capability + \arg RCU_LXTAL_MED_LOWDRI: medium low driving capability + \arg RCU_LXTAL_MED_HIGHDRI: medium high driving capability + \arg RCU_LXTAL_HIGHDRI: higher driving capability + \param[out] none + \retval none +*/ +void rcu_lxtal_drive_capability_config(uint32_t lxtal_dricap) +{ + uint32_t reg; + + reg = RCU_BDCTL; + + /* reset the LXTALDRI bits and set according to lxtal_dricap */ + reg &= ~RCU_BDCTL_LXTALDRI; + RCU_BDCTL = (reg | lxtal_dricap); +} + +/*! + \brief wait for oscillator stabilization flags is SET or oscillator startup is timeout + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC48M: internal 48M RC oscillators(IRC48M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL, EPRT and E508 series available) + \arg RCU_PLL2_CK: phase locked loop 2(CL, EPRT and E508 series available) + \arg RCU_PLLUSB_CK: phase locked loop USB(CL and E508 series available) + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus rcu_osci_stab_wait(rcu_osci_type_enum osci) +{ + uint32_t stb_cnt = 0U; + ErrStatus reval = ERROR; + FlagStatus osci_stat = RESET; + + switch(osci){ + /* wait HXTAL stable */ + case RCU_HXTAL: + while((RESET == osci_stat) && (HXTAL_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_HXTALSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_HXTALSTB)){ + reval = SUCCESS; + } + break; + + /* wait LXTAL stable */ + case RCU_LXTAL: + while((RESET == osci_stat) && (LXTAL_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_LXTALSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_LXTALSTB)){ + reval = SUCCESS; + } + break; + + /* wait IRC8M stable */ + case RCU_IRC8M: + while((RESET == osci_stat) && (IRC8M_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_IRC8MSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_IRC8MSTB)){ + reval = SUCCESS; + } + break; + + /* wait IRC48M stable */ + case RCU_IRC48M: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_IRC48MSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if (RESET != rcu_flag_get(RCU_FLAG_IRC48MSTB)){ + reval = SUCCESS; + } + break; + + /* wait IRC40K stable */ + case RCU_IRC40K: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_IRC40KSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_IRC40KSTB)){ + reval = SUCCESS; + } + break; + + /* wait PLL stable */ + case RCU_PLL_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLLSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLLSTB)){ + reval = SUCCESS; + } + break; + +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + /* wait PLL1 stable */ + case RCU_PLL1_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLL1STB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLL1STB)){ + reval = SUCCESS; + } + break; + /* wait PLL2 stable */ + case RCU_PLL2_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLL2STB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLL2STB)){ + reval = SUCCESS; + } + break; +#endif /* GD32E50X_CL and GD32E50X_EPRT and GD32E508 */ +#if defined(GD32E50X_CL) || defined(GD32E508) + case RCU_PLLUSB_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLLUSBSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLLUSBSTB)){ + reval = SUCCESS; + } + break; +#endif /* GD32E50X_CL and GD32E508 */ + + default: + break; + } + + /* return value */ + return reval; +} + +/*! + \brief turn on the oscillator + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC48M: internal 48M RC oscillators(IRC48M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL, EPRT and E508 series available) + \arg RCU_PLL2_CK: phase locked loop 2(CL, EPRT and E508 series available) + \arg RCU_PLLUSB_CK: phase locked loop USB(CL and E508 series available) + \param[out] none + \retval none +*/ +void rcu_osci_on(rcu_osci_type_enum osci) +{ + RCU_REG_VAL(osci) |= BIT(RCU_BIT_POS(osci)); +} + +/*! + \brief turn off the oscillator + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC48M: internal 48M RC oscillators(IRC48M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL, EPRT and E508 series available) + \arg RCU_PLL2_CK: phase locked loop 2(CL, EPRT and E508 series available) + \arg RCU_PLLUSB_CK: phase locked loop USB(CL and E508 series available) + \param[out] none + \retval none +*/ +void rcu_osci_off(rcu_osci_type_enum osci) +{ + RCU_REG_VAL(osci) &= ~BIT(RCU_BIT_POS(osci)); +} + +/*! + \brief enable the oscillator bypass mode, HXTALEN or LXTALEN must be reset before it + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \param[out] none + \retval none +*/ +void rcu_osci_bypass_mode_enable(rcu_osci_type_enum osci) +{ + uint32_t reg; + + switch(osci){ + /* enable HXTAL to bypass mode */ + case RCU_HXTAL: + reg = RCU_CTL; + RCU_CTL &= ~RCU_CTL_HXTALEN; + RCU_CTL = (reg | RCU_CTL_HXTALBPS); + break; + /* enable LXTAL to bypass mode */ + case RCU_LXTAL: + reg = RCU_BDCTL; + RCU_BDCTL &= ~RCU_BDCTL_LXTALEN; + RCU_BDCTL = (reg | RCU_BDCTL_LXTALBPS); + break; + case RCU_IRC8M: + case RCU_IRC48M: + case RCU_IRC40K: + case RCU_PLL_CK: +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + case RCU_PLL1_CK: + case RCU_PLL2_CK: +#endif /* GD32E50X_CL and GD32EPRT and GD32E508 */ + break; + default: + break; + } +} + +/*! + \brief disable the oscillator bypass mode, HXTALEN or LXTALEN must be reset before it + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \param[out] none + \retval none +*/ +void rcu_osci_bypass_mode_disable(rcu_osci_type_enum osci) +{ + uint32_t reg; + + switch(osci){ + /* disable HXTAL to bypass mode */ + case RCU_HXTAL: + reg = RCU_CTL; + RCU_CTL &= ~RCU_CTL_HXTALEN; + RCU_CTL = (reg & ~RCU_CTL_HXTALBPS); + break; + /* disable LXTAL to bypass mode */ + case RCU_LXTAL: + reg = RCU_BDCTL; + RCU_BDCTL &= ~RCU_BDCTL_LXTALEN; + RCU_BDCTL = (reg & ~RCU_BDCTL_LXTALBPS); + break; + case RCU_IRC8M: + case RCU_IRC48M: + case RCU_IRC40K: + case RCU_PLL_CK: +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + case RCU_PLL1_CK: + case RCU_PLL2_CK: +#endif /* GD32E50X_CL and GD32EPRT and GD32E508 */ + break; + default: + break; + } +} + +/*! + \brief set the IRC8M adjust value + \param[in] irc8m_adjval: IRC8M adjust value, must be between 0 and 0x1F + \arg 0x00 - 0x1F + \param[out] none + \retval none +*/ +void rcu_irc8m_adjust_value_set(uint32_t irc8m_adjval) +{ + uint32_t reg; + + reg = RCU_CTL; + /* reset the IRC8MADJ bits and set according to irc8m_adjval */ + reg &= ~RCU_CTL_IRC8MADJ; + RCU_CTL = (reg | ((irc8m_adjval & RCU_IRC8M_ADJUST_MASK) << RCU_IRC8M_ADJUST_OFFSET)); +} + +/*! + \brief configure the spread spectrum modulation for the main PLL clock + \param[in] spread_spectrum_type: PLL spread spectrum modulation type select + \arg RCU_SS_TYPE_CENTER: center spread type is selected + \arg RCU_SS_TYPE_DOWN: down spread type is selected + \param[in] modstep: configure PLL spread spectrum modulation profile amplitude and frequency + \arg This parameter should be selected between 0 and 7FFF.The following criteria must be met: MODSTEP*MODCNT <=2^15-1 + \param[in] modcnt: configure PLL spread spectrum modulation profile amplitude and frequency + \arg This parameter should be selected between 0 and 1FFF.The following criteria must be met: MODSTEP*MODCNT <=2^15-1 + \param[out] none + \retval none +*/ +void rcu_spread_spectrum_config(uint32_t spread_spectrum_type, uint32_t modstep, uint32_t modcnt) +{ + uint32_t reg; + + reg = RCU_PLLSSCTL; + /* reset the RCU_PLLSSCTL register bits */ + reg &= ~(RCU_PLLSSCTL_MODCNT | RCU_PLLSSCTL_MODSTEP | RCU_PLLSSCTL_SS_TYPE); + RCU_PLLSSCTL = (reg | spread_spectrum_type | modstep << 13 | modcnt); +} + +/*! + \brief enable the PLL spread spectrum modulation + \param[in] none + \param[out] none + \retval none +*/ +void rcu_spread_spectrum_enable(void) +{ + RCU_PLLSSCTL |= RCU_PLLSSCTL_SSCGON; +} + +/*! + \brief disable the PLL spread spectrum modulation + \param[in] none + \param[out] none + \retval none +*/ +void rcu_spread_spectrum_disable(void) +{ + RCU_PLLSSCTL &= ~RCU_PLLSSCTL_SSCGON; +} + +/*! + \brief enable the HXTAL clock monitor + \param[in] none + \param[out] none + \retval none +*/ + +void rcu_hxtal_clock_monitor_enable(void) +{ + RCU_CTL |= RCU_CTL_CKMEN; +} + +/*! + \brief disable the HXTAL clock monitor + \param[in] none + \param[out] none + \retval none +*/ +void rcu_hxtal_clock_monitor_disable(void) +{ + RCU_CTL &= ~RCU_CTL_CKMEN; +} + + +/*! + \brief deep-sleep mode voltage select + \param[in] dsvol: deep sleep mode voltage + only one parameter can be selected which is shown as below: + \arg RCU_DEEPSLEEP_V_1_0: the core voltage is 1.0V + \arg RCU_DEEPSLEEP_V_0_9: the core voltage is 0.9V + \arg RCU_DEEPSLEEP_V_0_8: the core voltage is 0.8V + \arg RCU_DEEPSLEEP_V_0_7: the core voltage is 0.7V + \param[out] none + \retval none +*/ +void rcu_deepsleep_voltage_set(uint32_t dsvol) +{ + dsvol &= RCU_DSV_DSLPVS; + RCU_DSV = dsvol; +} + +/*! + \brief get the system clock, bus and peripheral clock frequency + \param[in] clock: the clock frequency which to get + only one parameter can be selected which is shown as below: + \arg CK_SYS: system clock frequency + \arg CK_AHB: AHB clock frequency + \arg CK_APB1: APB1 clock frequency + \arg CK_APB2: APB2 clock frequency + \arg CK_USART: USART5 clock frequency + \param[out] none + \retval clock frequency of system, AHB, APB1, APB2 +*/ +uint32_t rcu_clock_freq_get(rcu_clock_freq_enum clock) +{ + uint32_t sws, ck_freq = 0U; + uint32_t cksys_freq, ahb_freq, apb1_freq, apb2_freq; + uint32_t usart_freq = 0U; + uint32_t pllsel, pllpresel, predv0sel, pllmf,ck_src, idx, clk_exp; +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + uint32_t predv0, predv1, pll1mf; +#endif /* GD32E50X_CL and GD32EPRT and GD32E508*/ + + /* exponent of AHB, APB1 and APB2 clock divider */ + uint8_t ahb_exp[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + uint8_t apb1_exp[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + uint8_t apb2_exp[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + + sws = GET_BITS(RCU_CFG0, 2, 3); + switch(sws){ + /* IRC8M is selected as CK_SYS */ + case SEL_IRC8M: + cksys_freq = IRC8M_VALUE; + break; + /* HXTAL is selected as CK_SYS */ + case SEL_HXTAL: + cksys_freq = HXTAL_VALUE; + break; + /* PLL is selected as CK_SYS */ + case SEL_PLL: + /* PLL clock source selection, HXTAL, IRC48M or IRC8M/2 */ + pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL); + + if(RCU_PLLSRC_HXTAL_IRC48M == pllsel) { + /* PLL clock source is HXTAL or IRC48M */ + pllpresel = (RCU_CFG1 & RCU_CFG1_PLLPRESEL); + + if(RCU_PLLPRESRC_HXTAL == pllpresel){ + /* PLL clock source is HXTAL */ + ck_src = HXTAL_VALUE; + }else{ + /* PLL clock source is IRC48 */ + ck_src = IRC48M_VALUE; + } + +#if defined(GD32E50X_HD) + predv0sel = (RCU_CFG0 & RCU_CFG0_PREDV0); + /* PREDV0 input source clock divided by 2 */ + if(RCU_CFG0_PREDV0 == predv0sel){ + ck_src = HXTAL_VALUE/2U; + } +#elif defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + predv0sel = (RCU_CFG1 & RCU_CFG1_PREDV0SEL); + /* source clock use PLL1 */ + if(RCU_PREDV0SRC_CKPLL1 == predv0sel){ + predv1 = ((RCU_CFG1 & RCU_CFG1_PREDV1) >> RCU_CFG1_PREDV1_OFFSET) + 1U; + pll1mf = (uint32_t)((RCU_CFG1 & RCU_CFG1_PLL1MF) >> RCU_CFG1_PLL1MF_OFFSET) + 2U; + if(17U == pll1mf){ + pll1mf = 20U; + } + ck_src = (ck_src/predv1)*pll1mf; + } + predv0 = (RCU_CFG1 & RCU_CFG1_PREDV0) + 1U; + ck_src /= predv0; +#endif /* GD32E50X_HD */ + }else{ + /* PLL clock source is IRC8M/2 */ + ck_src = IRC8M_VALUE/2U; + } + + /* PLL multiplication factor */ + pllmf = GET_BITS(RCU_CFG0, 18, 21); + if((RCU_CFG0 & RCU_CFG0_PLLMF_4)){ + pllmf |= 0x10U; + } + if((RCU_CFG0 & RCU_CFG0_PLLMF_5)){ + pllmf |= 0x20U; + } + if(pllmf < 15U){ + pllmf += 2U; + }else if((pllmf >= 15U) && (pllmf <= 64U)){ + pllmf += 1U; + }else{ + } + cksys_freq = ck_src*pllmf; +#if defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508) + if(15U == pllmf){ + cksys_freq = ck_src*6U + ck_src/2U; + } +#endif /* GD32E50X_CL and GD32EPRT and GD32E508 */ + + break; + /* IRC8M is selected as CK_SYS */ + default: + cksys_freq = IRC8M_VALUE; + break; + } + + /* calculate AHB clock frequency */ + idx = GET_BITS(RCU_CFG0, 4, 7); + clk_exp = ahb_exp[idx]; + ahb_freq = cksys_freq >> clk_exp; + + /* calculate APB1 clock frequency */ + idx = GET_BITS(RCU_CFG0, 8, 10); + clk_exp = apb1_exp[idx]; + apb1_freq = ahb_freq >> clk_exp; + + /* calculate APB2 clock frequency */ + idx = GET_BITS(RCU_CFG0, 11, 13); + clk_exp = apb2_exp[idx]; + apb2_freq = ahb_freq >> clk_exp; + + /* return the clocks frequency */ + switch(clock){ + case CK_SYS: + ck_freq = cksys_freq; + break; + case CK_AHB: + ck_freq = ahb_freq; + break; + case CK_APB1: + ck_freq = apb1_freq; + break; + case CK_APB2: + ck_freq = apb2_freq; + break; + case CK_USART: + /* calculate USART5 clock frequency */ + if(RCU_USART5SRC_CKAPB2 == (RCU_CFG2 & RCU_CFG2_USART5SEL)){ + usart_freq = apb2_freq; + }else if(RCU_USART5SRC_CKSYS == (RCU_CFG2 & RCU_CFG2_USART5SEL)){ + usart_freq = cksys_freq; + }else if(RCU_USART5SRC_LXTAL == (RCU_CFG2 & RCU_CFG2_USART5SEL)){ + usart_freq = LXTAL_VALUE; + }else if(RCU_USART5SRC_IRC8M == (RCU_CFG2 & RCU_CFG2_USART5SEL)){ + usart_freq = IRC8M_VALUE; + }else{ + } + ck_freq = usart_freq; + break; + default: + break; + } + return ck_freq; +} + +/*! + \brief get the clock stabilization and periphral reset flags + \param[in] flag: the clock stabilization and periphral reset flags, refer to rcu_flag_enum + only one parameter can be selected which is shown as below: + \arg RCU_FLAG_IRC8MSTB: IRC8M stabilization flag + \arg RCU_FLAG_HXTALSTB: HXTAL stabilization flag + \arg RCU_FLAG_PLLSTB: PLL stabilization flag + \arg RCU_FLAG_PLLUSBSTB: PLLUSB stabilization flag(CL and E508 series available) + \arg RCU_FLAG_PLL1STB: PLL1 stabilization flag(CL, EPRT and E508 series available) + \arg RCU_FLAG_PLL2STB: PLL2 stabilization flag(CL, EPRT and E508 series available) + \arg RCU_FLAG_LXTALSTB: LXTAL stabilization flag + \arg RCU_FLAG_IRC40KSTB: IRC40K stabilization flag + \arg RCU_FLAG_IRC48MSTB: IRC48M stabilization flag + \arg RCU_FLAG_EPRST: external PIN reset flag + \arg RCU_FLAG_PORRST: power reset flag + \arg RCU_FLAG_SWRST: software reset flag + \arg RCU_FLAG_FWDGTRST: free watchdog timer reset flag + \arg RCU_FLAG_WWDGTRST: window watchdog timer reset flag + \arg RCU_FLAG_LPRST: low-power reset flag + \param[out] none + \retval none +*/ +FlagStatus rcu_flag_get(rcu_flag_enum flag) +{ + /* get the rcu flag */ + if(RESET != (RCU_REG_VAL(flag) & BIT(RCU_BIT_POS(flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear all the reset flag + \param[in] none + \param[out] none + \retval none +*/ +void rcu_all_reset_flag_clear(void) +{ + RCU_RSTSCK |= RCU_RSTSCK_RSTFC; +} + +/*! + \brief get the clock stabilization interrupt and ckm flags + \param[in] int_flag: interrupt and ckm flags, refer to rcu_int_flag_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_FLAG_IRC40KSTB: IRC40K stabilization interrupt flag + \arg RCU_INT_FLAG_LXTALSTB: LXTAL stabilization interrupt flag + \arg RCU_INT_FLAG_IRC8MSTB: IRC8M stabilization interrupt flag + \arg RCU_INT_FLAG_HXTALSTB: HXTAL stabilization interrupt flag + \arg RCU_INT_FLAG_PLLSTB: PLL stabilization interrupt flag + \arg RCU_INT_FLAG_PLL1STB: PLL1 stabilization interrupt flag(CL, EPRT and E508 series available) + \arg RCU_INT_FLAG_PLL2STB: PLL2 stabilization interrupt flag(CL, EPRT and E508 series available) + \arg RCU_INT_FLAG_PLLUSBSTB: PLLUSB stabilization interrupt flag(CL and E508 series available) + \arg RCU_INT_FLAG_CKM: HXTAL clock stuck interrupt flag + \arg RCU_INT_FLAG_IRC48MSTB: IRC48M stabilization interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus rcu_interrupt_flag_get(rcu_int_flag_enum int_flag) +{ + /* get the rcu interrupt flag */ + if(RESET != (RCU_REG_VAL(int_flag) & BIT(RCU_BIT_POS(int_flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear the interrupt flags + \param[in] int_flag: clock stabilization and stuck interrupt flags clear, refer to rcu_int_flag_clear_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_FLAG_IRC40KSTB_CLR: IRC40K stabilization interrupt flag clear + \arg RCU_INT_FLAG_LXTALSTB_CLR: LXTAL stabilization interrupt flag clear + \arg RCU_INT_FLAG_IRC8MSTB_CLR: IRC8M stabilization interrupt flag clear + \arg RCU_INT_FLAG_HXTALSTB_CLR: HXTAL stabilization interrupt flag clear + \arg RCU_INT_FLAG_PLLSTB_CLR: PLL stabilization interrupt flag clear + \arg RCU_INT_FLAG_PLL1STB_CLR: PLL1 stabilization interrupt flag clear(CL, EPRT and E508 series available) + \arg RCU_INT_FLAG_PLL2STB_CLR: PLL2 stabilization interrupt flag clear(CL, EPRT and E508 series available) + \arg RCU_INT_FLAG_PLLUSBSTB_CLR: PLLUS stabilization interrupt flag clear(CL and E508 series available) + \arg RCU_INT_FLAG_CKM_CLR: clock stuck interrupt flag clear + \arg RCU_INT_FLAG_IRC48MSTB_CLR: IRC48M stabilization interrupt flag clear + \param[out] none + \retval none +*/ +void rcu_interrupt_flag_clear(rcu_int_flag_clear_enum int_flag) +{ + RCU_REG_VAL(int_flag) |= BIT(RCU_BIT_POS(int_flag)); +} + +/*! + \brief enable the stabilization interrupt + \param[in] interrupt clock stabilization interrupt, refer to rcu_int_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_IRC40KSTB: IRC40K stabilization interrupt enable + \arg RCU_INT_LXTALSTB: LXTAL stabilization interrupt enable + \arg RCU_INT_IRC8MSTB: IRC8M stabilization interrupt enable + \arg RCU_INT_HXTALSTB: HXTAL stabilization interrupt enable + \arg RCU_INT_PLLSTB: PLL stabilization interrupt enable + \arg RCU_INT_PLL1STB: PLL1 stabilization interrupt enable(CL, EPRT and E508 series available) + \arg RCU_INT_PLL2STB: PLL2 stabilization interrupt enable(CL, EPRT and E508 series available) + \arg RCU_INT_PLLUSBSTB: PLLUSB stabilization interrupt enable(CL and E508 series available) + \arg RCU_INT_IRC48MSTB: IRC48M stabilization interrupt enable + \param[out] none + \retval none +*/ +void rcu_interrupt_enable(rcu_int_enum interrupt) +{ + RCU_REG_VAL(interrupt) |= BIT(RCU_BIT_POS(interrupt)); +} + +/*! + \brief disable the stabilization interrupt + \param[in] interrupt clock stabilization interrupt, refer to rcu_int_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_IRC40KSTB: IRC40K stabilization interrupt enable + \arg RCU_INT_LXTALSTB: LXTAL stabilization interrupt enable + \arg RCU_INT_IRC8MSTB: IRC8M stabilization interrupt enable + \arg RCU_INT_HXTALSTB: HXTAL stabilization interrupt enable + \arg RCU_INT_PLLSTB: PLL stabilization interrupt enable + \arg RCU_INT_PLL1STB: PLL1 stabilization interrupt enable(CL, EPRT and E508 series available) + \arg RCU_INT_PLL2STB: PLL2 stabilization interrupt enable(CL, EPRT and E508 series available) + \arg RCU_INT_PLLUSBSTB: PLLUSB stabilization interrupt enable(CL and E508 series available) + \arg RCU_INT_IRC48MSTB: IRC48M stabilization interrupt enable + \param[out] none + \retval none +*/ +void rcu_interrupt_disable(rcu_int_enum interrupt) +{ + RCU_REG_VAL(interrupt) &= ~BIT(RCU_BIT_POS(interrupt)); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_spi.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_spi.c new file mode 100644 index 0000000..e81dafa --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_spi.c @@ -0,0 +1,895 @@ +/*! + \file gd32e50x_spi.c + \brief SPI driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_spi.h" + +/* SPI/I2S parameter initialization mask */ +#define SPI_INIT_MASK ((uint32_t)0x00003040U) /*!< SPI parameter initialization mask */ +#define I2S_INIT_MASK ((uint32_t)0x0000F047U) /*!< I2S parameter initialization mask */ +#define I2S_FULL_DUPLEX_MASK ((uint32_t)0x00000480U) /*!< I2S full duples mode configure parameter initialization mask */ + +/* default value */ +#define SPI_I2SPSC_DEFAULT_VALUE ((uint32_t)0x00000002U) /*!< default value of SPI_I2SPSC register */ + +/* I2S clock source selection, multiplication and division mask */ +#define I2S1_CLOCK_SEL ((uint32_t)0x00020000U) /*!< I2S1 clock source selection */ +#define I2S2_CLOCK_SEL ((uint32_t)0x00040000U) /*!< I2S2 clock source selection */ +#define I2S_CLOCK_MUL_MASK ((uint32_t)0x0000F000U) /*!< I2S clock multiplication mask */ +#define I2S_CLOCK_DIV_MASK ((uint32_t)0x000000F0U) /*!< I2S clock division mask */ + +/*! + \brief reset SPI and I2S + \param[in] spi_periph: SPIx(x=0,1,2),include I2S1_ADD and I2S2_ADD + \param[out] none + \retval none +*/ +void spi_i2s_deinit(uint32_t spi_periph) +{ + switch(spi_periph){ + case SPI0: + /* reset SPI0 */ + rcu_periph_reset_enable(RCU_SPI0RST); + rcu_periph_reset_disable(RCU_SPI0RST); + break; + case SPI1: + /* reset SPI1 , I2S1 and I2S1_ADD */ + rcu_periph_reset_enable(RCU_SPI1RST); + rcu_periph_reset_disable(RCU_SPI1RST); + break; + case SPI2: + /* reset SPI2 , I2S2 and I2S2_ADD */ + rcu_periph_reset_enable(RCU_SPI2RST); + rcu_periph_reset_disable(RCU_SPI2RST); + break; + default : + break; + } +} + +/*! + \brief initialize the parameters of SPI struct with default values + \param[in] none + \param[out] spi_parameter_struct: the initialized struct spi_parameter_struct pointer + \retval none +*/ +void spi_struct_para_init(spi_parameter_struct *spi_struct) +{ + /* configure the structure with default value */ + spi_struct->device_mode = SPI_SLAVE; + spi_struct->trans_mode = SPI_TRANSMODE_FULLDUPLEX; + spi_struct->frame_size = SPI_FRAMESIZE_8BIT; + spi_struct->nss = SPI_NSS_HARD; + spi_struct->clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; + spi_struct->prescale = SPI_PSC_2; + spi_struct->endian = SPI_ENDIAN_MSB; +} + +/*! + \brief initialize SPI parameter + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] spi_struct: SPI parameter initialization stuct members of the structure + and the member values are shown as below: + device_mode: SPI_MASTER, SPI_SLAVE + trans_mode: SPI_TRANSMODE_FULLDUPLEX, SPI_TRANSMODE_RECEIVEONLY, + SPI_TRANSMODE_BDRECEIVE, SPI_TRANSMODE_BDTRANSMIT + frame_size: SPI_FRAMESIZE_16BIT, SPI_FRAMESIZE_8BIT + nss: SPI_NSS_SOFT, SPI_NSS_HARD + endian: SPI_ENDIAN_MSB, SPI_ENDIAN_LSB + clock_polarity_phase: SPI_CK_PL_LOW_PH_1EDGE, SPI_CK_PL_HIGH_PH_1EDGE + SPI_CK_PL_LOW_PH_2EDGE, SPI_CK_PL_HIGH_PH_2EDGE + prescale: SPI_PSC_n (n=2,4,8,16,32,64,128,256) + \param[out] none + \retval none +*/ +void spi_init(uint32_t spi_periph, spi_parameter_struct* spi_struct) +{ + uint32_t reg = 0U; + reg = SPI_CTL0(spi_periph); + reg &= SPI_INIT_MASK; + + /* select SPI as master or slave */ + reg |= spi_struct->device_mode; + /* select SPI transfer mode */ + reg |= spi_struct->trans_mode; + /* select SPI frame size */ + reg |= spi_struct->frame_size; + /* select SPI NSS use hardware or software */ + reg |= spi_struct->nss; + /* select SPI LSB or MSB */ + reg |= spi_struct->endian; + /* select SPI polarity and phase */ + reg |= spi_struct->clock_polarity_phase; + /* select SPI prescale to adjust transmit speed */ + reg |= spi_struct->prescale; + + /* write to SPI_CTL0 register */ + SPI_CTL0(spi_periph) = (uint32_t)reg; + + SPI_I2SCTL(spi_periph) &= (uint32_t)(~SPI_I2SCTL_I2SSEL); +} + +/*! + \brief enable SPI + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_enable(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_SPIEN; +} + +/*! + \brief disable SPI + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_disable(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) &= (uint32_t)(~SPI_CTL0_SPIEN); +} + +/*! + \brief initialize I2S parameter + \param[in] spi_periph: SPIx(x=1,2) + \param[in] i2s_mode: I2S operation mode + only one parameter can be selected which is shown as below: + \arg I2S_MODE_SLAVETX: I2S slave transmit mode + \arg I2S_MODE_SLAVERX: I2S slave receive mode + \arg I2S_MODE_MASTERTX: I2S master transmit mode + \arg I2S_MODE_MASTERRX: I2S master receive mode + \param[in] i2s_standard: I2S standard + only one parameter can be selected which is shown as below: + \arg I2S_STD_PHILIPS: I2S philips standard + \arg I2S_STD_MSB: I2S MSB standard + \arg I2S_STD_LSB: I2S LSB standard + \arg I2S_STD_PCMSHORT: I2S PCM short standard + \arg I2S_STD_PCMLONG: I2S PCM long standard + \param[in] i2s_ckpl: I2S idle state clock polarity + only one parameter can be selected which is shown as below: + \arg I2S_CKPL_LOW: I2S clock polarity low level + \arg I2S_CKPL_HIGH: I2S clock polarity high level + \param[out] none + \retval none +*/ +void i2s_init(uint32_t spi_periph, uint32_t i2s_mode, uint32_t i2s_standard, uint32_t i2s_ckpl) +{ + uint32_t reg= 0U; + reg = SPI_I2SCTL(spi_periph); + reg &= I2S_INIT_MASK; + + /* enable I2S mode */ + reg |= (uint32_t)SPI_I2SCTL_I2SSEL; + /* select I2S mode */ + reg |= (uint32_t)i2s_mode; + /* select I2S standard */ + reg |= (uint32_t)i2s_standard; + /* select I2S polarity */ + reg |= (uint32_t)i2s_ckpl; + + /* write to SPI_I2SCTL register */ + SPI_I2SCTL(spi_periph) = (uint32_t)reg; +} + +/*! + \brief configure I2S prescaler + \param[in] spi_periph: SPIx(x=1,2) + \param[in] i2s_audiosample: I2S audio sample rate + only one parameter can be selected which is shown as below: + \arg I2S_AUDIOSAMPLE_8K: audio sample rate is 8KHz + \arg I2S_AUDIOSAMPLE_11K: audio sample rate is 11KHz + \arg I2S_AUDIOSAMPLE_16K: audio sample rate is 16KHz + \arg I2S_AUDIOSAMPLE_22K: audio sample rate is 22KHz + \arg I2S_AUDIOSAMPLE_32K: audio sample rate is 32KHz + \arg I2S_AUDIOSAMPLE_44K: audio sample rate is 44KHz + \arg I2S_AUDIOSAMPLE_48K: audio sample rate is 48KHz + \arg I2S_AUDIOSAMPLE_96K: audio sample rate is 96KHz + \arg I2S_AUDIOSAMPLE_192K: audio sample rate is 192KHz + \param[in] i2s_frameformat: I2S data length and channel length + only one parameter can be selected which is shown as below: + \arg I2S_FRAMEFORMAT_DT16B_CH16B: I2S data length is 16 bit and channel length is 16 bit + \arg I2S_FRAMEFORMAT_DT16B_CH32B: I2S data length is 16 bit and channel length is 32 bit + \arg I2S_FRAMEFORMAT_DT24B_CH32B: I2S data length is 24 bit and channel length is 32 bit + \arg I2S_FRAMEFORMAT_DT32B_CH32B: I2S data length is 32 bit and channel length is 32 bit + \param[in] i2s_mckout: I2S master clock output + only one parameter can be selected which is shown as below: + \arg I2S_MCKOUT_ENABLE: I2S master clock output enable + \arg I2S_MCKOUT_DISABLE: I2S master clock output disable + \param[out] none + \retval none +*/ +void i2s_psc_config(uint32_t spi_periph, uint32_t i2s_audiosample, uint32_t i2s_frameformat, uint32_t i2s_mckout) +{ + uint32_t i2sdiv = 2U, i2sof = 0U; + uint32_t clks = 0U; + uint32_t i2sclock = 0U; + +#ifdef GD32E50X_CL + uint32_t pll2mf_4 = 0U; +#endif /* GD32E50X_CL */ + + /* deinit SPI_I2SPSC register */ + SPI_I2SPSC(spi_periph) = SPI_I2SPSC_DEFAULT_VALUE; + +#ifdef GD32E50X_CL + /* get the I2S clock source */ + if(((uint32_t)spi_periph) == SPI1){ + /* I2S1 clock source selection */ + clks = I2S1_CLOCK_SEL; + }else{ + /* I2S2 clock source selection */ + clks = I2S2_CLOCK_SEL; + } + + if(0U != (RCU_CFG1 & clks)){ + /* get RCU PLL2 clock multiplication factor */ + clks = (uint32_t)((RCU_CFG1 & I2S_CLOCK_MUL_MASK) >> 12U); + + pll2mf_4 = RCU_CFG1 & RCU_CFG1_PLL2MF_4; + + if( 0U == pll2mf_4){ + if((clks > 5U) && (clks < 15U)){ + /* multiplier is between 8 and 16 */ + clks += 2U; + }else{ + if(15U == clks){ + /* multiplier is 20 */ + clks = 20U; + } + } + }else{ + if(clks < 15U){ + /* multiplier is between 18 and 32 */ + clks += 18U; + }else{ + if(15U == clks){ + /* multiplier is 40 */ + clks = 40U; + } + } + } + + /* get the PREDV1 value */ + i2sclock = (uint32_t)(((RCU_CFG1 & I2S_CLOCK_DIV_MASK) >> 4U) + 1U); + /* calculate i2sclock based on PLL2 and PREDV1 */ + i2sclock = (uint32_t)((HXTAL_VALUE / i2sclock) * clks * 2U); + }else{ + /* get system clock */ + i2sclock = rcu_clock_freq_get(CK_SYS); + } +#else + /* get system clock */ + i2sclock = rcu_clock_freq_get(CK_SYS); +#endif /* GD32E50X_CL */ + + /* config the prescaler depending on the mclk output state, the frame format and audio sample rate */ + if(I2S_MCKOUT_ENABLE == i2s_mckout){ + clks = (uint32_t)(((i2sclock / 256U) * 10U) / i2s_audiosample); + }else{ + if(I2S_FRAMEFORMAT_DT16B_CH16B == i2s_frameformat){ + clks = (uint32_t)(((i2sclock / 32U) *10U ) / i2s_audiosample); + }else{ + clks = (uint32_t)(((i2sclock / 64U) *10U ) / i2s_audiosample); + } + } + + /* remove the floating point */ + clks = (clks + 5U) / 10U; + i2sof = (clks & 0x00000001U); + i2sdiv = ((clks - i2sof) / 2U); + i2sof = (i2sof << 8U); + + /* set the default values */ + if((i2sdiv < 2U) || (i2sdiv > 255U)){ + i2sdiv = 2U; + i2sof = 0U; + } + + /* configure SPI_I2SPSC */ + SPI_I2SPSC(spi_periph) = (uint32_t)(i2sdiv | i2sof | i2s_mckout); + + /* clear SPI_I2SCTL_DTLEN and SPI_I2SCTL_CHLEN bits */ + SPI_I2SCTL(spi_periph) &= (uint32_t)(~(SPI_I2SCTL_DTLEN | SPI_I2SCTL_CHLEN)); + /* configure data frame format */ + SPI_I2SCTL(spi_periph) |= (uint32_t)i2s_frameformat; +} + +/*! + \brief enable I2S + \param[in] spi_periph: SPIx(x=1,2) + \param[out] none + \retval none +*/ +void i2s_enable(uint32_t spi_periph) +{ + SPI_I2SCTL(spi_periph) |= (uint32_t)SPI_I2SCTL_I2SEN; +} + +/*! + \brief disable I2S + \param[in] spi_periph: SPIx(x=1,2) + \param[out] none + \retval none +*/ +void i2s_disable(uint32_t spi_periph) +{ + SPI_I2SCTL(spi_periph) &= (uint32_t)(~SPI_I2SCTL_I2SEN); +} + +/*! + \brief enable SPI NSS output + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nss_output_enable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_NSSDRV; +} + +/*! + \brief disable SPI NSS output + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nss_output_disable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_NSSDRV); +} + +/*! + \brief SPI NSS pin high level in software mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nss_internal_high(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_SWNSS; +} + +/*! + \brief SPI NSS pin low level in software mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nss_internal_low(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) &= (uint32_t)(~SPI_CTL0_SWNSS); +} + +/*! + \brief enable SPI DMA send or receive + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] dma: SPI DMA mode + only one parameter can be selected which is shown as below: + \arg SPI_DMA_TRANSMIT: SPI transmit data use DMA + \arg SPI_DMA_RECEIVE: SPI receive data use DMA + \param[out] none + \retval none +*/ +void spi_dma_enable(uint32_t spi_periph, uint8_t dma) +{ + if(SPI_DMA_TRANSMIT == dma){ + SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_DMATEN; + }else{ + SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_DMAREN; + } +} + +/*! + \brief disable SPI DMA send or receive + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] dma: SPI DMA mode + only one parameter can be selected which is shown as below: + \arg SPI_DMA_TRANSMIT: SPI transmit data use DMA + \arg SPI_DMA_RECEIVE: SPI receive data use DMA + \param[out] none + \retval none +*/ +void spi_dma_disable(uint32_t spi_periph, uint8_t dma) +{ + if(SPI_DMA_TRANSMIT == dma){ + SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_DMATEN); + }else{ + SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_DMAREN); + } +} + +/*! + \brief configure SPI/I2S data frame format + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] frame_format: SPI frame size + only one parameter can be selected which is shown as below: + \arg SPI_FRAMESIZE_16BIT: SPI frame size is 16 bits + \arg SPI_FRAMESIZE_8BIT: SPI frame size is 8 bits + \param[out] none + \retval none +*/ +void spi_i2s_data_frame_format_config(uint32_t spi_periph, uint16_t frame_format) +{ + /* clear SPI_CTL0_FF16 bit */ + SPI_CTL0(spi_periph) &= (uint32_t)(~SPI_CTL0_FF16); + /* confige SPI_CTL0_FF16 bit */ + SPI_CTL0(spi_periph) |= (uint32_t)frame_format; +} + +/*! + \brief SPI transmit data + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] data: 16-bit data + \param[out] none + \retval none +*/ +void spi_i2s_data_transmit(uint32_t spi_periph, uint16_t data) +{ + SPI_DATA(spi_periph) = (uint32_t)data; +} + +/*! + \brief SPI receive data + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval 16-bit data +*/ +uint16_t spi_i2s_data_receive(uint32_t spi_periph) +{ + return ((uint16_t)SPI_DATA(spi_periph)); +} + +/*! + \brief configure SPI bidirectional transfer direction + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] transfer_direction: SPI transfer direction + only one parameter can be selected which is shown as below: + \arg SPI_BIDIRECTIONAL_TRANSMIT: SPI work in transmit-only mode + \arg SPI_BIDIRECTIONAL_RECEIVE: SPI work in receive-only mode + \retval none +*/ +void spi_bidirectional_transfer_config(uint32_t spi_periph, uint32_t transfer_direction) +{ + if(SPI_BIDIRECTIONAL_TRANSMIT == transfer_direction){ + /* set the transmit only mode */ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_BIDIRECTIONAL_TRANSMIT; + }else{ + /* set the receive only mode */ + SPI_CTL0(spi_periph) &= SPI_BIDIRECTIONAL_RECEIVE; + } +} + +/*! + \brief clear SPI/I2S format error flag status + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] flag: SPI/I2S frame format error flag + \arg SPI_FLAG_FERR: only for SPI work in TI mode + \arg I2S_FLAG_FERR: for I2S + \param[out] none + \retval none +*/ +void spi_i2s_format_error_clear(uint32_t spi_periph, uint32_t flag) +{ + SPI_STAT(spi_periph) = (uint32_t)(~flag); +} + +/*! + \brief set SPI CRC polynomial + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] crc_poly: CRC polynomial value + \param[out] none + \retval none +*/ +void spi_crc_polynomial_set(uint32_t spi_periph,uint16_t crc_poly) +{ + /* enable SPI CRC */ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_CRCEN; + + /* set SPI CRC polynomial */ + SPI_CRCPOLY(spi_periph) = (uint32_t)crc_poly; +} + +/*! + \brief get SPI CRC polynomial + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval 16-bit CRC polynomial +*/ +uint16_t spi_crc_polynomial_get(uint32_t spi_periph) +{ + return ((uint16_t)SPI_CRCPOLY(spi_periph)); +} + +/*! + \brief turn on CRC function + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_crc_on(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_CRCEN; +} + +/*! + \brief turn off CRC function + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_crc_off(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) &= (uint32_t)(~SPI_CTL0_CRCEN); +} + +/*! + \brief SPI next data is CRC value + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_crc_next(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_CRCNT; +} + +/*! + \brief get SPI CRC send value or receive value + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] crc: SPI crc value + only one parameter can be selected which is shown as below: + \arg SPI_CRC_TX: get transmit crc value + \arg SPI_CRC_RX: get receive crc value + \param[out] none + \retval 16-bit CRC value +*/ +uint16_t spi_crc_get(uint32_t spi_periph,uint8_t crc) +{ + if(SPI_CRC_TX == crc){ + return ((uint16_t)(SPI_TCRC(spi_periph))); + }else{ + return ((uint16_t)(SPI_RCRC(spi_periph))); + } +} + +/*! + \brief clear SPI CRC error flag status + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_crc_error_clear(uint32_t spi_periph) +{ + SPI_STAT(spi_periph) &= (uint32_t)(~SPI_FLAG_CRCERR); +} + +/*! + \brief enable SPI TI mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_ti_mode_enable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_TMOD; +} + +/*! + \brief disable SPI TI mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_ti_mode_disable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_TMOD); +} + +/*! + \brief enable SPI NSS pulse mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nssp_mode_enable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_NSSP; +} + +/*! + \brief disable SPI NSS pulse mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nssp_mode_disable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_NSSP); +} + +/*! + \brief configure i2s full duplex mode + \param[in] i2s_add_periph: I2Sx_ADD(x=1,2) + \param[in] i2s_mode: + \arg I2S_MODE_SLAVETX : I2S slave transmit mode + \arg I2S_MODE_SLAVERX : I2S slave receive mode + \arg I2S_MODE_MASTERTX : I2S master transmit mode + \arg I2S_MODE_MASTERRX : I2S master receive mode + \param[in] i2s_standard: + \arg I2S_STD_PHILIPS : I2S philips standard + \arg I2S_STD_MSB : I2S MSB standard + \arg I2S_STD_LSB : I2S LSB standard + \arg I2S_STD_PCMSHORT : I2S PCM short standard + \arg I2S_STD_PCMLONG : I2S PCM long standard + \param[in] i2s_ckpl: + \arg I2S_CKPL_LOW : I2S clock polarity low level + \arg I2S_CKPL_HIGH : I2S clock polarity high level + \param[in] i2s_frameformat: + \arg I2S_FRAMEFORMAT_DT16B_CH16B: I2S data length is 16 bit and channel length is 16 bit + \arg I2S_FRAMEFORMAT_DT16B_CH32B: I2S data length is 16 bit and channel length is 32 bit + \arg I2S_FRAMEFORMAT_DT24B_CH32B: I2S data length is 24 bit and channel length is 32 bit + \arg I2S_FRAMEFORMAT_DT32B_CH32B: I2S data length is 32 bit and channel length is 32 bit + \param[out] none + \retval none +*/ +void i2s_full_duplex_mode_config(uint32_t i2s_add_periph, uint32_t i2s_mode, uint32_t i2s_standard, + uint32_t i2s_ckpl, uint32_t i2s_frameformat) +{ + uint32_t reg = 0U, tmp = 0U; + + reg = I2S_ADD_I2SCTL(i2s_add_periph); + reg &= I2S_FULL_DUPLEX_MASK; + + /* get the mode of the extra I2S module I2Sx_ADD */ + if((I2S_MODE_MASTERTX == i2s_mode) || (I2S_MODE_SLAVETX == i2s_mode)){ + tmp = I2S_MODE_SLAVERX; + }else{ + tmp = I2S_MODE_SLAVETX; + } + + /* enable I2S mode */ + reg |= (uint32_t)SPI_I2SCTL_I2SSEL; + /* select I2S mode */ + reg |= (uint32_t)tmp; + /* select I2S standard */ + reg |= (uint32_t)i2s_standard; + /* select I2S polarity */ + reg |= (uint32_t)i2s_ckpl; + /* configure data frame format */ + reg |= (uint32_t)i2s_frameformat; + + /* write to SPI_I2SCTL register */ + I2S_ADD_I2SCTL(i2s_add_periph) = (uint32_t)reg; +} + +/*! + \brief enable SPI quad wire mode + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ +void spi_quad_enable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) |= (uint32_t)SPI_QCTL_QMOD; +} + +/*! + \brief disable SPI quad wire mode + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ +void spi_quad_disable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) &= (uint32_t)(~SPI_QCTL_QMOD); +} + +/*! + \brief enable SPI quad wire mode write + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ +void spi_quad_write_enable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) &= (uint32_t)(~SPI_QCTL_QRD); +} + +/*! + \brief enable SPI quad wire mode read + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ +void spi_quad_read_enable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) |= (uint32_t)SPI_QCTL_QRD; +} + +/*! + \brief enable SPI quad wire mode SPI_IO2 and SPI_IO3 pin output + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ +void spi_quad_io23_output_enable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) |= (uint32_t)SPI_QCTL_IO23_DRV; +} + + /*! + \brief disable SPI quad wire mode SPI_IO2 and SPI_IO3 pin output + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ + void spi_quad_io23_output_disable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) &= (uint32_t)(~SPI_QCTL_IO23_DRV); +} + +/*! + \brief enable SPI and I2S interrupt + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] interrupt: SPI/I2S interrupt + only one parameter can be selected which is shown as below: + \arg SPI_I2S_INT_TBE: transmit buffer empty interrupt + \arg SPI_I2S_INT_RBNE: receive buffer not empty interrupt + \arg SPI_I2S_INT_ERR: CRC error,configuration error,reception overrun error, + transmission underrun error and format error interrupt + \param[out] none + \retval none +*/ +void spi_i2s_interrupt_enable(uint32_t spi_periph, uint8_t interrupt) +{ + SPI_CTL1(spi_periph) |= (uint32_t)interrupt; +} + +/*! + \brief disable SPI and I2S interrupt + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] interrupt: SPI/I2S interrupt + only one parameter can be selected which is shown as below: + \arg SPI_I2S_INT_TBE: transmit buffer empty interrupt + \arg SPI_I2S_INT_RBNE: receive buffer not empty interrupt + \arg SPI_I2S_INT_ERR: CRC error,configuration error,reception overrun error, + transmission underrun error and format error interrupt + \param[out] none + \retval none +*/ +void spi_i2s_interrupt_disable(uint32_t spi_periph, uint8_t interrupt) +{ + SPI_CTL1(spi_periph) &= ~(uint32_t)interrupt; +} + +/*! + \brief get SPI and I2S interrupt flag status + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] interrupt: SPI/I2S interrupt flag status + only one parameter can be selected which is shown as below: + \arg SPI_I2S_INT_FLAG_TBE: transmit buffer empty interrupt flag + \arg SPI_I2S_INT_FLAG_RBNE: receive buffer not empty interrupt flag + \arg SPI_I2S_INT_FLAG_RXORERR: overrun interrupt flag + \arg SPI_INT_FLAG_CONFERR: config error interrupt flag + \arg SPI_INT_FLAG_CRCERR: CRC error interrupt flag + \arg I2S_INT_FLAG_TXURERR: underrun error interrupt flag + \arg SPI_I2S_INT_FLAG_FERR: format error interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus spi_i2s_interrupt_flag_get(uint32_t spi_periph, uint8_t interrupt) +{ + uint32_t reg1 = SPI_STAT(spi_periph); + uint32_t reg2 = SPI_CTL1(spi_periph); + + switch(interrupt){ + /* SPI/I2S transmit buffer empty interrupt */ + case SPI_I2S_INT_FLAG_TBE: + reg1 = reg1 & SPI_STAT_TBE; + reg2 = reg2 & SPI_CTL1_TBEIE; + break; + /* SPI/I2S receive buffer not empty interrupt */ + case SPI_I2S_INT_FLAG_RBNE: + reg1 = reg1 & SPI_STAT_RBNE; + reg2 = reg2 & SPI_CTL1_RBNEIE; + break; + /* SPI/I2S overrun interrupt */ + case SPI_I2S_INT_FLAG_RXORERR: + reg1 = reg1 & SPI_STAT_RXORERR; + reg2 = reg2 & SPI_CTL1_ERRIE; + break; + /* SPI config error interrupt */ + case SPI_INT_FLAG_CONFERR: + reg1 = reg1 & SPI_STAT_CONFERR; + reg2 = reg2 & SPI_CTL1_ERRIE; + break; + /* SPI CRC error interrupt */ + case SPI_INT_FLAG_CRCERR: + reg1 = reg1 & SPI_STAT_CRCERR; + reg2 = reg2 & SPI_CTL1_ERRIE; + break; + /* I2S underrun error interrupt */ + case I2S_INT_FLAG_TXURERR: + reg1 = reg1 & SPI_STAT_TXURERR; + reg2 = reg2 & SPI_CTL1_ERRIE; + break; + /* SPI/I2S format error interrupt */ + case SPI_I2S_INT_FLAG_FERR: + reg1 = reg1 & SPI_STAT_FERR; + reg2 = reg2 & SPI_CTL1_ERRIE; + break; + default : + break; + } + /*get SPI/I2S interrupt flag status */ + if(reg1 && reg2){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief get SPI and I2S flag status + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] flag: SPI/I2S flag status + only one parameter can be selected which is shown as below: + \arg SPI_FLAG_TBE: transmit buffer empty flag + \arg SPI_FLAG_RBNE: receive buffer not empty flag + \arg SPI_FLAG_TRANS: transmit on-going flag + \arg SPI_FLAG_RXORERR: receive overrun error flag + \arg SPI_FLAG_CONFERR: mode config error flag + \arg SPI_FLAG_CRCERR: CRC error flag + \arg SPI_FLAG_FERR: format error flag + \arg I2S_FLAG_TBE: transmit buffer empty flag + \arg I2S_FLAG_RBNE: receive buffer not empty flag + \arg I2S_FLAG_TRANS: transmit on-going flag + \arg I2S_FLAG_RXORERR: overrun error flag + \arg I2S_FLAG_TXURERR: underrun error flag + \arg I2S_FLAG_CH: channel side flag + \arg I2S_FLAG_FERR: format error flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus spi_i2s_flag_get(uint32_t spi_periph, uint32_t flag) +{ + if(SPI_STAT(spi_periph) & flag){ + return SET; + }else{ + return RESET; + } +} + + diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_spi.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_spi.c.base@1.5.0 new file mode 100644 index 0000000..e81dafa --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_spi.c.base@1.5.0 @@ -0,0 +1,895 @@ +/*! + \file gd32e50x_spi.c + \brief SPI driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_spi.h" + +/* SPI/I2S parameter initialization mask */ +#define SPI_INIT_MASK ((uint32_t)0x00003040U) /*!< SPI parameter initialization mask */ +#define I2S_INIT_MASK ((uint32_t)0x0000F047U) /*!< I2S parameter initialization mask */ +#define I2S_FULL_DUPLEX_MASK ((uint32_t)0x00000480U) /*!< I2S full duples mode configure parameter initialization mask */ + +/* default value */ +#define SPI_I2SPSC_DEFAULT_VALUE ((uint32_t)0x00000002U) /*!< default value of SPI_I2SPSC register */ + +/* I2S clock source selection, multiplication and division mask */ +#define I2S1_CLOCK_SEL ((uint32_t)0x00020000U) /*!< I2S1 clock source selection */ +#define I2S2_CLOCK_SEL ((uint32_t)0x00040000U) /*!< I2S2 clock source selection */ +#define I2S_CLOCK_MUL_MASK ((uint32_t)0x0000F000U) /*!< I2S clock multiplication mask */ +#define I2S_CLOCK_DIV_MASK ((uint32_t)0x000000F0U) /*!< I2S clock division mask */ + +/*! + \brief reset SPI and I2S + \param[in] spi_periph: SPIx(x=0,1,2),include I2S1_ADD and I2S2_ADD + \param[out] none + \retval none +*/ +void spi_i2s_deinit(uint32_t spi_periph) +{ + switch(spi_periph){ + case SPI0: + /* reset SPI0 */ + rcu_periph_reset_enable(RCU_SPI0RST); + rcu_periph_reset_disable(RCU_SPI0RST); + break; + case SPI1: + /* reset SPI1 , I2S1 and I2S1_ADD */ + rcu_periph_reset_enable(RCU_SPI1RST); + rcu_periph_reset_disable(RCU_SPI1RST); + break; + case SPI2: + /* reset SPI2 , I2S2 and I2S2_ADD */ + rcu_periph_reset_enable(RCU_SPI2RST); + rcu_periph_reset_disable(RCU_SPI2RST); + break; + default : + break; + } +} + +/*! + \brief initialize the parameters of SPI struct with default values + \param[in] none + \param[out] spi_parameter_struct: the initialized struct spi_parameter_struct pointer + \retval none +*/ +void spi_struct_para_init(spi_parameter_struct *spi_struct) +{ + /* configure the structure with default value */ + spi_struct->device_mode = SPI_SLAVE; + spi_struct->trans_mode = SPI_TRANSMODE_FULLDUPLEX; + spi_struct->frame_size = SPI_FRAMESIZE_8BIT; + spi_struct->nss = SPI_NSS_HARD; + spi_struct->clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; + spi_struct->prescale = SPI_PSC_2; + spi_struct->endian = SPI_ENDIAN_MSB; +} + +/*! + \brief initialize SPI parameter + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] spi_struct: SPI parameter initialization stuct members of the structure + and the member values are shown as below: + device_mode: SPI_MASTER, SPI_SLAVE + trans_mode: SPI_TRANSMODE_FULLDUPLEX, SPI_TRANSMODE_RECEIVEONLY, + SPI_TRANSMODE_BDRECEIVE, SPI_TRANSMODE_BDTRANSMIT + frame_size: SPI_FRAMESIZE_16BIT, SPI_FRAMESIZE_8BIT + nss: SPI_NSS_SOFT, SPI_NSS_HARD + endian: SPI_ENDIAN_MSB, SPI_ENDIAN_LSB + clock_polarity_phase: SPI_CK_PL_LOW_PH_1EDGE, SPI_CK_PL_HIGH_PH_1EDGE + SPI_CK_PL_LOW_PH_2EDGE, SPI_CK_PL_HIGH_PH_2EDGE + prescale: SPI_PSC_n (n=2,4,8,16,32,64,128,256) + \param[out] none + \retval none +*/ +void spi_init(uint32_t spi_periph, spi_parameter_struct* spi_struct) +{ + uint32_t reg = 0U; + reg = SPI_CTL0(spi_periph); + reg &= SPI_INIT_MASK; + + /* select SPI as master or slave */ + reg |= spi_struct->device_mode; + /* select SPI transfer mode */ + reg |= spi_struct->trans_mode; + /* select SPI frame size */ + reg |= spi_struct->frame_size; + /* select SPI NSS use hardware or software */ + reg |= spi_struct->nss; + /* select SPI LSB or MSB */ + reg |= spi_struct->endian; + /* select SPI polarity and phase */ + reg |= spi_struct->clock_polarity_phase; + /* select SPI prescale to adjust transmit speed */ + reg |= spi_struct->prescale; + + /* write to SPI_CTL0 register */ + SPI_CTL0(spi_periph) = (uint32_t)reg; + + SPI_I2SCTL(spi_periph) &= (uint32_t)(~SPI_I2SCTL_I2SSEL); +} + +/*! + \brief enable SPI + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_enable(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_SPIEN; +} + +/*! + \brief disable SPI + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_disable(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) &= (uint32_t)(~SPI_CTL0_SPIEN); +} + +/*! + \brief initialize I2S parameter + \param[in] spi_periph: SPIx(x=1,2) + \param[in] i2s_mode: I2S operation mode + only one parameter can be selected which is shown as below: + \arg I2S_MODE_SLAVETX: I2S slave transmit mode + \arg I2S_MODE_SLAVERX: I2S slave receive mode + \arg I2S_MODE_MASTERTX: I2S master transmit mode + \arg I2S_MODE_MASTERRX: I2S master receive mode + \param[in] i2s_standard: I2S standard + only one parameter can be selected which is shown as below: + \arg I2S_STD_PHILIPS: I2S philips standard + \arg I2S_STD_MSB: I2S MSB standard + \arg I2S_STD_LSB: I2S LSB standard + \arg I2S_STD_PCMSHORT: I2S PCM short standard + \arg I2S_STD_PCMLONG: I2S PCM long standard + \param[in] i2s_ckpl: I2S idle state clock polarity + only one parameter can be selected which is shown as below: + \arg I2S_CKPL_LOW: I2S clock polarity low level + \arg I2S_CKPL_HIGH: I2S clock polarity high level + \param[out] none + \retval none +*/ +void i2s_init(uint32_t spi_periph, uint32_t i2s_mode, uint32_t i2s_standard, uint32_t i2s_ckpl) +{ + uint32_t reg= 0U; + reg = SPI_I2SCTL(spi_periph); + reg &= I2S_INIT_MASK; + + /* enable I2S mode */ + reg |= (uint32_t)SPI_I2SCTL_I2SSEL; + /* select I2S mode */ + reg |= (uint32_t)i2s_mode; + /* select I2S standard */ + reg |= (uint32_t)i2s_standard; + /* select I2S polarity */ + reg |= (uint32_t)i2s_ckpl; + + /* write to SPI_I2SCTL register */ + SPI_I2SCTL(spi_periph) = (uint32_t)reg; +} + +/*! + \brief configure I2S prescaler + \param[in] spi_periph: SPIx(x=1,2) + \param[in] i2s_audiosample: I2S audio sample rate + only one parameter can be selected which is shown as below: + \arg I2S_AUDIOSAMPLE_8K: audio sample rate is 8KHz + \arg I2S_AUDIOSAMPLE_11K: audio sample rate is 11KHz + \arg I2S_AUDIOSAMPLE_16K: audio sample rate is 16KHz + \arg I2S_AUDIOSAMPLE_22K: audio sample rate is 22KHz + \arg I2S_AUDIOSAMPLE_32K: audio sample rate is 32KHz + \arg I2S_AUDIOSAMPLE_44K: audio sample rate is 44KHz + \arg I2S_AUDIOSAMPLE_48K: audio sample rate is 48KHz + \arg I2S_AUDIOSAMPLE_96K: audio sample rate is 96KHz + \arg I2S_AUDIOSAMPLE_192K: audio sample rate is 192KHz + \param[in] i2s_frameformat: I2S data length and channel length + only one parameter can be selected which is shown as below: + \arg I2S_FRAMEFORMAT_DT16B_CH16B: I2S data length is 16 bit and channel length is 16 bit + \arg I2S_FRAMEFORMAT_DT16B_CH32B: I2S data length is 16 bit and channel length is 32 bit + \arg I2S_FRAMEFORMAT_DT24B_CH32B: I2S data length is 24 bit and channel length is 32 bit + \arg I2S_FRAMEFORMAT_DT32B_CH32B: I2S data length is 32 bit and channel length is 32 bit + \param[in] i2s_mckout: I2S master clock output + only one parameter can be selected which is shown as below: + \arg I2S_MCKOUT_ENABLE: I2S master clock output enable + \arg I2S_MCKOUT_DISABLE: I2S master clock output disable + \param[out] none + \retval none +*/ +void i2s_psc_config(uint32_t spi_periph, uint32_t i2s_audiosample, uint32_t i2s_frameformat, uint32_t i2s_mckout) +{ + uint32_t i2sdiv = 2U, i2sof = 0U; + uint32_t clks = 0U; + uint32_t i2sclock = 0U; + +#ifdef GD32E50X_CL + uint32_t pll2mf_4 = 0U; +#endif /* GD32E50X_CL */ + + /* deinit SPI_I2SPSC register */ + SPI_I2SPSC(spi_periph) = SPI_I2SPSC_DEFAULT_VALUE; + +#ifdef GD32E50X_CL + /* get the I2S clock source */ + if(((uint32_t)spi_periph) == SPI1){ + /* I2S1 clock source selection */ + clks = I2S1_CLOCK_SEL; + }else{ + /* I2S2 clock source selection */ + clks = I2S2_CLOCK_SEL; + } + + if(0U != (RCU_CFG1 & clks)){ + /* get RCU PLL2 clock multiplication factor */ + clks = (uint32_t)((RCU_CFG1 & I2S_CLOCK_MUL_MASK) >> 12U); + + pll2mf_4 = RCU_CFG1 & RCU_CFG1_PLL2MF_4; + + if( 0U == pll2mf_4){ + if((clks > 5U) && (clks < 15U)){ + /* multiplier is between 8 and 16 */ + clks += 2U; + }else{ + if(15U == clks){ + /* multiplier is 20 */ + clks = 20U; + } + } + }else{ + if(clks < 15U){ + /* multiplier is between 18 and 32 */ + clks += 18U; + }else{ + if(15U == clks){ + /* multiplier is 40 */ + clks = 40U; + } + } + } + + /* get the PREDV1 value */ + i2sclock = (uint32_t)(((RCU_CFG1 & I2S_CLOCK_DIV_MASK) >> 4U) + 1U); + /* calculate i2sclock based on PLL2 and PREDV1 */ + i2sclock = (uint32_t)((HXTAL_VALUE / i2sclock) * clks * 2U); + }else{ + /* get system clock */ + i2sclock = rcu_clock_freq_get(CK_SYS); + } +#else + /* get system clock */ + i2sclock = rcu_clock_freq_get(CK_SYS); +#endif /* GD32E50X_CL */ + + /* config the prescaler depending on the mclk output state, the frame format and audio sample rate */ + if(I2S_MCKOUT_ENABLE == i2s_mckout){ + clks = (uint32_t)(((i2sclock / 256U) * 10U) / i2s_audiosample); + }else{ + if(I2S_FRAMEFORMAT_DT16B_CH16B == i2s_frameformat){ + clks = (uint32_t)(((i2sclock / 32U) *10U ) / i2s_audiosample); + }else{ + clks = (uint32_t)(((i2sclock / 64U) *10U ) / i2s_audiosample); + } + } + + /* remove the floating point */ + clks = (clks + 5U) / 10U; + i2sof = (clks & 0x00000001U); + i2sdiv = ((clks - i2sof) / 2U); + i2sof = (i2sof << 8U); + + /* set the default values */ + if((i2sdiv < 2U) || (i2sdiv > 255U)){ + i2sdiv = 2U; + i2sof = 0U; + } + + /* configure SPI_I2SPSC */ + SPI_I2SPSC(spi_periph) = (uint32_t)(i2sdiv | i2sof | i2s_mckout); + + /* clear SPI_I2SCTL_DTLEN and SPI_I2SCTL_CHLEN bits */ + SPI_I2SCTL(spi_periph) &= (uint32_t)(~(SPI_I2SCTL_DTLEN | SPI_I2SCTL_CHLEN)); + /* configure data frame format */ + SPI_I2SCTL(spi_periph) |= (uint32_t)i2s_frameformat; +} + +/*! + \brief enable I2S + \param[in] spi_periph: SPIx(x=1,2) + \param[out] none + \retval none +*/ +void i2s_enable(uint32_t spi_periph) +{ + SPI_I2SCTL(spi_periph) |= (uint32_t)SPI_I2SCTL_I2SEN; +} + +/*! + \brief disable I2S + \param[in] spi_periph: SPIx(x=1,2) + \param[out] none + \retval none +*/ +void i2s_disable(uint32_t spi_periph) +{ + SPI_I2SCTL(spi_periph) &= (uint32_t)(~SPI_I2SCTL_I2SEN); +} + +/*! + \brief enable SPI NSS output + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nss_output_enable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_NSSDRV; +} + +/*! + \brief disable SPI NSS output + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nss_output_disable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_NSSDRV); +} + +/*! + \brief SPI NSS pin high level in software mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nss_internal_high(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_SWNSS; +} + +/*! + \brief SPI NSS pin low level in software mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nss_internal_low(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) &= (uint32_t)(~SPI_CTL0_SWNSS); +} + +/*! + \brief enable SPI DMA send or receive + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] dma: SPI DMA mode + only one parameter can be selected which is shown as below: + \arg SPI_DMA_TRANSMIT: SPI transmit data use DMA + \arg SPI_DMA_RECEIVE: SPI receive data use DMA + \param[out] none + \retval none +*/ +void spi_dma_enable(uint32_t spi_periph, uint8_t dma) +{ + if(SPI_DMA_TRANSMIT == dma){ + SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_DMATEN; + }else{ + SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_DMAREN; + } +} + +/*! + \brief disable SPI DMA send or receive + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] dma: SPI DMA mode + only one parameter can be selected which is shown as below: + \arg SPI_DMA_TRANSMIT: SPI transmit data use DMA + \arg SPI_DMA_RECEIVE: SPI receive data use DMA + \param[out] none + \retval none +*/ +void spi_dma_disable(uint32_t spi_periph, uint8_t dma) +{ + if(SPI_DMA_TRANSMIT == dma){ + SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_DMATEN); + }else{ + SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_DMAREN); + } +} + +/*! + \brief configure SPI/I2S data frame format + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] frame_format: SPI frame size + only one parameter can be selected which is shown as below: + \arg SPI_FRAMESIZE_16BIT: SPI frame size is 16 bits + \arg SPI_FRAMESIZE_8BIT: SPI frame size is 8 bits + \param[out] none + \retval none +*/ +void spi_i2s_data_frame_format_config(uint32_t spi_periph, uint16_t frame_format) +{ + /* clear SPI_CTL0_FF16 bit */ + SPI_CTL0(spi_periph) &= (uint32_t)(~SPI_CTL0_FF16); + /* confige SPI_CTL0_FF16 bit */ + SPI_CTL0(spi_periph) |= (uint32_t)frame_format; +} + +/*! + \brief SPI transmit data + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] data: 16-bit data + \param[out] none + \retval none +*/ +void spi_i2s_data_transmit(uint32_t spi_periph, uint16_t data) +{ + SPI_DATA(spi_periph) = (uint32_t)data; +} + +/*! + \brief SPI receive data + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval 16-bit data +*/ +uint16_t spi_i2s_data_receive(uint32_t spi_periph) +{ + return ((uint16_t)SPI_DATA(spi_periph)); +} + +/*! + \brief configure SPI bidirectional transfer direction + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] transfer_direction: SPI transfer direction + only one parameter can be selected which is shown as below: + \arg SPI_BIDIRECTIONAL_TRANSMIT: SPI work in transmit-only mode + \arg SPI_BIDIRECTIONAL_RECEIVE: SPI work in receive-only mode + \retval none +*/ +void spi_bidirectional_transfer_config(uint32_t spi_periph, uint32_t transfer_direction) +{ + if(SPI_BIDIRECTIONAL_TRANSMIT == transfer_direction){ + /* set the transmit only mode */ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_BIDIRECTIONAL_TRANSMIT; + }else{ + /* set the receive only mode */ + SPI_CTL0(spi_periph) &= SPI_BIDIRECTIONAL_RECEIVE; + } +} + +/*! + \brief clear SPI/I2S format error flag status + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] flag: SPI/I2S frame format error flag + \arg SPI_FLAG_FERR: only for SPI work in TI mode + \arg I2S_FLAG_FERR: for I2S + \param[out] none + \retval none +*/ +void spi_i2s_format_error_clear(uint32_t spi_periph, uint32_t flag) +{ + SPI_STAT(spi_periph) = (uint32_t)(~flag); +} + +/*! + \brief set SPI CRC polynomial + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] crc_poly: CRC polynomial value + \param[out] none + \retval none +*/ +void spi_crc_polynomial_set(uint32_t spi_periph,uint16_t crc_poly) +{ + /* enable SPI CRC */ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_CRCEN; + + /* set SPI CRC polynomial */ + SPI_CRCPOLY(spi_periph) = (uint32_t)crc_poly; +} + +/*! + \brief get SPI CRC polynomial + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval 16-bit CRC polynomial +*/ +uint16_t spi_crc_polynomial_get(uint32_t spi_periph) +{ + return ((uint16_t)SPI_CRCPOLY(spi_periph)); +} + +/*! + \brief turn on CRC function + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_crc_on(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_CRCEN; +} + +/*! + \brief turn off CRC function + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_crc_off(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) &= (uint32_t)(~SPI_CTL0_CRCEN); +} + +/*! + \brief SPI next data is CRC value + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_crc_next(uint32_t spi_periph) +{ + SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_CRCNT; +} + +/*! + \brief get SPI CRC send value or receive value + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] crc: SPI crc value + only one parameter can be selected which is shown as below: + \arg SPI_CRC_TX: get transmit crc value + \arg SPI_CRC_RX: get receive crc value + \param[out] none + \retval 16-bit CRC value +*/ +uint16_t spi_crc_get(uint32_t spi_periph,uint8_t crc) +{ + if(SPI_CRC_TX == crc){ + return ((uint16_t)(SPI_TCRC(spi_periph))); + }else{ + return ((uint16_t)(SPI_RCRC(spi_periph))); + } +} + +/*! + \brief clear SPI CRC error flag status + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_crc_error_clear(uint32_t spi_periph) +{ + SPI_STAT(spi_periph) &= (uint32_t)(~SPI_FLAG_CRCERR); +} + +/*! + \brief enable SPI TI mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_ti_mode_enable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_TMOD; +} + +/*! + \brief disable SPI TI mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_ti_mode_disable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_TMOD); +} + +/*! + \brief enable SPI NSS pulse mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nssp_mode_enable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_NSSP; +} + +/*! + \brief disable SPI NSS pulse mode + \param[in] spi_periph: SPIx(x=0,1,2) + \param[out] none + \retval none +*/ +void spi_nssp_mode_disable(uint32_t spi_periph) +{ + SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_NSSP); +} + +/*! + \brief configure i2s full duplex mode + \param[in] i2s_add_periph: I2Sx_ADD(x=1,2) + \param[in] i2s_mode: + \arg I2S_MODE_SLAVETX : I2S slave transmit mode + \arg I2S_MODE_SLAVERX : I2S slave receive mode + \arg I2S_MODE_MASTERTX : I2S master transmit mode + \arg I2S_MODE_MASTERRX : I2S master receive mode + \param[in] i2s_standard: + \arg I2S_STD_PHILIPS : I2S philips standard + \arg I2S_STD_MSB : I2S MSB standard + \arg I2S_STD_LSB : I2S LSB standard + \arg I2S_STD_PCMSHORT : I2S PCM short standard + \arg I2S_STD_PCMLONG : I2S PCM long standard + \param[in] i2s_ckpl: + \arg I2S_CKPL_LOW : I2S clock polarity low level + \arg I2S_CKPL_HIGH : I2S clock polarity high level + \param[in] i2s_frameformat: + \arg I2S_FRAMEFORMAT_DT16B_CH16B: I2S data length is 16 bit and channel length is 16 bit + \arg I2S_FRAMEFORMAT_DT16B_CH32B: I2S data length is 16 bit and channel length is 32 bit + \arg I2S_FRAMEFORMAT_DT24B_CH32B: I2S data length is 24 bit and channel length is 32 bit + \arg I2S_FRAMEFORMAT_DT32B_CH32B: I2S data length is 32 bit and channel length is 32 bit + \param[out] none + \retval none +*/ +void i2s_full_duplex_mode_config(uint32_t i2s_add_periph, uint32_t i2s_mode, uint32_t i2s_standard, + uint32_t i2s_ckpl, uint32_t i2s_frameformat) +{ + uint32_t reg = 0U, tmp = 0U; + + reg = I2S_ADD_I2SCTL(i2s_add_periph); + reg &= I2S_FULL_DUPLEX_MASK; + + /* get the mode of the extra I2S module I2Sx_ADD */ + if((I2S_MODE_MASTERTX == i2s_mode) || (I2S_MODE_SLAVETX == i2s_mode)){ + tmp = I2S_MODE_SLAVERX; + }else{ + tmp = I2S_MODE_SLAVETX; + } + + /* enable I2S mode */ + reg |= (uint32_t)SPI_I2SCTL_I2SSEL; + /* select I2S mode */ + reg |= (uint32_t)tmp; + /* select I2S standard */ + reg |= (uint32_t)i2s_standard; + /* select I2S polarity */ + reg |= (uint32_t)i2s_ckpl; + /* configure data frame format */ + reg |= (uint32_t)i2s_frameformat; + + /* write to SPI_I2SCTL register */ + I2S_ADD_I2SCTL(i2s_add_periph) = (uint32_t)reg; +} + +/*! + \brief enable SPI quad wire mode + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ +void spi_quad_enable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) |= (uint32_t)SPI_QCTL_QMOD; +} + +/*! + \brief disable SPI quad wire mode + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ +void spi_quad_disable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) &= (uint32_t)(~SPI_QCTL_QMOD); +} + +/*! + \brief enable SPI quad wire mode write + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ +void spi_quad_write_enable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) &= (uint32_t)(~SPI_QCTL_QRD); +} + +/*! + \brief enable SPI quad wire mode read + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ +void spi_quad_read_enable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) |= (uint32_t)SPI_QCTL_QRD; +} + +/*! + \brief enable SPI quad wire mode SPI_IO2 and SPI_IO3 pin output + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ +void spi_quad_io23_output_enable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) |= (uint32_t)SPI_QCTL_IO23_DRV; +} + + /*! + \brief disable SPI quad wire mode SPI_IO2 and SPI_IO3 pin output + \param[in] spi_periph: SPIx(only x=0) + \param[out] none + \retval none +*/ + void spi_quad_io23_output_disable(uint32_t spi_periph) +{ + SPI_QCTL(spi_periph) &= (uint32_t)(~SPI_QCTL_IO23_DRV); +} + +/*! + \brief enable SPI and I2S interrupt + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] interrupt: SPI/I2S interrupt + only one parameter can be selected which is shown as below: + \arg SPI_I2S_INT_TBE: transmit buffer empty interrupt + \arg SPI_I2S_INT_RBNE: receive buffer not empty interrupt + \arg SPI_I2S_INT_ERR: CRC error,configuration error,reception overrun error, + transmission underrun error and format error interrupt + \param[out] none + \retval none +*/ +void spi_i2s_interrupt_enable(uint32_t spi_periph, uint8_t interrupt) +{ + SPI_CTL1(spi_periph) |= (uint32_t)interrupt; +} + +/*! + \brief disable SPI and I2S interrupt + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] interrupt: SPI/I2S interrupt + only one parameter can be selected which is shown as below: + \arg SPI_I2S_INT_TBE: transmit buffer empty interrupt + \arg SPI_I2S_INT_RBNE: receive buffer not empty interrupt + \arg SPI_I2S_INT_ERR: CRC error,configuration error,reception overrun error, + transmission underrun error and format error interrupt + \param[out] none + \retval none +*/ +void spi_i2s_interrupt_disable(uint32_t spi_periph, uint8_t interrupt) +{ + SPI_CTL1(spi_periph) &= ~(uint32_t)interrupt; +} + +/*! + \brief get SPI and I2S interrupt flag status + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] interrupt: SPI/I2S interrupt flag status + only one parameter can be selected which is shown as below: + \arg SPI_I2S_INT_FLAG_TBE: transmit buffer empty interrupt flag + \arg SPI_I2S_INT_FLAG_RBNE: receive buffer not empty interrupt flag + \arg SPI_I2S_INT_FLAG_RXORERR: overrun interrupt flag + \arg SPI_INT_FLAG_CONFERR: config error interrupt flag + \arg SPI_INT_FLAG_CRCERR: CRC error interrupt flag + \arg I2S_INT_FLAG_TXURERR: underrun error interrupt flag + \arg SPI_I2S_INT_FLAG_FERR: format error interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus spi_i2s_interrupt_flag_get(uint32_t spi_periph, uint8_t interrupt) +{ + uint32_t reg1 = SPI_STAT(spi_periph); + uint32_t reg2 = SPI_CTL1(spi_periph); + + switch(interrupt){ + /* SPI/I2S transmit buffer empty interrupt */ + case SPI_I2S_INT_FLAG_TBE: + reg1 = reg1 & SPI_STAT_TBE; + reg2 = reg2 & SPI_CTL1_TBEIE; + break; + /* SPI/I2S receive buffer not empty interrupt */ + case SPI_I2S_INT_FLAG_RBNE: + reg1 = reg1 & SPI_STAT_RBNE; + reg2 = reg2 & SPI_CTL1_RBNEIE; + break; + /* SPI/I2S overrun interrupt */ + case SPI_I2S_INT_FLAG_RXORERR: + reg1 = reg1 & SPI_STAT_RXORERR; + reg2 = reg2 & SPI_CTL1_ERRIE; + break; + /* SPI config error interrupt */ + case SPI_INT_FLAG_CONFERR: + reg1 = reg1 & SPI_STAT_CONFERR; + reg2 = reg2 & SPI_CTL1_ERRIE; + break; + /* SPI CRC error interrupt */ + case SPI_INT_FLAG_CRCERR: + reg1 = reg1 & SPI_STAT_CRCERR; + reg2 = reg2 & SPI_CTL1_ERRIE; + break; + /* I2S underrun error interrupt */ + case I2S_INT_FLAG_TXURERR: + reg1 = reg1 & SPI_STAT_TXURERR; + reg2 = reg2 & SPI_CTL1_ERRIE; + break; + /* SPI/I2S format error interrupt */ + case SPI_I2S_INT_FLAG_FERR: + reg1 = reg1 & SPI_STAT_FERR; + reg2 = reg2 & SPI_CTL1_ERRIE; + break; + default : + break; + } + /*get SPI/I2S interrupt flag status */ + if(reg1 && reg2){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief get SPI and I2S flag status + \param[in] spi_periph: SPIx(x=0,1,2) + \param[in] flag: SPI/I2S flag status + only one parameter can be selected which is shown as below: + \arg SPI_FLAG_TBE: transmit buffer empty flag + \arg SPI_FLAG_RBNE: receive buffer not empty flag + \arg SPI_FLAG_TRANS: transmit on-going flag + \arg SPI_FLAG_RXORERR: receive overrun error flag + \arg SPI_FLAG_CONFERR: mode config error flag + \arg SPI_FLAG_CRCERR: CRC error flag + \arg SPI_FLAG_FERR: format error flag + \arg I2S_FLAG_TBE: transmit buffer empty flag + \arg I2S_FLAG_RBNE: receive buffer not empty flag + \arg I2S_FLAG_TRANS: transmit on-going flag + \arg I2S_FLAG_RXORERR: overrun error flag + \arg I2S_FLAG_TXURERR: underrun error flag + \arg I2S_FLAG_CH: channel side flag + \arg I2S_FLAG_FERR: format error flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus spi_i2s_flag_get(uint32_t spi_periph, uint32_t flag) +{ + if(SPI_STAT(spi_periph) & flag){ + return SET; + }else{ + return RESET; + } +} + + diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_timer.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_timer.c new file mode 100644 index 0000000..9a53c16 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_timer.c @@ -0,0 +1,2039 @@ +/*! + \file gd32e50x_timer.c + \brief TIMER driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_timer.h" + +/* TIMER init parameter mask */ +#define ALIGNEDMODE_MASK ((uint32_t)0x00000060U) /*!< TIMER init parameter aligne dmode mask */ +#define COUNTERDIRECTION_MASK ((uint32_t)0x00000010U) /*!< TIMER init parameter counter direction mask */ +#define CLOCKDIVISION_MASK ((uint32_t)0x00000300U) /*!< TIMER init parameter clock division value mask */ + +/*! + \brief deinit a TIMER + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_deinit(uint32_t timer_periph) +{ + switch(timer_periph){ + case TIMER0: + /* reset TIMER0 */ + rcu_periph_reset_enable(RCU_TIMER0RST); + rcu_periph_reset_disable(RCU_TIMER0RST); + break; + case TIMER1: + /* reset TIMER1 */ + rcu_periph_reset_enable(RCU_TIMER1RST); + rcu_periph_reset_disable(RCU_TIMER1RST); + break; + case TIMER2: + /* reset TIMER2 */ + rcu_periph_reset_enable(RCU_TIMER2RST); + rcu_periph_reset_disable(RCU_TIMER2RST); + break; + case TIMER3: + /* reset TIMER3 */ + rcu_periph_reset_enable(RCU_TIMER3RST); + rcu_periph_reset_disable(RCU_TIMER3RST); + break; + case TIMER4: + /* reset TIMER4 */ + rcu_periph_reset_enable(RCU_TIMER4RST); + rcu_periph_reset_disable(RCU_TIMER4RST); + break; + case TIMER5: + /* reset TIMER5 */ + rcu_periph_reset_enable(RCU_TIMER5RST); + rcu_periph_reset_disable(RCU_TIMER5RST); + break; + case TIMER6: + /* reset TIMER6 */ + rcu_periph_reset_enable(RCU_TIMER6RST); + rcu_periph_reset_disable(RCU_TIMER6RST); + break; + case TIMER7: + /* reset TIMER7 */ + rcu_periph_reset_enable(RCU_TIMER7RST); + rcu_periph_reset_disable(RCU_TIMER7RST); + break; +#ifndef GD32EPRT + case TIMER8: + /* reset TIMER8 */ + rcu_periph_reset_enable(RCU_TIMER8RST); + rcu_periph_reset_disable(RCU_TIMER8RST); + break; + case TIMER9: + /* reset TIMER9 */ + rcu_periph_reset_enable(RCU_TIMER9RST); + rcu_periph_reset_disable(RCU_TIMER9RST); + break; + case TIMER10: + /* reset TIMER10 */ + rcu_periph_reset_enable(RCU_TIMER10RST); + rcu_periph_reset_disable(RCU_TIMER10RST); + break; + case TIMER11: + /* reset TIMER11 */ + rcu_periph_reset_enable(RCU_TIMER11RST); + rcu_periph_reset_disable(RCU_TIMER11RST); + break; + case TIMER12: + /* reset TIMER12 */ + rcu_periph_reset_enable(RCU_TIMER12RST); + rcu_periph_reset_disable(RCU_TIMER12RST); + break; + case TIMER13: + /* reset TIMER13 */ + rcu_periph_reset_enable(RCU_TIMER13RST); + rcu_periph_reset_disable(RCU_TIMER13RST); + break; +#endif /* GD32EPRT */ + default: + break; + } +} + +/*! + \brief initialize TIMER init parameter struct with a default value + \param[in] initpara: init parameter struct + \param[out] none + \retval none +*/ +void timer_struct_para_init(timer_parameter_struct* initpara) +{ + /* initialize the init parameter struct member with the default value */ + initpara->prescaler = 0U; + initpara->alignedmode = TIMER_COUNTER_EDGE; + initpara->counterdirection = TIMER_COUNTER_UP; + initpara->period = 65535U; + initpara->clockdivision = TIMER_CKDIV_DIV1; + initpara->repetitioncounter = 0U; +} + +/*! + \brief initialize TIMER counter + \param[in] timer_periph: TIMERx(x=0..13) + \param[in] initpara: init parameter struct + prescaler: prescaler value of the counter clock, 0~65535 + alignedmode: TIMER_COUNTER_EDGE, TIMER_COUNTER_CENTER_DOWN, TIMER_COUNTER_CENTER_UP, TIMER_COUNTER_CENTER_BOTH + counterdirection: TIMER_COUNTER_UP, TIMER_COUNTER_DOWN + period: counter auto reload value, 0~65535 + clockdivision: TIMER_CKDIV_DIV1, TIMER_CKDIV_DIV2, TIMER_CKDIV_DIV4 + repetitioncounter: counter repetition value, 0~255 + \param[out] none + \retval none +*/ +void timer_init(uint32_t timer_periph, timer_parameter_struct* initpara) +{ + /* configure the counter prescaler value */ + TIMER_PSC(timer_periph) = (uint16_t)initpara->prescaler; + + /* configure the counter direction and aligned mode */ + if((TIMER0 == timer_periph) || (TIMER1 == timer_periph) || (TIMER2 == timer_periph) + || (TIMER3 == timer_periph) || (TIMER4 == timer_periph) || (TIMER7 == timer_periph)){ + TIMER_CTL0(timer_periph) &= (~(uint32_t)(TIMER_CTL0_DIR | TIMER_CTL0_CAM)); + TIMER_CTL0(timer_periph) |= (uint32_t)(initpara->alignedmode & ALIGNEDMODE_MASK); + TIMER_CTL0(timer_periph) |= (uint32_t)(initpara->counterdirection & COUNTERDIRECTION_MASK); + } + + /* configure the autoreload value */ + TIMER_CAR(timer_periph) = (uint32_t)initpara->period; + + if((TIMER5 != timer_periph) && (TIMER6 != timer_periph)){ + /* reset the CKDIV bit */ + TIMER_CTL0(timer_periph) &= (~(uint32_t)TIMER_CTL0_CKDIV); + TIMER_CTL0(timer_periph) |= (uint32_t)(initpara->clockdivision & CLOCKDIVISION_MASK); + } + + if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){ + /* configure the repetition counter value */ + TIMER_CREP(timer_periph) = (uint32_t)initpara->repetitioncounter; + } + + /* generate an update event */ + TIMER_SWEVG(timer_periph) |= (uint32_t)TIMER_SWEVG_UPG; +} + +/*! + \brief enable a TIMER + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_enable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) |= (uint32_t)TIMER_CTL0_CEN; +} + +/*! + \brief disable a TIMER + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_disable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_CEN; +} + +/*! + \brief enable the auto reload shadow function + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_auto_reload_shadow_enable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) |= (uint32_t)TIMER_CTL0_ARSE; +} + +/*! + \brief disable the auto reload shadow function + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_auto_reload_shadow_disable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_ARSE; +} + +/*! + \brief enable the update event + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_update_event_enable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_UPDIS; +} + +/*! + \brief disable the update event + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_update_event_disable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) |= (uint32_t) TIMER_CTL0_UPDIS; +} + +/*! + \brief set TIMER counter alignment mode + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] aligned: + only one parameter can be selected which is shown as below: + \arg TIMER_COUNTER_EDGE: edge-aligned mode + \arg TIMER_COUNTER_CENTER_DOWN: center-aligned and counting down assert mode + \arg TIMER_COUNTER_CENTER_UP: center-aligned and counting up assert mode + \arg TIMER_COUNTER_CENTER_BOTH: center-aligned and counting up/down assert mode + \param[out] none + \retval none +*/ +void timer_counter_alignment(uint32_t timer_periph, uint16_t aligned) +{ + TIMER_CTL0(timer_periph) &= (uint32_t)(~TIMER_CTL0_CAM); + TIMER_CTL0(timer_periph) |= (uint32_t)aligned; +} + +/*! + \brief set TIMER counter up direction + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_counter_up_direction(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_DIR; +} + +/*! + \brief set TIMER counter down direction + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_counter_down_direction(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) |= (uint32_t)TIMER_CTL0_DIR; +} + +/*! + \brief configure TIMER prescaler + \param[in] timer_periph: TIMERx(x=0..13) + \param[in] prescaler: prescaler value,0~65535 + \param[in] pscreload: prescaler reload mode + only one parameter can be selected which is shown as below: + \arg TIMER_PSC_RELOAD_NOW: the prescaler is loaded right now + \arg TIMER_PSC_RELOAD_UPDATE: the prescaler is loaded at the next update event + \param[out] none + \retval none +*/ +void timer_prescaler_config(uint32_t timer_periph, uint16_t prescaler, uint32_t pscreload) +{ + TIMER_PSC(timer_periph) = (uint32_t)prescaler; + + if(TIMER_PSC_RELOAD_NOW == pscreload){ + TIMER_SWEVG(timer_periph) |= (uint32_t)TIMER_SWEVG_UPG; + } +} + +/*! + \brief configure TIMER repetition register value + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] repetition: the counter repetition value, 0~255 + \param[out] none + \retval none +*/ +void timer_repetition_value_config(uint32_t timer_periph, uint16_t repetition) +{ + TIMER_CREP(timer_periph) = (uint32_t)repetition; +} + +/*! + \brief configure TIMER autoreload register value + \param[in] timer_periph: TIMERx(x=0..13) + \param[in] autoreload: the counter auto-reload value,0~65535 + \param[out] none + \retval none +*/ +void timer_autoreload_value_config(uint32_t timer_periph, uint32_t autoreload) +{ + TIMER_CAR(timer_periph) = (uint32_t)autoreload; +} + +/*! + \brief configure TIMER counter register value + \param[in] timer_periph: TIMERx(x=0..13) + \param[in] counter: the counter value,0~65535 + \param[out] none + \retval none +*/ +void timer_counter_value_config(uint32_t timer_periph, uint32_t counter) +{ + TIMER_CNT(timer_periph) = (uint32_t)counter; +} + +/*! + \brief read TIMER counter value + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval counter value +*/ +uint32_t timer_counter_read(uint32_t timer_periph) +{ + uint32_t count_value = 0U; + count_value = TIMER_CNT(timer_periph); + return (count_value); +} + +/*! + \brief read TIMER prescaler value + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval prescaler register value +*/ +uint16_t timer_prescaler_read(uint32_t timer_periph) +{ + uint16_t prescaler_value = 0U; + prescaler_value = (uint16_t)(TIMER_PSC(timer_periph)); + return (prescaler_value); +} + +/*! + \brief configure TIMER single pulse mode + \param[in] timer_periph: TIMERx(x=0..8,11) + \param[in] spmode: + only one parameter can be selected which is shown as below: + \arg TIMER_SP_MODE_SINGLE: single pulse mode + \arg TIMER_SP_MODE_REPETITIVE: repetitive pulse mode + \param[out] none + \retval none +*/ +void timer_single_pulse_mode_config(uint32_t timer_periph, uint32_t spmode) +{ + if(TIMER_SP_MODE_SINGLE == spmode){ + TIMER_CTL0(timer_periph) |= (uint32_t)TIMER_CTL0_SPM; + }else if(TIMER_SP_MODE_REPETITIVE == spmode){ + TIMER_CTL0(timer_periph) &= ~((uint32_t)TIMER_CTL0_SPM); + }else{ + /* illegal parameters */ + } +} + +/*! + \brief configure TIMER update source + \param[in] timer_periph: TIMERx(x=0..13) + \param[in] update: + only one parameter can be selected which is shown as below: + \arg TIMER_UPDATE_SRC_GLOBAL: update generate by setting of UPG bit or the counter overflow/underflow,or the slave mode controller trigger + \arg TIMER_UPDATE_SRC_REGULAR: update generate only by counter overflow/underflow + \param[out] none + \retval none +*/ +void timer_update_source_config(uint32_t timer_periph, uint32_t update) +{ + if(TIMER_UPDATE_SRC_REGULAR == update){ + TIMER_CTL0(timer_periph) |= (uint32_t)TIMER_CTL0_UPS; + }else if(TIMER_UPDATE_SRC_GLOBAL == update){ + TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_UPS; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief enable the TIMER DMA + \param[in] timer_periph: please refer to the following parameters + \param[in] dma: specify which DMA to enable + only one parameter can be selected which is shown as below: + \arg TIMER_DMA_UPD: update DMA enable,TIMERx(x=0..7) + \arg TIMER_DMA_CH0D: channel 0 DMA enable,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH1D: channel 1 DMA enable,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH2D: channel 2 DMA enable,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH3D: channel 3 DMA enable,TIMERx(x=0..4,7) + \arg TIMER_DMA_CMTD: commutation DMA request enable,TIMERx(x=0,7) + \arg TIMER_DMA_TRGD: trigger DMA enable,TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_dma_enable(uint32_t timer_periph, uint16_t dma) +{ + TIMER_DMAINTEN(timer_periph) |= (uint32_t) dma; +} + +/*! + \brief disable the TIMER DMA + \param[in] timer_periph: please refer to the following parameters + \param[in] dma: specify which DMA to enable + one or more parameters can be selected which are shown as below: + \arg TIMER_DMA_UPD: update DMA ,TIMERx(x=0..7) + \arg TIMER_DMA_CH0D: channel 0 DMA request,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH1D: channel 1 DMA request,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH2D: channel 2 DMA request,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH3D: channel 3 DMA request,TIMERx(x=0..4,7) + \arg TIMER_DMA_CMTD: commutation DMA request ,TIMERx(x=0,7) + \arg TIMER_DMA_TRGD: trigger DMA request,TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_dma_disable(uint32_t timer_periph, uint16_t dma) +{ + TIMER_DMAINTEN(timer_periph) &= (~(uint32_t)(dma)); +} + +/*! + \brief channel DMA request source selection + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] dma_request: channel DMA request source selection + only one parameter can be selected which is shown as below: + \arg TIMER_DMAREQUEST_CHANNELEVENT: DMA request of channel y is sent when channel y event occurs + \arg TIMER_DMAREQUEST_UPDATEEVENT: DMA request of channel y is sent when update event occurs + \param[out] none + \retval none +*/ +void timer_channel_dma_request_source_select(uint32_t timer_periph, uint32_t dma_request) +{ + if(TIMER_DMAREQUEST_UPDATEEVENT == dma_request){ + TIMER_CTL1(timer_periph) |= (uint32_t)TIMER_CTL1_DMAS; + }else if(TIMER_DMAREQUEST_CHANNELEVENT == dma_request){ + TIMER_CTL1(timer_periph) &= ~(uint32_t)TIMER_CTL1_DMAS; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief configure the TIMER DMA transfer + \param[in] timer_periph: please refer to the following parameters + \param[in] dma_baseaddr: + only one parameter can be selected which is shown as below: + \arg TIMER_DMACFG_DMATA_CTL0: DMA transfer address is TIMER_CTL0,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CTL1: DMA transfer address is TIMER_CTL1,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_SMCFG: DMA transfer address is TIMER_SMCFG,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_DMAINTEN: DMA transfer address is TIMER_DMAINTEN,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_INTF: DMA transfer address is TIMER_INTF,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_SWEVG: DMA transfer address is TIMER_SWEVG,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CHCTL0: DMA transfer address is TIMER_CHCTL0,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CHCTL1: DMA transfer address is TIMER_CHCTL1,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CHCTL2: DMA transfer address is TIMER_CHCTL2,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CNT: DMA transfer address is TIMER_CNT,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_PSC: DMA transfer address is TIMER_PSC,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CAR: DMA transfer address is TIMER_CAR,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CREP: DMA transfer address is TIMER_CREP,TIMERx(x=0,7) + \arg TIMER_DMACFG_DMATA_CH0CV: DMA transfer address is TIMER_CH0CV,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CH1CV: DMA transfer address is TIMER_CH1CV,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CH2CV: DMA transfer address is TIMER_CH2CV,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CH3CV: DMA transfer address is TIMER_CH3CV,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CCHP: DMA transfer address is TIMER_CCHP,TIMERx(x=0,7) + \arg TIMER_DMACFG_DMATA_DMACFG: DMA transfer address is TIMER_DMACFG,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_DMATB: DMA transfer address is TIMER_DMATB,TIMERx(x=0..4,7) + \param[in] dma_lenth: + only one parameter can be selected which is shown as below: + \arg TIMER_DMACFG_DMATC_xTRANSFER(x=1..18): DMA transfer x time + \param[out] none + \retval none +*/ +void timer_dma_transfer_config(uint32_t timer_periph, uint32_t dma_baseaddr, uint32_t dma_lenth) +{ + TIMER_DMACFG(timer_periph) &= (~(uint32_t)(TIMER_DMACFG_DMATA | TIMER_DMACFG_DMATC)); + TIMER_DMACFG(timer_periph) |= (uint32_t)(dma_baseaddr | dma_lenth); +} + +/*! + \brief software generate events + \param[in] timer_periph: please refer to the following parameters + \param[in] event: the timer software event generation sources + one or more parameters can be selected which are shown as below: + \arg TIMER_EVENT_SRC_UPG: update event,TIMERx(x=0..13) + \arg TIMER_EVENT_SRC_CH0G: channel 0 capture or compare event generation,TIMERx(x=0..4,7..13) + \arg TIMER_EVENT_SRC_CH1G: channel 1 capture or compare event generation,TIMERx(x=0..4,7,8,11) + \arg TIMER_EVENT_SRC_CH2G: channel 2 capture or compare event generation,TIMERx(x=0..4,7) + \arg TIMER_EVENT_SRC_CH3G: channel 3 capture or compare event generation,TIMERx(x=0..4,7) + \arg TIMER_EVENT_SRC_CMTG: channel commutation event generation,TIMERx(x=0,7) + \arg TIMER_EVENT_SRC_TRGG: trigger event generation,TIMERx(x=0..4,7,8,11) + \arg TIMER_EVENT_SRC_BRKG: break event generation,TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_event_software_generate(uint32_t timer_periph, uint16_t event) +{ + TIMER_SWEVG(timer_periph) |= (uint32_t)event; +} + +/*! + \brief initialize TIMER break parameter struct with a default value + \param[in] breakpara: TIMER break parameter struct + \param[out] none + \retval none +*/ +void timer_break_struct_para_init(timer_break_parameter_struct* breakpara) +{ + /* initialize the break parameter struct member with the default value */ + breakpara->runoffstate = TIMER_ROS_STATE_DISABLE; + breakpara->ideloffstate = TIMER_IOS_STATE_DISABLE; + breakpara->deadtime = 0U; + breakpara->breakpolarity = TIMER_BREAK_POLARITY_LOW; + breakpara->outputautostate = TIMER_OUTAUTO_DISABLE; + breakpara->protectmode = TIMER_CCHP_PROT_OFF; + breakpara->breakstate = TIMER_BREAK_DISABLE; +} + +/*! + \brief configure TIMER break function + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] breakpara: TIMER break parameter struct + runoffstate: TIMER_ROS_STATE_ENABLE,TIMER_ROS_STATE_DISABLE + ideloffstate: TIMER_IOS_STATE_ENABLE,TIMER_IOS_STATE_DISABLE + deadtime: 0~255 + breakpolarity: TIMER_BREAK_POLARITY_LOW,TIMER_BREAK_POLARITY_HIGH + outputautostate: TIMER_OUTAUTO_ENABLE,TIMER_OUTAUTO_DISABLE + protectmode: TIMER_CCHP_PROT_OFF,TIMER_CCHP_PROT_0,TIMER_CCHP_PROT_1,TIMER_CCHP_PROT_2 + breakstate: TIMER_BREAK_ENABLE,TIMER_BREAK_DISABLE + \param[out] none + \retval none +*/ +void timer_break_config(uint32_t timer_periph, timer_break_parameter_struct* breakpara) +{ + TIMER_CCHP(timer_periph) = (uint32_t)(((uint32_t)(breakpara->runoffstate))| + ((uint32_t)(breakpara->ideloffstate))| + ((uint32_t)(breakpara->deadtime))| + ((uint32_t)(breakpara->breakpolarity))| + ((uint32_t)(breakpara->outputautostate)) | + ((uint32_t)(breakpara->protectmode))| + ((uint32_t)(breakpara->breakstate))) ; +} + +/*! + \brief enable TIMER break function + \param[in] timer_periph: TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_break_enable(uint32_t timer_periph) +{ + TIMER_CCHP(timer_periph) |= (uint32_t)TIMER_CCHP_BRKEN; +} + +/*! + \brief disable TIMER break function + \param[in] timer_periph: TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_break_disable(uint32_t timer_periph) +{ + TIMER_CCHP(timer_periph) &= ~(uint32_t)TIMER_CCHP_BRKEN; +} + +/*! + \brief enable TIMER output automatic function + \param[in] timer_periph: TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_automatic_output_enable(uint32_t timer_periph) +{ + TIMER_CCHP(timer_periph) |= (uint32_t)TIMER_CCHP_OAEN; +} + +/*! + \brief disable TIMER output automatic function + \param[in] timer_periph: TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_automatic_output_disable(uint32_t timer_periph) +{ + TIMER_CCHP(timer_periph) &= ~(uint32_t)TIMER_CCHP_OAEN; +} + +/*! + \brief configure TIMER primary output function + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void timer_primary_output_config(uint32_t timer_periph, ControlStatus newvalue) +{ + if(ENABLE == newvalue){ + TIMER_CCHP(timer_periph) |= (uint32_t)TIMER_CCHP_POEN; + }else{ + TIMER_CCHP(timer_periph) &= (~(uint32_t)TIMER_CCHP_POEN); + } +} + +/*! + \brief enable or disable channel capture/compare control shadow register + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void timer_channel_control_shadow_config(uint32_t timer_periph, ControlStatus newvalue) +{ + if(ENABLE == newvalue){ + TIMER_CTL1(timer_periph) |= (uint32_t)TIMER_CTL1_CCSE; + }else{ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_CCSE); + } +} + +/*! + \brief configure TIMER channel control shadow register update control + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] ccuctl: channel control shadow register update control + only one parameter can be selected which is shown as below: + \arg TIMER_UPDATECTL_CCU: the shadow registers update by when CMTG bit is set + \arg TIMER_UPDATECTL_CCUTRI: the shadow registers update by when CMTG bit is set or an rising edge of TRGI occurs + \param[out] none + \retval none +*/ +void timer_channel_control_shadow_update_config(uint32_t timer_periph, uint32_t ccuctl) +{ + if(TIMER_UPDATECTL_CCU == ccuctl){ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_CCUC); + }else if(TIMER_UPDATECTL_CCUTRI == ccuctl){ + TIMER_CTL1(timer_periph) |= (uint32_t)TIMER_CTL1_CCUC; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief initialize TIMER channel output parameter struct with a default value + \param[in] ocpara: TIMER channel n output parameter struct + \param[out] none + \retval none +*/ +void timer_channel_output_struct_para_init(timer_oc_parameter_struct* ocpara) +{ + /* initialize the channel output parameter struct member with the default value */ + ocpara->outputstate = TIMER_CCX_DISABLE; + ocpara->outputnstate = TIMER_CCXN_DISABLE; + ocpara->ocpolarity = TIMER_OC_POLARITY_HIGH; + ocpara->ocnpolarity = TIMER_OCN_POLARITY_HIGH; + ocpara->ocidlestate = TIMER_OC_IDLE_STATE_LOW; + ocpara->ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; +} + +/*! + \brief configure TIMER channel output function + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel 0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel 1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel 2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel 3(TIMERx(x=0..4,7)) + \param[in] ocpara: TIMER channeln output parameter struct + outputstate: TIMER_CCX_ENABLE,TIMER_CCX_DISABLE + outputnstate: TIMER_CCXN_ENABLE,TIMER_CCXN_DISABLE + ocpolarity: TIMER_OC_POLARITY_HIGH,TIMER_OC_POLARITY_LOW + ocnpolarity: TIMER_OCN_POLARITY_HIGH,TIMER_OCN_POLARITY_LOW + ocidlestate: TIMER_OC_IDLE_STATE_LOW,TIMER_OC_IDLE_STATE_HIGH + ocnidlestate: TIMER_OCN_IDLE_STATE_LOW,TIMER_OCN_IDLE_STATE_HIGH + \param[out] none + \retval none +*/ +void timer_channel_output_config(uint32_t timer_periph, uint16_t channel, timer_oc_parameter_struct* ocpara) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + /* reset the CH0EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + TIMER_CHCTL0(timer_periph) &= ~(uint32_t)TIMER_CHCTL0_CH0MS; + /* set the CH0EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocpara->outputstate; + /* reset the CH0P bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0P); + /* set the CH0P bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocpara->ocpolarity; + + if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){ + /* reset the CH0NEN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0NEN); + /* set the CH0NEN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocpara->outputnstate; + /* reset the CH0NP bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0NP); + /* set the CH0NP bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocpara->ocnpolarity; + /* reset the ISO0 bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO0); + /* set the ISO0 bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)ocpara->ocidlestate; + /* reset the ISO0N bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO0N); + /* set the ISO0N bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)ocpara->ocnidlestate; + } + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + /* reset the CH1EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + TIMER_CHCTL0(timer_periph) &= ~(uint32_t)TIMER_CHCTL0_CH1MS; + /* set the CH1EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->outputstate) << 4U); + /* reset the CH1P bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1P); + /* set the CH1P bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocpolarity) << 4U); + + if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){ + /* reset the CH1NEN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1NEN); + /* set the CH1NEN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->outputnstate) << 4U); + /* reset the CH1NP bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1NP); + /* set the CH1NP bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocnpolarity) << 4U); + /* reset the ISO1 bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO1); + /* set the ISO1 bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocidlestate) << 2U); + /* reset the ISO1N bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO1N); + /* set the ISO1N bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocnidlestate) << 2U); + } + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + /* reset the CH2EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2EN); + TIMER_CHCTL1(timer_periph) &= ~(uint32_t)TIMER_CHCTL1_CH2MS; + /* set the CH2EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->outputstate) << 8U); + /* reset the CH2P bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2P); + /* set the CH2P bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocpolarity) << 8U); + + if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){ + /* reset the CH2NEN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2NEN); + /* set the CH2NEN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->outputnstate) << 8U); + /* reset the CH2NP bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2NP); + /* set the CH2NP bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocnpolarity) << 8U); + /* reset the ISO2 bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO2); + /* set the ISO2 bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocidlestate) << 4U); + /* reset the ISO2N bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO2N); + /* set the ISO2N bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocnidlestate) << 4U); + } + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + /* reset the CH3EN bit */ + TIMER_CHCTL2(timer_periph) &=(~(uint32_t)TIMER_CHCTL2_CH3EN); + TIMER_CHCTL1(timer_periph) &= ~(uint32_t)TIMER_CHCTL1_CH3MS; + /* set the CH3EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->outputstate) << 12U); + /* reset the CH3P bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH3P); + /* set the CH3P bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocpolarity) << 12U); + + if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){ + /* reset the ISO3 bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO3); + /* set the ISO3 bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocidlestate) << 6U); + } + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output compare mode + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] ocmode: channel output compare mode + only one parameter can be selected which is shown as below: + \arg TIMER_OC_MODE_TIMING: timing mode + \arg TIMER_OC_MODE_ACTIVE: active mode + \arg TIMER_OC_MODE_INACTIVE: inactive mode + \arg TIMER_OC_MODE_TOGGLE: toggle mode + \arg TIMER_OC_MODE_LOW: force low mode + \arg TIMER_OC_MODE_HIGH: force high mode + \arg TIMER_OC_MODE_PWM0: PWM0 mode + \arg TIMER_OC_MODE_PWM1: PWM1 mode + \param[out] none + \retval none +*/ +void timer_channel_output_mode_config(uint32_t timer_periph, uint16_t channel, uint16_t ocmode) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0COMCTL); + TIMER_CHCTL0(timer_periph) |= (uint32_t)ocmode; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1COMCTL); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(ocmode) << 8U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2COMCTL); + TIMER_CHCTL1(timer_periph) |= (uint32_t)ocmode; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3COMCTL); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(ocmode) << 8U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output pulse value + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] pulse: channel output pulse value + \param[out] none + \retval none +*/ +void timer_channel_output_pulse_value_config(uint32_t timer_periph, uint16_t channel, uint32_t pulse) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CH0CV(timer_periph) = (uint32_t)pulse; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CH1CV(timer_periph) = (uint32_t)pulse; + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CH2CV(timer_periph) = (uint32_t)pulse; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CH3CV(timer_periph) = (uint32_t)pulse; + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output shadow function + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] ocshadow: channel output shadow state + only one parameter can be selected which is shown as below: + \arg TIMER_OC_SHADOW_ENABLE: channel output shadow state enable + \arg TIMER_OC_SHADOW_DISABLE: channel output shadow state disable + \param[out] none + \retval none +*/ +void timer_channel_output_shadow_config(uint32_t timer_periph, uint16_t channel, uint16_t ocshadow) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0COMSEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)ocshadow; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1COMSEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(ocshadow) << 8U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2COMSEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)ocshadow; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3COMSEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(ocshadow) << 8U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output fast function + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] ocfast: channel output fast function + only one parameter can be selected which is shown as below: + \arg TIMER_OC_FAST_ENABLE: channel output fast function enable + \arg TIMER_OC_FAST_DISABLE: channel output fast function disable + \param[out] none + \retval none +*/ +void timer_channel_output_fast_config(uint32_t timer_periph, uint16_t channel, uint16_t ocfast) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0COMFEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)ocfast; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1COMFEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)ocfast << 8U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2COMFEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)ocfast; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3COMFEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)ocfast << 8U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output clear function + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0 + \arg TIMER_CH_1: TIMER channel1 + \arg TIMER_CH_2: TIMER channel2 + \arg TIMER_CH_3: TIMER channel3 + \param[in] occlear: channel output clear function + only one parameter can be selected which is shown as below: + \arg TIMER_OC_CLEAR_ENABLE: channel output clear function enable + \arg TIMER_OC_CLEAR_DISABLE: channel output clear function disable + \param[out] none + \retval none +*/ +void timer_channel_output_clear_config(uint32_t timer_periph, uint16_t channel, uint16_t occlear) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0COMCEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)occlear; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1COMCEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)occlear << 8U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2COMCEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)occlear; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3COMCEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)occlear << 8U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output polarity + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] ocpolarity: channel output polarity + only one parameter can be selected which is shown as below: + \arg TIMER_OC_POLARITY_HIGH: channel output polarity is high + \arg TIMER_OC_POLARITY_LOW: channel output polarity is low + \param[out] none + \retval none +*/ +void timer_channel_output_polarity_config(uint32_t timer_periph, uint16_t channel, uint16_t ocpolarity) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0P); + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocpolarity; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1P); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocpolarity << 4U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2P); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocpolarity << 8U); + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH3P); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocpolarity << 12U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel complementary output polarity + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0,7)) + \param[in] ocnpolarity: channel complementary output polarity + only one parameter can be selected which is shown as below: + \arg TIMER_OCN_POLARITY_HIGH: channel complementary output polarity is high + \arg TIMER_OCN_POLARITY_LOW: channel complementary output polarity is low + \param[out] none + \retval none +*/ +void timer_channel_complementary_output_polarity_config(uint32_t timer_periph, uint16_t channel, uint16_t ocnpolarity) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0NP); + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocnpolarity; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1NP); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocnpolarity << 4U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2NP); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocnpolarity << 8U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel enable state + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] state: TIMER channel enable state + only one parameter can be selected which is shown as below: + \arg TIMER_CCX_ENABLE: channel enable + \arg TIMER_CCX_DISABLE: channel disable + \param[out] none + \retval none +*/ +void timer_channel_output_state_config(uint32_t timer_periph, uint16_t channel, uint32_t state) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)state; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)state << 4U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2EN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)state << 8U); + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH3EN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)state << 12U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel complementary output enable state + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0,7)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0,7)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0,7)) + \param[in] ocnstate: TIMER channel complementary output enable state + only one parameter can be selected which is shown as below: + \arg TIMER_CCXN_ENABLE: channel complementary enable + \arg TIMER_CCXN_DISABLE: channel complementary disable + \param[out] none + \retval none +*/ +void timer_channel_complementary_output_state_config(uint32_t timer_periph, uint16_t channel, uint16_t ocnstate) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0NEN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocnstate; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1NEN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocnstate << 4U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2NEN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocnstate << 8U); + break; + default: + break; + } +} + +/*! + \brief initialize TIMER channel input parameter struct with a default value + \param[in] icpara: TIMER channel intput parameter struct + \param[out] none + \retval none +*/ +void timer_channel_input_struct_para_init(timer_ic_parameter_struct* icpara) +{ + /* initialize the channel input parameter struct member with the default value */ + icpara->icpolarity = TIMER_IC_POLARITY_RISING; + icpara->icselection = TIMER_IC_SELECTION_DIRECTTI; + icpara->icprescaler = TIMER_IC_PSC_DIV1; + icpara->icfilter = 0U; +} + +/*! + \brief configure TIMER input capture parameter + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] icpara: TIMER channel intput parameter struct + icpolarity: TIMER_IC_POLARITY_RISING,TIMER_IC_POLARITY_FALLING,TIMER_IC_POLARITY_BOTH_EDGE + icselection: TIMER_IC_SELECTION_DIRECTTI,TIMER_IC_SELECTION_INDIRECTTI,TIMER_IC_SELECTION_ITS + icprescaler: TIMER_IC_PSC_DIV1,TIMER_IC_PSC_DIV2,TIMER_IC_PSC_DIV4,TIMER_IC_PSC_DIV8 + icfilter: 0~15 + \param[out] none + \retval none +*/ +void timer_input_capture_config(uint32_t timer_periph, uint16_t channel, timer_ic_parameter_struct* icpara) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + /* reset the CH0EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + + /* reset the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP)); + TIMER_CHCTL2(timer_periph) |= (uint32_t)(icpara->icpolarity); + /* reset the CH0MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0MS); + TIMER_CHCTL0(timer_periph) |= (uint32_t)(icpara->icselection); + /* reset the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0CAPFLT); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpara->icfilter) << 4U); + + /* set the CH0EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH0EN; + break; + + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + /* reset the CH1EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + + /* reset the CH1P and CH1NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP)); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(icpara->icpolarity) << 4U); + /* reset the CH1MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1MS); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpara->icselection) << 8U); + /* reset the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1CAPFLT); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpara->icfilter) << 12U); + + /* set the CH1EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH1EN; + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + /* reset the CH2EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2EN); + + /* reset the CH2P and CH2NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH2P | TIMER_CHCTL2_CH2NP)); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(icpara->icpolarity) << 8U); + + /* reset the CH2MS bit */ + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2MS); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(icpara->icselection)); + + /* reset the CH2CAPFLT bit */ + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2CAPFLT); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(icpara->icfilter) << 4U); + + /* set the CH2EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH2EN; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + /* reset the CH3EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH3EN); + + /* reset the CH3P and CH3NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH3P|TIMER_CHCTL2_CH3NP)); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(icpara->icpolarity) << 12U); + + /* reset the CH3MS bit */ + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3MS); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(icpara->icselection) << 8U); + + /* reset the CH3CAPFLT bit */ + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3CAPFLT); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(icpara->icfilter) << 12U); + + /* set the CH3EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH3EN; + break; + default: + break; + } + /* configure TIMER channel input capture prescaler value */ + timer_channel_input_capture_prescaler_config(timer_periph, channel, (uint16_t)(icpara->icprescaler)); +} + +/*! + \brief configure TIMER channel input capture prescaler value + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] prescaler: channel input capture prescaler value + only one parameter can be selected which is shown as below: + \arg TIMER_IC_PSC_DIV1: no prescaler + \arg TIMER_IC_PSC_DIV2: divided by 2 + \arg TIMER_IC_PSC_DIV4: divided by 4 + \arg TIMER_IC_PSC_DIV8: divided by 8 + \param[out] none + \retval none +*/ +void timer_channel_input_capture_prescaler_config(uint32_t timer_periph, uint16_t channel, uint16_t prescaler) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0CAPPSC); + TIMER_CHCTL0(timer_periph) |= (uint32_t)prescaler; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1CAPPSC); + TIMER_CHCTL0(timer_periph) |= ((uint32_t)prescaler << 8U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2CAPPSC); + TIMER_CHCTL1(timer_periph) |= (uint32_t)prescaler; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3CAPPSC); + TIMER_CHCTL1(timer_periph) |= ((uint32_t)prescaler << 8U); + break; + default: + break; + } +} + +/*! + \brief read TIMER channel capture compare register value + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[out] none + \retval channel capture compare register value +*/ +uint32_t timer_channel_capture_value_register_read(uint32_t timer_periph, uint16_t channel) +{ + uint32_t count_value = 0U; + + switch(channel){ + /* read TIMER channel 0 capture compare register value */ + case TIMER_CH_0: + count_value = TIMER_CH0CV(timer_periph); + break; + /* read TIMER channel 1 capture compare register value */ + case TIMER_CH_1: + count_value = TIMER_CH1CV(timer_periph); + break; + /* read TIMER channel 2 capture compare register value */ + case TIMER_CH_2: + count_value = TIMER_CH2CV(timer_periph); + break; + /* read TIMER channel 3 capture compare register value */ + case TIMER_CH_3: + count_value = TIMER_CH3CV(timer_periph); + break; + default: + break; + } + return (count_value); +} + +/*! + \brief configure TIMER input pwm capture function + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0 + \arg TIMER_CH_1: TIMER channel1 + \param[in] icpwm:TIMER channel intput pwm parameter struct + icpolarity: TIMER_IC_POLARITY_RISING,TIMER_IC_POLARITY_FALLING + icselection: TIMER_IC_SELECTION_DIRECTTI,TIMER_IC_SELECTION_INDIRECTTI + icprescaler: TIMER_IC_PSC_DIV1,TIMER_IC_PSC_DIV2,TIMER_IC_PSC_DIV4,TIMER_IC_PSC_DIV8 + icfilter: 0~15 + \param[out] none + \retval none +*/ +void timer_input_pwm_capture_config(uint32_t timer_periph, uint16_t channel, timer_ic_parameter_struct* icpwm) +{ + uint16_t icpolarity = 0x0U; + uint16_t icselection = 0x0U; + + /* Set channel input polarity */ + if(TIMER_IC_POLARITY_RISING == icpwm->icpolarity){ + icpolarity = TIMER_IC_POLARITY_FALLING; + }else{ + icpolarity = TIMER_IC_POLARITY_RISING; + } + /* Set channel input mode selection */ + if(TIMER_IC_SELECTION_DIRECTTI == icpwm->icselection){ + icselection = TIMER_IC_SELECTION_INDIRECTTI; + }else{ + icselection = TIMER_IC_SELECTION_DIRECTTI; + } + + if(TIMER_CH_0 == channel){ + /* reset the CH0EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + /* reset the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP)); + /* set the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)(icpwm->icpolarity); + /* reset the CH0MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0MS); + /* set the CH0MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)(icpwm->icselection); + /* reset the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0CAPFLT); + /* set the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= ((uint32_t)(icpwm->icfilter) << 4U); + /* set the CH0EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH0EN; + /* configure TIMER channel input capture prescaler value */ + timer_channel_input_capture_prescaler_config(timer_periph, TIMER_CH_0, (uint16_t)(icpwm->icprescaler)); + + /* reset the CH1EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + /* reset the CH1P and CH1NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP)); + /* set the CH1P and CH1NP bits */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)icpolarity<< 4U); + /* reset the CH1MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1MS); + /* set the CH1MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)icselection << 8U); + /* reset the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1CAPFLT); + /* set the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpwm->icfilter) << 12U); + /* set the CH1EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH1EN; + /* configure TIMER channel input capture prescaler value */ + timer_channel_input_capture_prescaler_config(timer_periph, TIMER_CH_1, (uint16_t)(icpwm->icprescaler)); + }else{ + /* reset the CH1EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + /* reset the CH1P and CH1NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP)); + /* set the CH1P and CH1NP bits */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(icpwm->icpolarity) << 4U); + /* reset the CH1MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1MS); + /* set the CH1MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpwm->icselection) << 8U); + /* reset the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1CAPFLT); + /* set the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpwm->icfilter) << 12U); + /* set the CH1EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH1EN; + /* configure TIMER channel input capture prescaler value */ + timer_channel_input_capture_prescaler_config(timer_periph, TIMER_CH_1, (uint16_t)(icpwm->icprescaler)); + + /* reset the CH0EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + /* reset the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP)); + /* set the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)icpolarity; + /* reset the CH0MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0MS); + /* set the CH0MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)icselection; + /* reset the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0CAPFLT); + /* set the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= ((uint32_t)(icpwm->icfilter) << 4U); + /* set the CH0EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH0EN; + /* configure TIMER channel input capture prescaler value */ + timer_channel_input_capture_prescaler_config(timer_periph, TIMER_CH_0, (uint16_t)(icpwm->icprescaler)); + } +} + +/*! + \brief configure TIMER hall sensor mode + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] hallmode: + only one parameter can be selected which is shown as below: + \arg TIMER_HALLINTERFACE_ENABLE: TIMER hall sensor mode enable + \arg TIMER_HALLINTERFACE_DISABLE: TIMER hall sensor mode disable + \param[out] none + \retval none +*/ +void timer_hall_mode_config(uint32_t timer_periph, uint32_t hallmode) +{ + if(TIMER_HALLINTERFACE_ENABLE == hallmode){ + TIMER_CTL1(timer_periph) |= (uint32_t)TIMER_CTL1_TI0S; + }else if(TIMER_HALLINTERFACE_DISABLE == hallmode){ + TIMER_CTL1(timer_periph) &= ~(uint32_t)TIMER_CTL1_TI0S; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief select TIMER input trigger source + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] intrigger: + only one parameter can be selected which is shown as below: + \arg TIMER_SMCFG_TRGSEL_ITI0: internal trigger 0 + \arg TIMER_SMCFG_TRGSEL_ITI1: internal trigger 1 + \arg TIMER_SMCFG_TRGSEL_ITI2: internal trigger 2 + \arg TIMER_SMCFG_TRGSEL_ITI3: internal trigger 3 + \arg TIMER_SMCFG_TRGSEL_CI0F_ED: TI0 edge detector + \arg TIMER_SMCFG_TRGSEL_CI0FE0: filtered TIMER input 0 + \arg TIMER_SMCFG_TRGSEL_CI1FE1: filtered TIMER input 1 + \arg TIMER_SMCFG_TRGSEL_ETIFP: external trigger(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_input_trigger_source_select(uint32_t timer_periph, uint32_t intrigger) +{ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_TRGS); + TIMER_SMCFG(timer_periph) |= (uint32_t)intrigger; +} + +/*! + \brief select TIMER master mode output trigger source + \param[in] timer_periph: TIMERx(x=0..7) + \param[in] outrigger: + only one parameter can be selected which is shown as below: + \arg TIMER_TRI_OUT_SRC_RESET: the UPG bit as trigger output + \arg TIMER_TRI_OUT_SRC_ENABLE: the counter enable signal TIMER_CTL0_CEN as trigger output + \arg TIMER_TRI_OUT_SRC_UPDATE: update event as trigger output + \arg TIMER_TRI_OUT_SRC_CH0: a capture or a compare match occurred in channal0 as trigger output TRGO + \arg TIMER_TRI_OUT_SRC_O0CPRE: O0CPRE as trigger output + \arg TIMER_TRI_OUT_SRC_O1CPRE: O1CPRE as trigger output + \arg TIMER_TRI_OUT_SRC_O2CPRE: O2CPRE as trigger output + \arg TIMER_TRI_OUT_SRC_O3CPRE: O3CPRE as trigger output + \param[out] none + \retval none +*/ +void timer_master_output_trigger_source_select(uint32_t timer_periph, uint32_t outrigger) +{ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_MMC); + TIMER_CTL1(timer_periph) |= (uint32_t)outrigger; +} + +/*! + \brief select TIMER slave mode + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] slavemode: + only one parameter can be selected which is shown as below: + \arg TIMER_SLAVE_MODE_DISABLE: slave mode disable + \arg TIMER_QUAD_DECODER_MODE0: quadrature decoder mode 0 + \arg TIMER_QUAD_DECODER_MODE1: quadrature decoder mode 1 + \arg TIMER_QUAD_DECODER_MODE2: quadrature decoder mode 2 + \arg TIMER_SLAVE_MODE_RESTART: restart mode + \arg TIMER_SLAVE_MODE_PAUSE: pause mode + \arg TIMER_SLAVE_MODE_EVENT: event mode + \arg TIMER_SLAVE_MODE_EXTERNAL0: external clock mode 0 + \param[out] none + \retval none +*/ + +void timer_slave_mode_select(uint32_t timer_periph, uint32_t slavemode) +{ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_SMC); + TIMER_SMCFG(timer_periph) |= (uint32_t)slavemode; +} + +/*! + \brief configure TIMER master slave mode + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] masterslave: + only one parameter can be selected which is shown as below: + \arg TIMER_MASTER_SLAVE_MODE_ENABLE: master slave mode enable + \arg TIMER_MASTER_SLAVE_MODE_DISABLE: master slave mode disable + \param[out] none + \retval none +*/ +void timer_master_slave_mode_config(uint32_t timer_periph, uint32_t masterslave) +{ + if(TIMER_MASTER_SLAVE_MODE_ENABLE == masterslave){ + TIMER_SMCFG(timer_periph) |= (uint32_t)TIMER_SMCFG_MSM; + }else if(TIMER_MASTER_SLAVE_MODE_DISABLE == masterslave){ + TIMER_SMCFG(timer_periph) &= ~(uint32_t)TIMER_SMCFG_MSM; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief configure TIMER external trigger input + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] extprescaler: + only one parameter can be selected which is shown as below: + \arg TIMER_EXT_TRI_PSC_OFF: no divided + \arg TIMER_EXT_TRI_PSC_DIV2: divided by 2 + \arg TIMER_EXT_TRI_PSC_DIV4: divided by 4 + \arg TIMER_EXT_TRI_PSC_DIV8: divided by 8 + \param[in] extpolarity: + only one parameter can be selected which is shown as below: + \arg TIMER_ETP_FALLING: active low or falling edge active + \arg TIMER_ETP_RISING: active high or rising edge active + \param[in] extfilter: a value between 0 and 15 + \param[out] none + \retval none +*/ +void timer_external_trigger_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter) +{ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)(TIMER_SMCFG_ETP | TIMER_SMCFG_ETPSC | TIMER_SMCFG_ETFC)); + TIMER_SMCFG(timer_periph) |= (uint32_t)(extprescaler | extpolarity); + TIMER_SMCFG(timer_periph) |= (uint32_t)(extfilter << 8U); +} + +/*! + \brief configure TIMER quadrature decoder mode + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] decomode: + only one parameter can be selected which is shown as below: + \arg TIMER_QUAD_DECODER_MODE0: counter counts on CI0FE0 edge depending on CI1FE1 level + \arg TIMER_QUAD_DECODER_MODE1: counter counts on CI1FE1 edge depending on CI0FE0 level + \arg TIMER_QUAD_DECODER_MODE2: counter counts on both CI0FE0 and CI1FE1 edges depending on the level of the other input + \param[in] ic0polarity: + only one parameter can be selected which is shown as below: + \arg TIMER_IC_POLARITY_RISING: capture rising edge + \arg TIMER_IC_POLARITY_FALLING: capture falling edge + \arg TIMER_IC_POLARITY_BOTH_EDGE: active both edge + \param[in] ic1polarity: + only one parameter can be selected which is shown as below: + \arg TIMER_IC_POLARITY_RISING: capture rising edge + \arg TIMER_IC_POLARITY_FALLING: capture falling edge + \arg TIMER_IC_POLARITY_BOTH_EDGE: active both edge + \param[out] none + \retval none +*/ +void timer_quadrature_decoder_mode_config(uint32_t timer_periph, uint32_t decomode, uint16_t ic0polarity, uint16_t ic1polarity) +{ + /* configure the quadrature decoder mode */ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_SMC); + TIMER_SMCFG(timer_periph) |= (uint32_t)decomode; + /* configure input capture selection */ + TIMER_CHCTL0(timer_periph) &= (uint32_t)(((~(uint32_t)TIMER_CHCTL0_CH0MS)) & ((~(uint32_t)TIMER_CHCTL0_CH1MS))); + TIMER_CHCTL0(timer_periph) |= (uint32_t)(TIMER_IC_SELECTION_DIRECTTI | ((uint32_t)TIMER_IC_SELECTION_DIRECTTI << 8U)); + /* configure channel input capture polarity */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP)); + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP)); + TIMER_CHCTL2(timer_periph) |= ((uint32_t)ic0polarity | ((uint32_t)ic1polarity << 4U)); +} + +/*! + \brief configure TIMER internal clock mode + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[out] none + \retval none +*/ +void timer_internal_clock_config(uint32_t timer_periph) +{ + TIMER_SMCFG(timer_periph) &= ~(uint32_t)TIMER_SMCFG_SMC; +} + +/*! + \brief configure TIMER the internal trigger as external clock input + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] intrigger: + only one parameter can be selected which is shown as below: + \arg TIMER_SMCFG_TRGSEL_ITI0: internal trigger 0 + \arg TIMER_SMCFG_TRGSEL_ITI1: internal trigger 1 + \arg TIMER_SMCFG_TRGSEL_ITI2: internal trigger 2 + \arg TIMER_SMCFG_TRGSEL_ITI3: internal trigger 3 + \param[out] none + \retval none +*/ +void timer_internal_trigger_as_external_clock_config(uint32_t timer_periph, uint32_t intrigger) +{ + timer_input_trigger_source_select(timer_periph, intrigger); + TIMER_SMCFG(timer_periph) &= ~(uint32_t)TIMER_SMCFG_SMC; + TIMER_SMCFG(timer_periph) |= (uint32_t)TIMER_SLAVE_MODE_EXTERNAL0; +} + +/*! + \brief configure TIMER the external trigger as external clock input + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] extrigger: + only one parameter can be selected which is shown as below: + \arg TIMER_SMCFG_TRGSEL_CI0F_ED: TI0 edge detector + \arg TIMER_SMCFG_TRGSEL_CI0FE0: filtered TIMER input 0 + \arg TIMER_SMCFG_TRGSEL_CI1FE1: filtered TIMER input 1 + \param[in] extpolarity: + only one parameter can be selected which is shown as below: + \arg TIMER_IC_POLARITY_RISING: active high or rising edge active + \arg TIMER_IC_POLARITY_FALLING: active low or falling edge active + \arg TIMER_IC_POLARITY_BOTH_EDGE: active both edge + \param[in] extfilter: a value between 0 and 15 + \param[out] none + \retval none +*/ +void timer_external_trigger_as_external_clock_config(uint32_t timer_periph, uint32_t extrigger, uint16_t extpolarity, uint32_t extfilter) +{ + if(TIMER_SMCFG_TRGSEL_CI1FE1 == extrigger){ + /* reset the CH1EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + /* reset the CH1NP bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP)); + /* set the CH1NP bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)extpolarity << 4U); + /* reset the CH1MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1MS); + /* set the CH1MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)TIMER_IC_SELECTION_DIRECTTI << 8U); + /* reset the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1CAPFLT); + /* set the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)(extfilter << 12U); + /* set the CH1EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH1EN; + }else{ + /* reset the CH0EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + /* reset the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP)); + /* set the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)extpolarity; + /* reset the CH0MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0MS); + /* set the CH0MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)TIMER_IC_SELECTION_DIRECTTI; + /* reset the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0CAPFLT); + /* reset the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)(extfilter << 4U); + /* set the CH0EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH0EN; + } + /* select TIMER input trigger source */ + timer_input_trigger_source_select(timer_periph, extrigger); + /* reset the SMC bit */ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_SMC); + /* set the SMC bit */ + TIMER_SMCFG(timer_periph) |= (uint32_t)TIMER_SLAVE_MODE_EXTERNAL0; +} + +/*! + \brief configure TIMER the external clock mode0 + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] extprescaler: + only one parameter can be selected which is shown as below: + \arg TIMER_EXT_TRI_PSC_OFF: no divided + \arg TIMER_EXT_TRI_PSC_DIV2: divided by 2 + \arg TIMER_EXT_TRI_PSC_DIV4: divided by 4 + \arg TIMER_EXT_TRI_PSC_DIV8: divided by 8 + \param[in] extpolarity: + only one parameter can be selected which is shown as below: + \arg TIMER_ETP_FALLING: active low or falling edge active + \arg TIMER_ETP_RISING: active high or rising edge active + \param[in] extfilter: a value between 0 and 15 + \param[out] none + \retval none +*/ +void timer_external_clock_mode0_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter) +{ + /* configure TIMER external trigger input */ + timer_external_trigger_config(timer_periph, extprescaler, extpolarity, extfilter); + + /* reset the SMC bit,TRGS bit */ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)(TIMER_SMCFG_SMC | TIMER_SMCFG_TRGS)); + /* set the SMC bit,TRGS bit */ + TIMER_SMCFG(timer_periph) |= (uint32_t)(TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_ETIFP); +} + +/*! + \brief configure TIMER the external clock mode1 + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] extprescaler: + only one parameter can be selected which is shown as below: + \arg TIMER_EXT_TRI_PSC_OFF: no divided + \arg TIMER_EXT_TRI_PSC_DIV2: divided by 2 + \arg TIMER_EXT_TRI_PSC_DIV4: divided by 4 + \arg TIMER_EXT_TRI_PSC_DIV8: divided by 8 + \param[in] extpolarity: + only one parameter can be selected which is shown as below: + \arg TIMER_ETP_FALLING: active low or falling edge active + \arg TIMER_ETP_RISING: active high or rising edge active + \param[in] extfilter: a value between 0 and 15 + \param[out] none + \retval none +*/ +void timer_external_clock_mode1_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter) +{ + /* configure TIMER external trigger input */ + timer_external_trigger_config(timer_periph, extprescaler, extpolarity, extfilter); + + TIMER_SMCFG(timer_periph) |= (uint32_t)TIMER_SMCFG_SMC1; +} + +/*! + \brief disable TIMER the external clock mode1 + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_external_clock_mode1_disable(uint32_t timer_periph) +{ + TIMER_SMCFG(timer_periph) &= ~(uint32_t)TIMER_SMCFG_SMC1; +} + +/*! + \brief configure TIMER write CHxVAL register selection + \param[in] timer_periph: TIMERx(x=0..4,7..13) + \param[in] ccsel: + only one parameter can be selected which is shown as below: + \arg TIMER_CHVSEL_DISABLE: no effect + \arg TIMER_CHVSEL_ENABLE: when write the CHxVAL register, if the write value is same as the CHxVAL value, the write access is ignored + \param[out] none + \retval none +*/ +void timer_write_chxval_register_config(uint32_t timer_periph, uint16_t ccsel) +{ + if(TIMER_CHVSEL_ENABLE == ccsel){ + TIMER_CFG(timer_periph) |= (uint32_t)TIMER_CFG_CHVSEL; + }else if(TIMER_CHVSEL_DISABLE == ccsel){ + TIMER_CFG(timer_periph) &= ~(uint32_t)TIMER_CFG_CHVSEL; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief configure TIMER output value selection + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] outsel: + only one parameter can be selected which is shown as below: + \arg TIMER_OUTSEL_DISABLE: no effect + \arg TIMER_OUTSEL_ENABLE: if POEN and IOS is 0, the output disabled + \param[out] none + \retval none +*/ +void timer_output_value_selection_config(uint32_t timer_periph, uint16_t outsel) +{ + if(TIMER_OUTSEL_ENABLE == outsel){ + TIMER_CFG(timer_periph) |= (uint32_t)TIMER_CFG_OUTSEL; + }else if(TIMER_OUTSEL_DISABLE == outsel){ + TIMER_CFG(timer_periph) &= ~(uint32_t)TIMER_CFG_OUTSEL; + }else{ + /* illegal parameters */ + } +} +/*! + \brief get TIMER flags + \param[in] timer_periph: please refer to the following parameters + \param[in] flag: the timer interrupt flags + only one parameter can be selected which is shown as below: + \arg TIMER_FLAG_UP: update flag,TIMERx(x=0..13) + \arg TIMER_FLAG_CH0: channel 0 flag,TIMERx(x=0..4,7..13) + \arg TIMER_FLAG_CH1: channel 1 flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_FLAG_CH2: channel 2 flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CH3: channel 3 flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CMT: channel control update flag,TIMERx(x=0,7) + \arg TIMER_FLAG_TRG: trigger flag,TIMERx(x=0,7,8,11) + \arg TIMER_FLAG_BRK: break flag,TIMERx(x=0,7) + \arg TIMER_FLAG_CH0O: channel 0 overcapture flag,TIMERx(x=0..4,7..11) + \arg TIMER_FLAG_CH1O: channel 1 overcapture flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_FLAG_CH2O: channel 2 overcapture flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CH3O: channel 3 overcapture flag,TIMERx(x=0..4,7) + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus timer_flag_get(uint32_t timer_periph, uint32_t flag) +{ + if(RESET != (TIMER_INTF(timer_periph) & flag)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear TIMER flags + \param[in] timer_periph: please refer to the following parameters + \param[in] flag: the timer interrupt flags + only one parameter can be selected which is shown as below: + \arg TIMER_FLAG_UP: update flag,TIMERx(x=0..13) + \arg TIMER_FLAG_CH0: channel 0 flag,TIMERx(x=0..4,7..13) + \arg TIMER_FLAG_CH1: channel 1 flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_FLAG_CH2: channel 2 flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CH3: channel 3 flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CMT: channel control update flag,TIMERx(x=0,7) + \arg TIMER_FLAG_TRG: trigger flag,TIMERx(x=0,7,8,11) + \arg TIMER_FLAG_BRK: break flag,TIMERx(x=0,7) + \arg TIMER_FLAG_CH0O: channel 0 overcapture flag,TIMERx(x=0..4,7..11) + \arg TIMER_FLAG_CH1O: channel 1 overcapture flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_FLAG_CH2O: channel 2 overcapture flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CH3O: channel 3 overcapture flag,TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_flag_clear(uint32_t timer_periph, uint32_t flag) +{ + TIMER_INTF(timer_periph) = (~(uint32_t)flag); +} +/*! + \brief enable the TIMER interrupt + \param[in] timer_periph: please refer to the following parameters + \param[in] interrupt: timer interrupt enable source + only one parameter can be selected which is shown as below: + \arg TIMER_INT_UP: update interrupt enable, TIMERx(x=0..13) + \arg TIMER_INT_CH0: channel 0 interrupt enable, TIMERx(x=0..4,7..13) + \arg TIMER_INT_CH1: channel 1 interrupt enable, TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_CH2: channel 2 interrupt enable, TIMERx(x=0..4,7) + \arg TIMER_INT_CH3: channel 3 interrupt enable , TIMERx(x=0..4,7) + \arg TIMER_INT_CMT: commutation interrupt enable, TIMERx(x=0,7) + \arg TIMER_INT_TRG: trigger interrupt enable, TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_BRK: break interrupt enable, TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_interrupt_enable(uint32_t timer_periph, uint32_t interrupt) +{ + TIMER_DMAINTEN(timer_periph) |= (uint32_t) interrupt; +} + +/*! + \brief disable the TIMER interrupt + \param[in] timer_periph: please refer to the following parameters + \param[in] interrupt: timer interrupt source disable + only one parameter can be selected which is shown as below: + \arg TIMER_INT_UP: update interrupt disable, TIMERx(x=0..13) + \arg TIMER_INT_CH0: channel 0 interrupt disable, TIMERx(x=0..4,7..13) + \arg TIMER_INT_CH1: channel 1 interrupt disable, TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_CH2: channel 2 interrupt disable, TIMERx(x=0..4,7) + \arg TIMER_INT_CH3: channel 3 interrupt disable , TIMERx(x=0..4,7) + \arg TIMER_INT_CMT: commutation interrupt disable, TIMERx(x=0,7) + \arg TIMER_INT_TRG: trigger interrupt disable, TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_BRK: break interrupt disable, TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_interrupt_disable(uint32_t timer_periph, uint32_t interrupt) +{ + TIMER_DMAINTEN(timer_periph) &= (~(uint32_t)interrupt); +} + +/*! + \brief get timer interrupt flag + \param[in] timer_periph: please refer to the following parameters + \param[in] int_flag: the timer interrupt bits + only one parameter can be selected which is shown as below: + \arg TIMER_INT_FLAG_UP: update interrupt flag,TIMERx(x=0..13) + \arg TIMER_INT_FLAG_CH0: channel 0 interrupt flag,TIMERx(x=0..4,7..13) + \arg TIMER_INT_FLAG_CH1: channel 1 interrupt flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_FLAG_CH2: channel 2 interrupt flag,TIMERx(x=0..4,7) + \arg TIMER_INT_FLAG_CH3: channel 3 interrupt flag,TIMERx(x=0..4,7) + \arg TIMER_INT_FLAG_CMT: channel commutation interrupt flag,TIMERx(x=0,7) + \arg TIMER_INT_FLAG_TRG: trigger interrupt flag,TIMERx(x=0,7,8,11) + \arg TIMER_INT_FLAG_BRK: break interrupt flag,TIMERx(x=0,7) + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus timer_interrupt_flag_get(uint32_t timer_periph, uint32_t int_flag) +{ + uint32_t val; + val = (TIMER_DMAINTEN(timer_periph) & int_flag); + if((RESET != (TIMER_INTF(timer_periph) & int_flag) ) && (RESET != val)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear TIMER interrupt flag + \param[in] timer_periph: please refer to the following parameters + \param[in] int_flag: the timer interrupt bits + only one parameter can be selected which is shown as below: + \arg TIMER_INT_FLAG_UP: update interrupt flag,TIMERx(x=0..13) + \arg TIMER_INT_FLAG_CH0: channel 0 interrupt flag,TIMERx(x=0..4,7..13) + \arg TIMER_INT_FLAG_CH1: channel 1 interrupt flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_FLAG_CH2: channel 2 interrupt flag,TIMERx(x=0..4,7) + \arg TIMER_INT_FLAG_CH3: channel 3 interrupt flag,TIMERx(x=0..4,7) + \arg TIMER_INT_FLAG_CMT: channel commutation interrupt flag,TIMERx(x=0,7) + \arg TIMER_INT_FLAG_TRG: trigger interrupt flag,TIMERx(x=0,7,8,11) + \arg TIMER_INT_FLAG_BRK: break interrupt flag,TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_interrupt_flag_clear(uint32_t timer_periph, uint32_t int_flag) +{ + TIMER_INTF(timer_periph) = (~(uint32_t)int_flag); +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_timer.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_timer.c.base@1.5.0 new file mode 100644 index 0000000..9a53c16 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_timer.c.base@1.5.0 @@ -0,0 +1,2039 @@ +/*! + \file gd32e50x_timer.c + \brief TIMER driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_timer.h" + +/* TIMER init parameter mask */ +#define ALIGNEDMODE_MASK ((uint32_t)0x00000060U) /*!< TIMER init parameter aligne dmode mask */ +#define COUNTERDIRECTION_MASK ((uint32_t)0x00000010U) /*!< TIMER init parameter counter direction mask */ +#define CLOCKDIVISION_MASK ((uint32_t)0x00000300U) /*!< TIMER init parameter clock division value mask */ + +/*! + \brief deinit a TIMER + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_deinit(uint32_t timer_periph) +{ + switch(timer_periph){ + case TIMER0: + /* reset TIMER0 */ + rcu_periph_reset_enable(RCU_TIMER0RST); + rcu_periph_reset_disable(RCU_TIMER0RST); + break; + case TIMER1: + /* reset TIMER1 */ + rcu_periph_reset_enable(RCU_TIMER1RST); + rcu_periph_reset_disable(RCU_TIMER1RST); + break; + case TIMER2: + /* reset TIMER2 */ + rcu_periph_reset_enable(RCU_TIMER2RST); + rcu_periph_reset_disable(RCU_TIMER2RST); + break; + case TIMER3: + /* reset TIMER3 */ + rcu_periph_reset_enable(RCU_TIMER3RST); + rcu_periph_reset_disable(RCU_TIMER3RST); + break; + case TIMER4: + /* reset TIMER4 */ + rcu_periph_reset_enable(RCU_TIMER4RST); + rcu_periph_reset_disable(RCU_TIMER4RST); + break; + case TIMER5: + /* reset TIMER5 */ + rcu_periph_reset_enable(RCU_TIMER5RST); + rcu_periph_reset_disable(RCU_TIMER5RST); + break; + case TIMER6: + /* reset TIMER6 */ + rcu_periph_reset_enable(RCU_TIMER6RST); + rcu_periph_reset_disable(RCU_TIMER6RST); + break; + case TIMER7: + /* reset TIMER7 */ + rcu_periph_reset_enable(RCU_TIMER7RST); + rcu_periph_reset_disable(RCU_TIMER7RST); + break; +#ifndef GD32EPRT + case TIMER8: + /* reset TIMER8 */ + rcu_periph_reset_enable(RCU_TIMER8RST); + rcu_periph_reset_disable(RCU_TIMER8RST); + break; + case TIMER9: + /* reset TIMER9 */ + rcu_periph_reset_enable(RCU_TIMER9RST); + rcu_periph_reset_disable(RCU_TIMER9RST); + break; + case TIMER10: + /* reset TIMER10 */ + rcu_periph_reset_enable(RCU_TIMER10RST); + rcu_periph_reset_disable(RCU_TIMER10RST); + break; + case TIMER11: + /* reset TIMER11 */ + rcu_periph_reset_enable(RCU_TIMER11RST); + rcu_periph_reset_disable(RCU_TIMER11RST); + break; + case TIMER12: + /* reset TIMER12 */ + rcu_periph_reset_enable(RCU_TIMER12RST); + rcu_periph_reset_disable(RCU_TIMER12RST); + break; + case TIMER13: + /* reset TIMER13 */ + rcu_periph_reset_enable(RCU_TIMER13RST); + rcu_periph_reset_disable(RCU_TIMER13RST); + break; +#endif /* GD32EPRT */ + default: + break; + } +} + +/*! + \brief initialize TIMER init parameter struct with a default value + \param[in] initpara: init parameter struct + \param[out] none + \retval none +*/ +void timer_struct_para_init(timer_parameter_struct* initpara) +{ + /* initialize the init parameter struct member with the default value */ + initpara->prescaler = 0U; + initpara->alignedmode = TIMER_COUNTER_EDGE; + initpara->counterdirection = TIMER_COUNTER_UP; + initpara->period = 65535U; + initpara->clockdivision = TIMER_CKDIV_DIV1; + initpara->repetitioncounter = 0U; +} + +/*! + \brief initialize TIMER counter + \param[in] timer_periph: TIMERx(x=0..13) + \param[in] initpara: init parameter struct + prescaler: prescaler value of the counter clock, 0~65535 + alignedmode: TIMER_COUNTER_EDGE, TIMER_COUNTER_CENTER_DOWN, TIMER_COUNTER_CENTER_UP, TIMER_COUNTER_CENTER_BOTH + counterdirection: TIMER_COUNTER_UP, TIMER_COUNTER_DOWN + period: counter auto reload value, 0~65535 + clockdivision: TIMER_CKDIV_DIV1, TIMER_CKDIV_DIV2, TIMER_CKDIV_DIV4 + repetitioncounter: counter repetition value, 0~255 + \param[out] none + \retval none +*/ +void timer_init(uint32_t timer_periph, timer_parameter_struct* initpara) +{ + /* configure the counter prescaler value */ + TIMER_PSC(timer_periph) = (uint16_t)initpara->prescaler; + + /* configure the counter direction and aligned mode */ + if((TIMER0 == timer_periph) || (TIMER1 == timer_periph) || (TIMER2 == timer_periph) + || (TIMER3 == timer_periph) || (TIMER4 == timer_periph) || (TIMER7 == timer_periph)){ + TIMER_CTL0(timer_periph) &= (~(uint32_t)(TIMER_CTL0_DIR | TIMER_CTL0_CAM)); + TIMER_CTL0(timer_periph) |= (uint32_t)(initpara->alignedmode & ALIGNEDMODE_MASK); + TIMER_CTL0(timer_periph) |= (uint32_t)(initpara->counterdirection & COUNTERDIRECTION_MASK); + } + + /* configure the autoreload value */ + TIMER_CAR(timer_periph) = (uint32_t)initpara->period; + + if((TIMER5 != timer_periph) && (TIMER6 != timer_periph)){ + /* reset the CKDIV bit */ + TIMER_CTL0(timer_periph) &= (~(uint32_t)TIMER_CTL0_CKDIV); + TIMER_CTL0(timer_periph) |= (uint32_t)(initpara->clockdivision & CLOCKDIVISION_MASK); + } + + if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){ + /* configure the repetition counter value */ + TIMER_CREP(timer_periph) = (uint32_t)initpara->repetitioncounter; + } + + /* generate an update event */ + TIMER_SWEVG(timer_periph) |= (uint32_t)TIMER_SWEVG_UPG; +} + +/*! + \brief enable a TIMER + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_enable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) |= (uint32_t)TIMER_CTL0_CEN; +} + +/*! + \brief disable a TIMER + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_disable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_CEN; +} + +/*! + \brief enable the auto reload shadow function + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_auto_reload_shadow_enable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) |= (uint32_t)TIMER_CTL0_ARSE; +} + +/*! + \brief disable the auto reload shadow function + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_auto_reload_shadow_disable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_ARSE; +} + +/*! + \brief enable the update event + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_update_event_enable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_UPDIS; +} + +/*! + \brief disable the update event + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval none +*/ +void timer_update_event_disable(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) |= (uint32_t) TIMER_CTL0_UPDIS; +} + +/*! + \brief set TIMER counter alignment mode + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] aligned: + only one parameter can be selected which is shown as below: + \arg TIMER_COUNTER_EDGE: edge-aligned mode + \arg TIMER_COUNTER_CENTER_DOWN: center-aligned and counting down assert mode + \arg TIMER_COUNTER_CENTER_UP: center-aligned and counting up assert mode + \arg TIMER_COUNTER_CENTER_BOTH: center-aligned and counting up/down assert mode + \param[out] none + \retval none +*/ +void timer_counter_alignment(uint32_t timer_periph, uint16_t aligned) +{ + TIMER_CTL0(timer_periph) &= (uint32_t)(~TIMER_CTL0_CAM); + TIMER_CTL0(timer_periph) |= (uint32_t)aligned; +} + +/*! + \brief set TIMER counter up direction + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_counter_up_direction(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_DIR; +} + +/*! + \brief set TIMER counter down direction + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_counter_down_direction(uint32_t timer_periph) +{ + TIMER_CTL0(timer_periph) |= (uint32_t)TIMER_CTL0_DIR; +} + +/*! + \brief configure TIMER prescaler + \param[in] timer_periph: TIMERx(x=0..13) + \param[in] prescaler: prescaler value,0~65535 + \param[in] pscreload: prescaler reload mode + only one parameter can be selected which is shown as below: + \arg TIMER_PSC_RELOAD_NOW: the prescaler is loaded right now + \arg TIMER_PSC_RELOAD_UPDATE: the prescaler is loaded at the next update event + \param[out] none + \retval none +*/ +void timer_prescaler_config(uint32_t timer_periph, uint16_t prescaler, uint32_t pscreload) +{ + TIMER_PSC(timer_periph) = (uint32_t)prescaler; + + if(TIMER_PSC_RELOAD_NOW == pscreload){ + TIMER_SWEVG(timer_periph) |= (uint32_t)TIMER_SWEVG_UPG; + } +} + +/*! + \brief configure TIMER repetition register value + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] repetition: the counter repetition value, 0~255 + \param[out] none + \retval none +*/ +void timer_repetition_value_config(uint32_t timer_periph, uint16_t repetition) +{ + TIMER_CREP(timer_periph) = (uint32_t)repetition; +} + +/*! + \brief configure TIMER autoreload register value + \param[in] timer_periph: TIMERx(x=0..13) + \param[in] autoreload: the counter auto-reload value,0~65535 + \param[out] none + \retval none +*/ +void timer_autoreload_value_config(uint32_t timer_periph, uint32_t autoreload) +{ + TIMER_CAR(timer_periph) = (uint32_t)autoreload; +} + +/*! + \brief configure TIMER counter register value + \param[in] timer_periph: TIMERx(x=0..13) + \param[in] counter: the counter value,0~65535 + \param[out] none + \retval none +*/ +void timer_counter_value_config(uint32_t timer_periph, uint32_t counter) +{ + TIMER_CNT(timer_periph) = (uint32_t)counter; +} + +/*! + \brief read TIMER counter value + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval counter value +*/ +uint32_t timer_counter_read(uint32_t timer_periph) +{ + uint32_t count_value = 0U; + count_value = TIMER_CNT(timer_periph); + return (count_value); +} + +/*! + \brief read TIMER prescaler value + \param[in] timer_periph: TIMERx(x=0..13) + \param[out] none + \retval prescaler register value +*/ +uint16_t timer_prescaler_read(uint32_t timer_periph) +{ + uint16_t prescaler_value = 0U; + prescaler_value = (uint16_t)(TIMER_PSC(timer_periph)); + return (prescaler_value); +} + +/*! + \brief configure TIMER single pulse mode + \param[in] timer_periph: TIMERx(x=0..8,11) + \param[in] spmode: + only one parameter can be selected which is shown as below: + \arg TIMER_SP_MODE_SINGLE: single pulse mode + \arg TIMER_SP_MODE_REPETITIVE: repetitive pulse mode + \param[out] none + \retval none +*/ +void timer_single_pulse_mode_config(uint32_t timer_periph, uint32_t spmode) +{ + if(TIMER_SP_MODE_SINGLE == spmode){ + TIMER_CTL0(timer_periph) |= (uint32_t)TIMER_CTL0_SPM; + }else if(TIMER_SP_MODE_REPETITIVE == spmode){ + TIMER_CTL0(timer_periph) &= ~((uint32_t)TIMER_CTL0_SPM); + }else{ + /* illegal parameters */ + } +} + +/*! + \brief configure TIMER update source + \param[in] timer_periph: TIMERx(x=0..13) + \param[in] update: + only one parameter can be selected which is shown as below: + \arg TIMER_UPDATE_SRC_GLOBAL: update generate by setting of UPG bit or the counter overflow/underflow,or the slave mode controller trigger + \arg TIMER_UPDATE_SRC_REGULAR: update generate only by counter overflow/underflow + \param[out] none + \retval none +*/ +void timer_update_source_config(uint32_t timer_periph, uint32_t update) +{ + if(TIMER_UPDATE_SRC_REGULAR == update){ + TIMER_CTL0(timer_periph) |= (uint32_t)TIMER_CTL0_UPS; + }else if(TIMER_UPDATE_SRC_GLOBAL == update){ + TIMER_CTL0(timer_periph) &= ~(uint32_t)TIMER_CTL0_UPS; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief enable the TIMER DMA + \param[in] timer_periph: please refer to the following parameters + \param[in] dma: specify which DMA to enable + only one parameter can be selected which is shown as below: + \arg TIMER_DMA_UPD: update DMA enable,TIMERx(x=0..7) + \arg TIMER_DMA_CH0D: channel 0 DMA enable,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH1D: channel 1 DMA enable,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH2D: channel 2 DMA enable,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH3D: channel 3 DMA enable,TIMERx(x=0..4,7) + \arg TIMER_DMA_CMTD: commutation DMA request enable,TIMERx(x=0,7) + \arg TIMER_DMA_TRGD: trigger DMA enable,TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_dma_enable(uint32_t timer_periph, uint16_t dma) +{ + TIMER_DMAINTEN(timer_periph) |= (uint32_t) dma; +} + +/*! + \brief disable the TIMER DMA + \param[in] timer_periph: please refer to the following parameters + \param[in] dma: specify which DMA to enable + one or more parameters can be selected which are shown as below: + \arg TIMER_DMA_UPD: update DMA ,TIMERx(x=0..7) + \arg TIMER_DMA_CH0D: channel 0 DMA request,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH1D: channel 1 DMA request,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH2D: channel 2 DMA request,TIMERx(x=0..4,7) + \arg TIMER_DMA_CH3D: channel 3 DMA request,TIMERx(x=0..4,7) + \arg TIMER_DMA_CMTD: commutation DMA request ,TIMERx(x=0,7) + \arg TIMER_DMA_TRGD: trigger DMA request,TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_dma_disable(uint32_t timer_periph, uint16_t dma) +{ + TIMER_DMAINTEN(timer_periph) &= (~(uint32_t)(dma)); +} + +/*! + \brief channel DMA request source selection + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] dma_request: channel DMA request source selection + only one parameter can be selected which is shown as below: + \arg TIMER_DMAREQUEST_CHANNELEVENT: DMA request of channel y is sent when channel y event occurs + \arg TIMER_DMAREQUEST_UPDATEEVENT: DMA request of channel y is sent when update event occurs + \param[out] none + \retval none +*/ +void timer_channel_dma_request_source_select(uint32_t timer_periph, uint32_t dma_request) +{ + if(TIMER_DMAREQUEST_UPDATEEVENT == dma_request){ + TIMER_CTL1(timer_periph) |= (uint32_t)TIMER_CTL1_DMAS; + }else if(TIMER_DMAREQUEST_CHANNELEVENT == dma_request){ + TIMER_CTL1(timer_periph) &= ~(uint32_t)TIMER_CTL1_DMAS; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief configure the TIMER DMA transfer + \param[in] timer_periph: please refer to the following parameters + \param[in] dma_baseaddr: + only one parameter can be selected which is shown as below: + \arg TIMER_DMACFG_DMATA_CTL0: DMA transfer address is TIMER_CTL0,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CTL1: DMA transfer address is TIMER_CTL1,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_SMCFG: DMA transfer address is TIMER_SMCFG,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_DMAINTEN: DMA transfer address is TIMER_DMAINTEN,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_INTF: DMA transfer address is TIMER_INTF,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_SWEVG: DMA transfer address is TIMER_SWEVG,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CHCTL0: DMA transfer address is TIMER_CHCTL0,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CHCTL1: DMA transfer address is TIMER_CHCTL1,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CHCTL2: DMA transfer address is TIMER_CHCTL2,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CNT: DMA transfer address is TIMER_CNT,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_PSC: DMA transfer address is TIMER_PSC,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CAR: DMA transfer address is TIMER_CAR,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CREP: DMA transfer address is TIMER_CREP,TIMERx(x=0,7) + \arg TIMER_DMACFG_DMATA_CH0CV: DMA transfer address is TIMER_CH0CV,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CH1CV: DMA transfer address is TIMER_CH1CV,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CH2CV: DMA transfer address is TIMER_CH2CV,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CH3CV: DMA transfer address is TIMER_CH3CV,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_CCHP: DMA transfer address is TIMER_CCHP,TIMERx(x=0,7) + \arg TIMER_DMACFG_DMATA_DMACFG: DMA transfer address is TIMER_DMACFG,TIMERx(x=0..4,7) + \arg TIMER_DMACFG_DMATA_DMATB: DMA transfer address is TIMER_DMATB,TIMERx(x=0..4,7) + \param[in] dma_lenth: + only one parameter can be selected which is shown as below: + \arg TIMER_DMACFG_DMATC_xTRANSFER(x=1..18): DMA transfer x time + \param[out] none + \retval none +*/ +void timer_dma_transfer_config(uint32_t timer_periph, uint32_t dma_baseaddr, uint32_t dma_lenth) +{ + TIMER_DMACFG(timer_periph) &= (~(uint32_t)(TIMER_DMACFG_DMATA | TIMER_DMACFG_DMATC)); + TIMER_DMACFG(timer_periph) |= (uint32_t)(dma_baseaddr | dma_lenth); +} + +/*! + \brief software generate events + \param[in] timer_periph: please refer to the following parameters + \param[in] event: the timer software event generation sources + one or more parameters can be selected which are shown as below: + \arg TIMER_EVENT_SRC_UPG: update event,TIMERx(x=0..13) + \arg TIMER_EVENT_SRC_CH0G: channel 0 capture or compare event generation,TIMERx(x=0..4,7..13) + \arg TIMER_EVENT_SRC_CH1G: channel 1 capture or compare event generation,TIMERx(x=0..4,7,8,11) + \arg TIMER_EVENT_SRC_CH2G: channel 2 capture or compare event generation,TIMERx(x=0..4,7) + \arg TIMER_EVENT_SRC_CH3G: channel 3 capture or compare event generation,TIMERx(x=0..4,7) + \arg TIMER_EVENT_SRC_CMTG: channel commutation event generation,TIMERx(x=0,7) + \arg TIMER_EVENT_SRC_TRGG: trigger event generation,TIMERx(x=0..4,7,8,11) + \arg TIMER_EVENT_SRC_BRKG: break event generation,TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_event_software_generate(uint32_t timer_periph, uint16_t event) +{ + TIMER_SWEVG(timer_periph) |= (uint32_t)event; +} + +/*! + \brief initialize TIMER break parameter struct with a default value + \param[in] breakpara: TIMER break parameter struct + \param[out] none + \retval none +*/ +void timer_break_struct_para_init(timer_break_parameter_struct* breakpara) +{ + /* initialize the break parameter struct member with the default value */ + breakpara->runoffstate = TIMER_ROS_STATE_DISABLE; + breakpara->ideloffstate = TIMER_IOS_STATE_DISABLE; + breakpara->deadtime = 0U; + breakpara->breakpolarity = TIMER_BREAK_POLARITY_LOW; + breakpara->outputautostate = TIMER_OUTAUTO_DISABLE; + breakpara->protectmode = TIMER_CCHP_PROT_OFF; + breakpara->breakstate = TIMER_BREAK_DISABLE; +} + +/*! + \brief configure TIMER break function + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] breakpara: TIMER break parameter struct + runoffstate: TIMER_ROS_STATE_ENABLE,TIMER_ROS_STATE_DISABLE + ideloffstate: TIMER_IOS_STATE_ENABLE,TIMER_IOS_STATE_DISABLE + deadtime: 0~255 + breakpolarity: TIMER_BREAK_POLARITY_LOW,TIMER_BREAK_POLARITY_HIGH + outputautostate: TIMER_OUTAUTO_ENABLE,TIMER_OUTAUTO_DISABLE + protectmode: TIMER_CCHP_PROT_OFF,TIMER_CCHP_PROT_0,TIMER_CCHP_PROT_1,TIMER_CCHP_PROT_2 + breakstate: TIMER_BREAK_ENABLE,TIMER_BREAK_DISABLE + \param[out] none + \retval none +*/ +void timer_break_config(uint32_t timer_periph, timer_break_parameter_struct* breakpara) +{ + TIMER_CCHP(timer_periph) = (uint32_t)(((uint32_t)(breakpara->runoffstate))| + ((uint32_t)(breakpara->ideloffstate))| + ((uint32_t)(breakpara->deadtime))| + ((uint32_t)(breakpara->breakpolarity))| + ((uint32_t)(breakpara->outputautostate)) | + ((uint32_t)(breakpara->protectmode))| + ((uint32_t)(breakpara->breakstate))) ; +} + +/*! + \brief enable TIMER break function + \param[in] timer_periph: TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_break_enable(uint32_t timer_periph) +{ + TIMER_CCHP(timer_periph) |= (uint32_t)TIMER_CCHP_BRKEN; +} + +/*! + \brief disable TIMER break function + \param[in] timer_periph: TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_break_disable(uint32_t timer_periph) +{ + TIMER_CCHP(timer_periph) &= ~(uint32_t)TIMER_CCHP_BRKEN; +} + +/*! + \brief enable TIMER output automatic function + \param[in] timer_periph: TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_automatic_output_enable(uint32_t timer_periph) +{ + TIMER_CCHP(timer_periph) |= (uint32_t)TIMER_CCHP_OAEN; +} + +/*! + \brief disable TIMER output automatic function + \param[in] timer_periph: TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_automatic_output_disable(uint32_t timer_periph) +{ + TIMER_CCHP(timer_periph) &= ~(uint32_t)TIMER_CCHP_OAEN; +} + +/*! + \brief configure TIMER primary output function + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void timer_primary_output_config(uint32_t timer_periph, ControlStatus newvalue) +{ + if(ENABLE == newvalue){ + TIMER_CCHP(timer_periph) |= (uint32_t)TIMER_CCHP_POEN; + }else{ + TIMER_CCHP(timer_periph) &= (~(uint32_t)TIMER_CCHP_POEN); + } +} + +/*! + \brief enable or disable channel capture/compare control shadow register + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void timer_channel_control_shadow_config(uint32_t timer_periph, ControlStatus newvalue) +{ + if(ENABLE == newvalue){ + TIMER_CTL1(timer_periph) |= (uint32_t)TIMER_CTL1_CCSE; + }else{ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_CCSE); + } +} + +/*! + \brief configure TIMER channel control shadow register update control + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] ccuctl: channel control shadow register update control + only one parameter can be selected which is shown as below: + \arg TIMER_UPDATECTL_CCU: the shadow registers update by when CMTG bit is set + \arg TIMER_UPDATECTL_CCUTRI: the shadow registers update by when CMTG bit is set or an rising edge of TRGI occurs + \param[out] none + \retval none +*/ +void timer_channel_control_shadow_update_config(uint32_t timer_periph, uint32_t ccuctl) +{ + if(TIMER_UPDATECTL_CCU == ccuctl){ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_CCUC); + }else if(TIMER_UPDATECTL_CCUTRI == ccuctl){ + TIMER_CTL1(timer_periph) |= (uint32_t)TIMER_CTL1_CCUC; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief initialize TIMER channel output parameter struct with a default value + \param[in] ocpara: TIMER channel n output parameter struct + \param[out] none + \retval none +*/ +void timer_channel_output_struct_para_init(timer_oc_parameter_struct* ocpara) +{ + /* initialize the channel output parameter struct member with the default value */ + ocpara->outputstate = TIMER_CCX_DISABLE; + ocpara->outputnstate = TIMER_CCXN_DISABLE; + ocpara->ocpolarity = TIMER_OC_POLARITY_HIGH; + ocpara->ocnpolarity = TIMER_OCN_POLARITY_HIGH; + ocpara->ocidlestate = TIMER_OC_IDLE_STATE_LOW; + ocpara->ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; +} + +/*! + \brief configure TIMER channel output function + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel 0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel 1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel 2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel 3(TIMERx(x=0..4,7)) + \param[in] ocpara: TIMER channeln output parameter struct + outputstate: TIMER_CCX_ENABLE,TIMER_CCX_DISABLE + outputnstate: TIMER_CCXN_ENABLE,TIMER_CCXN_DISABLE + ocpolarity: TIMER_OC_POLARITY_HIGH,TIMER_OC_POLARITY_LOW + ocnpolarity: TIMER_OCN_POLARITY_HIGH,TIMER_OCN_POLARITY_LOW + ocidlestate: TIMER_OC_IDLE_STATE_LOW,TIMER_OC_IDLE_STATE_HIGH + ocnidlestate: TIMER_OCN_IDLE_STATE_LOW,TIMER_OCN_IDLE_STATE_HIGH + \param[out] none + \retval none +*/ +void timer_channel_output_config(uint32_t timer_periph, uint16_t channel, timer_oc_parameter_struct* ocpara) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + /* reset the CH0EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + TIMER_CHCTL0(timer_periph) &= ~(uint32_t)TIMER_CHCTL0_CH0MS; + /* set the CH0EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocpara->outputstate; + /* reset the CH0P bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0P); + /* set the CH0P bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocpara->ocpolarity; + + if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){ + /* reset the CH0NEN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0NEN); + /* set the CH0NEN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocpara->outputnstate; + /* reset the CH0NP bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0NP); + /* set the CH0NP bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocpara->ocnpolarity; + /* reset the ISO0 bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO0); + /* set the ISO0 bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)ocpara->ocidlestate; + /* reset the ISO0N bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO0N); + /* set the ISO0N bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)ocpara->ocnidlestate; + } + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + /* reset the CH1EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + TIMER_CHCTL0(timer_periph) &= ~(uint32_t)TIMER_CHCTL0_CH1MS; + /* set the CH1EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->outputstate) << 4U); + /* reset the CH1P bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1P); + /* set the CH1P bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocpolarity) << 4U); + + if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){ + /* reset the CH1NEN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1NEN); + /* set the CH1NEN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->outputnstate) << 4U); + /* reset the CH1NP bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1NP); + /* set the CH1NP bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocnpolarity) << 4U); + /* reset the ISO1 bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO1); + /* set the ISO1 bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocidlestate) << 2U); + /* reset the ISO1N bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO1N); + /* set the ISO1N bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocnidlestate) << 2U); + } + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + /* reset the CH2EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2EN); + TIMER_CHCTL1(timer_periph) &= ~(uint32_t)TIMER_CHCTL1_CH2MS; + /* set the CH2EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->outputstate) << 8U); + /* reset the CH2P bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2P); + /* set the CH2P bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocpolarity) << 8U); + + if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){ + /* reset the CH2NEN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2NEN); + /* set the CH2NEN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->outputnstate) << 8U); + /* reset the CH2NP bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2NP); + /* set the CH2NP bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocnpolarity) << 8U); + /* reset the ISO2 bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO2); + /* set the ISO2 bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocidlestate) << 4U); + /* reset the ISO2N bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO2N); + /* set the ISO2N bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocnidlestate) << 4U); + } + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + /* reset the CH3EN bit */ + TIMER_CHCTL2(timer_periph) &=(~(uint32_t)TIMER_CHCTL2_CH3EN); + TIMER_CHCTL1(timer_periph) &= ~(uint32_t)TIMER_CHCTL1_CH3MS; + /* set the CH3EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->outputstate) << 12U); + /* reset the CH3P bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH3P); + /* set the CH3P bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocpolarity) << 12U); + + if((TIMER0 == timer_periph) || (TIMER7 == timer_periph)){ + /* reset the ISO3 bit */ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_ISO3); + /* set the ISO3 bit */ + TIMER_CTL1(timer_periph) |= (uint32_t)((uint32_t)(ocpara->ocidlestate) << 6U); + } + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output compare mode + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] ocmode: channel output compare mode + only one parameter can be selected which is shown as below: + \arg TIMER_OC_MODE_TIMING: timing mode + \arg TIMER_OC_MODE_ACTIVE: active mode + \arg TIMER_OC_MODE_INACTIVE: inactive mode + \arg TIMER_OC_MODE_TOGGLE: toggle mode + \arg TIMER_OC_MODE_LOW: force low mode + \arg TIMER_OC_MODE_HIGH: force high mode + \arg TIMER_OC_MODE_PWM0: PWM0 mode + \arg TIMER_OC_MODE_PWM1: PWM1 mode + \param[out] none + \retval none +*/ +void timer_channel_output_mode_config(uint32_t timer_periph, uint16_t channel, uint16_t ocmode) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0COMCTL); + TIMER_CHCTL0(timer_periph) |= (uint32_t)ocmode; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1COMCTL); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(ocmode) << 8U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2COMCTL); + TIMER_CHCTL1(timer_periph) |= (uint32_t)ocmode; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3COMCTL); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(ocmode) << 8U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output pulse value + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] pulse: channel output pulse value + \param[out] none + \retval none +*/ +void timer_channel_output_pulse_value_config(uint32_t timer_periph, uint16_t channel, uint32_t pulse) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CH0CV(timer_periph) = (uint32_t)pulse; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CH1CV(timer_periph) = (uint32_t)pulse; + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CH2CV(timer_periph) = (uint32_t)pulse; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CH3CV(timer_periph) = (uint32_t)pulse; + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output shadow function + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] ocshadow: channel output shadow state + only one parameter can be selected which is shown as below: + \arg TIMER_OC_SHADOW_ENABLE: channel output shadow state enable + \arg TIMER_OC_SHADOW_DISABLE: channel output shadow state disable + \param[out] none + \retval none +*/ +void timer_channel_output_shadow_config(uint32_t timer_periph, uint16_t channel, uint16_t ocshadow) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0COMSEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)ocshadow; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1COMSEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(ocshadow) << 8U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2COMSEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)ocshadow; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3COMSEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(ocshadow) << 8U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output fast function + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] ocfast: channel output fast function + only one parameter can be selected which is shown as below: + \arg TIMER_OC_FAST_ENABLE: channel output fast function enable + \arg TIMER_OC_FAST_DISABLE: channel output fast function disable + \param[out] none + \retval none +*/ +void timer_channel_output_fast_config(uint32_t timer_periph, uint16_t channel, uint16_t ocfast) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0COMFEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)ocfast; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1COMFEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)ocfast << 8U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2COMFEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)ocfast; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3COMFEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)ocfast << 8U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output clear function + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0 + \arg TIMER_CH_1: TIMER channel1 + \arg TIMER_CH_2: TIMER channel2 + \arg TIMER_CH_3: TIMER channel3 + \param[in] occlear: channel output clear function + only one parameter can be selected which is shown as below: + \arg TIMER_OC_CLEAR_ENABLE: channel output clear function enable + \arg TIMER_OC_CLEAR_DISABLE: channel output clear function disable + \param[out] none + \retval none +*/ +void timer_channel_output_clear_config(uint32_t timer_periph, uint16_t channel, uint16_t occlear) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0COMCEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)occlear; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1COMCEN); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)occlear << 8U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2COMCEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)occlear; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3COMCEN); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)occlear << 8U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel output polarity + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] ocpolarity: channel output polarity + only one parameter can be selected which is shown as below: + \arg TIMER_OC_POLARITY_HIGH: channel output polarity is high + \arg TIMER_OC_POLARITY_LOW: channel output polarity is low + \param[out] none + \retval none +*/ +void timer_channel_output_polarity_config(uint32_t timer_periph, uint16_t channel, uint16_t ocpolarity) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0P); + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocpolarity; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1P); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocpolarity << 4U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2P); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocpolarity << 8U); + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH3P); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocpolarity << 12U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel complementary output polarity + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0,7)) + \param[in] ocnpolarity: channel complementary output polarity + only one parameter can be selected which is shown as below: + \arg TIMER_OCN_POLARITY_HIGH: channel complementary output polarity is high + \arg TIMER_OCN_POLARITY_LOW: channel complementary output polarity is low + \param[out] none + \retval none +*/ +void timer_channel_complementary_output_polarity_config(uint32_t timer_periph, uint16_t channel, uint16_t ocnpolarity) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0NP); + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocnpolarity; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1NP); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocnpolarity << 4U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2NP); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocnpolarity << 8U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel enable state + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] state: TIMER channel enable state + only one parameter can be selected which is shown as below: + \arg TIMER_CCX_ENABLE: channel enable + \arg TIMER_CCX_DISABLE: channel disable + \param[out] none + \retval none +*/ +void timer_channel_output_state_config(uint32_t timer_periph, uint16_t channel, uint32_t state) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)state; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)state << 4U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2EN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)state << 8U); + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH3EN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)state << 12U); + break; + default: + break; + } +} + +/*! + \brief configure TIMER channel complementary output enable state + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0,7)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0,7)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0,7)) + \param[in] ocnstate: TIMER channel complementary output enable state + only one parameter can be selected which is shown as below: + \arg TIMER_CCXN_ENABLE: channel complementary enable + \arg TIMER_CCXN_DISABLE: channel complementary disable + \param[out] none + \retval none +*/ +void timer_channel_complementary_output_state_config(uint32_t timer_periph, uint16_t channel, uint16_t ocnstate) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0NEN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)ocnstate; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1NEN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocnstate << 4U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2NEN); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)ocnstate << 8U); + break; + default: + break; + } +} + +/*! + \brief initialize TIMER channel input parameter struct with a default value + \param[in] icpara: TIMER channel intput parameter struct + \param[out] none + \retval none +*/ +void timer_channel_input_struct_para_init(timer_ic_parameter_struct* icpara) +{ + /* initialize the channel input parameter struct member with the default value */ + icpara->icpolarity = TIMER_IC_POLARITY_RISING; + icpara->icselection = TIMER_IC_SELECTION_DIRECTTI; + icpara->icprescaler = TIMER_IC_PSC_DIV1; + icpara->icfilter = 0U; +} + +/*! + \brief configure TIMER input capture parameter + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] icpara: TIMER channel intput parameter struct + icpolarity: TIMER_IC_POLARITY_RISING,TIMER_IC_POLARITY_FALLING,TIMER_IC_POLARITY_BOTH_EDGE + icselection: TIMER_IC_SELECTION_DIRECTTI,TIMER_IC_SELECTION_INDIRECTTI,TIMER_IC_SELECTION_ITS + icprescaler: TIMER_IC_PSC_DIV1,TIMER_IC_PSC_DIV2,TIMER_IC_PSC_DIV4,TIMER_IC_PSC_DIV8 + icfilter: 0~15 + \param[out] none + \retval none +*/ +void timer_input_capture_config(uint32_t timer_periph, uint16_t channel, timer_ic_parameter_struct* icpara) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + /* reset the CH0EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + + /* reset the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP)); + TIMER_CHCTL2(timer_periph) |= (uint32_t)(icpara->icpolarity); + /* reset the CH0MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0MS); + TIMER_CHCTL0(timer_periph) |= (uint32_t)(icpara->icselection); + /* reset the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0CAPFLT); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpara->icfilter) << 4U); + + /* set the CH0EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH0EN; + break; + + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + /* reset the CH1EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + + /* reset the CH1P and CH1NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP)); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(icpara->icpolarity) << 4U); + /* reset the CH1MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1MS); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpara->icselection) << 8U); + /* reset the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1CAPFLT); + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpara->icfilter) << 12U); + + /* set the CH1EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH1EN; + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + /* reset the CH2EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH2EN); + + /* reset the CH2P and CH2NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH2P | TIMER_CHCTL2_CH2NP)); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(icpara->icpolarity) << 8U); + + /* reset the CH2MS bit */ + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2MS); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(icpara->icselection)); + + /* reset the CH2CAPFLT bit */ + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2CAPFLT); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(icpara->icfilter) << 4U); + + /* set the CH2EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH2EN; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + /* reset the CH3EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH3EN); + + /* reset the CH3P and CH3NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH3P|TIMER_CHCTL2_CH3NP)); + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(icpara->icpolarity) << 12U); + + /* reset the CH3MS bit */ + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3MS); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(icpara->icselection) << 8U); + + /* reset the CH3CAPFLT bit */ + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3CAPFLT); + TIMER_CHCTL1(timer_periph) |= (uint32_t)((uint32_t)(icpara->icfilter) << 12U); + + /* set the CH3EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH3EN; + break; + default: + break; + } + /* configure TIMER channel input capture prescaler value */ + timer_channel_input_capture_prescaler_config(timer_periph, channel, (uint16_t)(icpara->icprescaler)); +} + +/*! + \brief configure TIMER channel input capture prescaler value + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[in] prescaler: channel input capture prescaler value + only one parameter can be selected which is shown as below: + \arg TIMER_IC_PSC_DIV1: no prescaler + \arg TIMER_IC_PSC_DIV2: divided by 2 + \arg TIMER_IC_PSC_DIV4: divided by 4 + \arg TIMER_IC_PSC_DIV8: divided by 8 + \param[out] none + \retval none +*/ +void timer_channel_input_capture_prescaler_config(uint32_t timer_periph, uint16_t channel, uint16_t prescaler) +{ + switch(channel){ + /* configure TIMER_CH_0 */ + case TIMER_CH_0: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0CAPPSC); + TIMER_CHCTL0(timer_periph) |= (uint32_t)prescaler; + break; + /* configure TIMER_CH_1 */ + case TIMER_CH_1: + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1CAPPSC); + TIMER_CHCTL0(timer_periph) |= ((uint32_t)prescaler << 8U); + break; + /* configure TIMER_CH_2 */ + case TIMER_CH_2: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH2CAPPSC); + TIMER_CHCTL1(timer_periph) |= (uint32_t)prescaler; + break; + /* configure TIMER_CH_3 */ + case TIMER_CH_3: + TIMER_CHCTL1(timer_periph) &= (~(uint32_t)TIMER_CHCTL1_CH3CAPPSC); + TIMER_CHCTL1(timer_periph) |= ((uint32_t)prescaler << 8U); + break; + default: + break; + } +} + +/*! + \brief read TIMER channel capture compare register value + \param[in] timer_periph: please refer to the following parameters + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0(TIMERx(x=0..4,7..13)) + \arg TIMER_CH_1: TIMER channel1(TIMERx(x=0..4,7,8,11)) + \arg TIMER_CH_2: TIMER channel2(TIMERx(x=0..4,7)) + \arg TIMER_CH_3: TIMER channel3(TIMERx(x=0..4,7)) + \param[out] none + \retval channel capture compare register value +*/ +uint32_t timer_channel_capture_value_register_read(uint32_t timer_periph, uint16_t channel) +{ + uint32_t count_value = 0U; + + switch(channel){ + /* read TIMER channel 0 capture compare register value */ + case TIMER_CH_0: + count_value = TIMER_CH0CV(timer_periph); + break; + /* read TIMER channel 1 capture compare register value */ + case TIMER_CH_1: + count_value = TIMER_CH1CV(timer_periph); + break; + /* read TIMER channel 2 capture compare register value */ + case TIMER_CH_2: + count_value = TIMER_CH2CV(timer_periph); + break; + /* read TIMER channel 3 capture compare register value */ + case TIMER_CH_3: + count_value = TIMER_CH3CV(timer_periph); + break; + default: + break; + } + return (count_value); +} + +/*! + \brief configure TIMER input pwm capture function + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] channel: + only one parameter can be selected which is shown as below: + \arg TIMER_CH_0: TIMER channel0 + \arg TIMER_CH_1: TIMER channel1 + \param[in] icpwm:TIMER channel intput pwm parameter struct + icpolarity: TIMER_IC_POLARITY_RISING,TIMER_IC_POLARITY_FALLING + icselection: TIMER_IC_SELECTION_DIRECTTI,TIMER_IC_SELECTION_INDIRECTTI + icprescaler: TIMER_IC_PSC_DIV1,TIMER_IC_PSC_DIV2,TIMER_IC_PSC_DIV4,TIMER_IC_PSC_DIV8 + icfilter: 0~15 + \param[out] none + \retval none +*/ +void timer_input_pwm_capture_config(uint32_t timer_periph, uint16_t channel, timer_ic_parameter_struct* icpwm) +{ + uint16_t icpolarity = 0x0U; + uint16_t icselection = 0x0U; + + /* Set channel input polarity */ + if(TIMER_IC_POLARITY_RISING == icpwm->icpolarity){ + icpolarity = TIMER_IC_POLARITY_FALLING; + }else{ + icpolarity = TIMER_IC_POLARITY_RISING; + } + /* Set channel input mode selection */ + if(TIMER_IC_SELECTION_DIRECTTI == icpwm->icselection){ + icselection = TIMER_IC_SELECTION_INDIRECTTI; + }else{ + icselection = TIMER_IC_SELECTION_DIRECTTI; + } + + if(TIMER_CH_0 == channel){ + /* reset the CH0EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + /* reset the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP)); + /* set the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)(icpwm->icpolarity); + /* reset the CH0MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0MS); + /* set the CH0MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)(icpwm->icselection); + /* reset the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0CAPFLT); + /* set the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= ((uint32_t)(icpwm->icfilter) << 4U); + /* set the CH0EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH0EN; + /* configure TIMER channel input capture prescaler value */ + timer_channel_input_capture_prescaler_config(timer_periph, TIMER_CH_0, (uint16_t)(icpwm->icprescaler)); + + /* reset the CH1EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + /* reset the CH1P and CH1NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP)); + /* set the CH1P and CH1NP bits */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)icpolarity<< 4U); + /* reset the CH1MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1MS); + /* set the CH1MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)icselection << 8U); + /* reset the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1CAPFLT); + /* set the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpwm->icfilter) << 12U); + /* set the CH1EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH1EN; + /* configure TIMER channel input capture prescaler value */ + timer_channel_input_capture_prescaler_config(timer_periph, TIMER_CH_1, (uint16_t)(icpwm->icprescaler)); + }else{ + /* reset the CH1EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + /* reset the CH1P and CH1NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP)); + /* set the CH1P and CH1NP bits */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)(icpwm->icpolarity) << 4U); + /* reset the CH1MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1MS); + /* set the CH1MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpwm->icselection) << 8U); + /* reset the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1CAPFLT); + /* set the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)(icpwm->icfilter) << 12U); + /* set the CH1EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH1EN; + /* configure TIMER channel input capture prescaler value */ + timer_channel_input_capture_prescaler_config(timer_periph, TIMER_CH_1, (uint16_t)(icpwm->icprescaler)); + + /* reset the CH0EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + /* reset the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP)); + /* set the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)icpolarity; + /* reset the CH0MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0MS); + /* set the CH0MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)icselection; + /* reset the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0CAPFLT); + /* set the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= ((uint32_t)(icpwm->icfilter) << 4U); + /* set the CH0EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH0EN; + /* configure TIMER channel input capture prescaler value */ + timer_channel_input_capture_prescaler_config(timer_periph, TIMER_CH_0, (uint16_t)(icpwm->icprescaler)); + } +} + +/*! + \brief configure TIMER hall sensor mode + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] hallmode: + only one parameter can be selected which is shown as below: + \arg TIMER_HALLINTERFACE_ENABLE: TIMER hall sensor mode enable + \arg TIMER_HALLINTERFACE_DISABLE: TIMER hall sensor mode disable + \param[out] none + \retval none +*/ +void timer_hall_mode_config(uint32_t timer_periph, uint32_t hallmode) +{ + if(TIMER_HALLINTERFACE_ENABLE == hallmode){ + TIMER_CTL1(timer_periph) |= (uint32_t)TIMER_CTL1_TI0S; + }else if(TIMER_HALLINTERFACE_DISABLE == hallmode){ + TIMER_CTL1(timer_periph) &= ~(uint32_t)TIMER_CTL1_TI0S; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief select TIMER input trigger source + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] intrigger: + only one parameter can be selected which is shown as below: + \arg TIMER_SMCFG_TRGSEL_ITI0: internal trigger 0 + \arg TIMER_SMCFG_TRGSEL_ITI1: internal trigger 1 + \arg TIMER_SMCFG_TRGSEL_ITI2: internal trigger 2 + \arg TIMER_SMCFG_TRGSEL_ITI3: internal trigger 3 + \arg TIMER_SMCFG_TRGSEL_CI0F_ED: TI0 edge detector + \arg TIMER_SMCFG_TRGSEL_CI0FE0: filtered TIMER input 0 + \arg TIMER_SMCFG_TRGSEL_CI1FE1: filtered TIMER input 1 + \arg TIMER_SMCFG_TRGSEL_ETIFP: external trigger(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_input_trigger_source_select(uint32_t timer_periph, uint32_t intrigger) +{ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_TRGS); + TIMER_SMCFG(timer_periph) |= (uint32_t)intrigger; +} + +/*! + \brief select TIMER master mode output trigger source + \param[in] timer_periph: TIMERx(x=0..7) + \param[in] outrigger: + only one parameter can be selected which is shown as below: + \arg TIMER_TRI_OUT_SRC_RESET: the UPG bit as trigger output + \arg TIMER_TRI_OUT_SRC_ENABLE: the counter enable signal TIMER_CTL0_CEN as trigger output + \arg TIMER_TRI_OUT_SRC_UPDATE: update event as trigger output + \arg TIMER_TRI_OUT_SRC_CH0: a capture or a compare match occurred in channal0 as trigger output TRGO + \arg TIMER_TRI_OUT_SRC_O0CPRE: O0CPRE as trigger output + \arg TIMER_TRI_OUT_SRC_O1CPRE: O1CPRE as trigger output + \arg TIMER_TRI_OUT_SRC_O2CPRE: O2CPRE as trigger output + \arg TIMER_TRI_OUT_SRC_O3CPRE: O3CPRE as trigger output + \param[out] none + \retval none +*/ +void timer_master_output_trigger_source_select(uint32_t timer_periph, uint32_t outrigger) +{ + TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_MMC); + TIMER_CTL1(timer_periph) |= (uint32_t)outrigger; +} + +/*! + \brief select TIMER slave mode + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] slavemode: + only one parameter can be selected which is shown as below: + \arg TIMER_SLAVE_MODE_DISABLE: slave mode disable + \arg TIMER_QUAD_DECODER_MODE0: quadrature decoder mode 0 + \arg TIMER_QUAD_DECODER_MODE1: quadrature decoder mode 1 + \arg TIMER_QUAD_DECODER_MODE2: quadrature decoder mode 2 + \arg TIMER_SLAVE_MODE_RESTART: restart mode + \arg TIMER_SLAVE_MODE_PAUSE: pause mode + \arg TIMER_SLAVE_MODE_EVENT: event mode + \arg TIMER_SLAVE_MODE_EXTERNAL0: external clock mode 0 + \param[out] none + \retval none +*/ + +void timer_slave_mode_select(uint32_t timer_periph, uint32_t slavemode) +{ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_SMC); + TIMER_SMCFG(timer_periph) |= (uint32_t)slavemode; +} + +/*! + \brief configure TIMER master slave mode + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] masterslave: + only one parameter can be selected which is shown as below: + \arg TIMER_MASTER_SLAVE_MODE_ENABLE: master slave mode enable + \arg TIMER_MASTER_SLAVE_MODE_DISABLE: master slave mode disable + \param[out] none + \retval none +*/ +void timer_master_slave_mode_config(uint32_t timer_periph, uint32_t masterslave) +{ + if(TIMER_MASTER_SLAVE_MODE_ENABLE == masterslave){ + TIMER_SMCFG(timer_periph) |= (uint32_t)TIMER_SMCFG_MSM; + }else if(TIMER_MASTER_SLAVE_MODE_DISABLE == masterslave){ + TIMER_SMCFG(timer_periph) &= ~(uint32_t)TIMER_SMCFG_MSM; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief configure TIMER external trigger input + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] extprescaler: + only one parameter can be selected which is shown as below: + \arg TIMER_EXT_TRI_PSC_OFF: no divided + \arg TIMER_EXT_TRI_PSC_DIV2: divided by 2 + \arg TIMER_EXT_TRI_PSC_DIV4: divided by 4 + \arg TIMER_EXT_TRI_PSC_DIV8: divided by 8 + \param[in] extpolarity: + only one parameter can be selected which is shown as below: + \arg TIMER_ETP_FALLING: active low or falling edge active + \arg TIMER_ETP_RISING: active high or rising edge active + \param[in] extfilter: a value between 0 and 15 + \param[out] none + \retval none +*/ +void timer_external_trigger_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter) +{ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)(TIMER_SMCFG_ETP | TIMER_SMCFG_ETPSC | TIMER_SMCFG_ETFC)); + TIMER_SMCFG(timer_periph) |= (uint32_t)(extprescaler | extpolarity); + TIMER_SMCFG(timer_periph) |= (uint32_t)(extfilter << 8U); +} + +/*! + \brief configure TIMER quadrature decoder mode + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] decomode: + only one parameter can be selected which is shown as below: + \arg TIMER_QUAD_DECODER_MODE0: counter counts on CI0FE0 edge depending on CI1FE1 level + \arg TIMER_QUAD_DECODER_MODE1: counter counts on CI1FE1 edge depending on CI0FE0 level + \arg TIMER_QUAD_DECODER_MODE2: counter counts on both CI0FE0 and CI1FE1 edges depending on the level of the other input + \param[in] ic0polarity: + only one parameter can be selected which is shown as below: + \arg TIMER_IC_POLARITY_RISING: capture rising edge + \arg TIMER_IC_POLARITY_FALLING: capture falling edge + \arg TIMER_IC_POLARITY_BOTH_EDGE: active both edge + \param[in] ic1polarity: + only one parameter can be selected which is shown as below: + \arg TIMER_IC_POLARITY_RISING: capture rising edge + \arg TIMER_IC_POLARITY_FALLING: capture falling edge + \arg TIMER_IC_POLARITY_BOTH_EDGE: active both edge + \param[out] none + \retval none +*/ +void timer_quadrature_decoder_mode_config(uint32_t timer_periph, uint32_t decomode, uint16_t ic0polarity, uint16_t ic1polarity) +{ + /* configure the quadrature decoder mode */ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_SMC); + TIMER_SMCFG(timer_periph) |= (uint32_t)decomode; + /* configure input capture selection */ + TIMER_CHCTL0(timer_periph) &= (uint32_t)(((~(uint32_t)TIMER_CHCTL0_CH0MS)) & ((~(uint32_t)TIMER_CHCTL0_CH1MS))); + TIMER_CHCTL0(timer_periph) |= (uint32_t)(TIMER_IC_SELECTION_DIRECTTI | ((uint32_t)TIMER_IC_SELECTION_DIRECTTI << 8U)); + /* configure channel input capture polarity */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP)); + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP)); + TIMER_CHCTL2(timer_periph) |= ((uint32_t)ic0polarity | ((uint32_t)ic1polarity << 4U)); +} + +/*! + \brief configure TIMER internal clock mode + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[out] none + \retval none +*/ +void timer_internal_clock_config(uint32_t timer_periph) +{ + TIMER_SMCFG(timer_periph) &= ~(uint32_t)TIMER_SMCFG_SMC; +} + +/*! + \brief configure TIMER the internal trigger as external clock input + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] intrigger: + only one parameter can be selected which is shown as below: + \arg TIMER_SMCFG_TRGSEL_ITI0: internal trigger 0 + \arg TIMER_SMCFG_TRGSEL_ITI1: internal trigger 1 + \arg TIMER_SMCFG_TRGSEL_ITI2: internal trigger 2 + \arg TIMER_SMCFG_TRGSEL_ITI3: internal trigger 3 + \param[out] none + \retval none +*/ +void timer_internal_trigger_as_external_clock_config(uint32_t timer_periph, uint32_t intrigger) +{ + timer_input_trigger_source_select(timer_periph, intrigger); + TIMER_SMCFG(timer_periph) &= ~(uint32_t)TIMER_SMCFG_SMC; + TIMER_SMCFG(timer_periph) |= (uint32_t)TIMER_SLAVE_MODE_EXTERNAL0; +} + +/*! + \brief configure TIMER the external trigger as external clock input + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] extrigger: + only one parameter can be selected which is shown as below: + \arg TIMER_SMCFG_TRGSEL_CI0F_ED: TI0 edge detector + \arg TIMER_SMCFG_TRGSEL_CI0FE0: filtered TIMER input 0 + \arg TIMER_SMCFG_TRGSEL_CI1FE1: filtered TIMER input 1 + \param[in] extpolarity: + only one parameter can be selected which is shown as below: + \arg TIMER_IC_POLARITY_RISING: active high or rising edge active + \arg TIMER_IC_POLARITY_FALLING: active low or falling edge active + \arg TIMER_IC_POLARITY_BOTH_EDGE: active both edge + \param[in] extfilter: a value between 0 and 15 + \param[out] none + \retval none +*/ +void timer_external_trigger_as_external_clock_config(uint32_t timer_periph, uint32_t extrigger, uint16_t extpolarity, uint32_t extfilter) +{ + if(TIMER_SMCFG_TRGSEL_CI1FE1 == extrigger){ + /* reset the CH1EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH1EN); + /* reset the CH1NP bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP)); + /* set the CH1NP bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)extpolarity << 4U); + /* reset the CH1MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1MS); + /* set the CH1MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)((uint32_t)TIMER_IC_SELECTION_DIRECTTI << 8U); + /* reset the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH1CAPFLT); + /* set the CH1CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)(extfilter << 12U); + /* set the CH1EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH1EN; + }else{ + /* reset the CH0EN bit */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)TIMER_CHCTL2_CH0EN); + /* reset the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP)); + /* set the CH0P and CH0NP bits */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)extpolarity; + /* reset the CH0MS bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0MS); + /* set the CH0MS bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)TIMER_IC_SELECTION_DIRECTTI; + /* reset the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) &= (~(uint32_t)TIMER_CHCTL0_CH0CAPFLT); + /* reset the CH0CAPFLT bit */ + TIMER_CHCTL0(timer_periph) |= (uint32_t)(extfilter << 4U); + /* set the CH0EN bit */ + TIMER_CHCTL2(timer_periph) |= (uint32_t)TIMER_CHCTL2_CH0EN; + } + /* select TIMER input trigger source */ + timer_input_trigger_source_select(timer_periph, extrigger); + /* reset the SMC bit */ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_SMC); + /* set the SMC bit */ + TIMER_SMCFG(timer_periph) |= (uint32_t)TIMER_SLAVE_MODE_EXTERNAL0; +} + +/*! + \brief configure TIMER the external clock mode0 + \param[in] timer_periph: TIMERx(x=0..4,7,8,11) + \param[in] extprescaler: + only one parameter can be selected which is shown as below: + \arg TIMER_EXT_TRI_PSC_OFF: no divided + \arg TIMER_EXT_TRI_PSC_DIV2: divided by 2 + \arg TIMER_EXT_TRI_PSC_DIV4: divided by 4 + \arg TIMER_EXT_TRI_PSC_DIV8: divided by 8 + \param[in] extpolarity: + only one parameter can be selected which is shown as below: + \arg TIMER_ETP_FALLING: active low or falling edge active + \arg TIMER_ETP_RISING: active high or rising edge active + \param[in] extfilter: a value between 0 and 15 + \param[out] none + \retval none +*/ +void timer_external_clock_mode0_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter) +{ + /* configure TIMER external trigger input */ + timer_external_trigger_config(timer_periph, extprescaler, extpolarity, extfilter); + + /* reset the SMC bit,TRGS bit */ + TIMER_SMCFG(timer_periph) &= (~(uint32_t)(TIMER_SMCFG_SMC | TIMER_SMCFG_TRGS)); + /* set the SMC bit,TRGS bit */ + TIMER_SMCFG(timer_periph) |= (uint32_t)(TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_ETIFP); +} + +/*! + \brief configure TIMER the external clock mode1 + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[in] extprescaler: + only one parameter can be selected which is shown as below: + \arg TIMER_EXT_TRI_PSC_OFF: no divided + \arg TIMER_EXT_TRI_PSC_DIV2: divided by 2 + \arg TIMER_EXT_TRI_PSC_DIV4: divided by 4 + \arg TIMER_EXT_TRI_PSC_DIV8: divided by 8 + \param[in] extpolarity: + only one parameter can be selected which is shown as below: + \arg TIMER_ETP_FALLING: active low or falling edge active + \arg TIMER_ETP_RISING: active high or rising edge active + \param[in] extfilter: a value between 0 and 15 + \param[out] none + \retval none +*/ +void timer_external_clock_mode1_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter) +{ + /* configure TIMER external trigger input */ + timer_external_trigger_config(timer_periph, extprescaler, extpolarity, extfilter); + + TIMER_SMCFG(timer_periph) |= (uint32_t)TIMER_SMCFG_SMC1; +} + +/*! + \brief disable TIMER the external clock mode1 + \param[in] timer_periph: TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_external_clock_mode1_disable(uint32_t timer_periph) +{ + TIMER_SMCFG(timer_periph) &= ~(uint32_t)TIMER_SMCFG_SMC1; +} + +/*! + \brief configure TIMER write CHxVAL register selection + \param[in] timer_periph: TIMERx(x=0..4,7..13) + \param[in] ccsel: + only one parameter can be selected which is shown as below: + \arg TIMER_CHVSEL_DISABLE: no effect + \arg TIMER_CHVSEL_ENABLE: when write the CHxVAL register, if the write value is same as the CHxVAL value, the write access is ignored + \param[out] none + \retval none +*/ +void timer_write_chxval_register_config(uint32_t timer_periph, uint16_t ccsel) +{ + if(TIMER_CHVSEL_ENABLE == ccsel){ + TIMER_CFG(timer_periph) |= (uint32_t)TIMER_CFG_CHVSEL; + }else if(TIMER_CHVSEL_DISABLE == ccsel){ + TIMER_CFG(timer_periph) &= ~(uint32_t)TIMER_CFG_CHVSEL; + }else{ + /* illegal parameters */ + } +} + +/*! + \brief configure TIMER output value selection + \param[in] timer_periph: TIMERx(x=0,7) + \param[in] outsel: + only one parameter can be selected which is shown as below: + \arg TIMER_OUTSEL_DISABLE: no effect + \arg TIMER_OUTSEL_ENABLE: if POEN and IOS is 0, the output disabled + \param[out] none + \retval none +*/ +void timer_output_value_selection_config(uint32_t timer_periph, uint16_t outsel) +{ + if(TIMER_OUTSEL_ENABLE == outsel){ + TIMER_CFG(timer_periph) |= (uint32_t)TIMER_CFG_OUTSEL; + }else if(TIMER_OUTSEL_DISABLE == outsel){ + TIMER_CFG(timer_periph) &= ~(uint32_t)TIMER_CFG_OUTSEL; + }else{ + /* illegal parameters */ + } +} +/*! + \brief get TIMER flags + \param[in] timer_periph: please refer to the following parameters + \param[in] flag: the timer interrupt flags + only one parameter can be selected which is shown as below: + \arg TIMER_FLAG_UP: update flag,TIMERx(x=0..13) + \arg TIMER_FLAG_CH0: channel 0 flag,TIMERx(x=0..4,7..13) + \arg TIMER_FLAG_CH1: channel 1 flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_FLAG_CH2: channel 2 flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CH3: channel 3 flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CMT: channel control update flag,TIMERx(x=0,7) + \arg TIMER_FLAG_TRG: trigger flag,TIMERx(x=0,7,8,11) + \arg TIMER_FLAG_BRK: break flag,TIMERx(x=0,7) + \arg TIMER_FLAG_CH0O: channel 0 overcapture flag,TIMERx(x=0..4,7..11) + \arg TIMER_FLAG_CH1O: channel 1 overcapture flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_FLAG_CH2O: channel 2 overcapture flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CH3O: channel 3 overcapture flag,TIMERx(x=0..4,7) + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus timer_flag_get(uint32_t timer_periph, uint32_t flag) +{ + if(RESET != (TIMER_INTF(timer_periph) & flag)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear TIMER flags + \param[in] timer_periph: please refer to the following parameters + \param[in] flag: the timer interrupt flags + only one parameter can be selected which is shown as below: + \arg TIMER_FLAG_UP: update flag,TIMERx(x=0..13) + \arg TIMER_FLAG_CH0: channel 0 flag,TIMERx(x=0..4,7..13) + \arg TIMER_FLAG_CH1: channel 1 flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_FLAG_CH2: channel 2 flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CH3: channel 3 flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CMT: channel control update flag,TIMERx(x=0,7) + \arg TIMER_FLAG_TRG: trigger flag,TIMERx(x=0,7,8,11) + \arg TIMER_FLAG_BRK: break flag,TIMERx(x=0,7) + \arg TIMER_FLAG_CH0O: channel 0 overcapture flag,TIMERx(x=0..4,7..11) + \arg TIMER_FLAG_CH1O: channel 1 overcapture flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_FLAG_CH2O: channel 2 overcapture flag,TIMERx(x=0..4,7) + \arg TIMER_FLAG_CH3O: channel 3 overcapture flag,TIMERx(x=0..4,7) + \param[out] none + \retval none +*/ +void timer_flag_clear(uint32_t timer_periph, uint32_t flag) +{ + TIMER_INTF(timer_periph) = (~(uint32_t)flag); +} +/*! + \brief enable the TIMER interrupt + \param[in] timer_periph: please refer to the following parameters + \param[in] interrupt: timer interrupt enable source + only one parameter can be selected which is shown as below: + \arg TIMER_INT_UP: update interrupt enable, TIMERx(x=0..13) + \arg TIMER_INT_CH0: channel 0 interrupt enable, TIMERx(x=0..4,7..13) + \arg TIMER_INT_CH1: channel 1 interrupt enable, TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_CH2: channel 2 interrupt enable, TIMERx(x=0..4,7) + \arg TIMER_INT_CH3: channel 3 interrupt enable , TIMERx(x=0..4,7) + \arg TIMER_INT_CMT: commutation interrupt enable, TIMERx(x=0,7) + \arg TIMER_INT_TRG: trigger interrupt enable, TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_BRK: break interrupt enable, TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_interrupt_enable(uint32_t timer_periph, uint32_t interrupt) +{ + TIMER_DMAINTEN(timer_periph) |= (uint32_t) interrupt; +} + +/*! + \brief disable the TIMER interrupt + \param[in] timer_periph: please refer to the following parameters + \param[in] interrupt: timer interrupt source disable + only one parameter can be selected which is shown as below: + \arg TIMER_INT_UP: update interrupt disable, TIMERx(x=0..13) + \arg TIMER_INT_CH0: channel 0 interrupt disable, TIMERx(x=0..4,7..13) + \arg TIMER_INT_CH1: channel 1 interrupt disable, TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_CH2: channel 2 interrupt disable, TIMERx(x=0..4,7) + \arg TIMER_INT_CH3: channel 3 interrupt disable , TIMERx(x=0..4,7) + \arg TIMER_INT_CMT: commutation interrupt disable, TIMERx(x=0,7) + \arg TIMER_INT_TRG: trigger interrupt disable, TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_BRK: break interrupt disable, TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_interrupt_disable(uint32_t timer_periph, uint32_t interrupt) +{ + TIMER_DMAINTEN(timer_periph) &= (~(uint32_t)interrupt); +} + +/*! + \brief get timer interrupt flag + \param[in] timer_periph: please refer to the following parameters + \param[in] int_flag: the timer interrupt bits + only one parameter can be selected which is shown as below: + \arg TIMER_INT_FLAG_UP: update interrupt flag,TIMERx(x=0..13) + \arg TIMER_INT_FLAG_CH0: channel 0 interrupt flag,TIMERx(x=0..4,7..13) + \arg TIMER_INT_FLAG_CH1: channel 1 interrupt flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_FLAG_CH2: channel 2 interrupt flag,TIMERx(x=0..4,7) + \arg TIMER_INT_FLAG_CH3: channel 3 interrupt flag,TIMERx(x=0..4,7) + \arg TIMER_INT_FLAG_CMT: channel commutation interrupt flag,TIMERx(x=0,7) + \arg TIMER_INT_FLAG_TRG: trigger interrupt flag,TIMERx(x=0,7,8,11) + \arg TIMER_INT_FLAG_BRK: break interrupt flag,TIMERx(x=0,7) + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus timer_interrupt_flag_get(uint32_t timer_periph, uint32_t int_flag) +{ + uint32_t val; + val = (TIMER_DMAINTEN(timer_periph) & int_flag); + if((RESET != (TIMER_INTF(timer_periph) & int_flag) ) && (RESET != val)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear TIMER interrupt flag + \param[in] timer_periph: please refer to the following parameters + \param[in] int_flag: the timer interrupt bits + only one parameter can be selected which is shown as below: + \arg TIMER_INT_FLAG_UP: update interrupt flag,TIMERx(x=0..13) + \arg TIMER_INT_FLAG_CH0: channel 0 interrupt flag,TIMERx(x=0..4,7..13) + \arg TIMER_INT_FLAG_CH1: channel 1 interrupt flag,TIMERx(x=0..4,7,8,11) + \arg TIMER_INT_FLAG_CH2: channel 2 interrupt flag,TIMERx(x=0..4,7) + \arg TIMER_INT_FLAG_CH3: channel 3 interrupt flag,TIMERx(x=0..4,7) + \arg TIMER_INT_FLAG_CMT: channel commutation interrupt flag,TIMERx(x=0,7) + \arg TIMER_INT_FLAG_TRG: trigger interrupt flag,TIMERx(x=0,7,8,11) + \arg TIMER_INT_FLAG_BRK: break interrupt flag,TIMERx(x=0,7) + \param[out] none + \retval none +*/ +void timer_interrupt_flag_clear(uint32_t timer_periph, uint32_t int_flag) +{ + TIMER_INTF(timer_periph) = (~(uint32_t)int_flag); +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_usart.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_usart.c new file mode 100644 index 0000000..196f7cd --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_usart.c @@ -0,0 +1,1764 @@ +/*! + \file gd32e50x_usart.c + \brief USART driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#include "gd32e50x_usart.h" + +/* USART register bit offset */ +#define GP_GUAT_OFFSET ((uint32_t)8U) /* bit offset of GUAT in USART_GP */ +#define CTL2_SCRTNUM_OFFSET ((uint32_t)17U) /* bit offset of SCRTNUM in USART_CTL2 */ +#define CTL3_SCRTNUM_OFFSET ((uint32_t)1U) /* bit offset of SCRTNUM in USART_CTL3 */ +#define RT_BL_OFFSET ((uint32_t)24U) /* bit offset of BL in USART_RT */ + +/*! + \brief reset USART/UART + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_deinit(uint32_t usart_periph) +{ + switch(usart_periph){ + case USART0: + rcu_periph_reset_enable(RCU_USART0RST); + rcu_periph_reset_disable(RCU_USART0RST); + break; + case USART1: + rcu_periph_reset_enable(RCU_USART1RST); + rcu_periph_reset_disable(RCU_USART1RST); + break; + case USART2: + rcu_periph_reset_enable(RCU_USART2RST); + rcu_periph_reset_disable(RCU_USART2RST); + break; + case USART5: + rcu_periph_reset_enable(RCU_USART5RST); + rcu_periph_reset_disable(RCU_USART5RST); + break; + case UART3: + rcu_periph_reset_enable(RCU_UART3RST); + rcu_periph_reset_disable(RCU_UART3RST); + break; + case UART4: + rcu_periph_reset_enable(RCU_UART4RST); + rcu_periph_reset_disable(RCU_UART4RST); + break; + default: + break; + } +} + +/*! + \brief configure USART baud rate value + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] baudval: baud rate value + \param[out] none + \retval none +*/ +void usart_baudrate_set(uint32_t usart_periph, uint32_t baudval) +{ + uint32_t uclk=0U, intdiv=0U, fradiv=0U, udiv=0U; + switch(usart_periph){ + /* get clock frequency */ + case USART0: + uclk=rcu_clock_freq_get(CK_APB2); + break; + case USART5: + uclk=rcu_clock_freq_get(CK_USART); + break; + case USART1: + uclk=rcu_clock_freq_get(CK_APB1); + break; + case USART2: + uclk=rcu_clock_freq_get(CK_APB1); + break; + case UART3: + uclk=rcu_clock_freq_get(CK_APB1); + break; + case UART4: + uclk=rcu_clock_freq_get(CK_APB1); + break; + default: + break; + } + + if(USART5 == usart_periph){ + /* configure USART5 baud rate value */ + if(USART5_CTL0(usart_periph) & USART5_CTL0_OVSMOD){ + /* when oversampling by 8, configure the value of USART_BAUD */ + udiv = ((2U*uclk) + baudval/2U)/baudval; + intdiv = udiv & 0x0000fff0U; + fradiv = (udiv>>1) & 0x00000007U; + }else{ + /* when oversampling by 16, configure the value of USART_BAUD */ + udiv = (uclk+baudval/2U)/baudval; + intdiv = udiv & 0x0000fff0U; + fradiv = udiv & 0x0000000fU; + } + USART5_BAUD(usart_periph) = ((USART5_BAUD_FRADIV | USART5_BAUD_INTDIV) & (intdiv | fradiv)); + }else{ + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) baud rate value */ + if(USART_CTL0(usart_periph) & USART_CTL0_OVSMOD){ + /* when oversampling by 8, configure the value of USART_BAUD */ + udiv = ((2U*uclk) + baudval/2U)/baudval; + intdiv = udiv & 0x0000fff0U; + fradiv = (udiv>>1) & 0x00000007U; + }else{ + /* when oversampling by 16, configure the value of USART_BAUD */ + udiv = (uclk+baudval/2U)/baudval; + intdiv = udiv & 0x0000fff0U; + fradiv = udiv & 0x0000000fU; + } + USART_BAUD(usart_periph) = ((USART_BAUD_FRADIV | USART_BAUD_INTDIV) & (intdiv | fradiv)); + } +} + +/*! + \brief configure USART parity function + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] paritycfg: configure USART parity + only one parameter can be selected which is shown as below: + \arg USART_PM_NONE: no parity + \arg USART_PM_EVEN: even parity + \arg USART_PM_ODD: odd parity + \param[out] none + \retval none +*/ +void usart_parity_config(uint32_t usart_periph, uint32_t paritycfg) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear USART5_CTL0 PM,PCEN Bits */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_PM | USART5_CTL0_PCEN); + /* configure USART5 parity mode */ + USART5_CTL0(usart_periph) |= ((USART5_CTL0_PM | USART5_CTL0_PCEN) & paritycfg); + }else{ + /* clear USART_CTL0 PM,PCEN Bits */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_PM | USART_CTL0_PCEN); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) parity mode */ + USART_CTL0(usart_periph) |= ((USART_CTL0_PM | USART_CTL0_PCEN) & paritycfg); + } +} + +/*! + \brief configure USART word length + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] wlen: USART word length configure + only one parameter can be selected which is shown as below: + \arg USART_WL_8BIT: 8 bits + \arg USART_WL_9BIT: 9 bits + \param[out] none + \retval none +*/ +void usart_word_length_set(uint32_t usart_periph, uint32_t wlen) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear USART5_CTL0 WL bit */ + USART5_CTL0(usart_periph) &= ~USART5_CTL0_WL; + /* configure USART word length */ + USART5_CTL0(usart_periph) |= (USART5_CTL0_WL & wlen); + }else{ + /* clear USART_CTL0 WL bit */ + USART_CTL0(usart_periph) &= ~USART_CTL0_WL; + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) word length */ + USART_CTL0(usart_periph) |= (USART_CTL0_WL & wlen); + } +} + +/*! + \brief configure USART stop bit length + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] stblen: USART stop bit configure + only one parameter can be selected which is shown as below: + \arg USART_STB_1BIT: 1 bit + \arg USART_STB_0_5BIT: 0.5 bit(not available for UARTx(x=3,4,6,7)) + \arg USART_STB_2BIT: 2 bits + \arg USART_STB_1_5BIT: 1.5 bits(not available for UARTx(x=3,4,6,7)) + \param[out] none + \retval none +*/ +void usart_stop_bit_set(uint32_t usart_periph, uint32_t stblen) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear USART5_CTL1 STB bits */ + USART5_CTL1(usart_periph) &= ~USART5_CTL1_STB; + /* configure USART stop bits */ + USART5_CTL1(usart_periph) |= (USART5_CTL1_STB & stblen); + }else{ + /* clear USART_CTL1 STB bits */ + USART_CTL1(usart_periph) &= ~USART_CTL1_STB; + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) stop bits */ + USART_CTL1(usart_periph) |= (USART_CTL1_STB & stblen); + } +} + +/*! + \brief enable USART + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* enable USART5 */ + USART5_CTL0(usart_periph) |= USART5_CTL0_UEN; + }else{ + /* enable USARTx(x=0,1,2)/UARTx(x=3,4) */ + USART_CTL0(usart_periph) |= USART_CTL0_UEN; + } +} + +/*! + \brief disable USART + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + }else{ + /* disable USARTx(x=0,1,2)/UARTx(x=3,4) */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_UEN); + } +} + +/*! + \brief configure USART transmitter + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] txconfig: enable or disable USART transmitter + only one parameter can be selected which is shown as below: + \arg USART_TRANSMIT_ENABLE: enable USART transmission + \arg USART_TRANSMIT_DISABLE: enable USART transmission + \param[out] none + \retval none +*/ +void usart_transmit_config(uint32_t usart_periph, uint32_t txconfig) +{ + uint32_t ctl = 0U; + + if(USART5 == usart_periph){ + ctl = USART5_CTL0(usart_periph); + ctl &= ~USART5_CTL0_TEN; + ctl |= (USART5_CTL0_TEN & txconfig); + /* configure USART5 transfer mode */ + USART5_CTL0(usart_periph) = ctl; + }else{ + ctl = USART_CTL0(usart_periph); + ctl &= ~USART_CTL0_TEN; + ctl |= (USART_CTL0_TEN & txconfig); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) transfer mode */ + USART_CTL0(usart_periph) = ctl; + } +} + +/*! + \brief configure USART receiver + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] rxconfig: enable or disable USART receiver + only one parameter can be selected which is shown as below: + \arg USART_RECEIVE_ENABLE: enable USART reception + \arg USART_RECEIVE_DISABLE: disable USART reception + \param[out] none + \retval none +*/ +void usart_receive_config(uint32_t usart_periph, uint32_t rxconfig) +{ + uint32_t ctl = 0U; + + if(USART5 == usart_periph){ + ctl = USART5_CTL0(usart_periph); + ctl &= ~USART5_CTL0_REN; + ctl |= (USART5_CTL0_REN & rxconfig); + /* configure USART5 receive mode */ + USART5_CTL0(usart_periph) = ctl; + }else{ + ctl = USART_CTL0(usart_periph); + ctl &= ~USART_CTL0_REN; + ctl |= (USART_CTL0_REN & rxconfig); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) receive mode */ + USART_CTL0(usart_periph) = ctl; + } +} + +/*! + \brief configure the USART oversample mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] oversamp: oversample value + only one parameter can be selected which is shown as below: + \arg USART_OVSMOD_8: 8 bits + \arg USART_OVSMOD_16: 16 bits + \param[out] none + \retval none +*/ +void usart_oversample_config(uint32_t usart_periph, uint32_t oversamp) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear OVSMOD bit */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_OVSMOD); + /* configure the USART5 oversample mode */ + USART5_CTL0(usart_periph) |= (USART5_CTL0_OVSMOD & oversamp); + }else{ + /* clear OVSMOD bit */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_OVSMOD); + /* configure the USARTx(x=0,1,2)/UARTx(x=3,4) oversample mode */ + USART_CTL0(usart_periph) |= (USART_CTL0_OVSMOD & oversamp); + } +} + +/*! + \brief configure sample bit method + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] obsm: sample bit + only one parameter can be selected which is shown as below: + \arg USART_OSB_1bit: 1 bit + \arg USART_OSB_3bit: 3 bits + \param[out] none + \retval none +*/ +void usart_sample_bit_config(uint32_t usart_periph, uint32_t obsm) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear USART5 OSB bit */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_OSB); + /* configure USART5 sample bit method */ + USART5_CTL2(usart_periph) |= (USART5_CTL2_OSB & obsm); + }else{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_OSB); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) sample bit method */ + USART_CTL2(usart_periph) |= (USART_CTL2_OSB & obsm); + } +} + +/*! + \brief enable receiver timeout of USART + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_receiver_timeout_enable(uint32_t usart_periph) +{ + if(USART5 ==usart_periph){ + /* enable receiver timeout of USART5 */ + USART5_CTL1(usart_periph) |= USART5_CTL1_RTEN; + }else{ + /* enable receiver timeout of USARTx(x=0,1,2) */ + USART_CTL3(usart_periph) |= USART_CTL3_RTEN; + } +} + +/*! + \brief disable receiver timeout of USART + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_receiver_timeout_disable(uint32_t usart_periph) +{ + if(USART5 ==usart_periph){ + /* disable receiver timeout of USART5 */ + USART5_CTL1(usart_periph) &= ~USART5_CTL1_RTEN; + }else{ + /* disable receiver timeout of USARTx(x=0,1,2) */ + USART_CTL3(usart_periph) &= ~(USART_CTL3_RTEN); + } +} + +/*! + \brief configure the receiver timeout threshold of USART + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[in] rtimeout: 0x00000000-0x00FFFFFF + \param[out] none + \retval none +*/ +void usart_receiver_timeout_threshold_config(uint32_t usart_periph, uint32_t rtimeout) +{ + if(USART5 == usart_periph){ + USART5_RT(usart_periph) &= ~(USART5_RT_RT); + /* configure USART5 receiver timeout threshold */ + USART5_RT(usart_periph) |= (USART5_RT_RT & rtimeout); + }else{ + USART_RT(usart_periph) &= ~(USART_RT_RT); + /* configure USARTx(x=0,1,2) receiver timeout threshold */ + USART_RT(usart_periph) |= (USART_RT_RT & rtimeout); + } +} + +/*! + \brief USART transmit data function + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] data: data of transmission + \param[out] none + \retval none +*/ +void usart_data_transmit(uint32_t usart_periph, uint16_t data) +{ + if(USART5 == usart_periph){ + /* USART5 transmit data */ + USART5_TDATA(usart_periph) = USART5_TDATA_TDATA & (uint32_t)data; + }else{ + /* USARTx(x=0,1,2)/UARTx(x=3,4) transmit data */ + USART_DATA(usart_periph) = USART_DATA_DATA & (uint32_t)data; + } +} + +/*! + \brief USART receive data function + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval data of received +*/ +uint16_t usart_data_receive(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* USART5 receive data */ + return (uint16_t)(GET_BITS(USART5_RDATA(usart_periph), 0U, 8U)); + }else{ + /* USARTx(x=0,1,2)/UARTx(x=3,4) receive data */ + return (uint16_t)(GET_BITS(USART_DATA(usart_periph), 0U, 8U)); + } +} + +/*! + \brief enable mute mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_mute_mode_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* enable USART5 mute mode */ + USART5_CTL0(usart_periph) |= USART5_CTL0_MEN; + }else{ + /* enable USARTx(x=0,1,2)/UARTx(x=3,4) mute mode */ + USART_CTL0(usart_periph) |= USART_CTL0_RWU; + } +} + +/*! + \brief disable mute mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_mute_mode_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 mute mode */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_MEN); + }else{ + /* disable USARTx(x=0,1,2)/UARTx(x=3,4) mute mode */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_RWU); + } +} + +/*! + \brief configure wakeup method in mute mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] wmethod: two method be used to enter or exit the mute mode + only one parameter can be selected which is shown as below: + \arg USART_WM_IDLE: idle line + \arg USART_WM_ADDR: address mask + \param[out] none + \retval none +*/ +void usart_mute_mode_wakeup_config(uint32_t usart_periph, uint32_t wmethod) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear USART5 WM bit */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_WM); + /* configure USART5 wakeup method in mute mode */ + USART5_CTL0(usart_periph) |= (USART5_CTL0_WM & wmethod); + }else{ + /* clear USARTx(x=0,1,2)/UARTx(x=3,4) WM bit */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_WM); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) wakeup method in mute mode */ + USART_CTL0(usart_periph) |= (USART_CTL0_WM & wmethod); + } +} + +/*! + \brief enable LIN mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_lin_mode_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable USART5 LIN mode */ + USART5_CTL1(usart_periph) |= USART5_CTL1_LMEN; + }else{ + /* enable USARTx(x=0,1,2)/UARTx(x=3,4) LIN mode */ + USART_CTL1(usart_periph) |= USART_CTL1_LMEN; + } +} + +/*! + \brief disable LIN mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_lin_mode_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 LIN mode */ + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_LMEN); + }else{ + /* disable USARTx(x=0,1,2)/UARTx(x=3,4) LIN mode */ + USART_CTL1(usart_periph) &= ~(USART_CTL1_LMEN); + } +} + +/*! + \brief configure lin break frame length + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] lblen: lin break frame length + only one parameter can be selected which is shown as below: + \arg USART_LBLEN_10B: 10 bits + \arg USART_LBLEN_11B: 11 bits + \param[out] none + \retval none +*/ +void usart_lin_break_detection_length_config(uint32_t usart_periph, uint32_t lblen) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_LBLEN); + /* configure USART5 lin brek frame length */ + USART5_CTL1(usart_periph) |= USART5_CTL1_LBLEN & (lblen); + }else{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_LBLEN); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) lin brek frame length */ + USART_CTL1(usart_periph) |= (USART_CTL1_LBLEN & lblen); + } +} + +/*! + \brief enable half duplex mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_halfduplex_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable USART5 half duplex mode */ + USART5_CTL2(usart_periph) |= (USART5_CTL2_HDEN); + }else{ + /* enable USARTx(x=0,1,2)/UARTx(x=3,4) half duplex mode */ + USART_CTL2(usart_periph) |= USART_CTL2_HDEN; + } +} + +/*! + \brief disable half duplex mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_halfduplex_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 half duplex mode */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_HDEN); + }else{ + /* disable USARTx(x=0,1,2)/UARTx(x=3,4) half duplex mode */ + USART_CTL2(usart_periph) &= ~(USART_CTL2_HDEN); + } +} + +/*! + \brief enable CK pin in synchronous mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_synchronous_clock_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable USART5 CK pin in synchronous mode */ + USART5_CTL1(usart_periph) |= USART5_CTL1_CKEN; + }else{ + /* enable USARTx(x=0,1,2) CK pin in synchronous mode */ + USART_CTL1(usart_periph) |= USART_CTL1_CKEN; + } +} + +/*! + \brief disable CK pin in synchronous mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_synchronous_clock_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 CK pin in synchronous mode */ + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_CKEN); + }else{ + /* disable USARTx(x=0,1,2) CK pin in synchronous mode */ + USART_CTL1(usart_periph) &= ~(USART_CTL1_CKEN); + } +} + +/*! + \brief configure USART synchronous mode parameters + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[in] clen: CK length + only one parameter can be selected which is shown as below: + \arg USART_CLEN_NONE: there are 7 CK pulses for an 8 bit frame and 8 CK pulses for a 9 bit frame + \arg USART_CLEN_EN: there are 8 CK pulses for an 8 bit frame and 9 CK pulses for a 9 bit frame + \param[in] cph: clock phase + only one parameter can be selected which is shown as below: + \arg USART_CPH_1CK: first clock transition is the first data capture edge + \arg USART_CPH_2CK: second clock transition is the first data capture edge + \param[in] cpl: clock polarity + only one parameter can be selected which is shown as below: + \arg USART_CPL_LOW: steady low value on CK pin + \arg USART_CPL_HIGH: steady high value on CK pin + \param[out] none + \retval none +*/ +void usart_synchronous_clock_config(uint32_t usart_periph, uint32_t clen, uint32_t cph, uint32_t cpl) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL1(usart_periph) &= ~(USART_CTL1_CLEN | USART_CTL1_CPH | USART_CTL1_CPL); + USART5_CTL1(usart_periph) |= (USART_CTL1_CLEN & clen) | (USART_CTL1_CPH & cph) | (USART_CTL1_CPL & cpl); + }else{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_CLEN | USART_CTL1_CPH | USART_CTL1_CPL); + USART_CTL1(usart_periph) |= (USART_CTL1_CLEN & clen) | (USART_CTL1_CPH & cph) | (USART_CTL1_CPL & cpl); + } +} + +/*! + \brief configure guard time value in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[in] guat: guard time value, 0x00000000-0x000000FF + \param[out] none + \retval none +*/ +void usart_guard_time_config(uint32_t usart_periph,uint8_t guat) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_GP(usart_periph) &= ~(USART5_GP_GUAT); + /* configure USART5 guard time value */ + USART5_GP(usart_periph) |= (USART5_GP_GUAT & ((uint32_t)guat << GP_GUAT_OFFSET)); + }else{ + USART_GP(usart_periph) &= ~(USART_GP_GUAT); + /* configure USARTx(x=0,1,2) guard time value */ + USART_GP(usart_periph) |= (USART_GP_GUAT & ((uint32_t)guat << GP_GUAT_OFFSET)); + } +} + +/*! + \brief enable smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + /* enable USART5 smartcard mode */ + USART5_CTL2(usart_periph) |= USART5_CTL2_SCEN; + }else{ + /* enable USARTx(x=0,1,2) smartcard mode */ + USART_CTL2(usart_periph) |= USART_CTL2_SCEN; + } +} + +/*! + \brief disable smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 smartcard mode */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_SCEN); + }else{ + /* disable USARTx(x=0,1,2) smartcard mode */ + USART_CTL2(usart_periph) &= ~(USART_CTL2_SCEN); + } +} + +/*! + \brief enable NACK in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_nack_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable USART5 NACK in smartcard mode */ + USART5_CTL2(usart_periph) |= USART5_CTL2_NKEN; + }else{ + /* enable USARTx(x=0,1,2) NACK in smartcard mode */ + USART_CTL2(usart_periph) |= USART_CTL2_NKEN; + } +} + +/*! + \brief disable NACK in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_nack_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 NACK in smartcard mode */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_NKEN); + }else{ + /* disable USARTx(x=0,1,2) NACK in smartcard mode */ + USART_CTL2(usart_periph) &= ~(USART_CTL2_NKEN); + } +} + +/*! + \brief configure smartcard auto-retry number + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[in] scrtnum: smartcard auto-retry number, 0x00000000-0x00000007 + \param[out] none + \retval none +*/ +void usart_smartcard_autoretry_config(uint32_t usart_periph, uint8_t scrtnum) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_SCRTNUM); + /* configure USART5 smartcard auto-retry number */ + USART5_CTL2(usart_periph) |= (USART5_CTL2_SCRTNUM & ((uint32_t) scrtnum << CTL2_SCRTNUM_OFFSET)); + }else{ + USART_CTL3(usart_periph) &= ~(USART_CTL3_SCRTNUM); + /* configure USARTx(x=0,1,2) smartcard auto-retry number */ + USART_CTL3(usart_periph) |= (USART_CTL3_SCRTNUM & ((uint32_t)scrtnum << CTL3_SCRTNUM_OFFSET)); + } +} + +/*! + \brief configure block length in Smartcard T=1 reception + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[in] bl: block length, 0x00000000-0x000000FF + \param[out] none + \retval none +*/ +void usart_block_length_config(uint32_t usart_periph, uint8_t bl) +{ + if(USART5 == usart_periph){ + USART5_RT(usart_periph) &= ~(USART5_RT_BL); + /* configure USART5 block length */ + USART5_RT(usart_periph) |= (USART5_RT_BL & ((uint32_t)bl << RT_BL_OFFSET)); + }else{ + USART_RT(usart_periph) &= ~(USART_RT_BL); + /* configure USARTx(x=0,1,2) block length */ + USART_RT(usart_periph) |= (USART_RT_BL & ((uint32_t)bl << RT_BL_OFFSET)); + } +} + +/*! + \brief enable IrDA mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_irda_mode_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable USART5 IrDA mode */ + USART5_CTL2(usart_periph) |= USART5_CTL2_IREN; + }else{ + /* enable USARTx(x=0,1,2)/UARTx(x=3,4) IrDA mode */ + USART_CTL2(usart_periph) |= USART_CTL2_IREN; + } +} + +/*! + \brief disable IrDA mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_irda_mode_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 IrDA mode */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_IREN); + }else{ + /* disable USARTx(x=0,1,2)/UARTx(x=3,4) IrDA mode */ + USART_CTL2(usart_periph) &= ~(USART_CTL2_IREN); + } +} + +/*! + \brief configure the peripheral clock prescaler in USART IrDA low-power mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] psc: 0x00000000-0x000000FF + \param[out] none + \retval none +*/ +void usart_prescaler_config(uint32_t usart_periph, uint8_t psc) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_GP(usart_periph) &= ~(USART5_GP_PSC); + /* configure the psc in USART5 */ + USART5_GP(usart_periph) |= (uint32_t)psc; + }else{ + USART_GP(usart_periph) &= ~(USART_GP_PSC); + /* configure the psc in USARTx(x=0,1,2)/UARTx(x=3,4) */ + USART_GP(usart_periph) |= (uint32_t)psc; + } +} + +/*! + \brief configure IrDA low-power + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] irlp: IrDA low-power or normal + only one parameter can be selected which is shown as below: + \arg USART_IRLP_LOW: low-power + \arg USART_IRLP_NORMAL: normal + \param[out] none + \retval none +*/ +void usart_irda_lowpower_config(uint32_t usart_periph, uint32_t irlp) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_IRLP); + /* configure USART5 IrDA low-power */ + USART5_CTL2(usart_periph) |= (USART5_CTL2_IRLP & irlp); + }else{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_IRLP); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) IrDA low-power */ + USART_CTL2(usart_periph) |= (USART_CTL2_IRLP & irlp); + } +} + +/*! + \brief configure USART DMA reception + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] dmaconfig: enable or disable DMA for reception + only one parameter can be selected which is shown as below: + \arg USART_RECEIVE_DMA_ENABLE: DMA enable for reception + \arg USART_RECEIVE_DMA_DISABLE: DMA disable for reception + \param[out] none + \retval none +*/ +void usart_dma_receive_config(uint32_t usart_periph, uint8_t dmacmd) +{ + uint32_t ctl = 0U; + + if(USART5 == usart_periph){ + ctl = USART5_CTL2(usart_periph); + ctl &= ~USART5_CTL2_DENR; + ctl |= dmacmd; + /* configure DMA reception */ + USART5_CTL2(usart_periph) = ctl; + }else{ + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_DENR; + ctl |= dmacmd; + /* configure DMA reception */ + USART_CTL2(usart_periph) = ctl; + } +} + +/*! + \brief configure USART DMA transmission + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] dmaconfig: enable or disable DMA for transmission + only one parameter can be selected which is shown as below: + \arg USART_TRANSMIT_DMA_ENABLE: DMA enable for transmission + \arg USART_TRANSMIT_DMA_DISABLE: DMA disable for transmission + \param[out] none + \retval none +*/ +void usart_dma_transmit_config(uint32_t usart_periph, uint8_t dmacmd) +{ + uint32_t ctl = 0U; + + if(USART5 == usart_periph){ + ctl = USART5_CTL2(usart_periph); + ctl &= ~USART5_CTL2_DENT; + ctl |= dmacmd; + /* configure DMA reception */ + USART5_CTL2(usart_periph) = ctl; + }else{ + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_DENT; + ctl |= dmacmd; + /* configure DMA reception */ + USART_CTL2(usart_periph) = ctl; + } +} + +/*! + \brief configure hardware flow control RTS + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] rtsconfig: enable or disable RTS + only one parameter can be selected which is shown as below: + \arg USART_RTS_ENABLE: enable RTS + \arg USART_RTS_DISABLE: disable RTS + \param[out] none + \retval none +*/ +void usart_hardware_flow_rts_config(uint32_t usart_periph, uint32_t rtsconfig) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_RTSEN); + USART_CTL2(usart_periph) |= (USART_CTL2_RTSEN & rtsconfig); +} + +/*! + \brief configure hardware flow control CTS + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] ctsconfig: enable or disable CTS + only one parameter can be selected which is shown as below: + \arg USART_CTS_ENABLE: enable CTS + \arg USART_CTS_DISABLE: disable CTS + \param[out] none + \retval none +*/ +void usart_hardware_flow_cts_config(uint32_t usart_periph, uint32_t ctsconfig) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_CTSEN); + USART_CTL2(usart_periph) |= (USART_CTL2_CTSEN & ctsconfig); +} + +/*! + \brief data is transmitted/received with the LSB/MSB first + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] msbf: LSB/MSB + only one parameter can be selected which is shown as below: + \arg USART_MSBF_LSB: LSB first + \arg USART_MSBF_MSB: MSB first + \param[out] none + \retval none +*/ +void usart_data_first_config(uint32_t usart_periph, uint32_t msbf) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL3(usart_periph); + ctl &= ~(USART_CTL3_MSBF); + ctl |= (USART_CTL3_MSBF & msbf); + /* configure data transmitted/received mode */ + USART_CTL3(usart_periph) = ctl; +} + +/*! + \brief configure USART inversion + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] invertpara: refer to enum USART_INVERT_CONFIG + only one parameter can be selected which is shown as below: + \arg USART_DINV_ENABLE: data bit level inversion + \arg USART_DINV_DISABLE: data bit level not inversion + \arg USART_TXPIN_ENABLE: TX pin level inversion + \arg USART_TXPIN_DISABLE: TX pin level not inversion + \arg USART_RXPIN_ENABLE: RX pin level inversion + \arg USART_RXPIN_DISABLE: RX pin level not inversion + \param[out] none + \retval none +*/ +void usart_invert_config(uint32_t usart_periph, usart_invert_enum invertpara) +{ + /* inverted or not the specified siginal */ + switch(invertpara){ + case USART_DINV_ENABLE: + USART_CTL3(usart_periph) |= USART_CTL3_DINV; + break; + case USART_DINV_DISABLE: + USART_CTL3(usart_periph) &= ~(USART_CTL3_DINV); + break; + case USART_TXPIN_ENABLE: + USART_CTL3(usart_periph) |= USART_CTL3_TINV; + break; + case USART_TXPIN_DISABLE: + USART_CTL3(usart_periph) &= ~(USART_CTL3_TINV); + break; + case USART_RXPIN_ENABLE: + USART_CTL3(usart_periph) |= USART_CTL3_RINV; + break; + case USART_RXPIN_DISABLE: + USART_CTL3(usart_periph) &= ~(USART_CTL3_RINV); + break; + default: + break; + } +} + +/*! + \brief configure the address of the USART in wake up by address match mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] addr: address of USART/UART, 0x00000000-0x0000000F + \param[out] none + \retval none +*/ +void usart_address_config(uint32_t usart_periph, uint8_t addr) +{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_ADDR); + USART_CTL1(usart_periph) |= (USART_CTL1_ADDR & (uint32_t)addr); +} + +/*! + \brief send break frame + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_send_break(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) |= USART_CTL0_SBKCMD; +} + +/*! + \brief enable collision detected interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_collision_detected_interrupt_enable(uint32_t usart_periph) +{ + USART_GDCTL(usart_periph) |= USART_GDCTL_CDIE; +} + +/*! + \brief disable collision detected interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_collision_detected_interrupt_disable(uint32_t usart_periph) +{ + USART_GDCTL(usart_periph) &= ~(USART_GDCTL_CDIE); +} + +/*! + \brief enable collision detection + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_collision_detection_enable(uint32_t usart_periph) +{ + USART_GDCTL(usart_periph) |= USART_GDCTL_CDEN; +} + +/*! + \brief disable collision detection + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_collision_detection_disable(uint32_t usart_periph) +{ + USART_GDCTL(usart_periph) &= ~(USART_GDCTL_CDEN); +} + +/*! + \brief get flag in STAT0/STAT1/GDCTL register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] flag: USART flags, refer to usart_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_FLAG_CTS: CTS change flag + \arg USART_FLAG_LBD: LIN break detected flag + \arg USART_FLAG_TBE: transmit data buffer empty + \arg USART_FLAG_TC: transmission complete + \arg USART_FLAG_RBNE: read data buffer not empty + \arg USART_FLAG_IDLE: IDLE frame detected flag + \arg USART_FLAG_ORERR: overrun error flag + \arg USART_FLAG_NERR: noise error flag + \arg USART_FLAG_FERR: frame error flag + \arg USART_FLAG_PERR: parity error flag + \arg USART_FLAG_BSY: busy flag + \arg USART_FLAG_EB: end of block flag + \arg USART_FLAG_RT: receiver timeout flag + \arg USART_FLAG_CD: collision detected flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag) +{ + if(RESET != (USART_REG_VAL(usart_periph, flag) & BIT(USART_BIT_POS(flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear flag in STAT0/STAT1/GDCTL register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] flag: USART flags, refer to usart_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_FLAG_CTS: CTS change flag + \arg USART_FLAG_LBD: LIN break detected flag + \arg USART_FLAG_TC: transmission complete + \arg USART_FLAG_RBNE: read data buffer not empty + \arg USART_FLAG_EB: end of block flag + \arg USART_FLAG_RT: receiver timeout flag + \arg USART_FLAG_CD: collision detected flag + \param[out] none + \retval none +*/ +void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag) +{ + if (USART_FLAG_CD == flag) { + USART_REG_VAL(usart_periph, flag) &= ~BIT(USART_BIT_POS(flag)); + } else { + USART_REG_VAL(usart_periph, flag) = ~BIT(USART_BIT_POS(flag)); + } +} + +/*! + \brief enable USART interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] interrupt: USART interrupts, refer to usart_interrupt_enum + only one parameter can be selected which is shown as below: + \arg USART_INT_PERR: parity error interrupt + \arg USART_INT_TBE: transmitter buffer empty interrupt + \arg USART_INT_TC: transmission complete interrupt + \arg USART_INT_RBNE: read data buffer not empty interrupt and overrun error interrupt + \arg USART_INT_IDLE: IDLE line detected interrupt + \arg USART_INT_LBD: LIN break detected interrupt + \arg USART_INT_ERR: error interrupt + \arg USART_INT_CTS: CTS interrupt + \arg USART_INT_RT: interrupt enable bit of receive timeout event + \arg USART_INT_EB: interrupt enable bit of end of block event + \arg USART_INT_CD: collision detected interrupt + \param[out] none + \retval none +*/ +void usart_interrupt_enable(uint32_t usart_periph, usart_interrupt_enum interrupt) +{ + USART_REG_VAL(usart_periph, interrupt) |= BIT(USART_BIT_POS(interrupt)); +} + +/*! + \brief disable USART interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] interrupt: USART interrupts, refer to usart_interrupt_enum + only one parameter can be selected which is shown as below: + \arg USART_INT_PERR: parity error interrupt + \arg USART_INT_TBE: transmitter buffer empty interrupt + \arg USART_INT_TC: transmission complete interrupt + \arg USART_INT_RBNE: read data buffer not empty interrupt and overrun error interrupt + \arg USART_INT_IDLE: IDLE line detected interrupt + \arg USART_INT_LBD: LIN break detected interrupt + \arg USART_INT_ERR: error interrupt + \arg USART_INT_CTS: CTS interrupt + \arg USART_INT_RT: interrupt enable bit of receive timeout event + \arg USART_INT_EB: interrupt enable bit of end of block event + \arg USART_INT_CD: collision detected interrupt + \param[out] none + \retval none +*/ +void usart_interrupt_disable(uint32_t usart_periph, usart_interrupt_enum interrupt) +{ + USART_REG_VAL(usart_periph, interrupt) &= ~BIT(USART_BIT_POS(interrupt)); +} + +/*! + \brief get USART interrupt and flag status + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] int_flag: USART interrupt flags, refer to usart_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_INT_FLAG_PERR: parity error interrupt and flag + \arg USART_INT_FLAG_TBE: transmitter buffer empty interrupt and flag + \arg USART_INT_FLAG_TC: transmission complete interrupt and flag + \arg USART_INT_FLAG_RBNE: read data buffer not empty interrupt and flag + \arg USART_INT_FLAG_RBNE_ORERR: read data buffer not empty interrupt and overrun error flag + \arg USART_INT_FLAG_IDLE: IDLE line detected interrupt and flag + \arg USART_INT_FLAG_LBD: LIN break detected interrupt and flag + \arg USART_INT_FLAG_CTS: CTS interrupt and flag + \arg USART_INT_FLAG_ERR_ORERR: error interrupt and overrun error + \arg USART_INT_FLAG_ERR_NERR: error interrupt and noise error flag + \arg USART_INT_FLAG_ERR_FERR: error interrupt and frame error flag + \arg USART_INT_FLAG_EB: interrupt enable bit of end of block event and flag + \arg USART_INT_FLAG_RT: interrupt enable bit of receive timeout event and flag + \arg USART_INT_FLAG_CD: collision detected interrupt and flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, usart_interrupt_flag_enum int_flag) +{ + uint32_t intenable = 0U, flagstatus = 0U; + /* get the interrupt enable bit status */ + intenable = (USART_REG_VAL(usart_periph, int_flag) & BIT(USART_BIT_POS(int_flag))); + /* get the corresponding flag bit status */ + flagstatus = (USART_REG_VAL2(usart_periph, int_flag) & BIT(USART_BIT_POS2(int_flag))); + + if((0U != flagstatus) && (0U != intenable)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear USART interrupt flag in STAT0/STAT1 register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] int_flag: USART interrupt flags, refer to usart_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_INT_FLAG_CTS: CTS interrupt and flag + \arg USART_INT_FLAG_LBD: LIN break detected interrupt and flag + \arg USART_INT_FLAG_TC: transmission complete interrupt and flag + \arg USART_INT_FLAG_RBNE: read data buffer not empty interrupt and flag + \arg USART_INT_FLAG_EB: interrupt enable bit of end of block event and flag + \arg USART_INT_FLAG_RT: interrupt enable bit of receive timeout event and flag + \arg USART_INT_FLAG_CD: collision detected interrupt and flag + \param[out] none + \retval none +*/ +void usart_interrupt_flag_clear(uint32_t usart_periph, usart_interrupt_flag_enum int_flag) +{ + USART_REG_VAL2(usart_periph, int_flag) = ~BIT(USART_BIT_POS2(int_flag)); +} + +/*! + \brief data is transmitted/received with the LSB/MSB first + \param[in] usart_periph: USART5 + \param[in] msbf: LSB/MSB + only one parameter can be selected which is shown as below: + \arg USART5_MSBF_LSB: LSB first + \arg USART5_MSBF_MSB: MSB first + \param[out] none + \retval none +*/ +void usart5_data_first_config(uint32_t usart_periph, uint32_t msbf) +{ + uint32_t ctl = 0U; + + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + ctl = USART5_CTL1(usart_periph); + ctl &= ~(USART5_CTL1_MSBF); + ctl |= (USART5_CTL1_MSBF & msbf); + /* configure data transmitted/received mode */ + USART5_CTL1(usart_periph) = ctl; +} + +/*! + \brief configure USART5 inversion + \param[in] usart_periph: USART5 + \param[in] invertpara: refer to enum USART5_INVERT_CONFIG + only one parameter can be selected which is shown as below: + \arg USART5_DINV_ENABLE: data bit level inversion + \arg USART5_DINV_DISABLE: data bit level not inversion + \arg USART5_TXPIN_ENABLE: TX pin level inversion + \arg USART5_TXPIN_DISABLE: TX pin level not inversion + \arg USART5_RXPIN_ENABLE: RX pin level inversion + \arg USART5_RXPIN_DISABLE: RX pin level not inversion + \arg USART5_SWAP_ENABLE: swap TX/RX pins + \arg USART5_SWAP_DISABLE: not swap TX/RX pins + \param[out] none + \retval none +*/ +void usart5_invert_config(uint32_t usart_periph, usart5_invert_enum invertpara) +{ + /* inverted or not the specified siginal */ + switch(invertpara){ + case USART5_DINV_ENABLE: + USART5_CTL1(usart_periph) |= USART5_CTL1_DINV; + break; + case USART5_DINV_DISABLE: + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_DINV); + break; + case USART5_TXPIN_ENABLE: + USART5_CTL1(usart_periph) |= USART5_CTL1_TINV; + break; + case USART5_TXPIN_DISABLE: + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_TINV); + break; + case USART5_RXPIN_ENABLE: + USART5_CTL1(usart_periph) |= USART5_CTL1_RINV; + break; + case USART5_RXPIN_DISABLE: + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_RINV); + break; + case USART5_SWAP_ENABLE: + USART5_CTL1(usart_periph) |= USART5_CTL1_STRP; + break; + case USART5_SWAP_DISABLE: + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_STRP); + break; + default: + break; + } +} + +/*! + \brief enable the USART5 overrun function + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_overrun_enable(uint32_t usart_periph) +{ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable overrun function */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_OVRD); +} + +/*! + \brief disable the USART5 overrun function + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_overrun_disable(uint32_t usart_periph) +{ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable overrun function */ + USART5_CTL2(usart_periph) |= USART5_CTL2_OVRD; +} + +/*! + \brief address of the USART terminal + \param[in] usart_periph: USART5 + \param[in] addr: address of USART terminal, 0x00000000-0x000000FF + \param[out] none + \retval none +*/ +void usart5_address_config(uint32_t usart_periph, uint8_t addr) +{ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_ADDR); + USART5_CTL1(usart_periph) |= (USART5_CTL1_ADDR & (((uint32_t)addr) << 24)); +} + +/*! + \brief configure address detection mode + \param[in] usart_periph: USART5 + \param[in] addmod: address detection mode + only one parameter can be selected which is shown as below: + \arg USART5_ADDM_4BIT: 4 bits + \arg USART5_ADDM_FULLBIT: full bits + \param[out] none + \retval none +*/ +void usart5_address_detection_mode_config(uint32_t usart_periph, uint32_t addmod) +{ + /* disable USART */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_ADDM); + USART5_CTL1(usart_periph) |= (USART5_CTL1_ADDM & addmod); +} + +/*! + \brief enable early NACK in smartcard mode + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_smartcard_mode_early_nack_enable(uint32_t usart_periph) +{ + USART5_RFCS(usart_periph) |= USART5_RFCS_ELNACK; +} + +/*! + \brief disable early NACK in smartcard mode + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_smartcard_mode_early_nack_disable(uint32_t usart_periph) +{ + USART5_RFCS(usart_periph) &= ~USART5_RFCS_ELNACK; +} + +/*! + \brief enable DMA on reception error + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_reception_error_dma_enable(uint32_t usart_periph) +{ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_DDRE); +} + +/*! + \brief disable DMA on reception error + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_reception_error_dma_disable(uint32_t usart_periph) +{ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL2(usart_periph) |= USART5_CTL2_DDRE; +} + +/*! + \brief enable USART to wakeup the mcu from deep-sleep mode + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_wakeup_enable(uint32_t usart_periph) +{ + USART5_CTL0(usart_periph) |= USART5_CTL0_UESM; +} + +/*! + \brief disable USART to wakeup the mcu from deep-sleep mode + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_wakeup_disable(uint32_t usart_periph) +{ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UESM); +} + +/*! + \brief configure the USART wakeup mode from deep-sleep mode + \param[in] usart_periph: USART5 + \param[in] wum: wakeup mode + only one parameter can be selected which is shown as below: + \arg USART5_WUM_ADDR: WUF active on address match + \arg USART5_WUM_STARTB: WUF active on start bit + \arg USART5_WUM_RBNE: WUF active on RBNE + \param[out] none + \retval none +*/ +void usart5_wakeup_mode_config(uint32_t usart_periph, uint32_t wum) +{ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* reset WUM bit */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_WUM); + USART5_CTL2(usart_periph) |= (USART5_CTL2_WUM & wum); +} + +/*! + \brief enable receive FIFO + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_receive_fifo_enable(uint32_t usart_periph) +{ + USART5_RFCS(usart_periph) |= USART5_RFCS_RFEN; +} + +/*! + \brief disable receive FIFO + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_receive_fifo_disable(uint32_t usart_periph) +{ + USART5_RFCS(usart_periph) &= ~(USART5_RFCS_RFEN); +} + +/*! + \brief read receive FIFO counter number + \param[in] usart_periph: USART5 + \param[out] none + \retval receive FIFO counter number +*/ +uint8_t usart5_receive_fifo_counter_number(uint32_t usart_periph) +{ + return (uint8_t)(GET_BITS(USART5_RFCS(usart_periph), 12U, 14U)); +} + +/*! + \brief get flag in STAT/CHC/RFCS register + \param[in] usart_periph: USART5 + \param[in] flag: flag type + only one parameter can be selected which is shown as below: + \arg USART5_FLAG_PERR: parity error flag + \arg USART5_FLAG_FERR: frame error flag + \arg USART5_FLAG_NERR: noise error flag + \arg USART5_FLAG_ORERR: overrun error + \arg USART5_FLAG_IDLE: idle line detected flag + \arg USART5_FLAG_RBNE: read data buffer not empty + \arg USART5_FLAG_TC: transmission completed + \arg USART5_FLAG_TBE: transmit data register empty + \arg USART5_FLAG_LBD: LIN break detected flag + \arg USART5_FLAG_RT: receiver timeout flag + \arg USART5_FLAG_EB: end of block flag + \arg USART5_FLAG_BSY: busy flag + \arg USART5_FLAG_AM: address match flag + \arg USART5_FLAG_SB: send break flag + \arg USART5_FLAG_RWU: receiver wakeup from mute mode. + \arg USART5_FLAG_WU: wakeup from deep-sleep mode flag + \arg USART5_FLAG_TEA: transmit enable acknowledge flag + \arg USART5_FLAG_REA: receive enable acknowledge flag + \arg USART5_FLAG_EPERR: early parity error flag + \arg USART5_FLAG_RFE: receive FIFO empty flag + \arg USART5_FLAG_RFF: receive FIFO full flag + \arg USART5_FLAG_RFFINT: receive FIFO full interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart5_flag_get(uint32_t usart_periph, usart5_flag_enum flag) +{ + if(RESET != (USART_REG_VAL(usart_periph, flag) & BIT(USART_BIT_POS(flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear USART status + \param[in] usart_periph: USART5 + \param[in] flag: flag type + only one parameter can be selected which is shown as below: + \arg USART5_FLAG_PERR: parity error flag + \arg USART5_FLAG_FERR: frame error flag + \arg USART5_FLAG_NERR: noise detected flag + \arg USART5_FLAG_ORERR: overrun error flag + \arg USART5_FLAG_IDLE: idle line detected flag + \arg USART5_FLAG_TC: transmission complete flag + \arg USART5_FLAG_LBD: LIN break detected flag + \arg USART5_FLAG_RT: receiver timeout flag + \arg USART5_FLAG_EB: end of block flag + \arg USART5_FLAG_AM: address match flag + \arg USART5_FLAG_WU: wakeup from deep-sleep mode flag + \arg USART5_FLAG_EPERR: early parity error flag + \param[out] none + \retval none +*/ +void usart5_flag_clear(uint32_t usart_periph, usart5_flag_enum flag) +{ + USART5_INTC(usart_periph) |= BIT(USART_BIT_POS(flag)); +} + +/*! + \brief enable USART interrupt + \param[in] usart_periph: USART5 + \param[in] interrupt: USART5 interrupts, refer to usart5_interrupt_enum + only one parameter can be selected which is shown as below: + \arg USART5_INT_IDLE: idle interrupt + \arg USART5_INT_RBNE: read data buffer not empty interrupt and + overrun error interrupt enable interrupt + \arg USART5_INT_TC: transmission complete interrupt + \arg USART5_INT_TBE: transmit data register empty interrupt + \arg USART5_INT_PERR: parity error interrupt + \arg USART5_INT_AM: address match interrupt + \arg USART5_INT_RT: receiver timeout interrupt + \arg USART5_INT_EB: end of block interrupt + \arg USART5_INT_LBD: LIN break detection interrupt + \arg USART5_INT_ERR: error interrupt enable in multibuffer communication + \arg USART5_INT_WU: wakeup from deep-sleep mode interrupt + \arg USART5_INT_RFF: receive FIFO full interrupt enable + \param[out] none + \retval none +*/ +void usart5_interrupt_enable(uint32_t usart_periph, usart5_interrupt_enum interrupt) +{ + USART_REG_VAL(usart_periph, interrupt) |= BIT(USART_BIT_POS(interrupt)); +} + +/*! + \brief disable USART interrupt + \param[in] usart_periph: USART5 + \param[in] interrupt: USART5 interrupts, refer to usart5_interrupt_enum + only one parameter can be selected which is shown as below: + \arg USART5_INT_IDLE: idle interrupt + \arg USART5_INT_RBNE: read data buffer not empty interrupt and + overrun error interrupt + \arg USART5_INT_TC: transmission complete interrupt + \arg USART5_INT_TBE: transmit data register empty interrupt + \arg USART5_INT_PERR: parity error interrupt + \arg USART5_INT_AM: address match interrupt + \arg USART5_INT_RT: receiver timeout interrupt + \arg USART5_INT_EB: end of block interrupt + \arg USART5_INT_LBD: LIN break detection interrupt + \arg USART5_INT_ERR: error interrupt enable in multibuffer communication + \arg USART5_INT_WU: wakeup from deep-sleep mode interrupt + \arg USART5_INT_RFF: receive FIFO full interrupt enable + \param[out] none + \retval none +*/ +void usart5_interrupt_disable(uint32_t usart_periph, usart5_interrupt_enum interrupt) +{ + USART_REG_VAL(usart_periph, interrupt) &= ~BIT(USART_BIT_POS(interrupt)); +} + +/*! + \brief enable USART command + \param[in] usart_periph: USART5 + \param[in] cmdtype: command type + only one parameter can be selected which is shown as below: + \arg USART5_CMD_SBKCMD: send break command + \arg USART5_CMD_MMCMD: mute mode command + \arg USART5_CMD_RXFCMD: receive data flush command + \arg USART5_CMD_TXFCMD: transmit data flush request + \param[out] none + \retval none +*/ +void usart5_command_enable(uint32_t usart_periph, uint32_t cmdtype) +{ + USART5_CMD(usart_periph) |= (cmdtype); +} + +/*! + \brief get USART interrupt and flag status + \param[in] usart_periph: USART5 + \param[in] int_flag: interrupt and flag type, refer to usart5_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg USART5_INT_FLAG_EB: end of block interrupt and flag + \arg USART5_INT_FLAG_RT: receiver timeout interrupt and flag + \arg USART5_INT_FLAG_AM: address match interrupt and flag + \arg USART5_INT_FLAG_PERR: parity error interrupt and flag + \arg USART5_INT_FLAG_TBE: transmitter buffer empty interrupt and flag + \arg USART5_INT_FLAG_TC: transmission complete interrupt and flag + \arg USART5_INT_FLAG_RBNE: read data buffer not empty interrupt and flag + \arg USART5_INT_FLAG_RBNE_ORERR: read data buffer not empty interrupt and overrun error flag + \arg USART5_INT_FLAG_IDLE: IDLE line detected interrupt and flag + \arg USART5_INT_FLAG_LBD: LIN break detected interrupt and flag + \arg USART5_INT_FLAG_WU: wakeup from deep-sleep mode interrupt and flag + \arg USART5_INT_FLAG_ERR_NERR: error interrupt and noise error flag + \arg USART5_INT_FLAG_ERR_ORERR: error interrupt and overrun error + \arg USART5_INT_FLAG_ERR_FERR: error interrupt and frame error flag + \arg USART5_INT_FLAG_RFF: receive FIFO full interrupt and flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart5_interrupt_flag_get(uint32_t usart_periph, usart5_interrupt_flag_enum int_flag) +{ + uint32_t intenable = 0U, flagstatus = 0U; + /* get the interrupt enable bit status */ + intenable = (USART_REG_VAL(usart_periph, int_flag) & BIT(USART_BIT_POS(int_flag))); + /* get the corresponding flag bit status */ + flagstatus = (USART_REG_VAL2(usart_periph, int_flag) & BIT(USART_BIT_POS2(int_flag))); + + if(flagstatus && intenable){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear USART interrupt flag + \param[in] usart_periph: USART5 + \param[in] int_flag: interrupt and flag type, refer to usart5_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg USART5_INT_FLAG_PERR: parity error interrupt and flag + \arg USART5_INT_FLAG_ERR_FERR: error interrupt and frame error flag + \arg USART5_INT_FLAG_ERR_NERR: error interrupt and noise error flag + \arg USART5_INT_FLAG_RBNE_ORERR: read data buffer not empty interrupt and overrun error flag + \arg USART5_INT_FLAG_ERR_ORERR: error interrupt and overrun error + \arg USART5_INT_FLAG_IDLE: IDLE line detected interrupt and flag + \arg USART5_INT_FLAG_TC: transmission complete interrupt and flag + \arg USART5_INT_FLAG_LBD: LIN break detected interrupt and flag + \arg USART5_INT_FLAG_RT: receiver timeout interrupt and flag + \arg USART5_INT_FLAG_EB: end of block interrupt and flag + \arg USART5_INT_FLAG_AM: address match interrupt and flag + \arg USART5_INT_FLAG_WU: wakeup from deep-sleep mode interrupt and flag + \arg USART5_INT_FLAG_RFF: receive FIFO full interrupt and flag + \param[out] none + \retval none +*/ +void usart5_interrupt_flag_clear(uint32_t usart_periph, usart5_interrupt_flag_enum int_flag) +{ + if(USART5_INT_FLAG_RFF == int_flag){ + USART5_RFCS(usart_periph) &= (uint32_t)(~USART5_RFCS_RFFINT); + }else{ + USART5_INTC(usart_periph) |= BIT(USART_BIT_POS2(int_flag)); + } +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_usart.c.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_usart.c.base@1.5.0 new file mode 100644 index 0000000..196f7cd --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_usart.c.base@1.5.0 @@ -0,0 +1,1764 @@ +/*! + \file gd32e50x_usart.c + \brief USART driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#include "gd32e50x_usart.h" + +/* USART register bit offset */ +#define GP_GUAT_OFFSET ((uint32_t)8U) /* bit offset of GUAT in USART_GP */ +#define CTL2_SCRTNUM_OFFSET ((uint32_t)17U) /* bit offset of SCRTNUM in USART_CTL2 */ +#define CTL3_SCRTNUM_OFFSET ((uint32_t)1U) /* bit offset of SCRTNUM in USART_CTL3 */ +#define RT_BL_OFFSET ((uint32_t)24U) /* bit offset of BL in USART_RT */ + +/*! + \brief reset USART/UART + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_deinit(uint32_t usart_periph) +{ + switch(usart_periph){ + case USART0: + rcu_periph_reset_enable(RCU_USART0RST); + rcu_periph_reset_disable(RCU_USART0RST); + break; + case USART1: + rcu_periph_reset_enable(RCU_USART1RST); + rcu_periph_reset_disable(RCU_USART1RST); + break; + case USART2: + rcu_periph_reset_enable(RCU_USART2RST); + rcu_periph_reset_disable(RCU_USART2RST); + break; + case USART5: + rcu_periph_reset_enable(RCU_USART5RST); + rcu_periph_reset_disable(RCU_USART5RST); + break; + case UART3: + rcu_periph_reset_enable(RCU_UART3RST); + rcu_periph_reset_disable(RCU_UART3RST); + break; + case UART4: + rcu_periph_reset_enable(RCU_UART4RST); + rcu_periph_reset_disable(RCU_UART4RST); + break; + default: + break; + } +} + +/*! + \brief configure USART baud rate value + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] baudval: baud rate value + \param[out] none + \retval none +*/ +void usart_baudrate_set(uint32_t usart_periph, uint32_t baudval) +{ + uint32_t uclk=0U, intdiv=0U, fradiv=0U, udiv=0U; + switch(usart_periph){ + /* get clock frequency */ + case USART0: + uclk=rcu_clock_freq_get(CK_APB2); + break; + case USART5: + uclk=rcu_clock_freq_get(CK_USART); + break; + case USART1: + uclk=rcu_clock_freq_get(CK_APB1); + break; + case USART2: + uclk=rcu_clock_freq_get(CK_APB1); + break; + case UART3: + uclk=rcu_clock_freq_get(CK_APB1); + break; + case UART4: + uclk=rcu_clock_freq_get(CK_APB1); + break; + default: + break; + } + + if(USART5 == usart_periph){ + /* configure USART5 baud rate value */ + if(USART5_CTL0(usart_periph) & USART5_CTL0_OVSMOD){ + /* when oversampling by 8, configure the value of USART_BAUD */ + udiv = ((2U*uclk) + baudval/2U)/baudval; + intdiv = udiv & 0x0000fff0U; + fradiv = (udiv>>1) & 0x00000007U; + }else{ + /* when oversampling by 16, configure the value of USART_BAUD */ + udiv = (uclk+baudval/2U)/baudval; + intdiv = udiv & 0x0000fff0U; + fradiv = udiv & 0x0000000fU; + } + USART5_BAUD(usart_periph) = ((USART5_BAUD_FRADIV | USART5_BAUD_INTDIV) & (intdiv | fradiv)); + }else{ + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) baud rate value */ + if(USART_CTL0(usart_periph) & USART_CTL0_OVSMOD){ + /* when oversampling by 8, configure the value of USART_BAUD */ + udiv = ((2U*uclk) + baudval/2U)/baudval; + intdiv = udiv & 0x0000fff0U; + fradiv = (udiv>>1) & 0x00000007U; + }else{ + /* when oversampling by 16, configure the value of USART_BAUD */ + udiv = (uclk+baudval/2U)/baudval; + intdiv = udiv & 0x0000fff0U; + fradiv = udiv & 0x0000000fU; + } + USART_BAUD(usart_periph) = ((USART_BAUD_FRADIV | USART_BAUD_INTDIV) & (intdiv | fradiv)); + } +} + +/*! + \brief configure USART parity function + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] paritycfg: configure USART parity + only one parameter can be selected which is shown as below: + \arg USART_PM_NONE: no parity + \arg USART_PM_EVEN: even parity + \arg USART_PM_ODD: odd parity + \param[out] none + \retval none +*/ +void usart_parity_config(uint32_t usart_periph, uint32_t paritycfg) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear USART5_CTL0 PM,PCEN Bits */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_PM | USART5_CTL0_PCEN); + /* configure USART5 parity mode */ + USART5_CTL0(usart_periph) |= ((USART5_CTL0_PM | USART5_CTL0_PCEN) & paritycfg); + }else{ + /* clear USART_CTL0 PM,PCEN Bits */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_PM | USART_CTL0_PCEN); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) parity mode */ + USART_CTL0(usart_periph) |= ((USART_CTL0_PM | USART_CTL0_PCEN) & paritycfg); + } +} + +/*! + \brief configure USART word length + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] wlen: USART word length configure + only one parameter can be selected which is shown as below: + \arg USART_WL_8BIT: 8 bits + \arg USART_WL_9BIT: 9 bits + \param[out] none + \retval none +*/ +void usart_word_length_set(uint32_t usart_periph, uint32_t wlen) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear USART5_CTL0 WL bit */ + USART5_CTL0(usart_periph) &= ~USART5_CTL0_WL; + /* configure USART word length */ + USART5_CTL0(usart_periph) |= (USART5_CTL0_WL & wlen); + }else{ + /* clear USART_CTL0 WL bit */ + USART_CTL0(usart_periph) &= ~USART_CTL0_WL; + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) word length */ + USART_CTL0(usart_periph) |= (USART_CTL0_WL & wlen); + } +} + +/*! + \brief configure USART stop bit length + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] stblen: USART stop bit configure + only one parameter can be selected which is shown as below: + \arg USART_STB_1BIT: 1 bit + \arg USART_STB_0_5BIT: 0.5 bit(not available for UARTx(x=3,4,6,7)) + \arg USART_STB_2BIT: 2 bits + \arg USART_STB_1_5BIT: 1.5 bits(not available for UARTx(x=3,4,6,7)) + \param[out] none + \retval none +*/ +void usart_stop_bit_set(uint32_t usart_periph, uint32_t stblen) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear USART5_CTL1 STB bits */ + USART5_CTL1(usart_periph) &= ~USART5_CTL1_STB; + /* configure USART stop bits */ + USART5_CTL1(usart_periph) |= (USART5_CTL1_STB & stblen); + }else{ + /* clear USART_CTL1 STB bits */ + USART_CTL1(usart_periph) &= ~USART_CTL1_STB; + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) stop bits */ + USART_CTL1(usart_periph) |= (USART_CTL1_STB & stblen); + } +} + +/*! + \brief enable USART + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* enable USART5 */ + USART5_CTL0(usart_periph) |= USART5_CTL0_UEN; + }else{ + /* enable USARTx(x=0,1,2)/UARTx(x=3,4) */ + USART_CTL0(usart_periph) |= USART_CTL0_UEN; + } +} + +/*! + \brief disable USART + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + }else{ + /* disable USARTx(x=0,1,2)/UARTx(x=3,4) */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_UEN); + } +} + +/*! + \brief configure USART transmitter + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] txconfig: enable or disable USART transmitter + only one parameter can be selected which is shown as below: + \arg USART_TRANSMIT_ENABLE: enable USART transmission + \arg USART_TRANSMIT_DISABLE: enable USART transmission + \param[out] none + \retval none +*/ +void usart_transmit_config(uint32_t usart_periph, uint32_t txconfig) +{ + uint32_t ctl = 0U; + + if(USART5 == usart_periph){ + ctl = USART5_CTL0(usart_periph); + ctl &= ~USART5_CTL0_TEN; + ctl |= (USART5_CTL0_TEN & txconfig); + /* configure USART5 transfer mode */ + USART5_CTL0(usart_periph) = ctl; + }else{ + ctl = USART_CTL0(usart_periph); + ctl &= ~USART_CTL0_TEN; + ctl |= (USART_CTL0_TEN & txconfig); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) transfer mode */ + USART_CTL0(usart_periph) = ctl; + } +} + +/*! + \brief configure USART receiver + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] rxconfig: enable or disable USART receiver + only one parameter can be selected which is shown as below: + \arg USART_RECEIVE_ENABLE: enable USART reception + \arg USART_RECEIVE_DISABLE: disable USART reception + \param[out] none + \retval none +*/ +void usart_receive_config(uint32_t usart_periph, uint32_t rxconfig) +{ + uint32_t ctl = 0U; + + if(USART5 == usart_periph){ + ctl = USART5_CTL0(usart_periph); + ctl &= ~USART5_CTL0_REN; + ctl |= (USART5_CTL0_REN & rxconfig); + /* configure USART5 receive mode */ + USART5_CTL0(usart_periph) = ctl; + }else{ + ctl = USART_CTL0(usart_periph); + ctl &= ~USART_CTL0_REN; + ctl |= (USART_CTL0_REN & rxconfig); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) receive mode */ + USART_CTL0(usart_periph) = ctl; + } +} + +/*! + \brief configure the USART oversample mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] oversamp: oversample value + only one parameter can be selected which is shown as below: + \arg USART_OVSMOD_8: 8 bits + \arg USART_OVSMOD_16: 16 bits + \param[out] none + \retval none +*/ +void usart_oversample_config(uint32_t usart_periph, uint32_t oversamp) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear OVSMOD bit */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_OVSMOD); + /* configure the USART5 oversample mode */ + USART5_CTL0(usart_periph) |= (USART5_CTL0_OVSMOD & oversamp); + }else{ + /* clear OVSMOD bit */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_OVSMOD); + /* configure the USARTx(x=0,1,2)/UARTx(x=3,4) oversample mode */ + USART_CTL0(usart_periph) |= (USART_CTL0_OVSMOD & oversamp); + } +} + +/*! + \brief configure sample bit method + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] obsm: sample bit + only one parameter can be selected which is shown as below: + \arg USART_OSB_1bit: 1 bit + \arg USART_OSB_3bit: 3 bits + \param[out] none + \retval none +*/ +void usart_sample_bit_config(uint32_t usart_periph, uint32_t obsm) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear USART5 OSB bit */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_OSB); + /* configure USART5 sample bit method */ + USART5_CTL2(usart_periph) |= (USART5_CTL2_OSB & obsm); + }else{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_OSB); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) sample bit method */ + USART_CTL2(usart_periph) |= (USART_CTL2_OSB & obsm); + } +} + +/*! + \brief enable receiver timeout of USART + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_receiver_timeout_enable(uint32_t usart_periph) +{ + if(USART5 ==usart_periph){ + /* enable receiver timeout of USART5 */ + USART5_CTL1(usart_periph) |= USART5_CTL1_RTEN; + }else{ + /* enable receiver timeout of USARTx(x=0,1,2) */ + USART_CTL3(usart_periph) |= USART_CTL3_RTEN; + } +} + +/*! + \brief disable receiver timeout of USART + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_receiver_timeout_disable(uint32_t usart_periph) +{ + if(USART5 ==usart_periph){ + /* disable receiver timeout of USART5 */ + USART5_CTL1(usart_periph) &= ~USART5_CTL1_RTEN; + }else{ + /* disable receiver timeout of USARTx(x=0,1,2) */ + USART_CTL3(usart_periph) &= ~(USART_CTL3_RTEN); + } +} + +/*! + \brief configure the receiver timeout threshold of USART + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[in] rtimeout: 0x00000000-0x00FFFFFF + \param[out] none + \retval none +*/ +void usart_receiver_timeout_threshold_config(uint32_t usart_periph, uint32_t rtimeout) +{ + if(USART5 == usart_periph){ + USART5_RT(usart_periph) &= ~(USART5_RT_RT); + /* configure USART5 receiver timeout threshold */ + USART5_RT(usart_periph) |= (USART5_RT_RT & rtimeout); + }else{ + USART_RT(usart_periph) &= ~(USART_RT_RT); + /* configure USARTx(x=0,1,2) receiver timeout threshold */ + USART_RT(usart_periph) |= (USART_RT_RT & rtimeout); + } +} + +/*! + \brief USART transmit data function + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] data: data of transmission + \param[out] none + \retval none +*/ +void usart_data_transmit(uint32_t usart_periph, uint16_t data) +{ + if(USART5 == usart_periph){ + /* USART5 transmit data */ + USART5_TDATA(usart_periph) = USART5_TDATA_TDATA & (uint32_t)data; + }else{ + /* USARTx(x=0,1,2)/UARTx(x=3,4) transmit data */ + USART_DATA(usart_periph) = USART_DATA_DATA & (uint32_t)data; + } +} + +/*! + \brief USART receive data function + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval data of received +*/ +uint16_t usart_data_receive(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* USART5 receive data */ + return (uint16_t)(GET_BITS(USART5_RDATA(usart_periph), 0U, 8U)); + }else{ + /* USARTx(x=0,1,2)/UARTx(x=3,4) receive data */ + return (uint16_t)(GET_BITS(USART_DATA(usart_periph), 0U, 8U)); + } +} + +/*! + \brief enable mute mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_mute_mode_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* enable USART5 mute mode */ + USART5_CTL0(usart_periph) |= USART5_CTL0_MEN; + }else{ + /* enable USARTx(x=0,1,2)/UARTx(x=3,4) mute mode */ + USART_CTL0(usart_periph) |= USART_CTL0_RWU; + } +} + +/*! + \brief disable mute mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_mute_mode_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 mute mode */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_MEN); + }else{ + /* disable USARTx(x=0,1,2)/UARTx(x=3,4) mute mode */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_RWU); + } +} + +/*! + \brief configure wakeup method in mute mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] wmethod: two method be used to enter or exit the mute mode + only one parameter can be selected which is shown as below: + \arg USART_WM_IDLE: idle line + \arg USART_WM_ADDR: address mask + \param[out] none + \retval none +*/ +void usart_mute_mode_wakeup_config(uint32_t usart_periph, uint32_t wmethod) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* clear USART5 WM bit */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_WM); + /* configure USART5 wakeup method in mute mode */ + USART5_CTL0(usart_periph) |= (USART5_CTL0_WM & wmethod); + }else{ + /* clear USARTx(x=0,1,2)/UARTx(x=3,4) WM bit */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_WM); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) wakeup method in mute mode */ + USART_CTL0(usart_periph) |= (USART_CTL0_WM & wmethod); + } +} + +/*! + \brief enable LIN mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_lin_mode_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable USART5 LIN mode */ + USART5_CTL1(usart_periph) |= USART5_CTL1_LMEN; + }else{ + /* enable USARTx(x=0,1,2)/UARTx(x=3,4) LIN mode */ + USART_CTL1(usart_periph) |= USART_CTL1_LMEN; + } +} + +/*! + \brief disable LIN mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_lin_mode_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 LIN mode */ + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_LMEN); + }else{ + /* disable USARTx(x=0,1,2)/UARTx(x=3,4) LIN mode */ + USART_CTL1(usart_periph) &= ~(USART_CTL1_LMEN); + } +} + +/*! + \brief configure lin break frame length + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] lblen: lin break frame length + only one parameter can be selected which is shown as below: + \arg USART_LBLEN_10B: 10 bits + \arg USART_LBLEN_11B: 11 bits + \param[out] none + \retval none +*/ +void usart_lin_break_detection_length_config(uint32_t usart_periph, uint32_t lblen) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_LBLEN); + /* configure USART5 lin brek frame length */ + USART5_CTL1(usart_periph) |= USART5_CTL1_LBLEN & (lblen); + }else{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_LBLEN); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) lin brek frame length */ + USART_CTL1(usart_periph) |= (USART_CTL1_LBLEN & lblen); + } +} + +/*! + \brief enable half duplex mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_halfduplex_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable USART5 half duplex mode */ + USART5_CTL2(usart_periph) |= (USART5_CTL2_HDEN); + }else{ + /* enable USARTx(x=0,1,2)/UARTx(x=3,4) half duplex mode */ + USART_CTL2(usart_periph) |= USART_CTL2_HDEN; + } +} + +/*! + \brief disable half duplex mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_halfduplex_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 half duplex mode */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_HDEN); + }else{ + /* disable USARTx(x=0,1,2)/UARTx(x=3,4) half duplex mode */ + USART_CTL2(usart_periph) &= ~(USART_CTL2_HDEN); + } +} + +/*! + \brief enable CK pin in synchronous mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_synchronous_clock_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable USART5 CK pin in synchronous mode */ + USART5_CTL1(usart_periph) |= USART5_CTL1_CKEN; + }else{ + /* enable USARTx(x=0,1,2) CK pin in synchronous mode */ + USART_CTL1(usart_periph) |= USART_CTL1_CKEN; + } +} + +/*! + \brief disable CK pin in synchronous mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_synchronous_clock_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 CK pin in synchronous mode */ + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_CKEN); + }else{ + /* disable USARTx(x=0,1,2) CK pin in synchronous mode */ + USART_CTL1(usart_periph) &= ~(USART_CTL1_CKEN); + } +} + +/*! + \brief configure USART synchronous mode parameters + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[in] clen: CK length + only one parameter can be selected which is shown as below: + \arg USART_CLEN_NONE: there are 7 CK pulses for an 8 bit frame and 8 CK pulses for a 9 bit frame + \arg USART_CLEN_EN: there are 8 CK pulses for an 8 bit frame and 9 CK pulses for a 9 bit frame + \param[in] cph: clock phase + only one parameter can be selected which is shown as below: + \arg USART_CPH_1CK: first clock transition is the first data capture edge + \arg USART_CPH_2CK: second clock transition is the first data capture edge + \param[in] cpl: clock polarity + only one parameter can be selected which is shown as below: + \arg USART_CPL_LOW: steady low value on CK pin + \arg USART_CPL_HIGH: steady high value on CK pin + \param[out] none + \retval none +*/ +void usart_synchronous_clock_config(uint32_t usart_periph, uint32_t clen, uint32_t cph, uint32_t cpl) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL1(usart_periph) &= ~(USART_CTL1_CLEN | USART_CTL1_CPH | USART_CTL1_CPL); + USART5_CTL1(usart_periph) |= (USART_CTL1_CLEN & clen) | (USART_CTL1_CPH & cph) | (USART_CTL1_CPL & cpl); + }else{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_CLEN | USART_CTL1_CPH | USART_CTL1_CPL); + USART_CTL1(usart_periph) |= (USART_CTL1_CLEN & clen) | (USART_CTL1_CPH & cph) | (USART_CTL1_CPL & cpl); + } +} + +/*! + \brief configure guard time value in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[in] guat: guard time value, 0x00000000-0x000000FF + \param[out] none + \retval none +*/ +void usart_guard_time_config(uint32_t usart_periph,uint8_t guat) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_GP(usart_periph) &= ~(USART5_GP_GUAT); + /* configure USART5 guard time value */ + USART5_GP(usart_periph) |= (USART5_GP_GUAT & ((uint32_t)guat << GP_GUAT_OFFSET)); + }else{ + USART_GP(usart_periph) &= ~(USART_GP_GUAT); + /* configure USARTx(x=0,1,2) guard time value */ + USART_GP(usart_periph) |= (USART_GP_GUAT & ((uint32_t)guat << GP_GUAT_OFFSET)); + } +} + +/*! + \brief enable smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + /* enable USART5 smartcard mode */ + USART5_CTL2(usart_periph) |= USART5_CTL2_SCEN; + }else{ + /* enable USARTx(x=0,1,2) smartcard mode */ + USART_CTL2(usart_periph) |= USART_CTL2_SCEN; + } +} + +/*! + \brief disable smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 smartcard mode */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_SCEN); + }else{ + /* disable USARTx(x=0,1,2) smartcard mode */ + USART_CTL2(usart_periph) &= ~(USART_CTL2_SCEN); + } +} + +/*! + \brief enable NACK in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_nack_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable USART5 NACK in smartcard mode */ + USART5_CTL2(usart_periph) |= USART5_CTL2_NKEN; + }else{ + /* enable USARTx(x=0,1,2) NACK in smartcard mode */ + USART_CTL2(usart_periph) |= USART_CTL2_NKEN; + } +} + +/*! + \brief disable NACK in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_nack_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 NACK in smartcard mode */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_NKEN); + }else{ + /* disable USARTx(x=0,1,2) NACK in smartcard mode */ + USART_CTL2(usart_periph) &= ~(USART_CTL2_NKEN); + } +} + +/*! + \brief configure smartcard auto-retry number + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[in] scrtnum: smartcard auto-retry number, 0x00000000-0x00000007 + \param[out] none + \retval none +*/ +void usart_smartcard_autoretry_config(uint32_t usart_periph, uint8_t scrtnum) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_SCRTNUM); + /* configure USART5 smartcard auto-retry number */ + USART5_CTL2(usart_periph) |= (USART5_CTL2_SCRTNUM & ((uint32_t) scrtnum << CTL2_SCRTNUM_OFFSET)); + }else{ + USART_CTL3(usart_periph) &= ~(USART_CTL3_SCRTNUM); + /* configure USARTx(x=0,1,2) smartcard auto-retry number */ + USART_CTL3(usart_periph) |= (USART_CTL3_SCRTNUM & ((uint32_t)scrtnum << CTL3_SCRTNUM_OFFSET)); + } +} + +/*! + \brief configure block length in Smartcard T=1 reception + \param[in] usart_periph: USARTx(x=0,1,2,5) + \param[in] bl: block length, 0x00000000-0x000000FF + \param[out] none + \retval none +*/ +void usart_block_length_config(uint32_t usart_periph, uint8_t bl) +{ + if(USART5 == usart_periph){ + USART5_RT(usart_periph) &= ~(USART5_RT_BL); + /* configure USART5 block length */ + USART5_RT(usart_periph) |= (USART5_RT_BL & ((uint32_t)bl << RT_BL_OFFSET)); + }else{ + USART_RT(usart_periph) &= ~(USART_RT_BL); + /* configure USARTx(x=0,1,2) block length */ + USART_RT(usart_periph) |= (USART_RT_BL & ((uint32_t)bl << RT_BL_OFFSET)); + } +} + +/*! + \brief enable IrDA mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_irda_mode_enable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable USART5 IrDA mode */ + USART5_CTL2(usart_periph) |= USART5_CTL2_IREN; + }else{ + /* enable USARTx(x=0,1,2)/UARTx(x=3,4) IrDA mode */ + USART_CTL2(usart_periph) |= USART_CTL2_IREN; + } +} + +/*! + \brief disable IrDA mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_irda_mode_disable(uint32_t usart_periph) +{ + if(USART5 == usart_periph){ + /* disable USART */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable USART5 IrDA mode */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_IREN); + }else{ + /* disable USARTx(x=0,1,2)/UARTx(x=3,4) IrDA mode */ + USART_CTL2(usart_periph) &= ~(USART_CTL2_IREN); + } +} + +/*! + \brief configure the peripheral clock prescaler in USART IrDA low-power mode + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] psc: 0x00000000-0x000000FF + \param[out] none + \retval none +*/ +void usart_prescaler_config(uint32_t usart_periph, uint8_t psc) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_GP(usart_periph) &= ~(USART5_GP_PSC); + /* configure the psc in USART5 */ + USART5_GP(usart_periph) |= (uint32_t)psc; + }else{ + USART_GP(usart_periph) &= ~(USART_GP_PSC); + /* configure the psc in USARTx(x=0,1,2)/UARTx(x=3,4) */ + USART_GP(usart_periph) |= (uint32_t)psc; + } +} + +/*! + \brief configure IrDA low-power + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] irlp: IrDA low-power or normal + only one parameter can be selected which is shown as below: + \arg USART_IRLP_LOW: low-power + \arg USART_IRLP_NORMAL: normal + \param[out] none + \retval none +*/ +void usart_irda_lowpower_config(uint32_t usart_periph, uint32_t irlp) +{ + if(USART5 == usart_periph){ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_IRLP); + /* configure USART5 IrDA low-power */ + USART5_CTL2(usart_periph) |= (USART5_CTL2_IRLP & irlp); + }else{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_IRLP); + /* configure USARTx(x=0,1,2)/UARTx(x=3,4) IrDA low-power */ + USART_CTL2(usart_periph) |= (USART_CTL2_IRLP & irlp); + } +} + +/*! + \brief configure USART DMA reception + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] dmaconfig: enable or disable DMA for reception + only one parameter can be selected which is shown as below: + \arg USART_RECEIVE_DMA_ENABLE: DMA enable for reception + \arg USART_RECEIVE_DMA_DISABLE: DMA disable for reception + \param[out] none + \retval none +*/ +void usart_dma_receive_config(uint32_t usart_periph, uint8_t dmacmd) +{ + uint32_t ctl = 0U; + + if(USART5 == usart_periph){ + ctl = USART5_CTL2(usart_periph); + ctl &= ~USART5_CTL2_DENR; + ctl |= dmacmd; + /* configure DMA reception */ + USART5_CTL2(usart_periph) = ctl; + }else{ + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_DENR; + ctl |= dmacmd; + /* configure DMA reception */ + USART_CTL2(usart_periph) = ctl; + } +} + +/*! + \brief configure USART DMA transmission + \param[in] usart_periph: USARTx(x=0,1,2,5)/UARTx(x=3,4) + \param[in] dmaconfig: enable or disable DMA for transmission + only one parameter can be selected which is shown as below: + \arg USART_TRANSMIT_DMA_ENABLE: DMA enable for transmission + \arg USART_TRANSMIT_DMA_DISABLE: DMA disable for transmission + \param[out] none + \retval none +*/ +void usart_dma_transmit_config(uint32_t usart_periph, uint8_t dmacmd) +{ + uint32_t ctl = 0U; + + if(USART5 == usart_periph){ + ctl = USART5_CTL2(usart_periph); + ctl &= ~USART5_CTL2_DENT; + ctl |= dmacmd; + /* configure DMA reception */ + USART5_CTL2(usart_periph) = ctl; + }else{ + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_DENT; + ctl |= dmacmd; + /* configure DMA reception */ + USART_CTL2(usart_periph) = ctl; + } +} + +/*! + \brief configure hardware flow control RTS + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] rtsconfig: enable or disable RTS + only one parameter can be selected which is shown as below: + \arg USART_RTS_ENABLE: enable RTS + \arg USART_RTS_DISABLE: disable RTS + \param[out] none + \retval none +*/ +void usart_hardware_flow_rts_config(uint32_t usart_periph, uint32_t rtsconfig) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_RTSEN); + USART_CTL2(usart_periph) |= (USART_CTL2_RTSEN & rtsconfig); +} + +/*! + \brief configure hardware flow control CTS + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] ctsconfig: enable or disable CTS + only one parameter can be selected which is shown as below: + \arg USART_CTS_ENABLE: enable CTS + \arg USART_CTS_DISABLE: disable CTS + \param[out] none + \retval none +*/ +void usart_hardware_flow_cts_config(uint32_t usart_periph, uint32_t ctsconfig) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_CTSEN); + USART_CTL2(usart_periph) |= (USART_CTL2_CTSEN & ctsconfig); +} + +/*! + \brief data is transmitted/received with the LSB/MSB first + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] msbf: LSB/MSB + only one parameter can be selected which is shown as below: + \arg USART_MSBF_LSB: LSB first + \arg USART_MSBF_MSB: MSB first + \param[out] none + \retval none +*/ +void usart_data_first_config(uint32_t usart_periph, uint32_t msbf) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL3(usart_periph); + ctl &= ~(USART_CTL3_MSBF); + ctl |= (USART_CTL3_MSBF & msbf); + /* configure data transmitted/received mode */ + USART_CTL3(usart_periph) = ctl; +} + +/*! + \brief configure USART inversion + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] invertpara: refer to enum USART_INVERT_CONFIG + only one parameter can be selected which is shown as below: + \arg USART_DINV_ENABLE: data bit level inversion + \arg USART_DINV_DISABLE: data bit level not inversion + \arg USART_TXPIN_ENABLE: TX pin level inversion + \arg USART_TXPIN_DISABLE: TX pin level not inversion + \arg USART_RXPIN_ENABLE: RX pin level inversion + \arg USART_RXPIN_DISABLE: RX pin level not inversion + \param[out] none + \retval none +*/ +void usart_invert_config(uint32_t usart_periph, usart_invert_enum invertpara) +{ + /* inverted or not the specified siginal */ + switch(invertpara){ + case USART_DINV_ENABLE: + USART_CTL3(usart_periph) |= USART_CTL3_DINV; + break; + case USART_DINV_DISABLE: + USART_CTL3(usart_periph) &= ~(USART_CTL3_DINV); + break; + case USART_TXPIN_ENABLE: + USART_CTL3(usart_periph) |= USART_CTL3_TINV; + break; + case USART_TXPIN_DISABLE: + USART_CTL3(usart_periph) &= ~(USART_CTL3_TINV); + break; + case USART_RXPIN_ENABLE: + USART_CTL3(usart_periph) |= USART_CTL3_RINV; + break; + case USART_RXPIN_DISABLE: + USART_CTL3(usart_periph) &= ~(USART_CTL3_RINV); + break; + default: + break; + } +} + +/*! + \brief configure the address of the USART in wake up by address match mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] addr: address of USART/UART, 0x00000000-0x0000000F + \param[out] none + \retval none +*/ +void usart_address_config(uint32_t usart_periph, uint8_t addr) +{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_ADDR); + USART_CTL1(usart_periph) |= (USART_CTL1_ADDR & (uint32_t)addr); +} + +/*! + \brief send break frame + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_send_break(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) |= USART_CTL0_SBKCMD; +} + +/*! + \brief enable collision detected interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_collision_detected_interrupt_enable(uint32_t usart_periph) +{ + USART_GDCTL(usart_periph) |= USART_GDCTL_CDIE; +} + +/*! + \brief disable collision detected interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_collision_detected_interrupt_disable(uint32_t usart_periph) +{ + USART_GDCTL(usart_periph) &= ~(USART_GDCTL_CDIE); +} + +/*! + \brief enable collision detection + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_collision_detection_enable(uint32_t usart_periph) +{ + USART_GDCTL(usart_periph) |= USART_GDCTL_CDEN; +} + +/*! + \brief disable collision detection + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_collision_detection_disable(uint32_t usart_periph) +{ + USART_GDCTL(usart_periph) &= ~(USART_GDCTL_CDEN); +} + +/*! + \brief get flag in STAT0/STAT1/GDCTL register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] flag: USART flags, refer to usart_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_FLAG_CTS: CTS change flag + \arg USART_FLAG_LBD: LIN break detected flag + \arg USART_FLAG_TBE: transmit data buffer empty + \arg USART_FLAG_TC: transmission complete + \arg USART_FLAG_RBNE: read data buffer not empty + \arg USART_FLAG_IDLE: IDLE frame detected flag + \arg USART_FLAG_ORERR: overrun error flag + \arg USART_FLAG_NERR: noise error flag + \arg USART_FLAG_FERR: frame error flag + \arg USART_FLAG_PERR: parity error flag + \arg USART_FLAG_BSY: busy flag + \arg USART_FLAG_EB: end of block flag + \arg USART_FLAG_RT: receiver timeout flag + \arg USART_FLAG_CD: collision detected flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag) +{ + if(RESET != (USART_REG_VAL(usart_periph, flag) & BIT(USART_BIT_POS(flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear flag in STAT0/STAT1/GDCTL register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] flag: USART flags, refer to usart_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_FLAG_CTS: CTS change flag + \arg USART_FLAG_LBD: LIN break detected flag + \arg USART_FLAG_TC: transmission complete + \arg USART_FLAG_RBNE: read data buffer not empty + \arg USART_FLAG_EB: end of block flag + \arg USART_FLAG_RT: receiver timeout flag + \arg USART_FLAG_CD: collision detected flag + \param[out] none + \retval none +*/ +void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag) +{ + if (USART_FLAG_CD == flag) { + USART_REG_VAL(usart_periph, flag) &= ~BIT(USART_BIT_POS(flag)); + } else { + USART_REG_VAL(usart_periph, flag) = ~BIT(USART_BIT_POS(flag)); + } +} + +/*! + \brief enable USART interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] interrupt: USART interrupts, refer to usart_interrupt_enum + only one parameter can be selected which is shown as below: + \arg USART_INT_PERR: parity error interrupt + \arg USART_INT_TBE: transmitter buffer empty interrupt + \arg USART_INT_TC: transmission complete interrupt + \arg USART_INT_RBNE: read data buffer not empty interrupt and overrun error interrupt + \arg USART_INT_IDLE: IDLE line detected interrupt + \arg USART_INT_LBD: LIN break detected interrupt + \arg USART_INT_ERR: error interrupt + \arg USART_INT_CTS: CTS interrupt + \arg USART_INT_RT: interrupt enable bit of receive timeout event + \arg USART_INT_EB: interrupt enable bit of end of block event + \arg USART_INT_CD: collision detected interrupt + \param[out] none + \retval none +*/ +void usart_interrupt_enable(uint32_t usart_periph, usart_interrupt_enum interrupt) +{ + USART_REG_VAL(usart_periph, interrupt) |= BIT(USART_BIT_POS(interrupt)); +} + +/*! + \brief disable USART interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] interrupt: USART interrupts, refer to usart_interrupt_enum + only one parameter can be selected which is shown as below: + \arg USART_INT_PERR: parity error interrupt + \arg USART_INT_TBE: transmitter buffer empty interrupt + \arg USART_INT_TC: transmission complete interrupt + \arg USART_INT_RBNE: read data buffer not empty interrupt and overrun error interrupt + \arg USART_INT_IDLE: IDLE line detected interrupt + \arg USART_INT_LBD: LIN break detected interrupt + \arg USART_INT_ERR: error interrupt + \arg USART_INT_CTS: CTS interrupt + \arg USART_INT_RT: interrupt enable bit of receive timeout event + \arg USART_INT_EB: interrupt enable bit of end of block event + \arg USART_INT_CD: collision detected interrupt + \param[out] none + \retval none +*/ +void usart_interrupt_disable(uint32_t usart_periph, usart_interrupt_enum interrupt) +{ + USART_REG_VAL(usart_periph, interrupt) &= ~BIT(USART_BIT_POS(interrupt)); +} + +/*! + \brief get USART interrupt and flag status + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] int_flag: USART interrupt flags, refer to usart_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_INT_FLAG_PERR: parity error interrupt and flag + \arg USART_INT_FLAG_TBE: transmitter buffer empty interrupt and flag + \arg USART_INT_FLAG_TC: transmission complete interrupt and flag + \arg USART_INT_FLAG_RBNE: read data buffer not empty interrupt and flag + \arg USART_INT_FLAG_RBNE_ORERR: read data buffer not empty interrupt and overrun error flag + \arg USART_INT_FLAG_IDLE: IDLE line detected interrupt and flag + \arg USART_INT_FLAG_LBD: LIN break detected interrupt and flag + \arg USART_INT_FLAG_CTS: CTS interrupt and flag + \arg USART_INT_FLAG_ERR_ORERR: error interrupt and overrun error + \arg USART_INT_FLAG_ERR_NERR: error interrupt and noise error flag + \arg USART_INT_FLAG_ERR_FERR: error interrupt and frame error flag + \arg USART_INT_FLAG_EB: interrupt enable bit of end of block event and flag + \arg USART_INT_FLAG_RT: interrupt enable bit of receive timeout event and flag + \arg USART_INT_FLAG_CD: collision detected interrupt and flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, usart_interrupt_flag_enum int_flag) +{ + uint32_t intenable = 0U, flagstatus = 0U; + /* get the interrupt enable bit status */ + intenable = (USART_REG_VAL(usart_periph, int_flag) & BIT(USART_BIT_POS(int_flag))); + /* get the corresponding flag bit status */ + flagstatus = (USART_REG_VAL2(usart_periph, int_flag) & BIT(USART_BIT_POS2(int_flag))); + + if((0U != flagstatus) && (0U != intenable)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear USART interrupt flag in STAT0/STAT1 register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] int_flag: USART interrupt flags, refer to usart_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_INT_FLAG_CTS: CTS interrupt and flag + \arg USART_INT_FLAG_LBD: LIN break detected interrupt and flag + \arg USART_INT_FLAG_TC: transmission complete interrupt and flag + \arg USART_INT_FLAG_RBNE: read data buffer not empty interrupt and flag + \arg USART_INT_FLAG_EB: interrupt enable bit of end of block event and flag + \arg USART_INT_FLAG_RT: interrupt enable bit of receive timeout event and flag + \arg USART_INT_FLAG_CD: collision detected interrupt and flag + \param[out] none + \retval none +*/ +void usart_interrupt_flag_clear(uint32_t usart_periph, usart_interrupt_flag_enum int_flag) +{ + USART_REG_VAL2(usart_periph, int_flag) = ~BIT(USART_BIT_POS2(int_flag)); +} + +/*! + \brief data is transmitted/received with the LSB/MSB first + \param[in] usart_periph: USART5 + \param[in] msbf: LSB/MSB + only one parameter can be selected which is shown as below: + \arg USART5_MSBF_LSB: LSB first + \arg USART5_MSBF_MSB: MSB first + \param[out] none + \retval none +*/ +void usart5_data_first_config(uint32_t usart_periph, uint32_t msbf) +{ + uint32_t ctl = 0U; + + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + ctl = USART5_CTL1(usart_periph); + ctl &= ~(USART5_CTL1_MSBF); + ctl |= (USART5_CTL1_MSBF & msbf); + /* configure data transmitted/received mode */ + USART5_CTL1(usart_periph) = ctl; +} + +/*! + \brief configure USART5 inversion + \param[in] usart_periph: USART5 + \param[in] invertpara: refer to enum USART5_INVERT_CONFIG + only one parameter can be selected which is shown as below: + \arg USART5_DINV_ENABLE: data bit level inversion + \arg USART5_DINV_DISABLE: data bit level not inversion + \arg USART5_TXPIN_ENABLE: TX pin level inversion + \arg USART5_TXPIN_DISABLE: TX pin level not inversion + \arg USART5_RXPIN_ENABLE: RX pin level inversion + \arg USART5_RXPIN_DISABLE: RX pin level not inversion + \arg USART5_SWAP_ENABLE: swap TX/RX pins + \arg USART5_SWAP_DISABLE: not swap TX/RX pins + \param[out] none + \retval none +*/ +void usart5_invert_config(uint32_t usart_periph, usart5_invert_enum invertpara) +{ + /* inverted or not the specified siginal */ + switch(invertpara){ + case USART5_DINV_ENABLE: + USART5_CTL1(usart_periph) |= USART5_CTL1_DINV; + break; + case USART5_DINV_DISABLE: + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_DINV); + break; + case USART5_TXPIN_ENABLE: + USART5_CTL1(usart_periph) |= USART5_CTL1_TINV; + break; + case USART5_TXPIN_DISABLE: + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_TINV); + break; + case USART5_RXPIN_ENABLE: + USART5_CTL1(usart_periph) |= USART5_CTL1_RINV; + break; + case USART5_RXPIN_DISABLE: + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_RINV); + break; + case USART5_SWAP_ENABLE: + USART5_CTL1(usart_periph) |= USART5_CTL1_STRP; + break; + case USART5_SWAP_DISABLE: + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_STRP); + break; + default: + break; + } +} + +/*! + \brief enable the USART5 overrun function + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_overrun_enable(uint32_t usart_periph) +{ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* enable overrun function */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_OVRD); +} + +/*! + \brief disable the USART5 overrun function + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_overrun_disable(uint32_t usart_periph) +{ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* disable overrun function */ + USART5_CTL2(usart_periph) |= USART5_CTL2_OVRD; +} + +/*! + \brief address of the USART terminal + \param[in] usart_periph: USART5 + \param[in] addr: address of USART terminal, 0x00000000-0x000000FF + \param[out] none + \retval none +*/ +void usart5_address_config(uint32_t usart_periph, uint8_t addr) +{ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_ADDR); + USART5_CTL1(usart_periph) |= (USART5_CTL1_ADDR & (((uint32_t)addr) << 24)); +} + +/*! + \brief configure address detection mode + \param[in] usart_periph: USART5 + \param[in] addmod: address detection mode + only one parameter can be selected which is shown as below: + \arg USART5_ADDM_4BIT: 4 bits + \arg USART5_ADDM_FULLBIT: full bits + \param[out] none + \retval none +*/ +void usart5_address_detection_mode_config(uint32_t usart_periph, uint32_t addmod) +{ + /* disable USART */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL1(usart_periph) &= ~(USART5_CTL1_ADDM); + USART5_CTL1(usart_periph) |= (USART5_CTL1_ADDM & addmod); +} + +/*! + \brief enable early NACK in smartcard mode + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_smartcard_mode_early_nack_enable(uint32_t usart_periph) +{ + USART5_RFCS(usart_periph) |= USART5_RFCS_ELNACK; +} + +/*! + \brief disable early NACK in smartcard mode + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_smartcard_mode_early_nack_disable(uint32_t usart_periph) +{ + USART5_RFCS(usart_periph) &= ~USART5_RFCS_ELNACK; +} + +/*! + \brief enable DMA on reception error + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_reception_error_dma_enable(uint32_t usart_periph) +{ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_DDRE); +} + +/*! + \brief disable DMA on reception error + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_reception_error_dma_disable(uint32_t usart_periph) +{ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + + USART5_CTL2(usart_periph) |= USART5_CTL2_DDRE; +} + +/*! + \brief enable USART to wakeup the mcu from deep-sleep mode + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_wakeup_enable(uint32_t usart_periph) +{ + USART5_CTL0(usart_periph) |= USART5_CTL0_UESM; +} + +/*! + \brief disable USART to wakeup the mcu from deep-sleep mode + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_wakeup_disable(uint32_t usart_periph) +{ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UESM); +} + +/*! + \brief configure the USART wakeup mode from deep-sleep mode + \param[in] usart_periph: USART5 + \param[in] wum: wakeup mode + only one parameter can be selected which is shown as below: + \arg USART5_WUM_ADDR: WUF active on address match + \arg USART5_WUM_STARTB: WUF active on start bit + \arg USART5_WUM_RBNE: WUF active on RBNE + \param[out] none + \retval none +*/ +void usart5_wakeup_mode_config(uint32_t usart_periph, uint32_t wum) +{ + /* disable USART5 */ + USART5_CTL0(usart_periph) &= ~(USART5_CTL0_UEN); + /* reset WUM bit */ + USART5_CTL2(usart_periph) &= ~(USART5_CTL2_WUM); + USART5_CTL2(usart_periph) |= (USART5_CTL2_WUM & wum); +} + +/*! + \brief enable receive FIFO + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_receive_fifo_enable(uint32_t usart_periph) +{ + USART5_RFCS(usart_periph) |= USART5_RFCS_RFEN; +} + +/*! + \brief disable receive FIFO + \param[in] usart_periph: USART5 + \param[out] none + \retval none +*/ +void usart5_receive_fifo_disable(uint32_t usart_periph) +{ + USART5_RFCS(usart_periph) &= ~(USART5_RFCS_RFEN); +} + +/*! + \brief read receive FIFO counter number + \param[in] usart_periph: USART5 + \param[out] none + \retval receive FIFO counter number +*/ +uint8_t usart5_receive_fifo_counter_number(uint32_t usart_periph) +{ + return (uint8_t)(GET_BITS(USART5_RFCS(usart_periph), 12U, 14U)); +} + +/*! + \brief get flag in STAT/CHC/RFCS register + \param[in] usart_periph: USART5 + \param[in] flag: flag type + only one parameter can be selected which is shown as below: + \arg USART5_FLAG_PERR: parity error flag + \arg USART5_FLAG_FERR: frame error flag + \arg USART5_FLAG_NERR: noise error flag + \arg USART5_FLAG_ORERR: overrun error + \arg USART5_FLAG_IDLE: idle line detected flag + \arg USART5_FLAG_RBNE: read data buffer not empty + \arg USART5_FLAG_TC: transmission completed + \arg USART5_FLAG_TBE: transmit data register empty + \arg USART5_FLAG_LBD: LIN break detected flag + \arg USART5_FLAG_RT: receiver timeout flag + \arg USART5_FLAG_EB: end of block flag + \arg USART5_FLAG_BSY: busy flag + \arg USART5_FLAG_AM: address match flag + \arg USART5_FLAG_SB: send break flag + \arg USART5_FLAG_RWU: receiver wakeup from mute mode. + \arg USART5_FLAG_WU: wakeup from deep-sleep mode flag + \arg USART5_FLAG_TEA: transmit enable acknowledge flag + \arg USART5_FLAG_REA: receive enable acknowledge flag + \arg USART5_FLAG_EPERR: early parity error flag + \arg USART5_FLAG_RFE: receive FIFO empty flag + \arg USART5_FLAG_RFF: receive FIFO full flag + \arg USART5_FLAG_RFFINT: receive FIFO full interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart5_flag_get(uint32_t usart_periph, usart5_flag_enum flag) +{ + if(RESET != (USART_REG_VAL(usart_periph, flag) & BIT(USART_BIT_POS(flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear USART status + \param[in] usart_periph: USART5 + \param[in] flag: flag type + only one parameter can be selected which is shown as below: + \arg USART5_FLAG_PERR: parity error flag + \arg USART5_FLAG_FERR: frame error flag + \arg USART5_FLAG_NERR: noise detected flag + \arg USART5_FLAG_ORERR: overrun error flag + \arg USART5_FLAG_IDLE: idle line detected flag + \arg USART5_FLAG_TC: transmission complete flag + \arg USART5_FLAG_LBD: LIN break detected flag + \arg USART5_FLAG_RT: receiver timeout flag + \arg USART5_FLAG_EB: end of block flag + \arg USART5_FLAG_AM: address match flag + \arg USART5_FLAG_WU: wakeup from deep-sleep mode flag + \arg USART5_FLAG_EPERR: early parity error flag + \param[out] none + \retval none +*/ +void usart5_flag_clear(uint32_t usart_periph, usart5_flag_enum flag) +{ + USART5_INTC(usart_periph) |= BIT(USART_BIT_POS(flag)); +} + +/*! + \brief enable USART interrupt + \param[in] usart_periph: USART5 + \param[in] interrupt: USART5 interrupts, refer to usart5_interrupt_enum + only one parameter can be selected which is shown as below: + \arg USART5_INT_IDLE: idle interrupt + \arg USART5_INT_RBNE: read data buffer not empty interrupt and + overrun error interrupt enable interrupt + \arg USART5_INT_TC: transmission complete interrupt + \arg USART5_INT_TBE: transmit data register empty interrupt + \arg USART5_INT_PERR: parity error interrupt + \arg USART5_INT_AM: address match interrupt + \arg USART5_INT_RT: receiver timeout interrupt + \arg USART5_INT_EB: end of block interrupt + \arg USART5_INT_LBD: LIN break detection interrupt + \arg USART5_INT_ERR: error interrupt enable in multibuffer communication + \arg USART5_INT_WU: wakeup from deep-sleep mode interrupt + \arg USART5_INT_RFF: receive FIFO full interrupt enable + \param[out] none + \retval none +*/ +void usart5_interrupt_enable(uint32_t usart_periph, usart5_interrupt_enum interrupt) +{ + USART_REG_VAL(usart_periph, interrupt) |= BIT(USART_BIT_POS(interrupt)); +} + +/*! + \brief disable USART interrupt + \param[in] usart_periph: USART5 + \param[in] interrupt: USART5 interrupts, refer to usart5_interrupt_enum + only one parameter can be selected which is shown as below: + \arg USART5_INT_IDLE: idle interrupt + \arg USART5_INT_RBNE: read data buffer not empty interrupt and + overrun error interrupt + \arg USART5_INT_TC: transmission complete interrupt + \arg USART5_INT_TBE: transmit data register empty interrupt + \arg USART5_INT_PERR: parity error interrupt + \arg USART5_INT_AM: address match interrupt + \arg USART5_INT_RT: receiver timeout interrupt + \arg USART5_INT_EB: end of block interrupt + \arg USART5_INT_LBD: LIN break detection interrupt + \arg USART5_INT_ERR: error interrupt enable in multibuffer communication + \arg USART5_INT_WU: wakeup from deep-sleep mode interrupt + \arg USART5_INT_RFF: receive FIFO full interrupt enable + \param[out] none + \retval none +*/ +void usart5_interrupt_disable(uint32_t usart_periph, usart5_interrupt_enum interrupt) +{ + USART_REG_VAL(usart_periph, interrupt) &= ~BIT(USART_BIT_POS(interrupt)); +} + +/*! + \brief enable USART command + \param[in] usart_periph: USART5 + \param[in] cmdtype: command type + only one parameter can be selected which is shown as below: + \arg USART5_CMD_SBKCMD: send break command + \arg USART5_CMD_MMCMD: mute mode command + \arg USART5_CMD_RXFCMD: receive data flush command + \arg USART5_CMD_TXFCMD: transmit data flush request + \param[out] none + \retval none +*/ +void usart5_command_enable(uint32_t usart_periph, uint32_t cmdtype) +{ + USART5_CMD(usart_periph) |= (cmdtype); +} + +/*! + \brief get USART interrupt and flag status + \param[in] usart_periph: USART5 + \param[in] int_flag: interrupt and flag type, refer to usart5_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg USART5_INT_FLAG_EB: end of block interrupt and flag + \arg USART5_INT_FLAG_RT: receiver timeout interrupt and flag + \arg USART5_INT_FLAG_AM: address match interrupt and flag + \arg USART5_INT_FLAG_PERR: parity error interrupt and flag + \arg USART5_INT_FLAG_TBE: transmitter buffer empty interrupt and flag + \arg USART5_INT_FLAG_TC: transmission complete interrupt and flag + \arg USART5_INT_FLAG_RBNE: read data buffer not empty interrupt and flag + \arg USART5_INT_FLAG_RBNE_ORERR: read data buffer not empty interrupt and overrun error flag + \arg USART5_INT_FLAG_IDLE: IDLE line detected interrupt and flag + \arg USART5_INT_FLAG_LBD: LIN break detected interrupt and flag + \arg USART5_INT_FLAG_WU: wakeup from deep-sleep mode interrupt and flag + \arg USART5_INT_FLAG_ERR_NERR: error interrupt and noise error flag + \arg USART5_INT_FLAG_ERR_ORERR: error interrupt and overrun error + \arg USART5_INT_FLAG_ERR_FERR: error interrupt and frame error flag + \arg USART5_INT_FLAG_RFF: receive FIFO full interrupt and flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart5_interrupt_flag_get(uint32_t usart_periph, usart5_interrupt_flag_enum int_flag) +{ + uint32_t intenable = 0U, flagstatus = 0U; + /* get the interrupt enable bit status */ + intenable = (USART_REG_VAL(usart_periph, int_flag) & BIT(USART_BIT_POS(int_flag))); + /* get the corresponding flag bit status */ + flagstatus = (USART_REG_VAL2(usart_periph, int_flag) & BIT(USART_BIT_POS2(int_flag))); + + if(flagstatus && intenable){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear USART interrupt flag + \param[in] usart_periph: USART5 + \param[in] int_flag: interrupt and flag type, refer to usart5_interrupt_flag_enum + only one parameter can be selected which is shown as below: + \arg USART5_INT_FLAG_PERR: parity error interrupt and flag + \arg USART5_INT_FLAG_ERR_FERR: error interrupt and frame error flag + \arg USART5_INT_FLAG_ERR_NERR: error interrupt and noise error flag + \arg USART5_INT_FLAG_RBNE_ORERR: read data buffer not empty interrupt and overrun error flag + \arg USART5_INT_FLAG_ERR_ORERR: error interrupt and overrun error + \arg USART5_INT_FLAG_IDLE: IDLE line detected interrupt and flag + \arg USART5_INT_FLAG_TC: transmission complete interrupt and flag + \arg USART5_INT_FLAG_LBD: LIN break detected interrupt and flag + \arg USART5_INT_FLAG_RT: receiver timeout interrupt and flag + \arg USART5_INT_FLAG_EB: end of block interrupt and flag + \arg USART5_INT_FLAG_AM: address match interrupt and flag + \arg USART5_INT_FLAG_WU: wakeup from deep-sleep mode interrupt and flag + \arg USART5_INT_FLAG_RFF: receive FIFO full interrupt and flag + \param[out] none + \retval none +*/ +void usart5_interrupt_flag_clear(uint32_t usart_periph, usart5_interrupt_flag_enum int_flag) +{ + if(USART5_INT_FLAG_RFF == int_flag){ + USART5_RFCS(usart_periph) &= (uint32_t)(~USART5_RFCS_RFFINT); + }else{ + USART5_INTC(usart_periph) |= BIT(USART_BIT_POS2(int_flag)); + } +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/regions_GD32E508VE.h b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/regions_GD32E508VE.h new file mode 100644 index 0000000..68d0696 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/regions_GD32E508VE.h @@ -0,0 +1,101 @@ +#ifndef REGIONS_GD32E508VE_H +#define REGIONS_GD32E508VE_H + + +//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- +//------ With VS Code: Open Preview for Configuration Wizard ------------------- + +// Auto-generated using information from packs +// Device Family Pack (DFP): GigaDevice::GD32E50x_DFP@1.9.0 + +// ROM Configuration +// ======================= +// __ROM0 (is rx memory: IROM1 from DFP) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. Default: 0x08000000 +// Contains Startup and Vector Table +#define __ROM0_BASE 0x08000000 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. Default: 0x00080000 +#define __ROM0_SIZE 0x00080000 +// + +// __ROM1 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __ROM1_BASE 0x0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __ROM1_SIZE 0x0 +// + +// __ROM2 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __ROM2_BASE 0x0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __ROM2_SIZE 0x0 +// + +// __ROM3 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __ROM3_BASE 0x0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __ROM3_SIZE 0x0 +// + +// + +// RAM Configuration +// ======================= +// __RAM0 (is rwx memory: IRAM1 from DFP) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. Default: 0x20000000 +// Contains uninitialized RAM, Stack, and Heap +#define __RAM0_BASE 0x20000000 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. Default: 0x00020000 +#define __RAM0_SIZE 0x00020000 +// + +// __RAM1 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __RAM1_BASE 0x0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __RAM1_SIZE 0x0 +// + +// __RAM2 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __RAM2_BASE 0x0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __RAM2_SIZE 0x0 +// + +// __RAM3 (unused) +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +#define __RAM3_BASE 0x0 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +#define __RAM3_SIZE 0x0 +// + +// + +// Stack / Heap Configuration +// Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +// Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +#define __STACK_SIZE 0x00000200 +#define __HEAP_SIZE 0x00000C00 +// + + +#endif /* REGIONS_GD32E508VE_H */ diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s new file mode 100644 index 0000000..42956e4 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s @@ -0,0 +1,434 @@ +;/*! +; \file startup_gd32e50x_cl.s +; \brief start up file +; +; \version 2025-07-09, V1.6.1, firmware for GD32E50x +;*/ +; +;/* +; * Copyright (c) 2009-2018 Arm Limited. All rights reserved. +; * Copyright (c) 2023, GigaDevice Semiconductor Inc. +; * +; * SPDX-License-Identifier: Apache-2.0 +; * +; * Licensed under the Apache License, Version 2.0 (the License); you may +; * not use this file except in compliance with the License. +; * You may obtain a copy of the License at +; * +; * www.apache.org/licenses/LICENSE-2.0 +; * +; * Unless required by applicable law or agreed to in writing, software +; * distributed under the License is distributed on an AS IS BASIS, WITHOUT +; * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; * See the License for the specific language governing permissions and +; * limitations under the License. +; */ +;/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */ + + +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000800 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000400 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + +; /* reset Vector Mapped to at Address 0 */ + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + +; /* external interrupts handler */ + DCD WWDGT_IRQHandler ; 16:Window Watchdog Timer + DCD LVD_IRQHandler ; 17:LVD through EXTI Line detect + DCD TAMPER_IRQHandler ; 18:Tamper through EXTI Line detect + DCD RTC_IRQHandler ; 19:RTC through EXTI Line + DCD FMC_IRQHandler ; 20:FMC + DCD RCU_CTC_IRQHandler ; 21:RCU and CTC + DCD EXTI0_IRQHandler ; 22:EXTI Line 0 + DCD EXTI1_IRQHandler ; 23:EXTI Line 1 + DCD EXTI2_IRQHandler ; 24:EXTI Line 2 + DCD EXTI3_IRQHandler ; 25:EXTI Line 3 + DCD EXTI4_IRQHandler ; 26:EXTI Line 4 + DCD DMA0_Channel0_IRQHandler ; 27:DMA0 Channel0 + DCD DMA0_Channel1_IRQHandler ; 28:DMA0 Channel1 + DCD DMA0_Channel2_IRQHandler ; 29:DMA0 Channel2 + DCD DMA0_Channel3_IRQHandler ; 30:DMA0 Channel3 + DCD DMA0_Channel4_IRQHandler ; 31:DMA0 Channel4 + DCD DMA0_Channel5_IRQHandler ; 32:DMA0 Channel5 + DCD DMA0_Channel6_IRQHandler ; 33:DMA0 Channel6 + DCD ADC0_1_IRQHandler ; 34:ADC0 and ADC1 + DCD CAN0_TX_IRQHandler ; 35:CAN0 TX + DCD CAN0_RX0_IRQHandler ; 36:CAN0 RX0 + DCD CAN0_RX1_IRQHandler ; 37:CAN0 RX1 + DCD CAN0_EWMC_IRQHandler ; 38:CAN0 EWMC + DCD EXTI5_9_IRQHandler ; 39:EXTI5 to EXTI9 + DCD TIMER0_BRK_TIMER8_IRQHandler ; 40:TIMER0 Break and TIMER8 + DCD TIMER0_UP_TIMER9_IRQHandler ; 41:TIMER0 Update and TIMER9 + DCD TIMER0_TRG_CMT_TIMER10_IRQHandler ; 42:TIMER0 Trigger and Commutation and TIMER10 + DCD TIMER0_Channel_IRQHandler ; 43:TIMER0 Channel Capture Compare + DCD TIMER1_IRQHandler ; 44:TIMER1 + DCD TIMER2_IRQHandler ; 45:TIMER2 + DCD TIMER3_IRQHandler ; 46:TIMER3 + DCD I2C0_EV_IRQHandler ; 47:I2C0 Event + DCD I2C0_ER_IRQHandler ; 48:I2C0 Error + DCD I2C1_EV_IRQHandler ; 49:I2C1 Event + DCD I2C1_ER_IRQHandler ; 50:I2C1 Error + DCD SPI0_IRQHandler ; 51:SPI0 + DCD SPI1_I2S1ADD_IRQHandler ; 52:SPI1 or I2S1ADD + DCD USART0_IRQHandler ; 53:USART0 + DCD USART1_IRQHandler ; 54:USART1 + DCD USART2_IRQHandler ; 55:USART2 + DCD EXTI10_15_IRQHandler ; 56:EXTI10 to EXTI15 + DCD RTC_Alarm_IRQHandler ; 57:RTC Alarm + DCD USBHS_WKUP_IRQHandler ; 58:USBHS Wakeup + DCD TIMER7_BRK_TIMER11_IRQHandler ; 59:TIMER7 Break and TIMER11 + DCD TIMER7_UP_TIMER12_IRQHandler ; 60:TIMER7 Update and TIMER12 + DCD TIMER7_TRG_CMT_TIMER13_IRQHandler ; 61:TIMER7 Trigger and Commutation and TIMER13 + DCD TIMER7_Channel_IRQHandler ; 62:TIMER7 Channel Capture Compare + DCD 0 ; Reserved + DCD EXMC_IRQHandler ; 64:EXMC + DCD 0 ; Reserved + DCD TIMER4_IRQHandler ; 66:TIMER4 + DCD SPI2_I2S2ADD_IRQHandler ; 67:SPI2 or I2S2ADD + DCD UART3_IRQHandler ; 68:UART3 + DCD UART4_IRQHandler ; 69:UART4 + DCD TIMER5_DAC_IRQHandler ; 70:TIMER5 or DAC + DCD TIMER6_IRQHandler ; 71:TIMER6 + DCD DMA1_Channel0_IRQHandler ; 72:DMA1 Channel0 + DCD DMA1_Channel1_IRQHandler ; 73:DMA1 Channel1 + DCD DMA1_Channel2_IRQHandler ; 74:DMA1 Channel2 + DCD DMA1_Channel3_IRQHandler ; 75:DMA1 Channel3 + DCD DMA1_Channel4_IRQHandler ; 76:DMA1 Channel4 + DCD ENET_IRQHandler ; 77:Ethernet + DCD ENET_WKUP_IRQHandler ; 78:Ethernet Wakeup through EXTI Line + DCD CAN1_TX_IRQHandler ; 79:CAN1 TX + DCD CAN1_RX0_IRQHandler ; 80:CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; 81:CAN1 RX1 + DCD CAN1_EWMC_IRQHandler ; 82:CAN1 EWMC + DCD USBHS_IRQHandler ; 83:USBHS + DCD 0 ; Reserved + DCD SHRTIMER_IRQ2_IRQHandler ; 85:SHRTIMER IRQ2 + DCD SHRTIMER_IRQ3_IRQHandler ; 86:SHRTIMER IRQ3 + DCD SHRTIMER_IRQ4_IRQHandler ; 87:SHRTIMER IRQ4 + DCD SHRTIMER_IRQ5_IRQHandler ; 88:SHRTIMER IRQ5 + DCD SHRTIMER_IRQ6_IRQHandler ; 89:SHRTIMER IRQ6 + DCD USBHS_EP1_OUT_IRQHandler ; 90:USBHS end point 1 out + DCD USBHS_EP1_IN_IRQHandler ; 91:USBHS end point 1 in + DCD SHRTIMER_IRQ0_IRQHandler ; 92:SHRTIMER IRQ0 + DCD SHRTIMER_IRQ1_IRQHandler ; 93:SHRTIMER IRQ1 + DCD CAN2_TX_IRQHandler ; 94:CAN2 TX + DCD CAN2_RX0_IRQHandler ; 95:CAN2 RX0 + DCD CAN2_RX1_IRQHandler ; 96:CAN2 RX1 + DCD CAN2_EWMC_IRQHandler ; 97:CAN2 EWMC + DCD I2C2_EV_IRQHandler ; 98:I2C2 Event + DCD I2C2_ER_IRQHandler ; 99:I2C2 Error + DCD USART5_IRQHandler ; 100:USART5 + DCD I2C2_WKUP_IRQHandler ; 101:I2C2 Wakeup + DCD USART5_WKUP_IRQHandler ; 102:USART5 Wakeup + DCD TMU_IRQHandler ; 103:TMU + +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +;/* reset Handler */ +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT SystemInit + IMPORT __main + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +;/* dummy Exception Handlers */ +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler\ + PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler\ + PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC +; /* external interrupts handler */ + EXPORT WWDGT_IRQHandler [WEAK] + EXPORT LVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FMC_IRQHandler [WEAK] + EXPORT RCU_CTC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA0_Channel0_IRQHandler [WEAK] + EXPORT DMA0_Channel1_IRQHandler [WEAK] + EXPORT DMA0_Channel2_IRQHandler [WEAK] + EXPORT DMA0_Channel3_IRQHandler [WEAK] + EXPORT DMA0_Channel4_IRQHandler [WEAK] + EXPORT DMA0_Channel5_IRQHandler [WEAK] + EXPORT DMA0_Channel6_IRQHandler [WEAK] + EXPORT ADC0_1_IRQHandler [WEAK] + EXPORT CAN0_TX_IRQHandler [WEAK] + EXPORT CAN0_RX0_IRQHandler [WEAK] + EXPORT CAN0_RX1_IRQHandler [WEAK] + EXPORT CAN0_EWMC_IRQHandler [WEAK] + EXPORT EXTI5_9_IRQHandler [WEAK] + EXPORT TIMER0_BRK_TIMER8_IRQHandler [WEAK] + EXPORT TIMER0_UP_TIMER9_IRQHandler [WEAK] + EXPORT TIMER0_TRG_CMT_TIMER10_IRQHandler [WEAK] + EXPORT TIMER0_Channel_IRQHandler [WEAK] + EXPORT TIMER1_IRQHandler [WEAK] + EXPORT TIMER2_IRQHandler [WEAK] + EXPORT TIMER3_IRQHandler [WEAK] + EXPORT I2C0_EV_IRQHandler [WEAK] + EXPORT I2C0_ER_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT SPI0_IRQHandler [WEAK] + EXPORT SPI1_I2S1ADD_IRQHandler [WEAK] + EXPORT USART0_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT EXTI10_15_IRQHandler [WEAK] + EXPORT RTC_Alarm_IRQHandler [WEAK] + EXPORT USBHS_WKUP_IRQHandler [WEAK] + EXPORT TIMER7_BRK_TIMER11_IRQHandler [WEAK] + EXPORT TIMER7_UP_TIMER12_IRQHandler [WEAK] + EXPORT TIMER7_TRG_CMT_TIMER13_IRQHandler [WEAK] + EXPORT TIMER7_Channel_IRQHandler [WEAK] + EXPORT EXMC_IRQHandler [WEAK] + EXPORT TIMER4_IRQHandler [WEAK] + EXPORT SPI2_I2S2ADD_IRQHandler [WEAK] + EXPORT UART3_IRQHandler [WEAK] + EXPORT UART4_IRQHandler [WEAK] + EXPORT TIMER5_DAC_IRQHandler [WEAK] + EXPORT TIMER6_IRQHandler [WEAK] + EXPORT DMA1_Channel0_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT ENET_IRQHandler [WEAK] + EXPORT ENET_WKUP_IRQHandler [WEAK] + EXPORT CAN1_TX_IRQHandler [WEAK] + EXPORT CAN1_RX0_IRQHandler [WEAK] + EXPORT CAN1_RX1_IRQHandler [WEAK] + EXPORT CAN1_EWMC_IRQHandler [WEAK] + EXPORT USBHS_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ2_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ3_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ4_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ5_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ6_IRQHandler [WEAK] + EXPORT USBHS_EP1_OUT_IRQHandler [WEAK] + EXPORT USBHS_EP1_IN_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ0_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ1_IRQHandler [WEAK] + EXPORT CAN2_TX_IRQHandler [WEAK] + EXPORT CAN2_RX0_IRQHandler [WEAK] + EXPORT CAN2_RX1_IRQHandler [WEAK] + EXPORT CAN2_EWMC_IRQHandler [WEAK] + EXPORT I2C2_EV_IRQHandler [WEAK] + EXPORT I2C2_ER_IRQHandler [WEAK] + EXPORT USART5_IRQHandler [WEAK] + EXPORT I2C2_WKUP_IRQHandler [WEAK] + EXPORT USART5_WKUP_IRQHandler [WEAK] + EXPORT TMU_IRQHandler [WEAK] + +;/* external interrupts handler */ +WWDGT_IRQHandler +LVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FMC_IRQHandler +RCU_CTC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA0_Channel0_IRQHandler +DMA0_Channel1_IRQHandler +DMA0_Channel2_IRQHandler +DMA0_Channel3_IRQHandler +DMA0_Channel4_IRQHandler +DMA0_Channel5_IRQHandler +DMA0_Channel6_IRQHandler +ADC0_1_IRQHandler +CAN0_TX_IRQHandler +CAN0_RX0_IRQHandler +CAN0_RX1_IRQHandler +CAN0_EWMC_IRQHandler +EXTI5_9_IRQHandler +TIMER0_BRK_TIMER8_IRQHandler +TIMER0_UP_TIMER9_IRQHandler +TIMER0_TRG_CMT_TIMER10_IRQHandler +TIMER0_Channel_IRQHandler +TIMER1_IRQHandler +TIMER2_IRQHandler +TIMER3_IRQHandler +I2C0_EV_IRQHandler +I2C0_ER_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +SPI0_IRQHandler +SPI1_I2S1ADD_IRQHandler +USART0_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +EXTI10_15_IRQHandler +RTC_Alarm_IRQHandler +USBHS_WKUP_IRQHandler +TIMER7_BRK_TIMER11_IRQHandler +TIMER7_UP_TIMER12_IRQHandler +TIMER7_TRG_CMT_TIMER13_IRQHandler +TIMER7_Channel_IRQHandler +EXMC_IRQHandler +TIMER4_IRQHandler +SPI2_I2S2ADD_IRQHandler +UART3_IRQHandler +UART4_IRQHandler +TIMER5_DAC_IRQHandler +TIMER6_IRQHandler +DMA1_Channel0_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +ENET_IRQHandler +ENET_WKUP_IRQHandler +CAN1_TX_IRQHandler +CAN1_RX0_IRQHandler +CAN1_RX1_IRQHandler +CAN1_EWMC_IRQHandler +USBHS_IRQHandler +SHRTIMER_IRQ2_IRQHandler +SHRTIMER_IRQ3_IRQHandler +SHRTIMER_IRQ4_IRQHandler +SHRTIMER_IRQ5_IRQHandler +SHRTIMER_IRQ6_IRQHandler +USBHS_EP1_OUT_IRQHandler +USBHS_EP1_IN_IRQHandler +SHRTIMER_IRQ0_IRQHandler +SHRTIMER_IRQ1_IRQHandler +CAN2_TX_IRQHandler +CAN2_RX0_IRQHandler +CAN2_RX1_IRQHandler +CAN2_EWMC_IRQHandler +I2C2_EV_IRQHandler +I2C2_ER_IRQHandler +USART5_IRQHandler +I2C2_WKUP_IRQHandler +USART5_WKUP_IRQHandler +TMU_IRQHandler + + B . + ENDP + + ALIGN + +; user Initial Stack & Heap + + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap PROC + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + ENDP + + ALIGN + + ENDIF + + END diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s.base@1.5.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s.base@1.5.0 new file mode 100644 index 0000000..42956e4 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s.base@1.5.0 @@ -0,0 +1,434 @@ +;/*! +; \file startup_gd32e50x_cl.s +; \brief start up file +; +; \version 2025-07-09, V1.6.1, firmware for GD32E50x +;*/ +; +;/* +; * Copyright (c) 2009-2018 Arm Limited. All rights reserved. +; * Copyright (c) 2023, GigaDevice Semiconductor Inc. +; * +; * SPDX-License-Identifier: Apache-2.0 +; * +; * Licensed under the Apache License, Version 2.0 (the License); you may +; * not use this file except in compliance with the License. +; * You may obtain a copy of the License at +; * +; * www.apache.org/licenses/LICENSE-2.0 +; * +; * Unless required by applicable law or agreed to in writing, software +; * distributed under the License is distributed on an AS IS BASIS, WITHOUT +; * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; * See the License for the specific language governing permissions and +; * limitations under the License. +; */ +;/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */ + + +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000800 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000400 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + +; /* reset Vector Mapped to at Address 0 */ + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + +; /* external interrupts handler */ + DCD WWDGT_IRQHandler ; 16:Window Watchdog Timer + DCD LVD_IRQHandler ; 17:LVD through EXTI Line detect + DCD TAMPER_IRQHandler ; 18:Tamper through EXTI Line detect + DCD RTC_IRQHandler ; 19:RTC through EXTI Line + DCD FMC_IRQHandler ; 20:FMC + DCD RCU_CTC_IRQHandler ; 21:RCU and CTC + DCD EXTI0_IRQHandler ; 22:EXTI Line 0 + DCD EXTI1_IRQHandler ; 23:EXTI Line 1 + DCD EXTI2_IRQHandler ; 24:EXTI Line 2 + DCD EXTI3_IRQHandler ; 25:EXTI Line 3 + DCD EXTI4_IRQHandler ; 26:EXTI Line 4 + DCD DMA0_Channel0_IRQHandler ; 27:DMA0 Channel0 + DCD DMA0_Channel1_IRQHandler ; 28:DMA0 Channel1 + DCD DMA0_Channel2_IRQHandler ; 29:DMA0 Channel2 + DCD DMA0_Channel3_IRQHandler ; 30:DMA0 Channel3 + DCD DMA0_Channel4_IRQHandler ; 31:DMA0 Channel4 + DCD DMA0_Channel5_IRQHandler ; 32:DMA0 Channel5 + DCD DMA0_Channel6_IRQHandler ; 33:DMA0 Channel6 + DCD ADC0_1_IRQHandler ; 34:ADC0 and ADC1 + DCD CAN0_TX_IRQHandler ; 35:CAN0 TX + DCD CAN0_RX0_IRQHandler ; 36:CAN0 RX0 + DCD CAN0_RX1_IRQHandler ; 37:CAN0 RX1 + DCD CAN0_EWMC_IRQHandler ; 38:CAN0 EWMC + DCD EXTI5_9_IRQHandler ; 39:EXTI5 to EXTI9 + DCD TIMER0_BRK_TIMER8_IRQHandler ; 40:TIMER0 Break and TIMER8 + DCD TIMER0_UP_TIMER9_IRQHandler ; 41:TIMER0 Update and TIMER9 + DCD TIMER0_TRG_CMT_TIMER10_IRQHandler ; 42:TIMER0 Trigger and Commutation and TIMER10 + DCD TIMER0_Channel_IRQHandler ; 43:TIMER0 Channel Capture Compare + DCD TIMER1_IRQHandler ; 44:TIMER1 + DCD TIMER2_IRQHandler ; 45:TIMER2 + DCD TIMER3_IRQHandler ; 46:TIMER3 + DCD I2C0_EV_IRQHandler ; 47:I2C0 Event + DCD I2C0_ER_IRQHandler ; 48:I2C0 Error + DCD I2C1_EV_IRQHandler ; 49:I2C1 Event + DCD I2C1_ER_IRQHandler ; 50:I2C1 Error + DCD SPI0_IRQHandler ; 51:SPI0 + DCD SPI1_I2S1ADD_IRQHandler ; 52:SPI1 or I2S1ADD + DCD USART0_IRQHandler ; 53:USART0 + DCD USART1_IRQHandler ; 54:USART1 + DCD USART2_IRQHandler ; 55:USART2 + DCD EXTI10_15_IRQHandler ; 56:EXTI10 to EXTI15 + DCD RTC_Alarm_IRQHandler ; 57:RTC Alarm + DCD USBHS_WKUP_IRQHandler ; 58:USBHS Wakeup + DCD TIMER7_BRK_TIMER11_IRQHandler ; 59:TIMER7 Break and TIMER11 + DCD TIMER7_UP_TIMER12_IRQHandler ; 60:TIMER7 Update and TIMER12 + DCD TIMER7_TRG_CMT_TIMER13_IRQHandler ; 61:TIMER7 Trigger and Commutation and TIMER13 + DCD TIMER7_Channel_IRQHandler ; 62:TIMER7 Channel Capture Compare + DCD 0 ; Reserved + DCD EXMC_IRQHandler ; 64:EXMC + DCD 0 ; Reserved + DCD TIMER4_IRQHandler ; 66:TIMER4 + DCD SPI2_I2S2ADD_IRQHandler ; 67:SPI2 or I2S2ADD + DCD UART3_IRQHandler ; 68:UART3 + DCD UART4_IRQHandler ; 69:UART4 + DCD TIMER5_DAC_IRQHandler ; 70:TIMER5 or DAC + DCD TIMER6_IRQHandler ; 71:TIMER6 + DCD DMA1_Channel0_IRQHandler ; 72:DMA1 Channel0 + DCD DMA1_Channel1_IRQHandler ; 73:DMA1 Channel1 + DCD DMA1_Channel2_IRQHandler ; 74:DMA1 Channel2 + DCD DMA1_Channel3_IRQHandler ; 75:DMA1 Channel3 + DCD DMA1_Channel4_IRQHandler ; 76:DMA1 Channel4 + DCD ENET_IRQHandler ; 77:Ethernet + DCD ENET_WKUP_IRQHandler ; 78:Ethernet Wakeup through EXTI Line + DCD CAN1_TX_IRQHandler ; 79:CAN1 TX + DCD CAN1_RX0_IRQHandler ; 80:CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; 81:CAN1 RX1 + DCD CAN1_EWMC_IRQHandler ; 82:CAN1 EWMC + DCD USBHS_IRQHandler ; 83:USBHS + DCD 0 ; Reserved + DCD SHRTIMER_IRQ2_IRQHandler ; 85:SHRTIMER IRQ2 + DCD SHRTIMER_IRQ3_IRQHandler ; 86:SHRTIMER IRQ3 + DCD SHRTIMER_IRQ4_IRQHandler ; 87:SHRTIMER IRQ4 + DCD SHRTIMER_IRQ5_IRQHandler ; 88:SHRTIMER IRQ5 + DCD SHRTIMER_IRQ6_IRQHandler ; 89:SHRTIMER IRQ6 + DCD USBHS_EP1_OUT_IRQHandler ; 90:USBHS end point 1 out + DCD USBHS_EP1_IN_IRQHandler ; 91:USBHS end point 1 in + DCD SHRTIMER_IRQ0_IRQHandler ; 92:SHRTIMER IRQ0 + DCD SHRTIMER_IRQ1_IRQHandler ; 93:SHRTIMER IRQ1 + DCD CAN2_TX_IRQHandler ; 94:CAN2 TX + DCD CAN2_RX0_IRQHandler ; 95:CAN2 RX0 + DCD CAN2_RX1_IRQHandler ; 96:CAN2 RX1 + DCD CAN2_EWMC_IRQHandler ; 97:CAN2 EWMC + DCD I2C2_EV_IRQHandler ; 98:I2C2 Event + DCD I2C2_ER_IRQHandler ; 99:I2C2 Error + DCD USART5_IRQHandler ; 100:USART5 + DCD I2C2_WKUP_IRQHandler ; 101:I2C2 Wakeup + DCD USART5_WKUP_IRQHandler ; 102:USART5 Wakeup + DCD TMU_IRQHandler ; 103:TMU + +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +;/* reset Handler */ +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT SystemInit + IMPORT __main + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +;/* dummy Exception Handlers */ +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler\ + PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler\ + PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC +; /* external interrupts handler */ + EXPORT WWDGT_IRQHandler [WEAK] + EXPORT LVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FMC_IRQHandler [WEAK] + EXPORT RCU_CTC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA0_Channel0_IRQHandler [WEAK] + EXPORT DMA0_Channel1_IRQHandler [WEAK] + EXPORT DMA0_Channel2_IRQHandler [WEAK] + EXPORT DMA0_Channel3_IRQHandler [WEAK] + EXPORT DMA0_Channel4_IRQHandler [WEAK] + EXPORT DMA0_Channel5_IRQHandler [WEAK] + EXPORT DMA0_Channel6_IRQHandler [WEAK] + EXPORT ADC0_1_IRQHandler [WEAK] + EXPORT CAN0_TX_IRQHandler [WEAK] + EXPORT CAN0_RX0_IRQHandler [WEAK] + EXPORT CAN0_RX1_IRQHandler [WEAK] + EXPORT CAN0_EWMC_IRQHandler [WEAK] + EXPORT EXTI5_9_IRQHandler [WEAK] + EXPORT TIMER0_BRK_TIMER8_IRQHandler [WEAK] + EXPORT TIMER0_UP_TIMER9_IRQHandler [WEAK] + EXPORT TIMER0_TRG_CMT_TIMER10_IRQHandler [WEAK] + EXPORT TIMER0_Channel_IRQHandler [WEAK] + EXPORT TIMER1_IRQHandler [WEAK] + EXPORT TIMER2_IRQHandler [WEAK] + EXPORT TIMER3_IRQHandler [WEAK] + EXPORT I2C0_EV_IRQHandler [WEAK] + EXPORT I2C0_ER_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT SPI0_IRQHandler [WEAK] + EXPORT SPI1_I2S1ADD_IRQHandler [WEAK] + EXPORT USART0_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT EXTI10_15_IRQHandler [WEAK] + EXPORT RTC_Alarm_IRQHandler [WEAK] + EXPORT USBHS_WKUP_IRQHandler [WEAK] + EXPORT TIMER7_BRK_TIMER11_IRQHandler [WEAK] + EXPORT TIMER7_UP_TIMER12_IRQHandler [WEAK] + EXPORT TIMER7_TRG_CMT_TIMER13_IRQHandler [WEAK] + EXPORT TIMER7_Channel_IRQHandler [WEAK] + EXPORT EXMC_IRQHandler [WEAK] + EXPORT TIMER4_IRQHandler [WEAK] + EXPORT SPI2_I2S2ADD_IRQHandler [WEAK] + EXPORT UART3_IRQHandler [WEAK] + EXPORT UART4_IRQHandler [WEAK] + EXPORT TIMER5_DAC_IRQHandler [WEAK] + EXPORT TIMER6_IRQHandler [WEAK] + EXPORT DMA1_Channel0_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT ENET_IRQHandler [WEAK] + EXPORT ENET_WKUP_IRQHandler [WEAK] + EXPORT CAN1_TX_IRQHandler [WEAK] + EXPORT CAN1_RX0_IRQHandler [WEAK] + EXPORT CAN1_RX1_IRQHandler [WEAK] + EXPORT CAN1_EWMC_IRQHandler [WEAK] + EXPORT USBHS_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ2_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ3_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ4_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ5_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ6_IRQHandler [WEAK] + EXPORT USBHS_EP1_OUT_IRQHandler [WEAK] + EXPORT USBHS_EP1_IN_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ0_IRQHandler [WEAK] + EXPORT SHRTIMER_IRQ1_IRQHandler [WEAK] + EXPORT CAN2_TX_IRQHandler [WEAK] + EXPORT CAN2_RX0_IRQHandler [WEAK] + EXPORT CAN2_RX1_IRQHandler [WEAK] + EXPORT CAN2_EWMC_IRQHandler [WEAK] + EXPORT I2C2_EV_IRQHandler [WEAK] + EXPORT I2C2_ER_IRQHandler [WEAK] + EXPORT USART5_IRQHandler [WEAK] + EXPORT I2C2_WKUP_IRQHandler [WEAK] + EXPORT USART5_WKUP_IRQHandler [WEAK] + EXPORT TMU_IRQHandler [WEAK] + +;/* external interrupts handler */ +WWDGT_IRQHandler +LVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FMC_IRQHandler +RCU_CTC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA0_Channel0_IRQHandler +DMA0_Channel1_IRQHandler +DMA0_Channel2_IRQHandler +DMA0_Channel3_IRQHandler +DMA0_Channel4_IRQHandler +DMA0_Channel5_IRQHandler +DMA0_Channel6_IRQHandler +ADC0_1_IRQHandler +CAN0_TX_IRQHandler +CAN0_RX0_IRQHandler +CAN0_RX1_IRQHandler +CAN0_EWMC_IRQHandler +EXTI5_9_IRQHandler +TIMER0_BRK_TIMER8_IRQHandler +TIMER0_UP_TIMER9_IRQHandler +TIMER0_TRG_CMT_TIMER10_IRQHandler +TIMER0_Channel_IRQHandler +TIMER1_IRQHandler +TIMER2_IRQHandler +TIMER3_IRQHandler +I2C0_EV_IRQHandler +I2C0_ER_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +SPI0_IRQHandler +SPI1_I2S1ADD_IRQHandler +USART0_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +EXTI10_15_IRQHandler +RTC_Alarm_IRQHandler +USBHS_WKUP_IRQHandler +TIMER7_BRK_TIMER11_IRQHandler +TIMER7_UP_TIMER12_IRQHandler +TIMER7_TRG_CMT_TIMER13_IRQHandler +TIMER7_Channel_IRQHandler +EXMC_IRQHandler +TIMER4_IRQHandler +SPI2_I2S2ADD_IRQHandler +UART3_IRQHandler +UART4_IRQHandler +TIMER5_DAC_IRQHandler +TIMER6_IRQHandler +DMA1_Channel0_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +ENET_IRQHandler +ENET_WKUP_IRQHandler +CAN1_TX_IRQHandler +CAN1_RX0_IRQHandler +CAN1_RX1_IRQHandler +CAN1_EWMC_IRQHandler +USBHS_IRQHandler +SHRTIMER_IRQ2_IRQHandler +SHRTIMER_IRQ3_IRQHandler +SHRTIMER_IRQ4_IRQHandler +SHRTIMER_IRQ5_IRQHandler +SHRTIMER_IRQ6_IRQHandler +USBHS_EP1_OUT_IRQHandler +USBHS_EP1_IN_IRQHandler +SHRTIMER_IRQ0_IRQHandler +SHRTIMER_IRQ1_IRQHandler +CAN2_TX_IRQHandler +CAN2_RX0_IRQHandler +CAN2_RX1_IRQHandler +CAN2_EWMC_IRQHandler +I2C2_EV_IRQHandler +I2C2_ER_IRQHandler +USART5_IRQHandler +I2C2_WKUP_IRQHandler +USART5_WKUP_IRQHandler +TMU_IRQHandler + + B . + ENDP + + ALIGN + +; user Initial Stack & Heap + + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap PROC + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + ENDP + + ALIGN + + ENDIF + + END diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c new file mode 100644 index 0000000..62360fe --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c @@ -0,0 +1,1175 @@ +/*! + \file system_gd32e50x.c + \brief CMSIS Cortex-M33 Device Peripheral Access Layer Source File for + GD32E50x Device Series +*/ + +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * Copyright (c) 2023, GigaDevice Semiconductor Inc. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */ + +#include "gd32e50x.h" + +/* system frequency define */ +#define __IRC8M (IRC8M_VALUE) /* internal 8 MHz RC oscillator frequency */ +#define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */ + +#define VECT_TAB_OFFSET (uint32_t)0x00 /* vector table base offset */ + +/* select a system clock by uncommenting the following line */ +/* use IRC8M */ +//#define __SYSTEM_CLOCK_IRC8M (uint32_t)(__IRC8M) +//#define __SYSTEM_CLOCK_72M_PLL_IRC8M (uint32_t)(72000000) +//#define __SYSTEM_CLOCK_120M_PLL_IRC8M (uint32_t)(120000000) +//#define __SYSTEM_CLOCK_168M_PLL_IRC8M (uint32_t)(168000000) +//#define __SYSTEM_CLOCK_180M_PLL_IRC8M (uint32_t)(180000000) + +/* use HXTAL(EPRT/HD series CK_HXTAL = 8M, CL series CK_HXTAL = 25M) */ +//#define __SYSTEM_CLOCK_HXTAL (uint32_t)(__HXTAL) +//#define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000) +//#define __SYSTEM_CLOCK_120M_PLL_HXTAL (uint32_t)(120000000) +//#define __SYSTEM_CLOCK_168M_PLL_HXTAL (uint32_t)(168000000) +#define __SYSTEM_CLOCK_180M_PLL_HXTAL (uint32_t)(180000000) + +/* The following is to prevent Vcore fluctuations caused by frequency switching. + It is strongly recommended to include it to avoid issues caused by self-removal. */ +#define RCU_MODIFY(__delay) do{ \ + volatile uint32_t i,reg;; \ + if(0 != __delay){ \ + /* Insert a software delay */ \ + for(i=0; i<__delay; i++){ \ + } \ + reg = RCU_CFG0; \ + reg &= ~(RCU_CFG0_AHBPSC); \ + reg |= RCU_AHB_CKSYS_DIV2; \ + /* AHB = SYSCLK/2 */ \ + RCU_CFG0 = reg; \ + /* Insert a software delay */ \ + for(i=0; i<__delay; i++){ \ + } \ + reg = RCU_CFG0; \ + reg &= ~(RCU_CFG0_AHBPSC); \ + reg |= RCU_AHB_CKSYS_DIV4; \ + /* AHB = SYSCLK/4 */ \ + RCU_CFG0 = reg; \ + /* Insert a software delay */ \ + for(i=0; i<__delay; i++){ \ + } \ + } \ + }while(0) + + +/* The following is to prevent Vcore fluctuations caused by frequency switching. + It is strongly recommended to include it to avoid issues caused by self-removal. */ +#define RCU_MODIFY_UP(__delay) do{ \ + volatile uint32_t i,reg; \ + if(0 != __delay){ \ + for(i=0; i<__delay; i++){ \ + } \ + reg = RCU_CFG0; \ + reg &= ~(RCU_CFG0_AHBPSC); \ + reg |= RCU_AHB_CKSYS_DIV2; \ + RCU_CFG0 = reg; \ + for(i=0; i<__delay; i++){ \ + } \ + reg = RCU_CFG0; \ + reg &= ~(RCU_CFG0_AHBPSC); \ + reg |= RCU_AHB_CKSYS_DIV1; \ + RCU_CFG0 = reg; \ + } \ + }while(0) + +#define HXTALSTB_DELAY { \ + volatile uint32_t i; \ + for(i=0; i<0x1000; i++){ \ + } \ + } + +#define SEL_IRC8M 0x00 +#define SEL_HXTAL 0x01 +#define SEL_PLL 0x02 + +/* set the system clock frequency and declare the system clock configuration function */ +#ifdef __SYSTEM_CLOCK_IRC8M +uint32_t SystemCoreClock = __SYSTEM_CLOCK_IRC8M; +static void system_clock_8m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_IRC8M; +static void system_clock_72m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_IRC8M; +static void system_clock_120m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_168M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_168M_PLL_IRC8M; +static void system_clock_168m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_180M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_180M_PLL_IRC8M; +static void system_clock_180m_irc8m(void); + +#elif defined (__SYSTEM_CLOCK_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_HXTAL; +static void system_clock_hxtal(void); +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_HXTAL; +static void system_clock_72m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_120M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_HXTAL; +static void system_clock_120m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_168M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_168M_PLL_HXTAL; +static void system_clock_168m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_180M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_180M_PLL_HXTAL; +static void system_clock_180m_hxtal(void); +#endif /* __SYSTEM_CLOCK_IRC8M */ + +/* configure the system clock */ +static void system_clock_config(void); +/* software delay to prevent the impact of Vcore fluctuations. + It is strongly recommended to include it to avoid issues caused by self-removal. */ +static void _soft_delay_(uint32_t time) +{ + __IO uint32_t i; + for(i=0; iCPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ +#endif + /* reset the RCU clock configuration to the default reset state */ + /* Set IRC8MEN bit */ + RCU_CTL |= RCU_CTL_IRC8MEN; + while(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + } + if(((RCU_CFG0 & RCU_CFG0_SCSS) == RCU_SCSS_PLL)){ + RCU_MODIFY(0x100); + } + RCU_CFG0 &= ~RCU_CFG0_SCS; + _soft_delay_(100); +#if (defined(GD32EPRT) || defined(GD32E50X_HD)) + /* reset HXTALEN, CKMEN and PLLEN bits */ + RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN); + /* disable all interrupts */ + RCU_INT = 0x009f0000U; +#elif (defined(GD32E50X_CL) || defined(GD32E508)) + /* Reset HXTALEN, CKMEN, PLLEN, PLL1EN and PLL2EN bits */ + RCU_CTL &= ~(RCU_CTL_PLLEN |RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN); + /* disable all interrupts */ + RCU_INT = 0x00ff0000U; +#endif /* GD32E50X_EPRT and GD32E50X_HD */ + + /* Reset CFG0 and CFG1 registers */ + RCU_CFG0 = 0x00000000U; + RCU_CFG1 = 0x00000000U; + /* reset HXTALBPS bit */ + RCU_CTL &= ~(RCU_CTL_HXTALBPS); + + /* configure the system clock source, PLL Multiplier, AHB/APBx prescalers and Flash settings */ + system_clock_config(); + +#ifdef VECT_TAB_SRAM + nvic_vector_table_set(NVIC_VECTTAB_RAM,VECT_TAB_OFFSET); +#else + nvic_vector_table_set(NVIC_VECTTAB_FLASH,VECT_TAB_OFFSET); +#endif +} + +/*! + \brief configure the system clock + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_config(void) +{ +#ifdef __SYSTEM_CLOCK_IRC8M + system_clock_8m_irc8m(); +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) + system_clock_72m_irc8m(); +#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC8M) + system_clock_120m_irc8m(); +#elif defined (__SYSTEM_CLOCK_168M_PLL_IRC8M) + system_clock_168m_irc8m(); +#elif defined (__SYSTEM_CLOCK_180M_PLL_IRC8M) + system_clock_180m_irc8m(); + +#elif defined (__SYSTEM_CLOCK_HXTAL) + system_clock_hxtal(); +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) + system_clock_72m_hxtal(); +#elif defined (__SYSTEM_CLOCK_120M_PLL_HXTAL) + system_clock_120m_hxtal(); +#elif defined (__SYSTEM_CLOCK_168M_PLL_HXTAL) + system_clock_168m_hxtal(); +#elif defined (__SYSTEM_CLOCK_180M_PLL_HXTAL) + system_clock_180m_hxtal(); +#endif /* __SYSTEM_CLOCK_IRC8M */ +} + +#ifdef __SYSTEM_CLOCK_IRC8M +/*! + \brief configure the system clock to 8M by IRC8M + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_8m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + } + while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + reg_temp = RCU_CFG0; + /* select IRC8M as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_IRC8M; + RCU_CFG0 = reg_temp; + + /* wait until IRC8M is selected as system clock */ + while(RCU_SCSS_IRC8M != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } +} + +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) +/*! + \brief configure the system clock to 72M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_72m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + } + while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(1); + + /* LDO output voltage high mode */ + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* IRC8M is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL18; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 120 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC8M) +/*! + \brief configure the system clock to 120M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_120m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + }while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(3); + + /* LDO output voltage high mode */ + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* IRC8M is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 30 = 120 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL30; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 120 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_168M_PLL_IRC8M) +/*! + \brief configure the system clock to 168M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_168m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + }while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4); + + /* LDO output voltage high mode */ + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* IRC8M is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 42 = 168 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL42; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 120 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_180M_PLL_IRC8M) +/*! + \brief configure the system clock to 180M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_180m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + }while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4); + + /* LDO output voltage high mode */ + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* IRC8M is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 45 = 180 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL45; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 120 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_HXTAL) +/*! + \brief configure the system clock to HXTAL + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + HXTALSTB_DELAY + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + reg_temp = RCU_CFG0; + /* select HXTAL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_HXTAL; + RCU_CFG0 = reg_temp; + + /* wait until HXTAL is selected as system clock */ + while(RCU_SCSS_HXTAL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } +} + +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) +/*! + \brief configure the system clock to 72M by PLL which selects HXTAL as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_72m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + HXTALSTB_DELAY + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(1); + + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* HXTAL is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if defined(GD32E50X_HD) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL18; + +#elif (defined(GD32E50X_CL) || defined(GD32E508)) + /* CK_PLL = (CK_PREDIV0) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL18); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } + +#elif defined(GD32EPRT) + /* CK_PLL = (CK_PREDIV0) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL18); + + /* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV8); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } +#endif /* GD32E50X_HD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 180 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} +#elif defined (__SYSTEM_CLOCK_120M_PLL_HXTAL) +/*! + \brief configure the system clock to 72M by PLL which selects HXTAL as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_120m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + HXTALSTB_DELAY + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(3); + + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* HXTAL is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if defined(GD32E50X_HD) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 30 = 120 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL30; + +#elif (defined(GD32E50X_CL) || defined(GD32E508)) + /* CK_PLL = (CK_PREDIV0) * 30 = 120 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL30); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } + +#elif defined(GD32EPRT) + /* CK_PLL = (CK_PREDIV0) * 30 = 120 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL30); + + /* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV8); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } +#endif /* GD32F50X_HD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 180 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_168M_PLL_HXTAL) +/*! + \brief configure the system clock to 108M by PLL which selects HXTAL as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_168m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + HXTALSTB_DELAY + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4); + + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* HXTAL is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if defined(GD32E50X_HD) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 42 = 168 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL42; + +#elif (defined(GD32E50X_CL) || defined(GD32E508)) + /* CK_PLL = (CK_PREDIV0) * 42 = 168 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL42); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } + +#elif defined(GD32EPRT) + /* CK_PLL = (CK_PREDIV0) * 42 = 168 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL42); + + /* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV8); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } +#endif /* GD32F50X_HD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 180 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_180M_PLL_HXTAL) +/*! + \brief configure the system clock to 180M by PLL which selects HXTAL as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_180m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + HXTALSTB_DELAY + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4); + + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* HXTAL is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if defined(GD32E50X_HD) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 45 = 180 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL45; + +#elif (defined(GD32E50X_CL) || defined(GD32E508)) + /* CK_PLL = (CK_PREDIV0) * 45 = 180 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL45); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10) ; + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } + +#elif defined(GD32EPRT) + /* CK_PLL = (CK_PREDIV0) * 45 = 180 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL45); + + /* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV8); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } +#endif /* GD32F50X_HD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 180 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} +#endif /* __SYSTEM_CLOCK_IRC8M */ + +/*! + \brief update the SystemCoreClock with current core clock retrieved from CPU registers + \param[in] none + \param[out] none + \retval none +*/ +void SystemCoreClockUpdate (void) +{ + uint32_t sws; + uint32_t pllsel, pllpresel, predv0sel, pllmf, ck_src, idx, clk_exp; +#if (defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508)) + uint32_t predv0, predv1, pll1mf; +#endif /* GD32E50X_CL and GD32EPRT*/ + + /* exponent of AHB, APB1 and APB2 clock divider */ + uint8_t ahb_exp[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + + sws = GET_BITS(RCU_CFG0, 2, 3); + switch(sws){ + /* IRC8M is selected as CK_SYS */ + case SEL_IRC8M: + SystemCoreClock = IRC8M_VALUE; + break; + /* HXTAL is selected as CK_SYS */ + case SEL_HXTAL: + SystemCoreClock = HXTAL_VALUE; + break; + /* PLL is selected as CK_SYS */ + case SEL_PLL: + /* PLL clock source selection, HXTAL, IRC48M or IRC8M/2 */ + pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL); + + if(RCU_PLLSRC_HXTAL_IRC48M == pllsel) { + /* PLL clock source is HXTAL or IRC48M */ + pllpresel = (RCU_CFG1 & RCU_CFG1_PLLPRESEL); + + if(RCU_PLLPRESRC_HXTAL == pllpresel){ + /* PLL clock source is HXTAL */ + ck_src = HXTAL_VALUE; + }else{ + /* PLL clock source is IRC48 */ + ck_src = IRC48M_VALUE; + } + +#if defined(GD32E50X_HD) + predv0sel = (RCU_CFG0 & RCU_CFG0_PREDV0); + /* PREDV0 input source clock divided by 2 */ + if(RCU_CFG0_PREDV0 == predv0sel){ + ck_src = ck_src/2U; + } +#elif (defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508)) + predv0sel = (RCU_CFG1 & RCU_CFG1_PREDV0SEL); + /* source clock use PLL1 */ + if(RCU_PREDV0SRC_CKPLL1 == predv0sel){ + predv1 = ((RCU_CFG1 & RCU_CFG1_PREDV1) >> ((uint32_t)4U)) + 1U; + pll1mf = (uint32_t)((RCU_CFG1 & RCU_CFG1_PLL1MF) >> ((uint32_t)8U)) + 2U; + if(17U == pll1mf){ + pll1mf = 20U; + } + ck_src = (ck_src/predv1)*pll1mf; + } + predv0 = (RCU_CFG1 & RCU_CFG1_PREDV0) + 1U; + ck_src /= predv0; +#endif /* GD32E50X_HD */ + }else{ + /* PLL clock source is IRC8M/2 */ + ck_src = IRC8M_VALUE/2U; + } + + /* PLL multiplication factor */ + pllmf = GET_BITS(RCU_CFG0, 18, 21); + if((RCU_CFG0 & RCU_CFG0_PLLMF_4)){ + pllmf |= 0x10U; + } + if((RCU_CFG0 & RCU_CFG0_PLLMF_5)){ + pllmf |= 0x20U; + } + if(pllmf < 15U){ + pllmf += 2U; + }else if((pllmf >= 15U) && (pllmf <= 64U)){ + pllmf += 1U; + }else{ + } + SystemCoreClock = ck_src*pllmf; +#if (defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508)) + if(15U == pllmf){ + SystemCoreClock = ck_src*6U + ck_src/2U; + } +#endif /* GD32E50X_CL and GD32EPRT and GD32E508 */ + + break; + /* IRC8M is selected as CK_SYS */ + default: + SystemCoreClock = IRC8M_VALUE; + break; + } + + /* calculate AHB clock frequency */ + idx = GET_BITS(RCU_CFG0, 4, 7); + clk_exp = ahb_exp[idx]; + SystemCoreClock >>= clk_exp; +} + +#ifdef __FIRMWARE_VERSION_DEFINE +/*! + \brief get firmware version + \param[in] none + \param[out] none + \retval firmware version +*/ +uint32_t gd32e50x_firmware_version_get(void) +{ + return __GD32E50X_STDPERIPH_VERSION; +} +#endif /* __FIRMWARE_VERSION_DEFINE */ diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c.base@1.2.4 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c.base@1.2.4 new file mode 100644 index 0000000..62360fe --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c.base@1.2.4 @@ -0,0 +1,1175 @@ +/*! + \file system_gd32e50x.c + \brief CMSIS Cortex-M33 Device Peripheral Access Layer Source File for + GD32E50x Device Series +*/ + +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * Copyright (c) 2023, GigaDevice Semiconductor Inc. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */ + +#include "gd32e50x.h" + +/* system frequency define */ +#define __IRC8M (IRC8M_VALUE) /* internal 8 MHz RC oscillator frequency */ +#define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */ + +#define VECT_TAB_OFFSET (uint32_t)0x00 /* vector table base offset */ + +/* select a system clock by uncommenting the following line */ +/* use IRC8M */ +//#define __SYSTEM_CLOCK_IRC8M (uint32_t)(__IRC8M) +//#define __SYSTEM_CLOCK_72M_PLL_IRC8M (uint32_t)(72000000) +//#define __SYSTEM_CLOCK_120M_PLL_IRC8M (uint32_t)(120000000) +//#define __SYSTEM_CLOCK_168M_PLL_IRC8M (uint32_t)(168000000) +//#define __SYSTEM_CLOCK_180M_PLL_IRC8M (uint32_t)(180000000) + +/* use HXTAL(EPRT/HD series CK_HXTAL = 8M, CL series CK_HXTAL = 25M) */ +//#define __SYSTEM_CLOCK_HXTAL (uint32_t)(__HXTAL) +//#define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000) +//#define __SYSTEM_CLOCK_120M_PLL_HXTAL (uint32_t)(120000000) +//#define __SYSTEM_CLOCK_168M_PLL_HXTAL (uint32_t)(168000000) +#define __SYSTEM_CLOCK_180M_PLL_HXTAL (uint32_t)(180000000) + +/* The following is to prevent Vcore fluctuations caused by frequency switching. + It is strongly recommended to include it to avoid issues caused by self-removal. */ +#define RCU_MODIFY(__delay) do{ \ + volatile uint32_t i,reg;; \ + if(0 != __delay){ \ + /* Insert a software delay */ \ + for(i=0; i<__delay; i++){ \ + } \ + reg = RCU_CFG0; \ + reg &= ~(RCU_CFG0_AHBPSC); \ + reg |= RCU_AHB_CKSYS_DIV2; \ + /* AHB = SYSCLK/2 */ \ + RCU_CFG0 = reg; \ + /* Insert a software delay */ \ + for(i=0; i<__delay; i++){ \ + } \ + reg = RCU_CFG0; \ + reg &= ~(RCU_CFG0_AHBPSC); \ + reg |= RCU_AHB_CKSYS_DIV4; \ + /* AHB = SYSCLK/4 */ \ + RCU_CFG0 = reg; \ + /* Insert a software delay */ \ + for(i=0; i<__delay; i++){ \ + } \ + } \ + }while(0) + + +/* The following is to prevent Vcore fluctuations caused by frequency switching. + It is strongly recommended to include it to avoid issues caused by self-removal. */ +#define RCU_MODIFY_UP(__delay) do{ \ + volatile uint32_t i,reg; \ + if(0 != __delay){ \ + for(i=0; i<__delay; i++){ \ + } \ + reg = RCU_CFG0; \ + reg &= ~(RCU_CFG0_AHBPSC); \ + reg |= RCU_AHB_CKSYS_DIV2; \ + RCU_CFG0 = reg; \ + for(i=0; i<__delay; i++){ \ + } \ + reg = RCU_CFG0; \ + reg &= ~(RCU_CFG0_AHBPSC); \ + reg |= RCU_AHB_CKSYS_DIV1; \ + RCU_CFG0 = reg; \ + } \ + }while(0) + +#define HXTALSTB_DELAY { \ + volatile uint32_t i; \ + for(i=0; i<0x1000; i++){ \ + } \ + } + +#define SEL_IRC8M 0x00 +#define SEL_HXTAL 0x01 +#define SEL_PLL 0x02 + +/* set the system clock frequency and declare the system clock configuration function */ +#ifdef __SYSTEM_CLOCK_IRC8M +uint32_t SystemCoreClock = __SYSTEM_CLOCK_IRC8M; +static void system_clock_8m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_IRC8M; +static void system_clock_72m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_IRC8M; +static void system_clock_120m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_168M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_168M_PLL_IRC8M; +static void system_clock_168m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_180M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_180M_PLL_IRC8M; +static void system_clock_180m_irc8m(void); + +#elif defined (__SYSTEM_CLOCK_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_HXTAL; +static void system_clock_hxtal(void); +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_HXTAL; +static void system_clock_72m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_120M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_HXTAL; +static void system_clock_120m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_168M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_168M_PLL_HXTAL; +static void system_clock_168m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_180M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_180M_PLL_HXTAL; +static void system_clock_180m_hxtal(void); +#endif /* __SYSTEM_CLOCK_IRC8M */ + +/* configure the system clock */ +static void system_clock_config(void); +/* software delay to prevent the impact of Vcore fluctuations. + It is strongly recommended to include it to avoid issues caused by self-removal. */ +static void _soft_delay_(uint32_t time) +{ + __IO uint32_t i; + for(i=0; iCPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ +#endif + /* reset the RCU clock configuration to the default reset state */ + /* Set IRC8MEN bit */ + RCU_CTL |= RCU_CTL_IRC8MEN; + while(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + } + if(((RCU_CFG0 & RCU_CFG0_SCSS) == RCU_SCSS_PLL)){ + RCU_MODIFY(0x100); + } + RCU_CFG0 &= ~RCU_CFG0_SCS; + _soft_delay_(100); +#if (defined(GD32EPRT) || defined(GD32E50X_HD)) + /* reset HXTALEN, CKMEN and PLLEN bits */ + RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN); + /* disable all interrupts */ + RCU_INT = 0x009f0000U; +#elif (defined(GD32E50X_CL) || defined(GD32E508)) + /* Reset HXTALEN, CKMEN, PLLEN, PLL1EN and PLL2EN bits */ + RCU_CTL &= ~(RCU_CTL_PLLEN |RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN); + /* disable all interrupts */ + RCU_INT = 0x00ff0000U; +#endif /* GD32E50X_EPRT and GD32E50X_HD */ + + /* Reset CFG0 and CFG1 registers */ + RCU_CFG0 = 0x00000000U; + RCU_CFG1 = 0x00000000U; + /* reset HXTALBPS bit */ + RCU_CTL &= ~(RCU_CTL_HXTALBPS); + + /* configure the system clock source, PLL Multiplier, AHB/APBx prescalers and Flash settings */ + system_clock_config(); + +#ifdef VECT_TAB_SRAM + nvic_vector_table_set(NVIC_VECTTAB_RAM,VECT_TAB_OFFSET); +#else + nvic_vector_table_set(NVIC_VECTTAB_FLASH,VECT_TAB_OFFSET); +#endif +} + +/*! + \brief configure the system clock + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_config(void) +{ +#ifdef __SYSTEM_CLOCK_IRC8M + system_clock_8m_irc8m(); +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) + system_clock_72m_irc8m(); +#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC8M) + system_clock_120m_irc8m(); +#elif defined (__SYSTEM_CLOCK_168M_PLL_IRC8M) + system_clock_168m_irc8m(); +#elif defined (__SYSTEM_CLOCK_180M_PLL_IRC8M) + system_clock_180m_irc8m(); + +#elif defined (__SYSTEM_CLOCK_HXTAL) + system_clock_hxtal(); +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) + system_clock_72m_hxtal(); +#elif defined (__SYSTEM_CLOCK_120M_PLL_HXTAL) + system_clock_120m_hxtal(); +#elif defined (__SYSTEM_CLOCK_168M_PLL_HXTAL) + system_clock_168m_hxtal(); +#elif defined (__SYSTEM_CLOCK_180M_PLL_HXTAL) + system_clock_180m_hxtal(); +#endif /* __SYSTEM_CLOCK_IRC8M */ +} + +#ifdef __SYSTEM_CLOCK_IRC8M +/*! + \brief configure the system clock to 8M by IRC8M + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_8m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + } + while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + reg_temp = RCU_CFG0; + /* select IRC8M as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_IRC8M; + RCU_CFG0 = reg_temp; + + /* wait until IRC8M is selected as system clock */ + while(RCU_SCSS_IRC8M != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } +} + +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) +/*! + \brief configure the system clock to 72M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_72m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + } + while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(1); + + /* LDO output voltage high mode */ + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* IRC8M is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL18; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 120 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC8M) +/*! + \brief configure the system clock to 120M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_120m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + }while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(3); + + /* LDO output voltage high mode */ + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* IRC8M is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 30 = 120 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL30; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 120 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_168M_PLL_IRC8M) +/*! + \brief configure the system clock to 168M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_168m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + }while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4); + + /* LDO output voltage high mode */ + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* IRC8M is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 42 = 168 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL42; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 120 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_180M_PLL_IRC8M) +/*! + \brief configure the system clock to 180M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_180m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + }while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4); + + /* LDO output voltage high mode */ + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* IRC8M is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 45 = 180 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL45; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 120 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_HXTAL) +/*! + \brief configure the system clock to HXTAL + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + HXTALSTB_DELAY + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + reg_temp = RCU_CFG0; + /* select HXTAL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_HXTAL; + RCU_CFG0 = reg_temp; + + /* wait until HXTAL is selected as system clock */ + while(RCU_SCSS_HXTAL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } +} + +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) +/*! + \brief configure the system clock to 72M by PLL which selects HXTAL as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_72m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + HXTALSTB_DELAY + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(1); + + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* HXTAL is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if defined(GD32E50X_HD) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL18; + +#elif (defined(GD32E50X_CL) || defined(GD32E508)) + /* CK_PLL = (CK_PREDIV0) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL18); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } + +#elif defined(GD32EPRT) + /* CK_PLL = (CK_PREDIV0) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL18); + + /* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV8); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } +#endif /* GD32E50X_HD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 180 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} +#elif defined (__SYSTEM_CLOCK_120M_PLL_HXTAL) +/*! + \brief configure the system clock to 72M by PLL which selects HXTAL as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_120m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + HXTALSTB_DELAY + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(3); + + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* HXTAL is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if defined(GD32E50X_HD) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 30 = 120 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL30; + +#elif (defined(GD32E50X_CL) || defined(GD32E508)) + /* CK_PLL = (CK_PREDIV0) * 30 = 120 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL30); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } + +#elif defined(GD32EPRT) + /* CK_PLL = (CK_PREDIV0) * 30 = 120 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL30); + + /* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV8); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } +#endif /* GD32F50X_HD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 180 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_168M_PLL_HXTAL) +/*! + \brief configure the system clock to 108M by PLL which selects HXTAL as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_168m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + HXTALSTB_DELAY + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4); + + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* HXTAL is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if defined(GD32E50X_HD) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 42 = 168 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL42; + +#elif (defined(GD32E50X_CL) || defined(GD32E508)) + /* CK_PLL = (CK_PREDIV0) * 42 = 168 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL42); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } + +#elif defined(GD32EPRT) + /* CK_PLL = (CK_PREDIV0) * 42 = 168 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL42); + + /* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV8); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } +#endif /* GD32F50X_HD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 180 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} + +#elif defined (__SYSTEM_CLOCK_180M_PLL_HXTAL) +/*! + \brief configure the system clock to 180M by PLL which selects HXTAL as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_180m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + __IO uint32_t reg_temp; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + HXTALSTB_DELAY + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4); + + RCU_APB1EN |= RCU_APB1EN_PMUEN; + + /* HXTAL is stable */ + /* AHB = SYSCLK/4 */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV4; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if defined(GD32E50X_HD) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 45 = 180 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= RCU_PLL_MUL45; + +#elif (defined(GD32E50X_CL) || defined(GD32E508)) + /* CK_PLL = (CK_PREDIV0) * 45 = 180 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL45); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10) ; + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } + +#elif defined(GD32EPRT) + /* CK_PLL = (CK_PREDIV0) * 45 = 180 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL45); + + /* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV8); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){ + } +#endif /* GD32F50X_HD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* enable the high-drive to extend the clock frequency to 180 MHz */ + PMU_CTL0 |= PMU_CTL0_HDEN; + while(0U == (PMU_CS0 & PMU_CS0_HDRF)){ + } + + /* select the high-drive mode */ + PMU_CTL0 |= PMU_CTL0_HDS; + while(0U == (PMU_CS0 & PMU_CS0_HDSRF)){ + } + + reg_temp = RCU_CFG0; + /* select PLL as system clock */ + reg_temp &= ~RCU_CFG0_SCS; + reg_temp |= RCU_CKSYSSRC_PLL; + RCU_CFG0 = reg_temp; + + /* wait until PLL is selected as system clock */ + while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){ + } + RCU_MODIFY_UP(0x50); +} +#endif /* __SYSTEM_CLOCK_IRC8M */ + +/*! + \brief update the SystemCoreClock with current core clock retrieved from CPU registers + \param[in] none + \param[out] none + \retval none +*/ +void SystemCoreClockUpdate (void) +{ + uint32_t sws; + uint32_t pllsel, pllpresel, predv0sel, pllmf, ck_src, idx, clk_exp; +#if (defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508)) + uint32_t predv0, predv1, pll1mf; +#endif /* GD32E50X_CL and GD32EPRT*/ + + /* exponent of AHB, APB1 and APB2 clock divider */ + uint8_t ahb_exp[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + + sws = GET_BITS(RCU_CFG0, 2, 3); + switch(sws){ + /* IRC8M is selected as CK_SYS */ + case SEL_IRC8M: + SystemCoreClock = IRC8M_VALUE; + break; + /* HXTAL is selected as CK_SYS */ + case SEL_HXTAL: + SystemCoreClock = HXTAL_VALUE; + break; + /* PLL is selected as CK_SYS */ + case SEL_PLL: + /* PLL clock source selection, HXTAL, IRC48M or IRC8M/2 */ + pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL); + + if(RCU_PLLSRC_HXTAL_IRC48M == pllsel) { + /* PLL clock source is HXTAL or IRC48M */ + pllpresel = (RCU_CFG1 & RCU_CFG1_PLLPRESEL); + + if(RCU_PLLPRESRC_HXTAL == pllpresel){ + /* PLL clock source is HXTAL */ + ck_src = HXTAL_VALUE; + }else{ + /* PLL clock source is IRC48 */ + ck_src = IRC48M_VALUE; + } + +#if defined(GD32E50X_HD) + predv0sel = (RCU_CFG0 & RCU_CFG0_PREDV0); + /* PREDV0 input source clock divided by 2 */ + if(RCU_CFG0_PREDV0 == predv0sel){ + ck_src = ck_src/2U; + } +#elif (defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508)) + predv0sel = (RCU_CFG1 & RCU_CFG1_PREDV0SEL); + /* source clock use PLL1 */ + if(RCU_PREDV0SRC_CKPLL1 == predv0sel){ + predv1 = ((RCU_CFG1 & RCU_CFG1_PREDV1) >> ((uint32_t)4U)) + 1U; + pll1mf = (uint32_t)((RCU_CFG1 & RCU_CFG1_PLL1MF) >> ((uint32_t)8U)) + 2U; + if(17U == pll1mf){ + pll1mf = 20U; + } + ck_src = (ck_src/predv1)*pll1mf; + } + predv0 = (RCU_CFG1 & RCU_CFG1_PREDV0) + 1U; + ck_src /= predv0; +#endif /* GD32E50X_HD */ + }else{ + /* PLL clock source is IRC8M/2 */ + ck_src = IRC8M_VALUE/2U; + } + + /* PLL multiplication factor */ + pllmf = GET_BITS(RCU_CFG0, 18, 21); + if((RCU_CFG0 & RCU_CFG0_PLLMF_4)){ + pllmf |= 0x10U; + } + if((RCU_CFG0 & RCU_CFG0_PLLMF_5)){ + pllmf |= 0x20U; + } + if(pllmf < 15U){ + pllmf += 2U; + }else if((pllmf >= 15U) && (pllmf <= 64U)){ + pllmf += 1U; + }else{ + } + SystemCoreClock = ck_src*pllmf; +#if (defined(GD32E50X_CL) || defined(GD32EPRT) || defined(GD32E508)) + if(15U == pllmf){ + SystemCoreClock = ck_src*6U + ck_src/2U; + } +#endif /* GD32E50X_CL and GD32EPRT and GD32E508 */ + + break; + /* IRC8M is selected as CK_SYS */ + default: + SystemCoreClock = IRC8M_VALUE; + break; + } + + /* calculate AHB clock frequency */ + idx = GET_BITS(RCU_CFG0, 4, 7); + clk_exp = ahb_exp[idx]; + SystemCoreClock >>= clk_exp; +} + +#ifdef __FIRMWARE_VERSION_DEFINE +/*! + \brief get firmware version + \param[in] none + \param[out] none + \retval firmware version +*/ +uint32_t gd32e50x_firmware_version_get(void) +{ + return __GD32E50X_STDPERIPH_VERSION; +} +#endif /* __FIRMWARE_VERSION_DEFINE */ diff --git a/GD32E508_DCU_APP/device/ADC.cpp b/GD32E508_DCU_APP/device/ADC.cpp new file mode 100644 index 0000000..abb134f --- /dev/null +++ b/GD32E508_DCU_APP/device/ADC.cpp @@ -0,0 +1,126 @@ +/** + * @file ADC.cpp + * @brief ADC 采样模块实现。 + */ + +#include "main.h" +#include "device/adc.h" +#include "sys/systick.h" +#include "sys/config.h" + +namespace { +constexpr uint32_t kDmaPeriph = DMA0; +constexpr dma_channel_enum kDmaChannel = DMA_CH0; +constexpr uint32_t kChannelCount = 3U; +constexpr uint32_t kVinGpio = GPIOA; +constexpr uint32_t kVinPin = GPIO_PIN_6; +volatile uint16_t s_dmaBuffer[kChannelCount] = {0U, 0U, 0U}; +volatile uint8_t s_dmaReady = 0U; +} // namespace + +void Adc::initDma() +{ + dma_parameter_struct dma_initpara{}; + + rcu_periph_clock_enable(RCU_DMA0); + dma_deinit(kDmaPeriph, kDmaChannel); + dma_struct_para_init(&dma_initpara); + + dma_initpara.periph_addr = (uint32_t)(&ADC_RDATA(CONFIG_ADC_INSTANCE)); + dma_initpara.memory_addr = (uint32_t)s_dmaBuffer; + dma_initpara.direction = DMA_PERIPHERAL_TO_MEMORY; + dma_initpara.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; + dma_initpara.memory_width = DMA_MEMORY_WIDTH_16BIT; + dma_initpara.periph_inc = DMA_PERIPH_INCREASE_DISABLE; + dma_initpara.memory_inc = DMA_MEMORY_INCREASE_ENABLE; + dma_initpara.number = kChannelCount; + dma_initpara.priority = DMA_PRIORITY_HIGH; + dma_init(kDmaPeriph, kDmaChannel, &dma_initpara); + dma_circulation_enable(kDmaPeriph, kDmaChannel); + dma_channel_enable(kDmaPeriph, kDmaChannel); +} + +void Adc::init() +{ + rcu_periph_clock_enable(RCU_ADC0); + rcu_periph_clock_enable(RCU_GPIOA); + rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8); + + /* 原理图:PA6 -> VIN_ADC,为外部电源分压检测输入 */ + gpio_init(kVinGpio, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, kVinPin); + + initDma(); + adc_deinit(CONFIG_ADC_INSTANCE); + adc_mode_config(ADC_MODE_FREE); + adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, ENABLE); + adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, ENABLE); + adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT); + adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, kChannelCount); + adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, CONFIG_ADC_VIN_CHANNEL, CONFIG_ADC_SAMPLE_TIME); + adc_regular_channel_config(CONFIG_ADC_INSTANCE, 1U, CONFIG_ADC_TEMP_CHANNEL, CONFIG_ADC_SAMPLE_TIME); + adc_regular_channel_config(CONFIG_ADC_INSTANCE, 2U, CONFIG_ADC_VREFINT_CHANNEL, CONFIG_ADC_SAMPLE_TIME); + adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); + adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE); + adc_dma_mode_enable(CONFIG_ADC_INSTANCE); + + adc_tempsensor_vrefint_enable(); + SysTickTimer::delayMs(CONFIG_ADC_STABILIZE_DELAY_MS); + + adc_enable(CONFIG_ADC_INSTANCE); + SysTickTimer::delayMs(CONFIG_ADC_STABILIZE_DELAY_MS); + adc_calibration_enable(CONFIG_ADC_INSTANCE); + adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL); + SysTickTimer::delayMs(1U); + s_dmaReady = 1U; +} + +bool Adc::dmaReady() +{ + return s_dmaReady != 0U; +} + +const uint16_t *Adc::dmaBuffer() +{ + return (const uint16_t *)s_dmaBuffer; +} + +uint16_t Adc::readRaw(uint8_t channel) +{ + if(channel == CONFIG_ADC_VIN_CHANNEL) return s_dmaBuffer[0]; + if(channel == CONFIG_ADC_TEMP_CHANNEL) return s_dmaBuffer[1]; + if(channel == CONFIG_ADC_VREFINT_CHANNEL) return s_dmaBuffer[2]; + return 0U; +} + +uint16_t Adc::readVinRaw() { return readRaw(CONFIG_ADC_VIN_CHANNEL); } +uint16_t Adc::readVrefRaw() { return readRaw(CONFIG_ADC_VREFINT_CHANNEL); } +uint16_t Adc::readTempRaw() { return readRaw(CONFIG_ADC_TEMP_CHANNEL); } + +float Adc::calculateVinAdcVoltage(uint16_t vinRaw, float vdda) +{ + return (static_cast(vinRaw) * vdda) / CONFIG_ADC_MAX_VALUE; +} + +float Adc::calculateVdda(uint16_t vrefRaw) +{ + if(vrefRaw == 0U) return CONFIG_ADC_DEFAULT_VDDA; + return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast(vrefRaw); +} + +float Adc::calculateTemperature(uint16_t tempRaw, float vdda) +{ + const float sensorVoltage = (static_cast(tempRaw) * vdda) / CONFIG_ADC_MAX_VALUE; + return ((CONFIG_ADC_TEMP_V25 - sensorVoltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f; +} + +void Adc::collectSample(Sample *sample) +{ + if(sample == nullptr) return; + + sample->vin_raw = readVinRaw(); + sample->temp_raw = readTempRaw(); + sample->vref_raw = readVrefRaw(); + sample->vdda = calculateVdda(sample->vref_raw); + sample->vin_adc_v = calculateVinAdcVoltage(sample->vin_raw, sample->vdda); + sample->temperature = calculateTemperature(sample->temp_raw, sample->vdda); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/ADC.cpp.bak_config b/GD32E508_DCU_APP/device/ADC.cpp.bak_config new file mode 100644 index 0000000..c526f1e --- /dev/null +++ b/GD32E508_DCU_APP/device/ADC.cpp.bak_config @@ -0,0 +1,126 @@ +#include "main.h" +#include "device/adc.h" +#include "sys/systick.h" + +#define TEMP_ADC ADC0 +#define TEMP_ADC_CHANNEL ADC_CHANNEL_16 +#define VREFINT_ADC_CHANNEL ADC_CHANNEL_17 +#define ADC_MAX_VALUE 4095.0f +#define VREFINT_TYPICAL_VOLTAGE 1.20f +#define TEMP_V25 1.43f +#define TEMP_AVG_SLOPE 0.0043f + +/** + * @brief 初始化 ADC 硬件及内部温度/VREF 通道。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 完成 ADC 时钟、模式、触发方式、内部通道和校准流程配置。 + */ +void adc_hw_init(void) +{ + rcu_periph_clock_enable(RCU_ADC0); + rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8); + + adc_deinit(TEMP_ADC); + adc_mode_config(ADC_MODE_FREE); + adc_special_function_config(TEMP_ADC, ADC_SCAN_MODE, DISABLE); + adc_special_function_config(TEMP_ADC, ADC_CONTINUOUS_MODE, DISABLE); + adc_data_alignment_config(TEMP_ADC, ADC_DATAALIGN_RIGHT); + adc_channel_length_config(TEMP_ADC, ADC_REGULAR_CHANNEL, 1U); + adc_external_trigger_source_config(TEMP_ADC, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); + adc_external_trigger_config(TEMP_ADC, ADC_REGULAR_CHANNEL, DISABLE); + + adc_tempsensor_vrefint_enable(); + delay_ms(2U); + + adc_enable(TEMP_ADC); + delay_ms(2U); + adc_calibration_enable(TEMP_ADC); +} + +/** + * @brief 执行指定通道的一次 ADC 单次采样。 + * @param[in] channel 待采样的 ADC 通道号。 + * @param[out] 无。 + * @retval uint16_t 指定通道的原始 ADC 采样值。 + * @note 函数采用阻塞式等待,直到 EOC 置位后返回。 + */ +uint16_t adc_single_read(uint8_t channel) +{ + adc_regular_channel_config(TEMP_ADC, 0U, channel, ADC_SAMPLETIME_239POINT5); + adc_flag_clear(TEMP_ADC, ADC_FLAG_EOC); + adc_software_trigger_enable(TEMP_ADC, ADC_REGULAR_CHANNEL); + while(RESET == adc_flag_get(TEMP_ADC, ADC_FLAG_EOC)) { + } + return adc_regular_data_read(TEMP_ADC); +} + +/** + * @brief 读取内部参考电压通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部参考电压通道的原始 ADC 值。 + */ +uint16_t adc_read_vref_raw(void) +{ + return adc_single_read(VREFINT_ADC_CHANNEL); +} + +/** + * @brief 读取内部温度通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部温度通道的原始 ADC 值。 + */ +uint16_t adc_read_temp_raw(void) +{ + return adc_single_read(TEMP_ADC_CHANNEL); +} + +/** + * @brief 根据内部参考电压原始值计算实际 VDDA。 + * @param[in] vref_raw 内部参考电压通道原始 ADC 数值。 + * @param[out] 无。 + * @retval float 换算得到的 VDDA 电压值,单位 V。 + * @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。 + */ +float adc_calculate_vdda(uint16_t vref_raw) +{ + if(vref_raw == 0U) { + return 3.3f; + } + return (VREFINT_TYPICAL_VOLTAGE * ADC_MAX_VALUE) / static_cast(vref_raw); +} + +/** + * @brief 根据温度原始值与 VDDA 计算芯片温度。 + * @param[in] temp_raw 内部温度通道原始 ADC 数值。 + * @param[in] vdda 当前供电电压,单位 V。 + * @param[out] 无。 + * @retval float 换算得到的芯片温度,单位 ℃。 + */ +float adc_calculate_temperature(uint16_t temp_raw, float vdda) +{ + const float sensor_voltage = (static_cast(temp_raw) * vdda) / ADC_MAX_VALUE; + return ((TEMP_V25 - sensor_voltage) / TEMP_AVG_SLOPE) + 25.0f; +} + +/** + * @brief 采集一组完整 ADC 样本并完成工程值换算。 + * @param[in] sample 指向输出样本结构体的指针;允许为空。 + * @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。 + * @retval 无返回值。 + * @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。 + */ +void adc_collect_sample(adc_sample_t *sample) +{ + if(sample == nullptr) { + return; + } + + sample->vref_raw = adc_read_vref_raw(); + sample->temp_raw = adc_read_temp_raw(); + sample->vdda = adc_calculate_vdda(sample->vref_raw); + sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda); +} diff --git a/GD32E508_DCU_APP/device/ADC.cpp.bak_dma b/GD32E508_DCU_APP/device/ADC.cpp.bak_dma new file mode 100644 index 0000000..3894495 --- /dev/null +++ b/GD32E508_DCU_APP/device/ADC.cpp.bak_dma @@ -0,0 +1,119 @@ +#include "main.h" +#include "device/adc.h" +#include "sys/systick.h" +#include "sys/config.h" + +/** + * @brief 初始化 ADC 硬件及内部温度/VREF 通道。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 完成 ADC 时钟、模式、触发方式、内部通道和校准流程配置。 + */ +void adc_hw_init(void) +{ + rcu_periph_clock_enable(RCU_ADC0); + rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8); + + adc_deinit(CONFIG_ADC_INSTANCE); + adc_mode_config(ADC_MODE_FREE); + adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, DISABLE); + adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, DISABLE); + adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT); + adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, 1U); + adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); + adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE); + + adc_tempsensor_vrefint_enable(); + delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS); + + adc_enable(CONFIG_ADC_INSTANCE); + delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS); + adc_calibration_enable(CONFIG_ADC_INSTANCE); +} + +/** + * @brief 执行指定通道的一次 ADC 单次采样。 + * @param[in] channel 待采样的 ADC 通道号。 + * @param[out] 无。 + * @retval uint16_t 指定通道的原始 ADC 采样值。 + * @note 函数采用阻塞式等待,直到 EOC 置位后返回。 + */ +uint16_t adc_single_read(uint8_t channel) +{ + adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, channel, CONFIG_ADC_SAMPLE_TIME); + adc_flag_clear(CONFIG_ADC_INSTANCE, ADC_FLAG_EOC); + adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL); + while(RESET == adc_flag_get(CONFIG_ADC_INSTANCE, ADC_FLAG_EOC)) { + } + return adc_regular_data_read(CONFIG_ADC_INSTANCE); +} + +/** + * @brief 读取内部参考电压通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部参考电压通道的原始 ADC 值。 + */ +uint16_t adc_read_vref_raw(void) +{ + return adc_single_read(CONFIG_ADC_VREFINT_CHANNEL); +} + +/** + * @brief 读取内部温度通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部温度通道的原始 ADC 值。 + */ +uint16_t adc_read_temp_raw(void) +{ + return adc_single_read(CONFIG_ADC_TEMP_CHANNEL); +} + +/** + * @brief 根据内部参考电压原始值计算实际 VDDA。 + * @param[in] vref_raw 内部参考电压通道原始 ADC 数值。 + * @param[out] 无。 + * @retval float 换算得到的 VDDA 电压值,单位 V。 + * @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。 + */ +float adc_calculate_vdda(uint16_t vref_raw) +{ + if(vref_raw == 0U) { + return CONFIG_ADC_DEFAULT_VDDA; + } + return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast(vref_raw); +} + +/** + * @brief 根据温度原始值与 VDDA 计算芯片温度。 + * @param[in] temp_raw 内部温度通道原始 ADC 数值。 + * @param[in] vdda 当前供电电压,单位 V。 + * @param[out] 无。 + * @retval float 换算得到的芯片温度,单位 ℃。 + */ +float adc_calculate_temperature(uint16_t temp_raw, float vdda) +{ + const float sensor_voltage = (static_cast(temp_raw) * vdda) / CONFIG_ADC_MAX_VALUE; + return ((CONFIG_ADC_TEMP_V25 - sensor_voltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f; +} + +/** + * @brief 采集一组完整 ADC 样本并完成工程值换算。 + * @param[in] sample 指向输出样本结构体的指针;允许为空。 + * @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。 + * @retval 无返回值。 + * @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。 + */ +void adc_collect_sample(adc_sample_t *sample) +{ + if(sample == nullptr) { + return; + } + + sample->vref_raw = adc_read_vref_raw(); + sample->temp_raw = adc_read_temp_raw(); + sample->vdda = adc_calculate_vdda(sample->vref_raw); + sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda); +} diff --git a/GD32E508_DCU_APP/device/ADC.cpp.bak_oop b/GD32E508_DCU_APP/device/ADC.cpp.bak_oop new file mode 100644 index 0000000..316c66c --- /dev/null +++ b/GD32E508_DCU_APP/device/ADC.cpp.bak_oop @@ -0,0 +1,218 @@ +#include "main.h" +#include "device/adc.h" +#include "sys/systick.h" +#include "sys/config.h" + +namespace { +constexpr uint32_t kAdcDmaPeriph = DMA0; +constexpr dma_channel_enum kAdcDmaChannel = DMA_CH0; +constexpr uint32_t kAdcChannelCount = 3U; +constexpr uint32_t kVinAdcGpio = GPIOA; +constexpr uint32_t kVinAdcPin = GPIO_PIN_6; +volatile uint16_t g_adc_dma_buffer[kAdcChannelCount] = {0U, 0U, 0U}; +volatile uint8_t g_adc_dma_ready = 0U; + +/** + * @brief 配置 ADC 原始数据到内存的 DMA 循环搬运。 + */ +void adc_dma_init(void) +{ + dma_parameter_struct dma_initpara{}; + + rcu_periph_clock_enable(RCU_DMA0); + dma_deinit(kAdcDmaPeriph, kAdcDmaChannel); + dma_struct_para_init(&dma_initpara); + + dma_initpara.periph_addr = (uint32_t)(&ADC_RDATA(CONFIG_ADC_INSTANCE)); + dma_initpara.memory_addr = (uint32_t)g_adc_dma_buffer; + dma_initpara.direction = DMA_PERIPHERAL_TO_MEMORY; + dma_initpara.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; + dma_initpara.memory_width = DMA_MEMORY_WIDTH_16BIT; + dma_initpara.periph_inc = DMA_PERIPH_INCREASE_DISABLE; + dma_initpara.memory_inc = DMA_MEMORY_INCREASE_ENABLE; + dma_initpara.number = kAdcChannelCount; + dma_initpara.priority = DMA_PRIORITY_HIGH; + dma_init(kAdcDmaPeriph, kAdcDmaChannel, &dma_initpara); + dma_circulation_enable(kAdcDmaPeriph, kAdcDmaChannel); + dma_channel_enable(kAdcDmaPeriph, kAdcDmaChannel); +} +} // namespace + +/** + * @brief 初始化 ADC 硬件及内部温度/VREF/VIN_ADC 通道。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 完成 ADC 三通道扫描、DMA 循环搬运、PA6(VIN_ADC) 模拟输入配置以及内部通道校准。 + */ +void adc_hw_init(void) +{ + rcu_periph_clock_enable(RCU_ADC0); + rcu_periph_clock_enable(RCU_GPIOA); + rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8); + + /* 原理图:PA6 -> VIN_ADC,为外部电源分压检测输入 */ + gpio_init(kVinAdcGpio, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, kVinAdcPin); + + adc_dma_init(); + adc_deinit(CONFIG_ADC_INSTANCE); + adc_mode_config(ADC_MODE_FREE); + adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, ENABLE); + adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, ENABLE); + adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT); + adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, kAdcChannelCount); + adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, CONFIG_ADC_VIN_CHANNEL, CONFIG_ADC_SAMPLE_TIME); + adc_regular_channel_config(CONFIG_ADC_INSTANCE, 1U, CONFIG_ADC_TEMP_CHANNEL, CONFIG_ADC_SAMPLE_TIME); + adc_regular_channel_config(CONFIG_ADC_INSTANCE, 2U, CONFIG_ADC_VREFINT_CHANNEL, CONFIG_ADC_SAMPLE_TIME); + adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); + adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE); + adc_dma_mode_enable(CONFIG_ADC_INSTANCE); + + adc_tempsensor_vrefint_enable(); + delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS); + + adc_enable(CONFIG_ADC_INSTANCE); + delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS); + adc_calibration_enable(CONFIG_ADC_INSTANCE); + adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL); + delay_ms(1U); + g_adc_dma_ready = 1U; +} + +/** + * @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。 + */ +uint8_t adc_dma_ready(void) +{ + return g_adc_dma_ready; +} + +/** + * @brief 获取 ADC DMA 原始循环缓存只读指针。 + * @param[in] 无。 + * @param[out] 无。 + * @retval const uint16_t* 返回长度为 3 的原始缓存指针,顺序为 VIN_ADC、温度通道、VREF 通道。 + */ +const uint16_t *adc_dma_buffer_get(void) +{ + return (const uint16_t *)g_adc_dma_buffer; +} + +/** + * @brief 执行指定通道的一次 ADC 单次采样。 + * @param[in] channel 待采样的 ADC 通道号。 + * @param[out] 无。 + * @retval uint16_t 指定通道的原始 ADC 采样值。 + * @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。 + */ +uint16_t adc_single_read(uint8_t channel) +{ + if(channel == CONFIG_ADC_VIN_CHANNEL) { + return g_adc_dma_buffer[0]; + } + if(channel == CONFIG_ADC_TEMP_CHANNEL) { + return g_adc_dma_buffer[1]; + } + if(channel == CONFIG_ADC_VREFINT_CHANNEL) { + return g_adc_dma_buffer[2]; + } + return 0U; +} + +/** + * @brief 读取外部 VIN_ADC(PA6)通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 外部 VIN_ADC 通道的原始 ADC 值。 + */ +uint16_t adc_read_vin_raw(void) +{ + return adc_single_read(CONFIG_ADC_VIN_CHANNEL); +} + +/** + * @brief 读取内部参考电压通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部参考电压通道的原始 ADC 值。 + */ +uint16_t adc_read_vref_raw(void) +{ + return adc_single_read(CONFIG_ADC_VREFINT_CHANNEL); +} + +/** + * @brief 读取内部温度通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部温度通道的原始 ADC 值。 + */ +uint16_t adc_read_temp_raw(void) +{ + return adc_single_read(CONFIG_ADC_TEMP_CHANNEL); +} + +/** + * @brief 根据外部 VIN_ADC 原始值与 VDDA 计算 ADC 引脚处电压。 + * @param[in] vin_raw 外部 VIN_ADC 通道原始 ADC 数值。 + * @param[in] vdda 当前供电电压,单位 V。 + * @param[out] 无。 + * @retval float VIN_ADC 引脚电压,单位 V。 + * @note 当前仅计算分压后的 ADC 端电压;若需换算输入母线电压,需要补充完整分压比。 + */ +float adc_calculate_vin_adc_voltage(uint16_t vin_raw, float vdda) +{ + return (static_cast(vin_raw) * vdda) / CONFIG_ADC_MAX_VALUE; +} + +/** + * @brief 根据内部参考电压原始值计算实际 VDDA。 + * @param[in] vref_raw 内部参考电压通道原始 ADC 数值。 + * @param[out] 无。 + * @retval float 换算得到的 VDDA 电压值,单位 V。 + * @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。 + */ +float adc_calculate_vdda(uint16_t vref_raw) +{ + if(vref_raw == 0U) { + return CONFIG_ADC_DEFAULT_VDDA; + } + return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast(vref_raw); +} + +/** + * @brief 根据温度原始值与 VDDA 计算芯片温度。 + * @param[in] temp_raw 内部温度通道原始 ADC 数值。 + * @param[in] vdda 当前供电电压,单位 V。 + * @param[out] 无。 + * @retval float 换算得到的芯片温度,单位 ℃。 + */ +float adc_calculate_temperature(uint16_t temp_raw, float vdda) +{ + const float sensor_voltage = (static_cast(temp_raw) * vdda) / CONFIG_ADC_MAX_VALUE; + return ((CONFIG_ADC_TEMP_V25 - sensor_voltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f; +} + +/** + * @brief 采集一组完整 ADC 样本并完成工程值换算。 + * @param[in] sample 指向输出样本结构体的指针;允许为空。 + * @param[out] sample 当指针非空时,写入 VIN/温度/VREF 原始值与换算结果。 + * @retval 无返回值。 + * @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。 + */ +void adc_collect_sample(adc_sample_t *sample) +{ + if(sample == nullptr) { + return; + } + + sample->vin_raw = adc_read_vin_raw(); + sample->temp_raw = adc_read_temp_raw(); + sample->vref_raw = adc_read_vref_raw(); + sample->vdda = adc_calculate_vdda(sample->vref_raw); + sample->vin_adc_v = adc_calculate_vin_adc_voltage(sample->vin_raw, sample->vdda); + sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda); +} diff --git a/GD32E508_DCU_APP/device/ADC.cpp.bak_sch b/GD32E508_DCU_APP/device/ADC.cpp.bak_sch new file mode 100644 index 0000000..e075262 --- /dev/null +++ b/GD32E508_DCU_APP/device/ADC.cpp.bak_sch @@ -0,0 +1,182 @@ +#include "main.h" +#include "device/adc.h" +#include "sys/systick.h" +#include "sys/config.h" + +namespace { +constexpr uint32_t kAdcDmaPeriph = DMA0; +constexpr dma_channel_enum kAdcDmaChannel = DMA_CH0; +constexpr uint32_t kAdcChannelCount = 2U; +volatile uint16_t g_adc_dma_buffer[kAdcChannelCount] = {0U, 0U}; +volatile uint8_t g_adc_dma_ready = 0U; + +/** + * @brief 配置 ADC 原始数据到内存的 DMA 循环搬运。 + */ +void adc_dma_init(void) +{ + dma_parameter_struct dma_initpara{}; + + rcu_periph_clock_enable(RCU_DMA0); + dma_deinit(kAdcDmaPeriph, kAdcDmaChannel); + dma_struct_para_init(&dma_initpara); + + dma_initpara.periph_addr = (uint32_t)(&ADC_RDATA(CONFIG_ADC_INSTANCE)); + dma_initpara.memory_addr = (uint32_t)g_adc_dma_buffer; + dma_initpara.direction = DMA_PERIPHERAL_TO_MEMORY; + dma_initpara.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; + dma_initpara.memory_width = DMA_MEMORY_WIDTH_16BIT; + dma_initpara.periph_inc = DMA_PERIPH_INCREASE_DISABLE; + dma_initpara.memory_inc = DMA_MEMORY_INCREASE_ENABLE; + dma_initpara.number = kAdcChannelCount; + dma_initpara.priority = DMA_PRIORITY_HIGH; + dma_init(kAdcDmaPeriph, kAdcDmaChannel, &dma_initpara); + dma_circulation_enable(kAdcDmaPeriph, kAdcDmaChannel); + dma_channel_enable(kAdcDmaPeriph, kAdcDmaChannel); +} +} // namespace + +/** + * @brief 初始化 ADC 硬件及内部温度/VREF 通道。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 完成 ADC 双通道扫描、DMA 循环搬运、内部通道使能与校准流程配置。 + */ +void adc_hw_init(void) +{ + rcu_periph_clock_enable(RCU_ADC0); + rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8); + + adc_dma_init(); + adc_deinit(CONFIG_ADC_INSTANCE); + adc_mode_config(ADC_MODE_FREE); + adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, ENABLE); + adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, ENABLE); + adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT); + adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, kAdcChannelCount); + adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, CONFIG_ADC_TEMP_CHANNEL, CONFIG_ADC_SAMPLE_TIME); + adc_regular_channel_config(CONFIG_ADC_INSTANCE, 1U, CONFIG_ADC_VREFINT_CHANNEL, CONFIG_ADC_SAMPLE_TIME); + adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); + adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE); + adc_dma_mode_enable(CONFIG_ADC_INSTANCE); + + adc_tempsensor_vrefint_enable(); + delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS); + + adc_enable(CONFIG_ADC_INSTANCE); + delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS); + adc_calibration_enable(CONFIG_ADC_INSTANCE); + adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL); + delay_ms(1U); + g_adc_dma_ready = 1U; +} + +/** + * @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。 + */ +uint8_t adc_dma_ready(void) +{ + return g_adc_dma_ready; +} + +/** + * @brief 获取 ADC DMA 原始循环缓存只读指针。 + * @param[in] 无。 + * @param[out] 无。 + * @retval const uint16_t* 返回长度为 2 的原始缓存指针,顺序为温度通道、VREF 通道。 + */ +const uint16_t *adc_dma_buffer_get(void) +{ + return (const uint16_t *)g_adc_dma_buffer; +} + +/** + * @brief 执行指定通道的一次 ADC 单次采样。 + * @param[in] channel 待采样的 ADC 通道号。 + * @param[out] 无。 + * @retval uint16_t 指定通道的原始 ADC 采样值。 + * @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。 + */ +uint16_t adc_single_read(uint8_t channel) +{ + if(channel == CONFIG_ADC_TEMP_CHANNEL) { + return g_adc_dma_buffer[0]; + } + if(channel == CONFIG_ADC_VREFINT_CHANNEL) { + return g_adc_dma_buffer[1]; + } + return 0U; +} + +/** + * @brief 读取内部参考电压通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部参考电压通道的原始 ADC 值。 + */ +uint16_t adc_read_vref_raw(void) +{ + return adc_single_read(CONFIG_ADC_VREFINT_CHANNEL); +} + +/** + * @brief 读取内部温度通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部温度通道的原始 ADC 值。 + */ +uint16_t adc_read_temp_raw(void) +{ + return adc_single_read(CONFIG_ADC_TEMP_CHANNEL); +} + +/** + * @brief 根据内部参考电压原始值计算实际 VDDA。 + * @param[in] vref_raw 内部参考电压通道原始 ADC 数值。 + * @param[out] 无。 + * @retval float 换算得到的 VDDA 电压值,单位 V。 + * @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。 + */ +float adc_calculate_vdda(uint16_t vref_raw) +{ + if(vref_raw == 0U) { + return CONFIG_ADC_DEFAULT_VDDA; + } + return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast(vref_raw); +} + +/** + * @brief 根据温度原始值与 VDDA 计算芯片温度。 + * @param[in] temp_raw 内部温度通道原始 ADC 数值。 + * @param[in] vdda 当前供电电压,单位 V。 + * @param[out] 无。 + * @retval float 换算得到的芯片温度,单位 ℃。 + */ +float adc_calculate_temperature(uint16_t temp_raw, float vdda) +{ + const float sensor_voltage = (static_cast(temp_raw) * vdda) / CONFIG_ADC_MAX_VALUE; + return ((CONFIG_ADC_TEMP_V25 - sensor_voltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f; +} + +/** + * @brief 采集一组完整 ADC 样本并完成工程值换算。 + * @param[in] sample 指向输出样本结构体的指针;允许为空。 + * @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。 + * @retval 无返回值。 + * @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。 + */ +void adc_collect_sample(adc_sample_t *sample) +{ + if(sample == nullptr) { + return; + } + + sample->temp_raw = adc_read_temp_raw(); + sample->vref_raw = adc_read_vref_raw(); + sample->vdda = adc_calculate_vdda(sample->vref_raw); + sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda); +} diff --git a/GD32E508_DCU_APP/device/CANFD.cpp b/GD32E508_DCU_APP/device/CANFD.cpp new file mode 100644 index 0000000..1f3846d --- /dev/null +++ b/GD32E508_DCU_APP/device/CANFD.cpp @@ -0,0 +1,232 @@ +/** + * @file CANFD.cpp + * @brief CANFD 通信模块实现。 + */ + +#include "main.h" +#include "device/canfd.h" +#include "sys/config.h" + +namespace { +constexpr uint32_t kSlpGpio = GPIOA; +constexpr uint32_t kSlpPin = GPIO_PIN_8; + +struct CanFdTxBuffer { + can_trasnmit_message_struct tx_message; + uint8_t payload[64]; + uint8_t valid; +}; + +CanFdTxBuffer s_txBuffer; +} // namespace + +CanFd::Diag CanFd::s_diag; +CanFd::RxFrame CanFd::s_rxFrame; + +void CanFd::initGpio() +{ + rcu_periph_clock_enable(CONFIG_CAN_RCU); + rcu_periph_clock_enable(RCU_GPIOA); + rcu_periph_clock_enable(RCU_AF); + + gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11); + gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12); + gpio_init(kSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kSlpPin); +} + +void CanFd::filterAcceptAll() +{ + can_filter_parameter_struct can_filter{}; + can_filter.filter_number = 0U; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_list_high = 0x0000U; + can_filter.filter_list_low = 0x0000U; + can_filter.filter_mask_high = 0x0000U; + can_filter.filter_mask_low = 0x0000U; + can_filter.filter_fifo_number = CAN_FIFO0; + can_filter.filter_enable = ENABLE; + can_filter_init(CONFIG_CAN_INSTANCE, &can_filter); +} + +void CanFd::initNominalTiming() +{ + can_parameter_struct can_parameter{}; + + can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); + can_parameter.time_triggered = DISABLE; + can_parameter.auto_bus_off_recovery = ENABLE; + can_parameter.auto_wake_up = DISABLE; + can_parameter.auto_retrans = ENABLE; + can_parameter.rec_fifo_overwrite = ENABLE; + can_parameter.trans_fifo_order = ENABLE; + can_parameter.working_mode = CAN_NORMAL_MODE; + can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; + can_parameter.time_segment_1 = CAN_BT_BS1_7TQ; + can_parameter.time_segment_2 = CAN_BT_BS2_2TQ; + can_parameter.prescaler = 6U; + can_init(CONFIG_CAN_INSTANCE, &can_parameter); +} + +void CanFd::initFdMode() +{ +#ifdef GD32E508 + can_fdframe_struct can_fd_parameter{}; + can_fd_tdc_struct can_fd_tdc_parameter{}; + + can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); + can_fd_parameter.fd_frame = ENABLE; + can_fd_parameter.excp_event_detect = ENABLE; + can_fd_parameter.delay_compensation = ENABLE; + can_fd_tdc_parameter.tdc_filter = 0x04U; + can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET; + can_fd_tdc_parameter.tdc_offset = 0x04U; + can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter; + can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; + can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; + can_fd_parameter.data_prescaler = 6U; + can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ; + can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ; + can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ; + can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter); +#else + can_fd_function_enable(CONFIG_CAN_INSTANCE); +#endif +} + +void CanFd::buildStatusFrame(const StatusPayload *status) +{ + const int16_t temp_x100 = static_cast(status->temperature * 100.0f); + const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); + + can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &s_txBuffer.tx_message); + s_txBuffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; + s_txBuffer.tx_message.tx_efid = 0U; + s_txBuffer.tx_message.tx_ff = CAN_FF_STANDARD; + s_txBuffer.tx_message.tx_ft = CAN_FT_DATA; + s_txBuffer.tx_message.tx_dlen = CANFD_STATUS_DLEN; +#ifdef GD32E508 + s_txBuffer.tx_message.fd_flag = ENABLE; + s_txBuffer.tx_message.fd_brs = ENABLE; + s_txBuffer.tx_message.fd_esi = CAN_ESI_DOMINANT; +#endif + + auto &p = s_txBuffer.payload; + p[0] = static_cast(status->temp_raw & 0xFFU); + p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); + p[2] = static_cast(status->vref_raw & 0xFFU); + p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); + p[4] = static_cast(temp_x100 & 0xFF); + p[5] = static_cast((temp_x100 >> 8) & 0xFF); + p[6] = static_cast(vdda_x1000 & 0xFFU); + p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); + p[8] = CONFIG_CAN_STATUS_TAG0; + p[9] = CONFIG_CAN_STATUS_TAG1; + p[10] = CONFIG_CAN_STATUS_TAG2; + p[11] = CONFIG_CAN_STATUS_TAG3; + + for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) { + s_txBuffer.tx_message.tx_data[i] = p[i]; + } + s_txBuffer.valid = 1U; +} + +void CanFd::init() +{ + initGpio(); + setNormalMode(); + can_deinit(CONFIG_CAN_INSTANCE); + initNominalTiming(); + initFdMode(); + filterAcceptAll(); + + can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0); + nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U); + + s_rxFrame.valid = 0U; + s_txBuffer.valid = 0U; +} + +void CanFd::poll() +{ + /* 已由 CAN0_RX0_IRQHandler 中断驱动 */ +} + +void CanFd::prepareStatus(const StatusPayload *status) +{ + if(status == nullptr) { + s_txBuffer.valid = 0U; + return; + } + buildStatusFrame(status); +} + +void CanFd::sendPrepared() +{ + if(s_txBuffer.valid == 0U) return; + const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &s_txBuffer.tx_message); + if(mailbox != CAN_NOMAILBOX) { + s_diag.tx_count++; + s_diag.last_payload_length = s_txBuffer.tx_message.tx_dlen; + s_diag.last_tx_id = s_txBuffer.tx_message.tx_sfid; + } else { + s_diag.error_count++; + } +} + +bool CanFd::hasRxFrame() +{ + return s_rxFrame.valid != 0U; +} + +bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length) +{ + if(s_rxFrame.valid == 0U) return false; + if(id) *id = s_rxFrame.id; + if(length) *length = s_rxFrame.length; + if(data) { for(uint8_t i = 0; i < s_rxFrame.length; ++i) data[i] = s_rxFrame.data[i]; } + s_rxFrame.valid = 0U; + return true; +} + +const CanFd::Diag *CanFd::diag() +{ + return &s_diag; +} + +void CanFd::setNormalMode() +{ + gpio_bit_reset(kSlpGpio, kSlpPin); +} + +void CanFd::setSilentMode() +{ + gpio_bit_set(kSlpGpio, kSlpPin); +} + +void CanFd::onRx0Interrupt() +{ + if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) return; + + can_receive_message_struct rx_message{}; + can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); + can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message); + + s_diag.rx_count++; + s_diag.last_payload_length = rx_message.rx_dlen; + s_diag.last_rx_fd_flag = rx_message.fd_flag; + s_diag.last_rx_brs = rx_message.fd_brs; + s_diag.last_rx_esi = rx_message.fd_esi; + s_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; + + s_rxFrame.id = s_diag.last_rx_id; + s_rxFrame.length = rx_message.rx_dlen; + for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) + s_rxFrame.data[i] = rx_message.rx_data[i]; + s_rxFrame.valid = 1U; + + can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0); +} + +/* ----- 中断入口 C 包装 ------------------------------------------------- */ +extern "C" void CAN0_RX0_IRQHandler(void) { CanFd::onRx0Interrupt(); } \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/CANFD.cpp.bak_align_app b/GD32E508_DCU_APP/device/CANFD.cpp.bak_align_app new file mode 100644 index 0000000..8481d48 --- /dev/null +++ b/GD32E508_DCU_APP/device/CANFD.cpp.bak_align_app @@ -0,0 +1,278 @@ +/** + * @file CANFD.cpp + * @brief CANFD 通信外设初始化、收发与诊断实现。 + */ + +#include "main.h" +#include "device/canfd.h" +#include "sys/config.h" + +namespace { + +/** @brief 内部接收帧缓存结构。 */ +struct CanfdRxFrame { + uint32_t id = 0U; + uint8_t data[64] = {0}; + uint8_t length = 0U; + uint8_t valid = 0U; +}; + +/** @brief 内部发送帧缓存结构。 */ +struct CanfdTxBuffer { + can_trasnmit_message_struct tx_message{}; + uint8_t payload[64] = {0}; + uint8_t valid = 0U; +}; + +canfd_diag_t g_can_diag{}; +CanfdRxFrame g_rx_frame{}; +CanfdTxBuffer g_tx_buffer{}; + +/** + * @brief 初始化 CAN 外设所需 GPIO 引脚。 + */ +inline void can_gpio_init() +{ + rcu_periph_clock_enable(CONFIG_CAN_RCU); + rcu_periph_clock_enable(RCU_GPIOB); + rcu_periph_clock_enable(RCU_AF); + + gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8); + gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); + gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE); +} + +/** + * @brief 配置 CAN 接收过滤器为接受所有报文。 + */ +inline void can_filter_accept_all() +{ + can_filter_parameter_struct can_filter{}; + can_filter.filter_number = 0U; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_list_high = 0x0000U; + can_filter.filter_list_low = 0x0000U; + can_filter.filter_mask_high = 0x0000U; + can_filter.filter_mask_low = 0x0000U; + can_filter.filter_fifo_number = CAN_FIFO0; + can_filter.filter_enable = ENABLE; + can_filter_init(CONFIG_CAN_INSTANCE, &can_filter); +} + +/** + * @brief 配置 CAN 标称位定时参数。 + * @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。 + */ +inline void can_nominal_init() +{ + can_parameter_struct can_parameter{}; + + can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); + can_parameter.time_triggered = DISABLE; + can_parameter.auto_bus_off_recovery = ENABLE; + can_parameter.auto_wake_up = DISABLE; + can_parameter.auto_retrans = ENABLE; + can_parameter.rec_fifo_overwrite = DISABLE; + can_parameter.trans_fifo_order = DISABLE; + can_parameter.working_mode = CAN_NORMAL_MODE; + can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; + can_parameter.time_segment_1 = CAN_BT_BS1_7TQ; + can_parameter.time_segment_2 = CAN_BT_BS2_2TQ; + can_parameter.prescaler = 6U; + can_init(CONFIG_CAN_INSTANCE, &can_parameter); +} + +/** + * @brief 配置 CANFD 可变速率模式参数。 + * @note 参考 APP 项目配置: + * - 仲裁段保持 1 Mbps; + * - 数据段配置为 2 Mbps,采样点约 80%; + * - 发送时启用 BRS 后才会切到数据段速率。 + */ +inline void can_fd_mode_init() +{ +#ifdef GD32E508 + can_fdframe_struct can_fd_parameter{}; + + can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); + can_fd_parameter.fd_frame = ENABLE; + can_fd_parameter.excp_event_detect = ENABLE; + can_fd_parameter.delay_compensation = ENABLE; + can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; + can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; + can_fd_parameter.data_prescaler = 6U; + can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ; + can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ; + can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ; + can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter); +#else + can_fd_function_enable(CONFIG_CAN_INSTANCE); +#endif +} + +/** + * @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。 + * @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。 + */ +inline void canfd_build_status_frame(const canfd_status_payload_t *status) +{ + const int16_t temp_x100 = static_cast(status->temperature * 100.0f); + const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); + + can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message); + g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; + g_tx_buffer.tx_message.tx_efid = 0U; + g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD; + g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA; + g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN; +#ifdef GD32E508 + g_tx_buffer.tx_message.fd_flag = ENABLE; + g_tx_buffer.tx_message.fd_brs = ENABLE; + g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT; +#endif + + auto &p = g_tx_buffer.payload; + p[0] = static_cast(status->temp_raw & 0xFFU); + p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); + p[2] = static_cast(status->vref_raw & 0xFFU); + p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); + p[4] = static_cast(temp_x100 & 0xFF); + p[5] = static_cast((temp_x100 >> 8) & 0xFF); + p[6] = static_cast(vdda_x1000 & 0xFFU); + p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); + p[8] = CONFIG_CAN_STATUS_TAG0; + p[9] = CONFIG_CAN_STATUS_TAG1; + p[10] = CONFIG_CAN_STATUS_TAG2; + p[11] = CONFIG_CAN_STATUS_TAG3; + + for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) { + g_tx_buffer.tx_message.tx_data[i] = p[i]; + } + g_tx_buffer.valid = 1U; +} +} // namespace + +/** + * @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。 + */ +void canfd_init(void) +{ + can_gpio_init(); + can_deinit(CONFIG_CAN_INSTANCE); + can_nominal_init(); + can_fd_mode_init(); + can_filter_accept_all(); + g_rx_frame.valid = 0U; + g_tx_buffer.valid = 0U; +} + +/** + * @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。 + * @note 若当前没有待接收报文,直接返回。 + */ +void canfd_poll(void) +{ + can_receive_message_struct rx_message{}; + + if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) { + return; + } + + can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); + can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message); + g_can_diag.rx_count++; + g_can_diag.last_payload_length = rx_message.rx_dlen; + g_can_diag.last_rx_fd_flag = rx_message.fd_flag; + g_can_diag.last_rx_brs = rx_message.fd_brs; + g_can_diag.last_rx_esi = rx_message.fd_esi; + g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; + + g_rx_frame.id = g_can_diag.last_rx_id; + g_rx_frame.length = rx_message.rx_dlen; + for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) { + g_rx_frame.data[i] = rx_message.rx_data[i]; + } + g_rx_frame.valid = 1U; +} + +/** + * @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。 + * @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。 + */ +void canfd_prepare_status(const canfd_status_payload_t *status) +{ + if(status == nullptr) { + g_tx_buffer.valid = 0U; + return; + } + + canfd_build_status_frame(status); +} + +/** + * @brief 发送已准备好的 CANFD 状态报文。 + * @note 若当前没有有效待发送报文,直接返回。 + */ +void canfd_send_prepared(void) +{ + if(g_tx_buffer.valid == 0U) { + return; + } + + const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message); + if(mailbox != CAN_NOMAILBOX) { + g_can_diag.tx_count++; + g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen; + g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid; + } else { + g_can_diag.error_count++; + } +} + +/** + * @brief 查询是否存在尚未消费的接收报文。 + * @retval 非 0 存在待消费报文,0 无报文。 + */ +uint8_t canfd_has_rx_frame(void) +{ + return g_rx_frame.valid; +} + +/** + * @brief 读取并消费最近一次缓存的接收报文。 + * @param[out] id 输出报文标识符的指针;允许为空。 + * @param[out] data 输出报文数据缓存区指针;允许为空。 + * @param[out] length 输出报文数据长度的指针;允许为空。 + * @retval 1 成功消费一帧报文,0 当前无有效缓存报文。 + */ +uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length) +{ + if(g_rx_frame.valid == 0U) { + return 0U; + } + + if(id != nullptr) { + *id = g_rx_frame.id; + } + if(length != nullptr) { + *length = g_rx_frame.length; + } + if(data != nullptr) { + for(uint8_t i = 0; i < g_rx_frame.length; ++i) { + data[i] = g_rx_frame.data[i]; + } + } + + g_rx_frame.valid = 0U; + return 1U; +} + +/** + * @brief 获取 CANFD 诊断统计信息只读指针。 + * @retval 指向内部诊断统计结构体的只读指针。 + */ +const canfd_diag_t *canfd_get_diag(void) +{ + return &g_can_diag; +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/CANFD.cpp.bak_can_isr b/GD32E508_DCU_APP/device/CANFD.cpp.bak_can_isr new file mode 100644 index 0000000..651b365 --- /dev/null +++ b/GD32E508_DCU_APP/device/CANFD.cpp.bak_can_isr @@ -0,0 +1,283 @@ +/** + * @file CANFD.cpp + * @brief CANFD 通信外设初始化、收发与诊断实现。 + */ + +#include "main.h" +#include "device/canfd.h" +#include "sys/config.h" + +namespace { + +/** @brief 内部接收帧缓存结构。 */ +struct CanfdRxFrame { + uint32_t id = 0U; + uint8_t data[64] = {0}; + uint8_t length = 0U; + uint8_t valid = 0U; +}; + +/** @brief 内部发送帧缓存结构。 */ +struct CanfdTxBuffer { + can_trasnmit_message_struct tx_message{}; + uint8_t payload[64] = {0}; + uint8_t valid = 0U; +}; + +canfd_diag_t g_can_diag{}; +CanfdRxFrame g_rx_frame{}; +CanfdTxBuffer g_tx_buffer{}; + +/** + * @brief 初始化 CAN 外设所需 GPIO 引脚。 + */ +inline void can_gpio_init() +{ + rcu_periph_clock_enable(CONFIG_CAN_RCU); + rcu_periph_clock_enable(RCU_GPIOB); + rcu_periph_clock_enable(RCU_AF); + + gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8); + gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); + gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE); +} + +/** + * @brief 配置 CAN 接收过滤器为接受所有报文。 + */ +inline void can_filter_accept_all() +{ + can_filter_parameter_struct can_filter{}; + can_filter.filter_number = 0U; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_list_high = 0x0000U; + can_filter.filter_list_low = 0x0000U; + can_filter.filter_mask_high = 0x0000U; + can_filter.filter_mask_low = 0x0000U; + can_filter.filter_fifo_number = CAN_FIFO0; + can_filter.filter_enable = ENABLE; + can_filter_init(CONFIG_CAN_INSTANCE, &can_filter); +} + +/** + * @brief 配置 CAN 标称位定时参数。 + * @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。 + */ +inline void can_nominal_init() +{ + can_parameter_struct can_parameter{}; + + can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); + can_parameter.time_triggered = DISABLE; + can_parameter.auto_bus_off_recovery = ENABLE; + can_parameter.auto_wake_up = DISABLE; + can_parameter.auto_retrans = ENABLE; + can_parameter.rec_fifo_overwrite = ENABLE; + can_parameter.trans_fifo_order = ENABLE; + can_parameter.working_mode = CAN_NORMAL_MODE; + can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; + can_parameter.time_segment_1 = CAN_BT_BS1_7TQ; + can_parameter.time_segment_2 = CAN_BT_BS2_2TQ; + can_parameter.prescaler = 6U; + can_init(CONFIG_CAN_INSTANCE, &can_parameter); +} + +/** + * @brief 配置 CANFD 可变速率模式参数。 + * @note 参考 APP 项目配置: + * - 仲裁段保持 1 Mbps; + * - 数据段配置为 2 Mbps,采样点约 80%; + * - 发送时启用 BRS 后才会切到数据段速率。 + */ +inline void can_fd_mode_init() +{ +#ifdef GD32E508 + can_fdframe_struct can_fd_parameter{}; + can_fd_tdc_struct can_fd_tdc_parameter{}; + + can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); + can_fd_parameter.fd_frame = ENABLE; + can_fd_parameter.excp_event_detect = ENABLE; + can_fd_parameter.delay_compensation = ENABLE; + can_fd_tdc_parameter.tdc_filter = 0x04U; + can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET; + can_fd_tdc_parameter.tdc_offset = 0x04U; + can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter; + can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; + can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; + can_fd_parameter.data_prescaler = 6U; + can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ; + can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ; + can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ; + can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter); +#else + can_fd_function_enable(CONFIG_CAN_INSTANCE); +#endif +} + +/** + * @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。 + * @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。 + */ +inline void canfd_build_status_frame(const canfd_status_payload_t *status) +{ + const int16_t temp_x100 = static_cast(status->temperature * 100.0f); + const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); + + can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message); + g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; + g_tx_buffer.tx_message.tx_efid = 0U; + g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD; + g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA; + g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN; +#ifdef GD32E508 + g_tx_buffer.tx_message.fd_flag = ENABLE; + g_tx_buffer.tx_message.fd_brs = ENABLE; + g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT; +#endif + + auto &p = g_tx_buffer.payload; + p[0] = static_cast(status->temp_raw & 0xFFU); + p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); + p[2] = static_cast(status->vref_raw & 0xFFU); + p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); + p[4] = static_cast(temp_x100 & 0xFF); + p[5] = static_cast((temp_x100 >> 8) & 0xFF); + p[6] = static_cast(vdda_x1000 & 0xFFU); + p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); + p[8] = CONFIG_CAN_STATUS_TAG0; + p[9] = CONFIG_CAN_STATUS_TAG1; + p[10] = CONFIG_CAN_STATUS_TAG2; + p[11] = CONFIG_CAN_STATUS_TAG3; + + for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) { + g_tx_buffer.tx_message.tx_data[i] = p[i]; + } + g_tx_buffer.valid = 1U; +} +} // namespace + +/** + * @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。 + */ +void canfd_init(void) +{ + can_gpio_init(); + can_deinit(CONFIG_CAN_INSTANCE); + can_nominal_init(); + can_fd_mode_init(); + can_filter_accept_all(); + g_rx_frame.valid = 0U; + g_tx_buffer.valid = 0U; +} + +/** + * @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。 + * @note 若当前没有待接收报文,直接返回。 + */ +void canfd_poll(void) +{ + can_receive_message_struct rx_message{}; + + if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) { + return; + } + + can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); + can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message); + g_can_diag.rx_count++; + g_can_diag.last_payload_length = rx_message.rx_dlen; + g_can_diag.last_rx_fd_flag = rx_message.fd_flag; + g_can_diag.last_rx_brs = rx_message.fd_brs; + g_can_diag.last_rx_esi = rx_message.fd_esi; + g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; + + g_rx_frame.id = g_can_diag.last_rx_id; + g_rx_frame.length = rx_message.rx_dlen; + for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) { + g_rx_frame.data[i] = rx_message.rx_data[i]; + } + g_rx_frame.valid = 1U; +} + +/** + * @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。 + * @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。 + */ +void canfd_prepare_status(const canfd_status_payload_t *status) +{ + if(status == nullptr) { + g_tx_buffer.valid = 0U; + return; + } + + canfd_build_status_frame(status); +} + +/** + * @brief 发送已准备好的 CANFD 状态报文。 + * @note 若当前没有有效待发送报文,直接返回。 + */ +void canfd_send_prepared(void) +{ + if(g_tx_buffer.valid == 0U) { + return; + } + + const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message); + if(mailbox != CAN_NOMAILBOX) { + g_can_diag.tx_count++; + g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen; + g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid; + } else { + g_can_diag.error_count++; + } +} + +/** + * @brief 查询是否存在尚未消费的接收报文。 + * @retval 非 0 存在待消费报文,0 无报文。 + */ +uint8_t canfd_has_rx_frame(void) +{ + return g_rx_frame.valid; +} + +/** + * @brief 读取并消费最近一次缓存的接收报文。 + * @param[out] id 输出报文标识符的指针;允许为空。 + * @param[out] data 输出报文数据缓存区指针;允许为空。 + * @param[out] length 输出报文数据长度的指针;允许为空。 + * @retval 1 成功消费一帧报文,0 当前无有效缓存报文。 + */ +uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length) +{ + if(g_rx_frame.valid == 0U) { + return 0U; + } + + if(id != nullptr) { + *id = g_rx_frame.id; + } + if(length != nullptr) { + *length = g_rx_frame.length; + } + if(data != nullptr) { + for(uint8_t i = 0; i < g_rx_frame.length; ++i) { + data[i] = g_rx_frame.data[i]; + } + } + + g_rx_frame.valid = 0U; + return 1U; +} + +/** + * @brief 获取 CANFD 诊断统计信息只读指针。 + * @retval 指向内部诊断统计结构体的只读指针。 + */ +const canfd_diag_t *canfd_get_diag(void) +{ + return &g_can_diag; +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/CANFD.cpp.bak_canrate b/GD32E508_DCU_APP/device/CANFD.cpp.bak_canrate new file mode 100644 index 0000000..1246bc5 --- /dev/null +++ b/GD32E508_DCU_APP/device/CANFD.cpp.bak_canrate @@ -0,0 +1,274 @@ +/** + * @file CANFD.cpp + * @brief CANFD 通信外设初始化、收发与诊断实现。 + */ + +#include "main.h" +#include "device/canfd.h" +#include "sys/config.h" + +namespace { + +/** @brief 内部接收帧缓存结构。 */ +struct CanfdRxFrame { + uint32_t id = 0U; + uint8_t data[64] = {0}; + uint8_t length = 0U; + uint8_t valid = 0U; +}; + +/** @brief 内部发送帧缓存结构。 */ +struct CanfdTxBuffer { + can_trasnmit_message_struct tx_message{}; + uint8_t payload[64] = {0}; + uint8_t valid = 0U; +}; + +canfd_diag_t g_can_diag{}; +CanfdRxFrame g_rx_frame{}; +CanfdTxBuffer g_tx_buffer{}; + +/** + * @brief 初始化 CAN 外设所需 GPIO 引脚。 + */ +inline void can_gpio_init() +{ + rcu_periph_clock_enable(CONFIG_CAN_RCU); + rcu_periph_clock_enable(RCU_GPIOB); + rcu_periph_clock_enable(RCU_AF); + + gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8); + gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); + gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE); +} + +/** + * @brief 配置 CAN 接收过滤器为接受所有报文。 + */ +inline void can_filter_accept_all() +{ + can_filter_parameter_struct can_filter{}; + can_filter.filter_number = 0U; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_list_high = 0x0000U; + can_filter.filter_list_low = 0x0000U; + can_filter.filter_mask_high = 0x0000U; + can_filter.filter_mask_low = 0x0000U; + can_filter.filter_fifo_number = CAN_FIFO0; + can_filter.filter_enable = ENABLE; + can_filter_init(CONFIG_CAN_INSTANCE, &can_filter); +} + +/** + * @brief 配置 CAN 标称位定时参数。 + */ +inline void can_nominal_init() +{ + can_parameter_struct can_parameter{}; + + can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); + can_parameter.time_triggered = DISABLE; + can_parameter.auto_bus_off_recovery = ENABLE; + can_parameter.auto_wake_up = DISABLE; + can_parameter.auto_retrans = ENABLE; + can_parameter.rec_fifo_overwrite = DISABLE; + can_parameter.trans_fifo_order = DISABLE; + can_parameter.working_mode = CAN_NORMAL_MODE; + can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; + can_parameter.time_segment_1 = CAN_BT_BS1_8TQ; + can_parameter.time_segment_2 = CAN_BT_BS2_3TQ; + can_parameter.prescaler = CONFIG_CAN_NOMINAL_BAUD_PRESCALER; + can_init(CONFIG_CAN_INSTANCE, &can_parameter); +} + +/** + * @brief 配置 CANFD 可变速率模式参数。 + * @note 在 GD32E508 上启用 ISO 模式;非 GD32E508 平台仅启用 FD 功能。 + */ +inline void can_fd_mode_init() +{ +#ifdef GD32E508 + can_fdframe_struct can_fd_parameter{}; + + can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); + can_fd_parameter.fd_frame = ENABLE; + can_fd_parameter.excp_event_detect = ENABLE; + can_fd_parameter.delay_compensation = DISABLE; + can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; + can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; + can_fd_parameter.data_prescaler = 1U; + can_fd_parameter.data_resync_jump_width = 0U; + can_fd_parameter.data_time_segment_1 = 2U; + can_fd_parameter.data_time_segment_2 = 1U; + can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter); +#else + can_fd_function_enable(CONFIG_CAN_INSTANCE); +#endif +} + +/** + * @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。 + * @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。 + */ +inline void canfd_build_status_frame(const canfd_status_payload_t *status) +{ + const int16_t temp_x100 = static_cast(status->temperature * 100.0f); + const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); + + can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message); + g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; + g_tx_buffer.tx_message.tx_efid = 0U; + g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD; + g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA; + g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN; +#ifdef GD32E508 + g_tx_buffer.tx_message.fd_flag = ENABLE; + g_tx_buffer.tx_message.fd_brs = CAN_BRS_DISABLE; + g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT; +#endif + + auto &p = g_tx_buffer.payload; + p[0] = static_cast(status->temp_raw & 0xFFU); + p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); + p[2] = static_cast(status->vref_raw & 0xFFU); + p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); + p[4] = static_cast(temp_x100 & 0xFF); + p[5] = static_cast((temp_x100 >> 8) & 0xFF); + p[6] = static_cast(vdda_x1000 & 0xFFU); + p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); + p[8] = CONFIG_CAN_STATUS_TAG0; + p[9] = CONFIG_CAN_STATUS_TAG1; + p[10] = CONFIG_CAN_STATUS_TAG2; + p[11] = CONFIG_CAN_STATUS_TAG3; + + for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) { + g_tx_buffer.tx_message.tx_data[i] = p[i]; + } + g_tx_buffer.valid = 1U; +} +} // namespace + +/** + * @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。 + */ +void canfd_init(void) +{ + can_gpio_init(); + can_deinit(CONFIG_CAN_INSTANCE); + can_nominal_init(); + can_fd_mode_init(); + can_filter_accept_all(); + g_rx_frame.valid = 0U; + g_tx_buffer.valid = 0U; +} + +/** + * @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。 + * @note 若当前没有待接收报文,直接返回。 + */ +void canfd_poll(void) +{ + can_receive_message_struct rx_message{}; + + if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) { + return; + } + + can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); + can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message); + g_can_diag.rx_count++; + g_can_diag.last_payload_length = rx_message.rx_dlen; + g_can_diag.last_rx_fd_flag = rx_message.fd_flag; + g_can_diag.last_rx_brs = rx_message.fd_brs; + g_can_diag.last_rx_esi = rx_message.fd_esi; + g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; + + g_rx_frame.id = g_can_diag.last_rx_id; + g_rx_frame.length = rx_message.rx_dlen; + for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) { + g_rx_frame.data[i] = rx_message.rx_data[i]; + } + g_rx_frame.valid = 1U; +} + +/** + * @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。 + * @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。 + */ +void canfd_prepare_status(const canfd_status_payload_t *status) +{ + if(status == nullptr) { + g_tx_buffer.valid = 0U; + return; + } + + canfd_build_status_frame(status); +} + +/** + * @brief 发送已准备好的 CANFD 状态报文。 + * @note 若当前没有有效待发送报文,直接返回。 + */ +void canfd_send_prepared(void) +{ + if(g_tx_buffer.valid == 0U) { + return; + } + + const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message); + if(mailbox != CAN_NOMAILBOX) { + g_can_diag.tx_count++; + g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen; + g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid; + } else { + g_can_diag.error_count++; + } +} + +/** + * @brief 查询是否存在尚未消费的接收报文。 + * @retval 非 0 存在待消费报文,0 无报文。 + */ +uint8_t canfd_has_rx_frame(void) +{ + return g_rx_frame.valid; +} + +/** + * @brief 读取并消费最近一次缓存的接收报文。 + * @param[out] id 输出报文标识符的指针;允许为空。 + * @param[out] data 输出报文数据缓存区指针;允许为空。 + * @param[out] length 输出报文数据长度的指针;允许为空。 + * @retval 1 成功消费一帧报文,0 当前无有效缓存报文。 + */ +uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length) +{ + if(g_rx_frame.valid == 0U) { + return 0U; + } + + if(id != nullptr) { + *id = g_rx_frame.id; + } + if(length != nullptr) { + *length = g_rx_frame.length; + } + if(data != nullptr) { + for(uint8_t i = 0; i < g_rx_frame.length; ++i) { + data[i] = g_rx_frame.data[i]; + } + } + + g_rx_frame.valid = 0U; + return 1U; +} + +/** + * @brief 获取 CANFD 诊断统计信息只读指针。 + * @retval 指向内部诊断统计结构体的只读指针。 + */ +const canfd_diag_t *canfd_get_diag(void) +{ + return &g_can_diag; +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/CANFD.cpp.bak_canrcu b/GD32E508_DCU_APP/device/CANFD.cpp.bak_canrcu new file mode 100644 index 0000000..099c72e --- /dev/null +++ b/GD32E508_DCU_APP/device/CANFD.cpp.bak_canrcu @@ -0,0 +1,274 @@ +/** + * @file CANFD.cpp + * @brief CANFD 通信外设初始化、收发与诊断实现。 + */ + +#include "main.h" +#include "device/canfd.h" +#include "sys/config.h" + +namespace { + +/** @brief 内部接收帧缓存结构。 */ +struct CanfdRxFrame { + uint32_t id = 0U; + uint8_t data[64] = {0}; + uint8_t length = 0U; + uint8_t valid = 0U; +}; + +/** @brief 内部发送帧缓存结构。 */ +struct CanfdTxBuffer { + can_trasnmit_message_struct tx_message{}; + uint8_t payload[64] = {0}; + uint8_t valid = 0U; +}; + +canfd_diag_t g_can_diag{}; +CanfdRxFrame g_rx_frame{}; +CanfdTxBuffer g_tx_buffer{}; + +/** + * @brief 初始化 CAN 外设所需 GPIO 引脚。 + */ +inline void can_gpio_init() +{ + rcu_periph_clock_enable(CONFIG_CAN_INSTANCE); + rcu_periph_clock_enable(RCU_GPIOB); + rcu_periph_clock_enable(RCU_AF); + + gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8); + gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); + gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE); +} + +/** + * @brief 配置 CAN 接收过滤器为接受所有报文。 + */ +inline void can_filter_accept_all() +{ + can_filter_parameter_struct can_filter{}; + can_filter.filter_number = 0U; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_list_high = 0x0000U; + can_filter.filter_list_low = 0x0000U; + can_filter.filter_mask_high = 0x0000U; + can_filter.filter_mask_low = 0x0000U; + can_filter.filter_fifo_number = CAN_FIFO0; + can_filter.filter_enable = ENABLE; + can_filter_init(CONFIG_CAN_INSTANCE, &can_filter); +} + +/** + * @brief 配置 CAN 标称位定时参数。 + */ +inline void can_nominal_init() +{ + can_parameter_struct can_parameter{}; + + can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); + can_parameter.time_triggered = DISABLE; + can_parameter.auto_bus_off_recovery = ENABLE; + can_parameter.auto_wake_up = DISABLE; + can_parameter.auto_retrans = ENABLE; + can_parameter.rec_fifo_overwrite = DISABLE; + can_parameter.trans_fifo_order = DISABLE; + can_parameter.working_mode = CAN_NORMAL_MODE; + can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; + can_parameter.time_segment_1 = CAN_BT_BS1_8TQ; + can_parameter.time_segment_2 = CAN_BT_BS2_3TQ; + can_parameter.prescaler = CONFIG_CAN_NOMINAL_BAUD_PRESCALER; + can_init(CONFIG_CAN_INSTANCE, &can_parameter); +} + +/** + * @brief 配置 CANFD 可变速率模式参数。 + * @note 在 GD32E508 上启用 ISO 模式;非 GD32E508 平台仅启用 FD 功能。 + */ +inline void can_fd_mode_init() +{ +#ifdef GD32E508 + can_fdframe_struct can_fd_parameter{}; + + can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); + can_fd_parameter.fd_frame = ENABLE; + can_fd_parameter.excp_event_detect = ENABLE; + can_fd_parameter.delay_compensation = DISABLE; + can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; + can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; + can_fd_parameter.data_prescaler = 1U; + can_fd_parameter.data_resync_jump_width = 0U; + can_fd_parameter.data_time_segment_1 = 2U; + can_fd_parameter.data_time_segment_2 = 1U; + can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter); +#else + can_fd_function_enable(CONFIG_CAN_INSTANCE); +#endif +} + +/** + * @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。 + * @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。 + */ +inline void canfd_build_status_frame(const canfd_status_payload_t *status) +{ + const int16_t temp_x100 = static_cast(status->temperature * 100.0f); + const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); + + can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message); + g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; + g_tx_buffer.tx_message.tx_efid = 0U; + g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD; + g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA; + g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN; +#ifdef GD32E508 + g_tx_buffer.tx_message.fd_flag = ENABLE; + g_tx_buffer.tx_message.fd_brs = CAN_BRS_DISABLE; + g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT; +#endif + + auto &p = g_tx_buffer.payload; + p[0] = static_cast(status->temp_raw & 0xFFU); + p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); + p[2] = static_cast(status->vref_raw & 0xFFU); + p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); + p[4] = static_cast(temp_x100 & 0xFF); + p[5] = static_cast((temp_x100 >> 8) & 0xFF); + p[6] = static_cast(vdda_x1000 & 0xFFU); + p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); + p[8] = CONFIG_CAN_STATUS_TAG0; + p[9] = CONFIG_CAN_STATUS_TAG1; + p[10] = CONFIG_CAN_STATUS_TAG2; + p[11] = CONFIG_CAN_STATUS_TAG3; + + for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) { + g_tx_buffer.tx_message.tx_data[i] = p[i]; + } + g_tx_buffer.valid = 1U; +} +} // namespace + +/** + * @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。 + */ +void canfd_init(void) +{ + can_gpio_init(); + can_deinit(CONFIG_CAN_INSTANCE); + can_nominal_init(); + can_fd_mode_init(); + can_filter_accept_all(); + g_rx_frame.valid = 0U; + g_tx_buffer.valid = 0U; +} + +/** + * @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。 + * @note 若当前没有待接收报文,直接返回。 + */ +void canfd_poll(void) +{ + can_receive_message_struct rx_message{}; + + if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) { + return; + } + + can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); + can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message); + g_can_diag.rx_count++; + g_can_diag.last_payload_length = rx_message.rx_dlen; + g_can_diag.last_rx_fd_flag = rx_message.fd_flag; + g_can_diag.last_rx_brs = rx_message.fd_brs; + g_can_diag.last_rx_esi = rx_message.fd_esi; + g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; + + g_rx_frame.id = g_can_diag.last_rx_id; + g_rx_frame.length = rx_message.rx_dlen; + for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) { + g_rx_frame.data[i] = rx_message.rx_data[i]; + } + g_rx_frame.valid = 1U; +} + +/** + * @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。 + * @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。 + */ +void canfd_prepare_status(const canfd_status_payload_t *status) +{ + if(status == nullptr) { + g_tx_buffer.valid = 0U; + return; + } + + canfd_build_status_frame(status); +} + +/** + * @brief 发送已准备好的 CANFD 状态报文。 + * @note 若当前没有有效待发送报文,直接返回。 + */ +void canfd_send_prepared(void) +{ + if(g_tx_buffer.valid == 0U) { + return; + } + + const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message); + if(mailbox != CAN_NOMAILBOX) { + g_can_diag.tx_count++; + g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen; + g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid; + } else { + g_can_diag.error_count++; + } +} + +/** + * @brief 查询是否存在尚未消费的接收报文。 + * @retval 非 0 存在待消费报文,0 无报文。 + */ +uint8_t canfd_has_rx_frame(void) +{ + return g_rx_frame.valid; +} + +/** + * @brief 读取并消费最近一次缓存的接收报文。 + * @param[out] id 输出报文标识符的指针;允许为空。 + * @param[out] data 输出报文数据缓存区指针;允许为空。 + * @param[out] length 输出报文数据长度的指针;允许为空。 + * @retval 1 成功消费一帧报文,0 当前无有效缓存报文。 + */ +uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length) +{ + if(g_rx_frame.valid == 0U) { + return 0U; + } + + if(id != nullptr) { + *id = g_rx_frame.id; + } + if(length != nullptr) { + *length = g_rx_frame.length; + } + if(data != nullptr) { + for(uint8_t i = 0; i < g_rx_frame.length; ++i) { + data[i] = g_rx_frame.data[i]; + } + } + + g_rx_frame.valid = 0U; + return 1U; +} + +/** + * @brief 获取 CANFD 诊断统计信息只读指针。 + * @retval 指向内部诊断统计结构体的只读指针。 + */ +const canfd_diag_t *canfd_get_diag(void) +{ + return &g_can_diag; +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/CANFD.cpp.bak_hideimpl b/GD32E508_DCU_APP/device/CANFD.cpp.bak_hideimpl new file mode 100644 index 0000000..8014881 --- /dev/null +++ b/GD32E508_DCU_APP/device/CANFD.cpp.bak_hideimpl @@ -0,0 +1,225 @@ +/** + * @file CANFD.cpp + * @brief CANFD 通信模块实现。 + */ + +#include "main.h" +#include "device/canfd.h" +#include "sys/config.h" + +namespace { +constexpr uint32_t kSlpGpio = GPIOA; +constexpr uint32_t kSlpPin = GPIO_PIN_8; +} // namespace + +CanFd::Diag CanFd::s_diag; +CanFd::RxFrame CanFd::s_rxFrame; +CanFd::TxBuffer CanFd::s_txBuffer; + +void CanFd::initGpio() +{ + rcu_periph_clock_enable(CONFIG_CAN_RCU); + rcu_periph_clock_enable(RCU_GPIOA); + rcu_periph_clock_enable(RCU_AF); + + gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11); + gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12); + gpio_init(kSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kSlpPin); +} + +void CanFd::filterAcceptAll() +{ + can_filter_parameter_struct can_filter{}; + can_filter.filter_number = 0U; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_list_high = 0x0000U; + can_filter.filter_list_low = 0x0000U; + can_filter.filter_mask_high = 0x0000U; + can_filter.filter_mask_low = 0x0000U; + can_filter.filter_fifo_number = CAN_FIFO0; + can_filter.filter_enable = ENABLE; + can_filter_init(CONFIG_CAN_INSTANCE, &can_filter); +} + +void CanFd::initNominalTiming() +{ + can_parameter_struct can_parameter{}; + + can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); + can_parameter.time_triggered = DISABLE; + can_parameter.auto_bus_off_recovery = ENABLE; + can_parameter.auto_wake_up = DISABLE; + can_parameter.auto_retrans = ENABLE; + can_parameter.rec_fifo_overwrite = ENABLE; + can_parameter.trans_fifo_order = ENABLE; + can_parameter.working_mode = CAN_NORMAL_MODE; + can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; + can_parameter.time_segment_1 = CAN_BT_BS1_7TQ; + can_parameter.time_segment_2 = CAN_BT_BS2_2TQ; + can_parameter.prescaler = 6U; + can_init(CONFIG_CAN_INSTANCE, &can_parameter); +} + +void CanFd::initFdMode() +{ +#ifdef GD32E508 + can_fdframe_struct can_fd_parameter{}; + can_fd_tdc_struct can_fd_tdc_parameter{}; + + can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); + can_fd_parameter.fd_frame = ENABLE; + can_fd_parameter.excp_event_detect = ENABLE; + can_fd_parameter.delay_compensation = ENABLE; + can_fd_tdc_parameter.tdc_filter = 0x04U; + can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET; + can_fd_tdc_parameter.tdc_offset = 0x04U; + can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter; + can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; + can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; + can_fd_parameter.data_prescaler = 6U; + can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ; + can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ; + can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ; + can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter); +#else + can_fd_function_enable(CONFIG_CAN_INSTANCE); +#endif +} + +void CanFd::buildStatusFrame(const StatusPayload *status) +{ + const int16_t temp_x100 = static_cast(status->temperature * 100.0f); + const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); + + can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &s_txBuffer.tx_message); + s_txBuffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; + s_txBuffer.tx_message.tx_efid = 0U; + s_txBuffer.tx_message.tx_ff = CAN_FF_STANDARD; + s_txBuffer.tx_message.tx_ft = CAN_FT_DATA; + s_txBuffer.tx_message.tx_dlen = CANFD_STATUS_DLEN; +#ifdef GD32E508 + s_txBuffer.tx_message.fd_flag = ENABLE; + s_txBuffer.tx_message.fd_brs = ENABLE; + s_txBuffer.tx_message.fd_esi = CAN_ESI_DOMINANT; +#endif + + auto &p = s_txBuffer.payload; + p[0] = static_cast(status->temp_raw & 0xFFU); + p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); + p[2] = static_cast(status->vref_raw & 0xFFU); + p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); + p[4] = static_cast(temp_x100 & 0xFF); + p[5] = static_cast((temp_x100 >> 8) & 0xFF); + p[6] = static_cast(vdda_x1000 & 0xFFU); + p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); + p[8] = CONFIG_CAN_STATUS_TAG0; + p[9] = CONFIG_CAN_STATUS_TAG1; + p[10] = CONFIG_CAN_STATUS_TAG2; + p[11] = CONFIG_CAN_STATUS_TAG3; + + for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) { + s_txBuffer.tx_message.tx_data[i] = p[i]; + } + s_txBuffer.valid = 1U; +} + +void CanFd::init() +{ + initGpio(); + setNormalMode(); + can_deinit(CONFIG_CAN_INSTANCE); + initNominalTiming(); + initFdMode(); + filterAcceptAll(); + + can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0); + nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U); + + s_rxFrame.valid = 0U; + s_txBuffer.valid = 0U; +} + +void CanFd::poll() +{ + /* 已由 CAN0_RX0_IRQHandler 中断驱动 */ +} + +void CanFd::prepareStatus(const StatusPayload *status) +{ + if(status == nullptr) { + s_txBuffer.valid = 0U; + return; + } + buildStatusFrame(status); +} + +void CanFd::sendPrepared() +{ + if(s_txBuffer.valid == 0U) return; + const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &s_txBuffer.tx_message); + if(mailbox != CAN_NOMAILBOX) { + s_diag.tx_count++; + s_diag.last_payload_length = s_txBuffer.tx_message.tx_dlen; + s_diag.last_tx_id = s_txBuffer.tx_message.tx_sfid; + } else { + s_diag.error_count++; + } +} + +bool CanFd::hasRxFrame() +{ + return s_rxFrame.valid != 0U; +} + +bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length) +{ + if(s_rxFrame.valid == 0U) return false; + if(id) *id = s_rxFrame.id; + if(length) *length = s_rxFrame.length; + if(data) { for(uint8_t i = 0; i < s_rxFrame.length; ++i) data[i] = s_rxFrame.data[i]; } + s_rxFrame.valid = 0U; + return true; +} + +const CanFd::Diag *CanFd::diag() +{ + return &s_diag; +} + +void CanFd::setNormalMode() +{ + gpio_bit_reset(kSlpGpio, kSlpPin); +} + +void CanFd::setSilentMode() +{ + gpio_bit_set(kSlpGpio, kSlpPin); +} + +void CanFd::onRx0Interrupt() +{ + if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) return; + + can_receive_message_struct rx_message{}; + can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); + can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message); + + s_diag.rx_count++; + s_diag.last_payload_length = rx_message.rx_dlen; + s_diag.last_rx_fd_flag = rx_message.fd_flag; + s_diag.last_rx_brs = rx_message.fd_brs; + s_diag.last_rx_esi = rx_message.fd_esi; + s_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; + + s_rxFrame.id = s_diag.last_rx_id; + s_rxFrame.length = rx_message.rx_dlen; + for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) + s_rxFrame.data[i] = rx_message.rx_data[i]; + s_rxFrame.valid = 1U; + + can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0); +} + +/* ----- 中断入口 C 包装 ------------------------------------------------- */ +extern "C" void CAN0_RX0_IRQHandler(void) { CanFd::onRx0Interrupt(); } \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/CANFD.cpp.bak_oop b/GD32E508_DCU_APP/device/CANFD.cpp.bak_oop new file mode 100644 index 0000000..0c6674a --- /dev/null +++ b/GD32E508_DCU_APP/device/CANFD.cpp.bak_oop @@ -0,0 +1,326 @@ +/** + * @file CANFD.cpp + * @brief CANFD 通信外设初始化、收发与诊断实现。 + */ + +#include "main.h" +#include "device/canfd.h" +#include "sys/config.h" + +namespace { +constexpr uint32_t kCanSlpGpio = GPIOA; +constexpr uint32_t kCanSlpPin = GPIO_PIN_8; + +/** @brief 内部接收帧缓存结构。 */ +struct CanfdRxFrame { + uint32_t id = 0U; + uint8_t data[64] = {0}; + uint8_t length = 0U; + uint8_t valid = 0U; +}; + +/** @brief 内部发送帧缓存结构。 */ +struct CanfdTxBuffer { + can_trasnmit_message_struct tx_message{}; + uint8_t payload[64] = {0}; + uint8_t valid = 0U; +}; + +canfd_diag_t g_can_diag{}; +CanfdRxFrame g_rx_frame{}; +CanfdTxBuffer g_tx_buffer{}; + +/** + * @brief 初始化 CAN0 外设所需 GPIO 引脚。 + * @note 按原理图连接关系配置: + * - PA12 -> CAN0_TX + * - PA11 -> CAN0_RX + * - PA8 -> CAN0_SLP(连接 TCAN1057A #0 模式控制脚) + */ +inline void can_gpio_init() +{ + rcu_periph_clock_enable(CONFIG_CAN_RCU); + rcu_periph_clock_enable(RCU_GPIOA); + rcu_periph_clock_enable(RCU_AF); + + gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11); + gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12); + gpio_init(kCanSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kCanSlpPin); +} + +/** + * @brief 将 TCAN1057A #0 置为正常工作模式。 + * @note 模式控制脚按 STB 语义处理:低电平为 Normal,高电平为 Silent。 + */ +inline void can_transceiver_normal_mode() +{ + gpio_bit_reset(kCanSlpGpio, kCanSlpPin); +} + +/** + * @brief 将 TCAN1057A #0 置为静默模式。 + * @note 静默模式下收发器不驱动总线显性位,可用于总线监听或受控隔离场景。 + */ +inline void can_transceiver_silent_mode() +{ + gpio_bit_set(kCanSlpGpio, kCanSlpPin); +} + +/** + * @brief 配置 CAN 接收过滤器为接受所有报文。 + */ +inline void can_filter_accept_all() +{ + can_filter_parameter_struct can_filter{}; + can_filter.filter_number = 0U; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_list_high = 0x0000U; + can_filter.filter_list_low = 0x0000U; + can_filter.filter_mask_high = 0x0000U; + can_filter.filter_mask_low = 0x0000U; + can_filter.filter_fifo_number = CAN_FIFO0; + can_filter.filter_enable = ENABLE; + can_filter_init(CONFIG_CAN_INSTANCE, &can_filter); +} + +/** + * @brief 配置 CAN 标称位定时参数。 + * @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。 + */ +inline void can_nominal_init() +{ + can_parameter_struct can_parameter{}; + + can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); + can_parameter.time_triggered = DISABLE; + can_parameter.auto_bus_off_recovery = ENABLE; + can_parameter.auto_wake_up = DISABLE; + can_parameter.auto_retrans = ENABLE; + can_parameter.rec_fifo_overwrite = ENABLE; + can_parameter.trans_fifo_order = ENABLE; + can_parameter.working_mode = CAN_NORMAL_MODE; + can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; + can_parameter.time_segment_1 = CAN_BT_BS1_7TQ; + can_parameter.time_segment_2 = CAN_BT_BS2_2TQ; + can_parameter.prescaler = 6U; + can_init(CONFIG_CAN_INSTANCE, &can_parameter); +} + +/** + * @brief 配置 CANFD 可变速率模式参数。 + * @note 参考 APP 项目配置: + * - 仲裁段保持 1 Mbps; + * - 数据段配置为 2 Mbps,采样点约 80%; + * - 发送时启用 BRS 后才会切到数据段速率。 + */ +inline void can_fd_mode_init() +{ +#ifdef GD32E508 + can_fdframe_struct can_fd_parameter{}; + can_fd_tdc_struct can_fd_tdc_parameter{}; + + can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); + can_fd_parameter.fd_frame = ENABLE; + can_fd_parameter.excp_event_detect = ENABLE; + can_fd_parameter.delay_compensation = ENABLE; + can_fd_tdc_parameter.tdc_filter = 0x04U; + can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET; + can_fd_tdc_parameter.tdc_offset = 0x04U; + can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter; + can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; + can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; + can_fd_parameter.data_prescaler = 6U; + can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ; + can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ; + can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ; + can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter); +#else + can_fd_function_enable(CONFIG_CAN_INSTANCE); +#endif +} + +/** + * @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。 + * @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。 + */ +inline void canfd_build_status_frame(const canfd_status_payload_t *status) +{ + const int16_t temp_x100 = static_cast(status->temperature * 100.0f); + const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); + + can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message); + g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; + g_tx_buffer.tx_message.tx_efid = 0U; + g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD; + g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA; + g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN; +#ifdef GD32E508 + g_tx_buffer.tx_message.fd_flag = ENABLE; + g_tx_buffer.tx_message.fd_brs = ENABLE; + g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT; +#endif + + auto &p = g_tx_buffer.payload; + p[0] = static_cast(status->temp_raw & 0xFFU); + p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); + p[2] = static_cast(status->vref_raw & 0xFFU); + p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); + p[4] = static_cast(temp_x100 & 0xFF); + p[5] = static_cast((temp_x100 >> 8) & 0xFF); + p[6] = static_cast(vdda_x1000 & 0xFFU); + p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); + p[8] = CONFIG_CAN_STATUS_TAG0; + p[9] = CONFIG_CAN_STATUS_TAG1; + p[10] = CONFIG_CAN_STATUS_TAG2; + p[11] = CONFIG_CAN_STATUS_TAG3; + + for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) { + g_tx_buffer.tx_message.tx_data[i] = p[i]; + } + g_tx_buffer.valid = 1U; +} +} // namespace + +/** + * @brief CAN0 RX0 中断服务例程。 + * @note 在 CANFD 帧到达 FIFO0 时自动执行,将报文缓存到内部接收帧。 + */ +extern "C" void CAN0_RX0_IRQHandler(void) +{ + if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) { + return; + } + + can_receive_message_struct rx_message{}; + can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); + can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message); + + g_can_diag.rx_count++; + g_can_diag.last_payload_length = rx_message.rx_dlen; + g_can_diag.last_rx_fd_flag = rx_message.fd_flag; + g_can_diag.last_rx_brs = rx_message.fd_brs; + g_can_diag.last_rx_esi = rx_message.fd_esi; + g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; + + g_rx_frame.id = g_can_diag.last_rx_id; + g_rx_frame.length = rx_message.rx_dlen; + for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) { + g_rx_frame.data[i] = rx_message.rx_data[i]; + } + g_rx_frame.valid = 1U; + + can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0); +} + +/** + * @brief 初始化 CANFD 外设、GPIO、波特率参数、接收过滤器与中断。 + * @note 启用 FIFO0 RX 中断替代轮询,TX 保持 mailbox 异步发送;GPIO 与原理图一致使用 PA11/PA12/PA8。 + * 其中 PA8 已封装为正常/静默两个模式控制函数,初始化时默认进入 Normal 模式。 + */ +void canfd_init(void) +{ + can_gpio_init(); + can_transceiver_normal_mode(); + can_deinit(CONFIG_CAN_INSTANCE); + can_nominal_init(); + can_fd_mode_init(); + can_filter_accept_all(); + + /* 使能 FIFO0 接收中断,替代轮询 */ + can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0); + nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U); + + g_rx_frame.valid = 0U; + g_tx_buffer.valid = 0U; +} + +/** + * @brief 轮询 CANFD 接收 FIFO(中断模式下退化为兼容空壳)。 + * @note 实际接收已由 CAN0_RX0_IRQHandler 以中断方式完成,此函数保留接口不破坏现有调用链。 + */ +void canfd_poll(void) +{ + /* 接收已由 CAN0_RX0_IRQHandler 中断驱动 */ +} + +/** + * @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。 + * @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。 + */ +void canfd_prepare_status(const canfd_status_payload_t *status) +{ + if(status == nullptr) { + g_tx_buffer.valid = 0U; + return; + } + + canfd_build_status_frame(status); +} + +/** + * @brief 发送已准备好的 CANFD 状态报文。 + * @note 若当前没有有效待发送报文,直接返回。 + */ +void canfd_send_prepared(void) +{ + if(g_tx_buffer.valid == 0U) { + return; + } + + const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message); + if(mailbox != CAN_NOMAILBOX) { + g_can_diag.tx_count++; + g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen; + g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid; + } else { + g_can_diag.error_count++; + } +} + +/** + * @brief 查询是否存在尚未消费的接收报文。 + * @retval 非 0 存在待消费报文,0 无报文。 + */ +uint8_t canfd_has_rx_frame(void) +{ + return g_rx_frame.valid; +} + +/** + * @brief 读取并消费最近一次缓存的接收报文。 + * @param[out] id 输出报文标识符的指针;允许为空。 + * @param[out] data 输出报文数据缓存区指针;允许为空。 + * @param[out] length 输出报文数据长度的指针;允许为空。 + * @retval 1 成功消费一帧报文,0 当前无有效缓存报文。 + */ +uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length) +{ + if(g_rx_frame.valid == 0U) { + return 0U; + } + + if(id != nullptr) { + *id = g_rx_frame.id; + } + if(length != nullptr) { + *length = g_rx_frame.length; + } + if(data != nullptr) { + for(uint8_t i = 0; i < g_rx_frame.length; ++i) { + data[i] = g_rx_frame.data[i]; + } + } + + g_rx_frame.valid = 0U; + return 1U; +} + +/** + * @brief 获取 CANFD 诊断统计信息只读指针。 + * @retval 指向内部诊断统计结构体的只读指针。 + */ +const canfd_diag_t *canfd_get_diag(void) +{ + return &g_can_diag; +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/CANFD.cpp.bak_pa8mode b/GD32E508_DCU_APP/device/CANFD.cpp.bak_pa8mode new file mode 100644 index 0000000..5708b56 --- /dev/null +++ b/GD32E508_DCU_APP/device/CANFD.cpp.bak_pa8mode @@ -0,0 +1,319 @@ +/** + * @file CANFD.cpp + * @brief CANFD 通信外设初始化、收发与诊断实现。 + */ + +#include "main.h" +#include "device/canfd.h" +#include "sys/config.h" + +namespace { +constexpr uint32_t kCanSlpGpio = GPIOA; +constexpr uint32_t kCanSlpPin = GPIO_PIN_8; + +/** @brief 内部接收帧缓存结构。 */ +struct CanfdRxFrame { + uint32_t id = 0U; + uint8_t data[64] = {0}; + uint8_t length = 0U; + uint8_t valid = 0U; +}; + +/** @brief 内部发送帧缓存结构。 */ +struct CanfdTxBuffer { + can_trasnmit_message_struct tx_message{}; + uint8_t payload[64] = {0}; + uint8_t valid = 0U; +}; + +canfd_diag_t g_can_diag{}; +CanfdRxFrame g_rx_frame{}; +CanfdTxBuffer g_tx_buffer{}; + +/** + * @brief 初始化 CAN0 外设所需 GPIO 引脚。 + * @note 按原理图连接关系配置: + * - PA12 -> CAN0_TX + * - PA11 -> CAN0_RX + * - PA8 -> CAN0_SLP(连接 TCAN1057A #0 模式控制脚) + */ +inline void can_gpio_init() +{ + rcu_periph_clock_enable(CONFIG_CAN_RCU); + rcu_periph_clock_enable(RCU_GPIOA); + rcu_periph_clock_enable(RCU_AF); + + gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11); + gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12); + gpio_init(kCanSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kCanSlpPin); +} + +/** + * @brief 将 TCAN1057A #0 置为正常工作模式。 + * @note 结合 TCAN1057A-Q1 产品资料:器件工作模式为 Normal / Silent, + * 当前原理图网络名虽然写作 CAN0_SLP,但本质是模式控制脚; + * 这里按 STB=0 -> Normal,STB=1 -> Silent 处理,因此 PA8 拉低进入正常模式。 + */ +inline void can_transceiver_normal_mode() +{ + gpio_bit_reset(kCanSlpGpio, kCanSlpPin); +} + +/** + * @brief 配置 CAN 接收过滤器为接受所有报文。 + */ +inline void can_filter_accept_all() +{ + can_filter_parameter_struct can_filter{}; + can_filter.filter_number = 0U; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_list_high = 0x0000U; + can_filter.filter_list_low = 0x0000U; + can_filter.filter_mask_high = 0x0000U; + can_filter.filter_mask_low = 0x0000U; + can_filter.filter_fifo_number = CAN_FIFO0; + can_filter.filter_enable = ENABLE; + can_filter_init(CONFIG_CAN_INSTANCE, &can_filter); +} + +/** + * @brief 配置 CAN 标称位定时参数。 + * @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。 + */ +inline void can_nominal_init() +{ + can_parameter_struct can_parameter{}; + + can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); + can_parameter.time_triggered = DISABLE; + can_parameter.auto_bus_off_recovery = ENABLE; + can_parameter.auto_wake_up = DISABLE; + can_parameter.auto_retrans = ENABLE; + can_parameter.rec_fifo_overwrite = ENABLE; + can_parameter.trans_fifo_order = ENABLE; + can_parameter.working_mode = CAN_NORMAL_MODE; + can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; + can_parameter.time_segment_1 = CAN_BT_BS1_7TQ; + can_parameter.time_segment_2 = CAN_BT_BS2_2TQ; + can_parameter.prescaler = 6U; + can_init(CONFIG_CAN_INSTANCE, &can_parameter); +} + +/** + * @brief 配置 CANFD 可变速率模式参数。 + * @note 参考 APP 项目配置: + * - 仲裁段保持 1 Mbps; + * - 数据段配置为 2 Mbps,采样点约 80%; + * - 发送时启用 BRS 后才会切到数据段速率。 + */ +inline void can_fd_mode_init() +{ +#ifdef GD32E508 + can_fdframe_struct can_fd_parameter{}; + can_fd_tdc_struct can_fd_tdc_parameter{}; + + can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); + can_fd_parameter.fd_frame = ENABLE; + can_fd_parameter.excp_event_detect = ENABLE; + can_fd_parameter.delay_compensation = ENABLE; + can_fd_tdc_parameter.tdc_filter = 0x04U; + can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET; + can_fd_tdc_parameter.tdc_offset = 0x04U; + can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter; + can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; + can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; + can_fd_parameter.data_prescaler = 6U; + can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ; + can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ; + can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ; + can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter); +#else + can_fd_function_enable(CONFIG_CAN_INSTANCE); +#endif +} + +/** + * @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。 + * @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。 + */ +inline void canfd_build_status_frame(const canfd_status_payload_t *status) +{ + const int16_t temp_x100 = static_cast(status->temperature * 100.0f); + const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); + + can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message); + g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; + g_tx_buffer.tx_message.tx_efid = 0U; + g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD; + g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA; + g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN; +#ifdef GD32E508 + g_tx_buffer.tx_message.fd_flag = ENABLE; + g_tx_buffer.tx_message.fd_brs = ENABLE; + g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT; +#endif + + auto &p = g_tx_buffer.payload; + p[0] = static_cast(status->temp_raw & 0xFFU); + p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); + p[2] = static_cast(status->vref_raw & 0xFFU); + p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); + p[4] = static_cast(temp_x100 & 0xFF); + p[5] = static_cast((temp_x100 >> 8) & 0xFF); + p[6] = static_cast(vdda_x1000 & 0xFFU); + p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); + p[8] = CONFIG_CAN_STATUS_TAG0; + p[9] = CONFIG_CAN_STATUS_TAG1; + p[10] = CONFIG_CAN_STATUS_TAG2; + p[11] = CONFIG_CAN_STATUS_TAG3; + + for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) { + g_tx_buffer.tx_message.tx_data[i] = p[i]; + } + g_tx_buffer.valid = 1U; +} +} // namespace + +/** + * @brief CAN0 RX0 中断服务例程。 + * @note 在 CANFD 帧到达 FIFO0 时自动执行,将报文缓存到内部接收帧。 + */ +extern "C" void CAN0_RX0_IRQHandler(void) +{ + if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) { + return; + } + + can_receive_message_struct rx_message{}; + can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); + can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message); + + g_can_diag.rx_count++; + g_can_diag.last_payload_length = rx_message.rx_dlen; + g_can_diag.last_rx_fd_flag = rx_message.fd_flag; + g_can_diag.last_rx_brs = rx_message.fd_brs; + g_can_diag.last_rx_esi = rx_message.fd_esi; + g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; + + g_rx_frame.id = g_can_diag.last_rx_id; + g_rx_frame.length = rx_message.rx_dlen; + for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) { + g_rx_frame.data[i] = rx_message.rx_data[i]; + } + g_rx_frame.valid = 1U; + + can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0); +} + +/** + * @brief 初始化 CANFD 外设、GPIO、波特率参数、接收过滤器与中断。 + * @note 启用 FIFO0 RX 中断替代轮询,TX 保持 mailbox 异步发送;GPIO 与原理图一致使用 PA11/PA12/PA8。 + * 其中 PA8 按 TCAN1057A 的模式控制脚处理:低电平为 Normal,高电平为 Silent。 + */ +void canfd_init(void) +{ + can_gpio_init(); + can_transceiver_normal_mode(); + can_deinit(CONFIG_CAN_INSTANCE); + can_nominal_init(); + can_fd_mode_init(); + can_filter_accept_all(); + + /* 使能 FIFO0 接收中断,替代轮询 */ + can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0); + nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U); + + g_rx_frame.valid = 0U; + g_tx_buffer.valid = 0U; +} + +/** + * @brief 轮询 CANFD 接收 FIFO(中断模式下退化为兼容空壳)。 + * @note 实际接收已由 CAN0_RX0_IRQHandler 以中断方式完成,此函数保留接口不破坏现有调用链。 + */ +void canfd_poll(void) +{ + /* 接收已由 CAN0_RX0_IRQHandler 中断驱动 */ +} + +/** + * @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。 + * @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。 + */ +void canfd_prepare_status(const canfd_status_payload_t *status) +{ + if(status == nullptr) { + g_tx_buffer.valid = 0U; + return; + } + + canfd_build_status_frame(status); +} + +/** + * @brief 发送已准备好的 CANFD 状态报文。 + * @note 若当前没有有效待发送报文,直接返回。 + */ +void canfd_send_prepared(void) +{ + if(g_tx_buffer.valid == 0U) { + return; + } + + const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message); + if(mailbox != CAN_NOMAILBOX) { + g_can_diag.tx_count++; + g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen; + g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid; + } else { + g_can_diag.error_count++; + } +} + +/** + * @brief 查询是否存在尚未消费的接收报文。 + * @retval 非 0 存在待消费报文,0 无报文。 + */ +uint8_t canfd_has_rx_frame(void) +{ + return g_rx_frame.valid; +} + +/** + * @brief 读取并消费最近一次缓存的接收报文。 + * @param[out] id 输出报文标识符的指针;允许为空。 + * @param[out] data 输出报文数据缓存区指针;允许为空。 + * @param[out] length 输出报文数据长度的指针;允许为空。 + * @retval 1 成功消费一帧报文,0 当前无有效缓存报文。 + */ +uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length) +{ + if(g_rx_frame.valid == 0U) { + return 0U; + } + + if(id != nullptr) { + *id = g_rx_frame.id; + } + if(length != nullptr) { + *length = g_rx_frame.length; + } + if(data != nullptr) { + for(uint8_t i = 0; i < g_rx_frame.length; ++i) { + data[i] = g_rx_frame.data[i]; + } + } + + g_rx_frame.valid = 0U; + return 1U; +} + +/** + * @brief 获取 CANFD 诊断统计信息只读指针。 + * @retval 指向内部诊断统计结构体的只读指针。 + */ +const canfd_diag_t *canfd_get_diag(void) +{ + return &g_can_diag; +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/CANFD.cpp.bak_sch b/GD32E508_DCU_APP/device/CANFD.cpp.bak_sch new file mode 100644 index 0000000..8da69f4 --- /dev/null +++ b/GD32E508_DCU_APP/device/CANFD.cpp.bak_sch @@ -0,0 +1,300 @@ +/** + * @file CANFD.cpp + * @brief CANFD 通信外设初始化、收发与诊断实现。 + */ + +#include "main.h" +#include "device/canfd.h" +#include "sys/config.h" + +namespace { + +/** @brief 内部接收帧缓存结构。 */ +struct CanfdRxFrame { + uint32_t id = 0U; + uint8_t data[64] = {0}; + uint8_t length = 0U; + uint8_t valid = 0U; +}; + +/** @brief 内部发送帧缓存结构。 */ +struct CanfdTxBuffer { + can_trasnmit_message_struct tx_message{}; + uint8_t payload[64] = {0}; + uint8_t valid = 0U; +}; + +canfd_diag_t g_can_diag{}; +CanfdRxFrame g_rx_frame{}; +CanfdTxBuffer g_tx_buffer{}; + +/** + * @brief 初始化 CAN 外设所需 GPIO 引脚。 + */ +inline void can_gpio_init() +{ + rcu_periph_clock_enable(CONFIG_CAN_RCU); + rcu_periph_clock_enable(RCU_GPIOB); + rcu_periph_clock_enable(RCU_AF); + + gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8); + gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); + gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE); +} + +/** + * @brief 配置 CAN 接收过滤器为接受所有报文。 + */ +inline void can_filter_accept_all() +{ + can_filter_parameter_struct can_filter{}; + can_filter.filter_number = 0U; + can_filter.filter_mode = CAN_FILTERMODE_MASK; + can_filter.filter_bits = CAN_FILTERBITS_32BIT; + can_filter.filter_list_high = 0x0000U; + can_filter.filter_list_low = 0x0000U; + can_filter.filter_mask_high = 0x0000U; + can_filter.filter_mask_low = 0x0000U; + can_filter.filter_fifo_number = CAN_FIFO0; + can_filter.filter_enable = ENABLE; + can_filter_init(CONFIG_CAN_INSTANCE, &can_filter); +} + +/** + * @brief 配置 CAN 标称位定时参数。 + * @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。 + */ +inline void can_nominal_init() +{ + can_parameter_struct can_parameter{}; + + can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); + can_parameter.time_triggered = DISABLE; + can_parameter.auto_bus_off_recovery = ENABLE; + can_parameter.auto_wake_up = DISABLE; + can_parameter.auto_retrans = ENABLE; + can_parameter.rec_fifo_overwrite = ENABLE; + can_parameter.trans_fifo_order = ENABLE; + can_parameter.working_mode = CAN_NORMAL_MODE; + can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; + can_parameter.time_segment_1 = CAN_BT_BS1_7TQ; + can_parameter.time_segment_2 = CAN_BT_BS2_2TQ; + can_parameter.prescaler = 6U; + can_init(CONFIG_CAN_INSTANCE, &can_parameter); +} + +/** + * @brief 配置 CANFD 可变速率模式参数。 + * @note 参考 APP 项目配置: + * - 仲裁段保持 1 Mbps; + * - 数据段配置为 2 Mbps,采样点约 80%; + * - 发送时启用 BRS 后才会切到数据段速率。 + */ +inline void can_fd_mode_init() +{ +#ifdef GD32E508 + can_fdframe_struct can_fd_parameter{}; + can_fd_tdc_struct can_fd_tdc_parameter{}; + + can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); + can_fd_parameter.fd_frame = ENABLE; + can_fd_parameter.excp_event_detect = ENABLE; + can_fd_parameter.delay_compensation = ENABLE; + can_fd_tdc_parameter.tdc_filter = 0x04U; + can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET; + can_fd_tdc_parameter.tdc_offset = 0x04U; + can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter; + can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; + can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; + can_fd_parameter.data_prescaler = 6U; + can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ; + can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ; + can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ; + can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter); +#else + can_fd_function_enable(CONFIG_CAN_INSTANCE); +#endif +} + +/** + * @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。 + * @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。 + */ +inline void canfd_build_status_frame(const canfd_status_payload_t *status) +{ + const int16_t temp_x100 = static_cast(status->temperature * 100.0f); + const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); + + can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message); + g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; + g_tx_buffer.tx_message.tx_efid = 0U; + g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD; + g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA; + g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN; +#ifdef GD32E508 + g_tx_buffer.tx_message.fd_flag = ENABLE; + g_tx_buffer.tx_message.fd_brs = ENABLE; + g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT; +#endif + + auto &p = g_tx_buffer.payload; + p[0] = static_cast(status->temp_raw & 0xFFU); + p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); + p[2] = static_cast(status->vref_raw & 0xFFU); + p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); + p[4] = static_cast(temp_x100 & 0xFF); + p[5] = static_cast((temp_x100 >> 8) & 0xFF); + p[6] = static_cast(vdda_x1000 & 0xFFU); + p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); + p[8] = CONFIG_CAN_STATUS_TAG0; + p[9] = CONFIG_CAN_STATUS_TAG1; + p[10] = CONFIG_CAN_STATUS_TAG2; + p[11] = CONFIG_CAN_STATUS_TAG3; + + for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) { + g_tx_buffer.tx_message.tx_data[i] = p[i]; + } + g_tx_buffer.valid = 1U; +} +} // namespace + +/** + * @brief CAN0 RX0 中断服务例程。 + * @note 在 CANFD 帧到达 FIFO0 时自动执行,将报文缓存到内部接收帧。 + */ +extern "C" void CAN0_RX0_IRQHandler(void) +{ + if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) { + return; + } + + can_receive_message_struct rx_message{}; + can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); + can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message); + + g_can_diag.rx_count++; + g_can_diag.last_payload_length = rx_message.rx_dlen; + g_can_diag.last_rx_fd_flag = rx_message.fd_flag; + g_can_diag.last_rx_brs = rx_message.fd_brs; + g_can_diag.last_rx_esi = rx_message.fd_esi; + g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; + + g_rx_frame.id = g_can_diag.last_rx_id; + g_rx_frame.length = rx_message.rx_dlen; + for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) { + g_rx_frame.data[i] = rx_message.rx_data[i]; + } + g_rx_frame.valid = 1U; + + can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0); +} + +/** + * @brief 初始化 CANFD 外设、GPIO、波特率参数、接收过滤器与中断。 + * @note 启用 FIFO0 RX 中断替代轮询,TX 保持 mailbox 异步发送。 + */ +void canfd_init(void) +{ + can_gpio_init(); + can_deinit(CONFIG_CAN_INSTANCE); + can_nominal_init(); + can_fd_mode_init(); + can_filter_accept_all(); + + /* 使能 FIFO0 接收中断,替代轮询 */ + can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0); + nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U); + + g_rx_frame.valid = 0U; + g_tx_buffer.valid = 0U; +} + +/** + * @brief 轮询 CANFD 接收 FIFO(中断模式下退化为兼容空壳)。 + * @note 实际接收已由 CAN0_RX0_IRQHandler 以中断方式完成,此函数保留接口不破坏现有调用链。 + */ +void canfd_poll(void) +{ + /* 接收已由 CAN0_RX0_IRQHandler 中断驱动 */ +} + +/** + * @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。 + * @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。 + */ +void canfd_prepare_status(const canfd_status_payload_t *status) +{ + if(status == nullptr) { + g_tx_buffer.valid = 0U; + return; + } + + canfd_build_status_frame(status); +} + +/** + * @brief 发送已准备好的 CANFD 状态报文。 + * @note 若当前没有有效待发送报文,直接返回。 + */ +void canfd_send_prepared(void) +{ + if(g_tx_buffer.valid == 0U) { + return; + } + + const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message); + if(mailbox != CAN_NOMAILBOX) { + g_can_diag.tx_count++; + g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen; + g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid; + } else { + g_can_diag.error_count++; + } +} + +/** + * @brief 查询是否存在尚未消费的接收报文。 + * @retval 非 0 存在待消费报文,0 无报文。 + */ +uint8_t canfd_has_rx_frame(void) +{ + return g_rx_frame.valid; +} + +/** + * @brief 读取并消费最近一次缓存的接收报文。 + * @param[out] id 输出报文标识符的指针;允许为空。 + * @param[out] data 输出报文数据缓存区指针;允许为空。 + * @param[out] length 输出报文数据长度的指针;允许为空。 + * @retval 1 成功消费一帧报文,0 当前无有效缓存报文。 + */ +uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length) +{ + if(g_rx_frame.valid == 0U) { + return 0U; + } + + if(id != nullptr) { + *id = g_rx_frame.id; + } + if(length != nullptr) { + *length = g_rx_frame.length; + } + if(data != nullptr) { + for(uint8_t i = 0; i < g_rx_frame.length; ++i) { + data[i] = g_rx_frame.data[i]; + } + } + + g_rx_frame.valid = 0U; + return 1U; +} + +/** + * @brief 获取 CANFD 诊断统计信息只读指针。 + * @retval 指向内部诊断统计结构体的只读指针。 + */ +const canfd_diag_t *canfd_get_diag(void) +{ + return &g_can_diag; +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/Timer.cpp b/GD32E508_DCU_APP/device/Timer.cpp new file mode 100644 index 0000000..343c384 --- /dev/null +++ b/GD32E508_DCU_APP/device/Timer.cpp @@ -0,0 +1,51 @@ +/** + * @file Timer.cpp + * @brief 用户调度定时器模块实现。 + */ + +#include "main.h" +#include "device/Timer.h" +#include "sys/task_manager.h" +#include "sys/config.h" + +void SchedulerTimer::configureTimer() +{ + timer_parameter_struct timer_initpara; + + rcu_periph_clock_enable(CONFIG_SCHED_TIMER_RCU); + timer_deinit(CONFIG_SCHED_TIMER_INSTANCE); + timer_struct_para_init(&timer_initpara); + + timer_initpara.prescaler = CONFIG_SCHED_TIMER_PRESCALER; + timer_initpara.alignedmode = TIMER_COUNTER_EDGE; + timer_initpara.counterdirection = TIMER_COUNTER_UP; + timer_initpara.period = CONFIG_SCHED_TIMER_PERIOD; + timer_initpara.clockdivision = TIMER_CKDIV_DIV1; + timer_init(CONFIG_SCHED_TIMER_INSTANCE, &timer_initpara); + + timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP); + timer_interrupt_enable(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_UP); + timer_enable(CONFIG_SCHED_TIMER_INSTANCE); +} + +void SchedulerTimer::configureNvic() +{ + nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); + nvic_irq_enable(CONFIG_SCHED_TIMER_IRQn, CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO, CONFIG_SCHED_TIMER_IRQ_SUB_PRIO); +} + +void SchedulerTimer::init() +{ + configureTimer(); + configureNvic(); +} + +void SchedulerTimer::onInterrupt() +{ + if(timer_interrupt_flag_get(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP) == RESET) return; + timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP); + TaskManager::tick100us(); +} + +/* ----- 中断入口 C 包装 ---------------------------------------------- */ +void TIMER2_IRQHandler(void) { SchedulerTimer::onInterrupt(); } \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/Timer.cpp.bak_dma b/GD32E508_DCU_APP/device/Timer.cpp.bak_dma new file mode 100644 index 0000000..45c6af3 --- /dev/null +++ b/GD32E508_DCU_APP/device/Timer.cpp.bak_dma @@ -0,0 +1,65 @@ +/** + * @file Timer.cpp + * @brief 用户定时器初始化与中断处理实现。 + */ + +#include "main.h" +#include "device/Timer.h" +#include "sys/task_manager.h" +#include "sys/config.h" + +/** + * @brief 配置定时器 2 参数:180MHz / 180 / 100 → 100us 中断周期。 + */ +static void timer2_config(void) +{ + timer_parameter_struct timer_initpara; + + rcu_periph_clock_enable(CONFIG_SCHED_TIMER_RCU); + timer_deinit(CONFIG_SCHED_TIMER_INSTANCE); + timer_struct_para_init(&timer_initpara); + + timer_initpara.prescaler = CONFIG_SCHED_TIMER_PRESCALER; + timer_initpara.alignedmode = TIMER_COUNTER_EDGE; + timer_initpara.counterdirection = TIMER_COUNTER_UP; + timer_initpara.period = CONFIG_SCHED_TIMER_PERIOD; + timer_initpara.clockdivision = TIMER_CKDIV_DIV1; + timer_init(CONFIG_SCHED_TIMER_INSTANCE, &timer_initpara); + + timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP); + timer_interrupt_enable(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_UP); + timer_enable(CONFIG_SCHED_TIMER_INSTANCE); +} + +/** + * @brief 配置定时器 2 的中断优先级并使能中断。 + */ +static void timer2_nvic_config(void) +{ + nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); + nvic_irq_enable(CONFIG_SCHED_TIMER_IRQn, CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO, CONFIG_SCHED_TIMER_IRQ_SUB_PRIO); +} + +/** + * @brief 初始化用户定时器及其中断配置。 + * @note 当前用于建立 100us 基准节拍,驱动任务调度器时基。 + */ +void user_timer_init(void) +{ + timer2_config(); + timer2_nvic_config(); +} + +/** + * @brief 定时器 2 中断服务例程。 + * @note 每次计数溢出时清除标志并推进一次 100us 任务调度节拍。 + */ +void TIMER2_IRQHandler(void) +{ + if(timer_interrupt_flag_get(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP) == RESET) { + return; + } + + timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP); + task_manager_tick_100us(); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/Timer.cpp.bak_oop b/GD32E508_DCU_APP/device/Timer.cpp.bak_oop new file mode 100644 index 0000000..45c6af3 --- /dev/null +++ b/GD32E508_DCU_APP/device/Timer.cpp.bak_oop @@ -0,0 +1,65 @@ +/** + * @file Timer.cpp + * @brief 用户定时器初始化与中断处理实现。 + */ + +#include "main.h" +#include "device/Timer.h" +#include "sys/task_manager.h" +#include "sys/config.h" + +/** + * @brief 配置定时器 2 参数:180MHz / 180 / 100 → 100us 中断周期。 + */ +static void timer2_config(void) +{ + timer_parameter_struct timer_initpara; + + rcu_periph_clock_enable(CONFIG_SCHED_TIMER_RCU); + timer_deinit(CONFIG_SCHED_TIMER_INSTANCE); + timer_struct_para_init(&timer_initpara); + + timer_initpara.prescaler = CONFIG_SCHED_TIMER_PRESCALER; + timer_initpara.alignedmode = TIMER_COUNTER_EDGE; + timer_initpara.counterdirection = TIMER_COUNTER_UP; + timer_initpara.period = CONFIG_SCHED_TIMER_PERIOD; + timer_initpara.clockdivision = TIMER_CKDIV_DIV1; + timer_init(CONFIG_SCHED_TIMER_INSTANCE, &timer_initpara); + + timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP); + timer_interrupt_enable(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_UP); + timer_enable(CONFIG_SCHED_TIMER_INSTANCE); +} + +/** + * @brief 配置定时器 2 的中断优先级并使能中断。 + */ +static void timer2_nvic_config(void) +{ + nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); + nvic_irq_enable(CONFIG_SCHED_TIMER_IRQn, CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO, CONFIG_SCHED_TIMER_IRQ_SUB_PRIO); +} + +/** + * @brief 初始化用户定时器及其中断配置。 + * @note 当前用于建立 100us 基准节拍,驱动任务调度器时基。 + */ +void user_timer_init(void) +{ + timer2_config(); + timer2_nvic_config(); +} + +/** + * @brief 定时器 2 中断服务例程。 + * @note 每次计数溢出时清除标志并推进一次 100us 任务调度节拍。 + */ +void TIMER2_IRQHandler(void) +{ + if(timer_interrupt_flag_get(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP) == RESET) { + return; + } + + timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP); + task_manager_tick_100us(); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/Timer.h b/GD32E508_DCU_APP/device/Timer.h new file mode 100644 index 0000000..5f25164 --- /dev/null +++ b/GD32E508_DCU_APP/device/Timer.h @@ -0,0 +1,18 @@ +#ifndef DEVICE_TIMER_H +#define DEVICE_TIMER_H + +/** + * @brief 用户调度定时器模块。 + * @note 负责建立 100us 基准节拍,并通过中断驱动任务调度器。 + */ +class SchedulerTimer { +public: + static void init(); + static void onInterrupt(); + +private: + static void configureTimer(); + static void configureNvic(); +}; + +#endif /* DEVICE_TIMER_H */ diff --git a/GD32E508_DCU_APP/device/Timer.h.bak_oop b/GD32E508_DCU_APP/device/Timer.h.bak_oop new file mode 100644 index 0000000..f0a5135 --- /dev/null +++ b/GD32E508_DCU_APP/device/Timer.h.bak_oop @@ -0,0 +1,17 @@ +/** + * @file Timer.h + * @brief 用户定时器初始化接口定义。 + */ +#ifndef DEVICE_TIMER_H +#define DEVICE_TIMER_H + +/** + * @brief 初始化用户定时器及其中断配置。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 当前用于建立 100us 基准节拍,驱动任务调度器时基。 + */ +void user_timer_init(void); + +#endif /* DEVICE_TIMER_H */ diff --git a/GD32E508_DCU_APP/device/adc.h b/GD32E508_DCU_APP/device/adc.h new file mode 100644 index 0000000..f21d248 --- /dev/null +++ b/GD32E508_DCU_APP/device/adc.h @@ -0,0 +1,43 @@ +#ifndef DEVICE_ADC_H +#define DEVICE_ADC_H + +#include + +/** + * @brief ADC 采样模块。 + * @note 负责 VIN_ADC、内部温度与内部参考电压的 DMA 扫描采样与工程值换算。 + */ +class Adc { +public: + /** + * @brief ADC 采样结果结构体。 + * @note 保存外部 VIN_ADC、内部温度与内部参考电压的原始值,以及可直接使用的工程值。 + */ + struct Sample { + uint16_t vin_raw; /**< @brief 外部 VIN_ADC(PA6)原始 ADC 数值。 */ + uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */ + uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */ + float vin_adc_v; /**< @brief VIN_ADC 引脚处电压,单位 V(仅为分压后的 ADC 端电压)。 */ + float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */ + float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */ + }; + + static void init(); + static bool dmaReady(); + static const uint16_t *dmaBuffer(); + static uint16_t readRaw(uint8_t channel); + static uint16_t readVinRaw(); + static uint16_t readVrefRaw(); + static uint16_t readTempRaw(); + static float calculateVinAdcVoltage(uint16_t vinRaw, float vdda); + static float calculateVdda(uint16_t vrefRaw); + static float calculateTemperature(uint16_t tempRaw, float vdda); + static void collectSample(Sample *sample); + +private: + static void initDma(); +}; + +using adc_sample_t = Adc::Sample; + +#endif /* DEVICE_ADC_H */ diff --git a/GD32E508_DCU_APP/device/adc.h.bak_dma b/GD32E508_DCU_APP/device/adc.h.bak_dma new file mode 100644 index 0000000..c3215a3 --- /dev/null +++ b/GD32E508_DCU_APP/device/adc.h.bak_dma @@ -0,0 +1,78 @@ +#ifndef DEVICE_ADC_H +#define DEVICE_ADC_H + +#include + +/** + * @brief ADC 采样结果结构体。 + * @note 同时保存内部温度与内部参考电压的原始值及换算后的工程值。 + */ +typedef struct { + uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */ + uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */ + float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */ + float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */ +} adc_sample_t; + +/** + * @brief 初始化 ADC 硬件及内部温度/VREF 通道。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 该函数完成 ADC 时钟、模式、校准以及内部通道使能。 + */ +void adc_hw_init(void); + +/** + * @brief 执行指定通道的一次 ADC 单次采样。 + * @param[in] channel 待采样的 ADC 通道号。 + * @param[out] 无。 + * @retval uint16_t 指定通道的原始 ADC 采样值。 + * @note 函数内部会阻塞等待本次转换完成。 + */ +uint16_t adc_single_read(uint8_t channel); + +/** + * @brief 读取内部参考电压通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部参考电压通道的原始 ADC 值。 + */ +uint16_t adc_read_vref_raw(void); + +/** + * @brief 读取内部温度通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部温度通道的原始 ADC 值。 + */ +uint16_t adc_read_temp_raw(void); + +/** + * @brief 根据内部参考电压原始值计算实际 VDDA。 + * @param[in] vref_raw 内部参考电压通道原始 ADC 数值。 + * @param[out] 无。 + * @retval float 换算得到的 VDDA 电压值,单位 V。 + * @note 当输入为 0 时返回默认 3.3V,避免除零异常。 + */ +float adc_calculate_vdda(uint16_t vref_raw); + +/** + * @brief 根据温度原始值与 VDDA 计算芯片温度。 + * @param[in] temp_raw 内部温度通道原始 ADC 数值。 + * @param[in] vdda 当前供电电压,单位 V。 + * @param[out] 无。 + * @retval float 换算得到的芯片温度,单位 ℃。 + */ +float adc_calculate_temperature(uint16_t temp_raw, float vdda); + +/** + * @brief 采集一组完整 ADC 样本并完成工程值换算。 + * @param[in] sample 指向输出样本结构体的指针;允许为空。 + * @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。 + * @retval 无返回值。 + * @note 当 sample 为 NULL 时函数直接返回,不执行写操作。 + */ +void adc_collect_sample(adc_sample_t *sample); + +#endif /* DEVICE_ADC_H */ diff --git a/GD32E508_DCU_APP/device/adc.h.bak_oop b/GD32E508_DCU_APP/device/adc.h.bak_oop new file mode 100644 index 0000000..f2e6991 --- /dev/null +++ b/GD32E508_DCU_APP/device/adc.h.bak_oop @@ -0,0 +1,114 @@ +#ifndef DEVICE_ADC_H +#define DEVICE_ADC_H + +#include + +/** + * @brief ADC 采样结果结构体。 + * @note 保存外部 VIN_ADC、内部温度与内部参考电压的原始值,以及可直接使用的工程值。 + */ +typedef struct { + uint16_t vin_raw; /**< @brief 外部 VIN_ADC(PA6)原始 ADC 数值。 */ + uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */ + uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */ + float vin_adc_v; /**< @brief VIN_ADC 引脚处电压,单位 V(仅为分压后的 ADC 端电压)。 */ + float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */ + float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */ +} adc_sample_t; + +/** + * @brief 初始化 ADC 硬件及内部温度/VREF/VIN_ADC 通道。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 该函数完成 ADC 时钟、三通道扫描、DMA 循环缓存以及内部/外部采样通道使能。 + */ +void adc_hw_init(void); + +/** + * @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。 + */ +uint8_t adc_dma_ready(void); + +/** + * @brief 获取 ADC DMA 原始循环缓存只读指针。 + * @param[in] 无。 + * @param[out] 无。 + * @retval const uint16_t* 返回长度为 3 的原始缓存指针,顺序为 VIN_ADC、温度通道、VREF 通道。 + */ +const uint16_t *adc_dma_buffer_get(void); + +/** + * @brief 执行指定通道的一次 ADC 单次采样。 + * @param[in] channel 待采样的 ADC 通道号。 + * @param[out] 无。 + * @retval uint16_t 指定通道的原始 ADC 采样值。 + * @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。 + */ +uint16_t adc_single_read(uint8_t channel); + +/** + * @brief 读取外部 VIN_ADC(PA6)通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 外部 VIN_ADC 通道的原始 ADC 值。 + */ +uint16_t adc_read_vin_raw(void); + +/** + * @brief 读取内部参考电压通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部参考电压通道的原始 ADC 值。 + */ +uint16_t adc_read_vref_raw(void); + +/** + * @brief 读取内部温度通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部温度通道的原始 ADC 值。 + */ +uint16_t adc_read_temp_raw(void); + +/** + * @brief 根据外部 VIN_ADC 原始值与 VDDA 计算 ADC 引脚处电压。 + * @param[in] vin_raw 外部 VIN_ADC 通道原始 ADC 数值。 + * @param[in] vdda 当前供电电压,单位 V。 + * @param[out] 无。 + * @retval float VIN_ADC 引脚电压,单位 V。 + * @note 当前仅计算分压后的 ADC 端电压;若需换算输入母线电压,需要补充完整分压比。 + */ +float adc_calculate_vin_adc_voltage(uint16_t vin_raw, float vdda); + +/** + * @brief 根据内部参考电压原始值计算实际 VDDA。 + * @param[in] vref_raw 内部参考电压通道原始 ADC 数值。 + * @param[out] 无。 + * @retval float 换算得到的 VDDA 电压值,单位 V。 + * @note 当输入为 0 时返回默认 3.3V,避免除零异常。 + */ +float adc_calculate_vdda(uint16_t vref_raw); + +/** + * @brief 根据温度原始值与 VDDA 计算芯片温度。 + * @param[in] temp_raw 内部温度通道原始 ADC 数值。 + * @param[in] vdda 当前供电电压,单位 V。 + * @param[out] 无。 + * @retval float 换算得到的芯片温度,单位 ℃。 + */ +float adc_calculate_temperature(uint16_t temp_raw, float vdda); + +/** + * @brief 采集一组完整 ADC 样本并完成工程值换算。 + * @param[in] sample 指向输出样本结构体的指针;允许为空。 + * @param[out] sample 当指针非空时,写入 VIN/温度/VREF 原始值与换算结果。 + * @retval 无返回值。 + * @note 当 sample 为 NULL 时函数直接返回,不执行写操作。 + */ +void adc_collect_sample(adc_sample_t *sample); + +#endif /* DEVICE_ADC_H */ diff --git a/GD32E508_DCU_APP/device/adc.h.bak_sch b/GD32E508_DCU_APP/device/adc.h.bak_sch new file mode 100644 index 0000000..eae2838 --- /dev/null +++ b/GD32E508_DCU_APP/device/adc.h.bak_sch @@ -0,0 +1,94 @@ +#ifndef DEVICE_ADC_H +#define DEVICE_ADC_H + +#include + +/** + * @brief ADC 采样结果结构体。 + * @note 同时保存内部温度与内部参考电压的原始值及换算后的工程值。 + */ +typedef struct { + uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */ + uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */ + float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */ + float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */ +} adc_sample_t; + +/** + * @brief 初始化 ADC 硬件及内部温度/VREF 通道。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 该函数完成 ADC 时钟、双通道扫描、DMA 循环缓存以及内部通道使能。 + */ +void adc_hw_init(void); + +/** + * @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。 + */ +uint8_t adc_dma_ready(void); + +/** + * @brief 获取 ADC DMA 原始循环缓存只读指针。 + * @param[in] 无。 + * @param[out] 无。 + * @retval const uint16_t* 返回长度为 2 的原始缓存指针,顺序为温度通道、VREF 通道。 + */ +const uint16_t *adc_dma_buffer_get(void); + +/** + * @brief 执行指定通道的一次 ADC 单次采样。 + * @param[in] channel 待采样的 ADC 通道号。 + * @param[out] 无。 + * @retval uint16_t 指定通道的原始 ADC 采样值。 + * @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。 + */ +uint16_t adc_single_read(uint8_t channel); + +/** + * @brief 读取内部参考电压通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部参考电压通道的原始 ADC 值。 + */ +uint16_t adc_read_vref_raw(void); + +/** + * @brief 读取内部温度通道的原始 ADC 值。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint16_t 内部温度通道的原始 ADC 值。 + */ +uint16_t adc_read_temp_raw(void); + +/** + * @brief 根据内部参考电压原始值计算实际 VDDA。 + * @param[in] vref_raw 内部参考电压通道原始 ADC 数值。 + * @param[out] 无。 + * @retval float 换算得到的 VDDA 电压值,单位 V。 + * @note 当输入为 0 时返回默认 3.3V,避免除零异常。 + */ +float adc_calculate_vdda(uint16_t vref_raw); + +/** + * @brief 根据温度原始值与 VDDA 计算芯片温度。 + * @param[in] temp_raw 内部温度通道原始 ADC 数值。 + * @param[in] vdda 当前供电电压,单位 V。 + * @param[out] 无。 + * @retval float 换算得到的芯片温度,单位 ℃。 + */ +float adc_calculate_temperature(uint16_t temp_raw, float vdda); + +/** + * @brief 采集一组完整 ADC 样本并完成工程值换算。 + * @param[in] sample 指向输出样本结构体的指针;允许为空。 + * @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。 + * @retval 无返回值。 + * @note 当 sample 为 NULL 时函数直接返回,不执行写操作。 + */ +void adc_collect_sample(adc_sample_t *sample); + +#endif /* DEVICE_ADC_H */ diff --git a/GD32E508_DCU_APP/device/canfd.h b/GD32E508_DCU_APP/device/canfd.h new file mode 100644 index 0000000..3cf6778 --- /dev/null +++ b/GD32E508_DCU_APP/device/canfd.h @@ -0,0 +1,74 @@ +#ifndef DEVICE_CANFD_H +#define DEVICE_CANFD_H + +#include + +/** @brief CANFD 状态报文的数据长度,单位字节。 */ +#define CANFD_STATUS_DLEN 12U + +/** + * @brief CANFD 通信模块。 + * @note 负责 GPIO/模式控制、波特率配置、异步接收缓存与状态帧发送。 + */ +class CanFd { +public: + /** + * @brief CANFD 运行诊断统计信息。 + * @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。 + */ + struct Diag { + uint32_t tx_count; /**< @brief 成功发送的报文计数。 */ + uint32_t rx_count; /**< @brief 成功接收的报文计数。 */ + uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */ + uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */ + uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */ + uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */ + uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */ + uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */ + uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */ + }; + + /** + * @brief CANFD 状态发送载荷。 + * @note 当前保持协议兼容,仅包含温度与供电相关字段;VIN_ADC 单独在诊断打印中输出。 + */ + struct StatusPayload { + uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */ + uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */ + float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */ + float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */ + }; + + static void init(); + static void poll(); + static void prepareStatus(const StatusPayload *status); + static void sendPrepared(); + static bool hasRxFrame(); + static bool consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length); + static const Diag *diag(); + static void setNormalMode(); + static void setSilentMode(); + static void onRx0Interrupt(); + +private: + struct RxFrame { + uint32_t id; + uint8_t data[64]; + uint8_t length; + uint8_t valid; + }; + + static void initGpio(); + static void filterAcceptAll(); + static void initNominalTiming(); + static void initFdMode(); + static void buildStatusFrame(const StatusPayload *status); + + static Diag s_diag; + static RxFrame s_rxFrame; +}; + +using canfd_diag_t = CanFd::Diag; +using canfd_status_payload_t = CanFd::StatusPayload; + +#endif /* DEVICE_CANFD_H */ diff --git a/GD32E508_DCU_APP/device/canfd.h.bak_hideimpl b/GD32E508_DCU_APP/device/canfd.h.bak_hideimpl new file mode 100644 index 0000000..e21157a --- /dev/null +++ b/GD32E508_DCU_APP/device/canfd.h.bak_hideimpl @@ -0,0 +1,81 @@ +#ifndef DEVICE_CANFD_H +#define DEVICE_CANFD_H + +#include + +/** @brief CANFD 状态报文的数据长度,单位字节。 */ +#define CANFD_STATUS_DLEN 12U + +/** + * @brief CANFD 通信模块。 + * @note 负责 GPIO/模式控制、波特率配置、异步接收缓存与状态帧发送。 + */ +class CanFd { +public: + /** + * @brief CANFD 运行诊断统计信息。 + * @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。 + */ + struct Diag { + uint32_t tx_count; /**< @brief 成功发送的报文计数。 */ + uint32_t rx_count; /**< @brief 成功接收的报文计数。 */ + uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */ + uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */ + uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */ + uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */ + uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */ + uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */ + uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */ + }; + + /** + * @brief CANFD 状态发送载荷。 + * @note 当前保持协议兼容,仅包含温度与供电相关字段;VIN_ADC 单独在诊断打印中输出。 + */ + struct StatusPayload { + uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */ + uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */ + float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */ + float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */ + }; + + static void init(); + static void poll(); + static void prepareStatus(const StatusPayload *status); + static void sendPrepared(); + static bool hasRxFrame(); + static bool consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length); + static const Diag *diag(); + static void setNormalMode(); + static void setSilentMode(); + static void onRx0Interrupt(); + +private: + struct RxFrame { + uint32_t id; + uint8_t data[64]; + uint8_t length; + uint8_t valid; + }; + + struct TxBuffer { + can_trasnmit_message_struct tx_message; + uint8_t payload[64]; + uint8_t valid; + }; + + static void initGpio(); + static void filterAcceptAll(); + static void initNominalTiming(); + static void initFdMode(); + static void buildStatusFrame(const StatusPayload *status); + + static Diag s_diag; + static RxFrame s_rxFrame; + static TxBuffer s_txBuffer; +}; + +using canfd_diag_t = CanFd::Diag; +using canfd_status_payload_t = CanFd::StatusPayload; + +#endif /* DEVICE_CANFD_H */ diff --git a/GD32E508_DCU_APP/device/canfd.h.bak_oop b/GD32E508_DCU_APP/device/canfd.h.bak_oop new file mode 100644 index 0000000..f3cf947 --- /dev/null +++ b/GD32E508_DCU_APP/device/canfd.h.bak_oop @@ -0,0 +1,99 @@ +/** + * @file canfd.h + * @brief CANFD 通信接口、状态载荷与诊断信息定义。 + */ +#ifndef DEVICE_CANFD_H +#define DEVICE_CANFD_H + +#include + +/** @brief CANFD 状态报文的数据长度,单位字节。 */ +#define CANFD_STATUS_DLEN 12U + +/** + * @brief CANFD 运行诊断统计信息。 + * @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。 + */ +typedef struct { + uint32_t tx_count; /**< @brief 成功发送的报文计数。 */ + uint32_t rx_count; /**< @brief 成功接收的报文计数。 */ + uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */ + uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */ + uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */ + uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */ + uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */ + uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */ + uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */ +} canfd_diag_t; + +/** + * @brief CANFD 状态发送载荷。 + * @note 用于封装供 CAN 状态帧发送的核心状态数据。当前保持协议兼容,仅包含温度与供电相关字段;VIN_ADC 单独在诊断打印中输出。 + */ +typedef struct { + uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */ + uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */ + float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */ + float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */ +} canfd_status_payload_t; + +/** + * @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void canfd_init(void); + +/** + * @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 若当前没有待接收报文,函数直接返回。 + */ +void canfd_poll(void); + +/** + * @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。 + * @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void canfd_prepare_status(const canfd_status_payload_t *status); + +/** + * @brief 发送已准备好的 CANFD 状态报文。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 若当前没有有效待发送报文,函数直接返回。 + */ +void canfd_send_prepared(void); + +/** + * @brief 查询是否存在尚未消费的接收报文。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint8_t 返回非 0 表示存在待消费报文,返回 0 表示没有待消费报文。 + */ +uint8_t canfd_has_rx_frame(void); + +/** + * @brief 读取并消费最近一次缓存的接收报文。 + * @param[out] id 输出报文标识符的指针;允许为空。 + * @param[out] data 输出报文数据缓存区指针;允许为空。 + * @param[out] length 输出报文数据长度的指针;允许为空。 + * @retval uint8_t 返回 1 表示成功消费一帧报文,返回 0 表示当前无有效缓存报文。 + */ +uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length); + +/** + * @brief 获取 CANFD 诊断统计信息只读指针。 + * @param[in] 无。 + * @param[out] 无。 + * @retval const canfd_diag_t* 指向内部诊断统计结构体的只读指针。 + */ +const canfd_diag_t *canfd_get_diag(void); + +#endif /* DEVICE_CANFD_H */ diff --git a/GD32E508_DCU_APP/device/canfd.h.bak_sch b/GD32E508_DCU_APP/device/canfd.h.bak_sch new file mode 100644 index 0000000..9830943 --- /dev/null +++ b/GD32E508_DCU_APP/device/canfd.h.bak_sch @@ -0,0 +1,99 @@ +/** + * @file canfd.h + * @brief CANFD 通信接口、状态载荷与诊断信息定义。 + */ +#ifndef DEVICE_CANFD_H +#define DEVICE_CANFD_H + +#include + +/** @brief CANFD 状态报文的数据长度,单位字节。 */ +#define CANFD_STATUS_DLEN 12U + +/** + * @brief CANFD 运行诊断统计信息。 + * @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。 + */ +typedef struct { + uint32_t tx_count; /**< @brief 成功发送的报文计数。 */ + uint32_t rx_count; /**< @brief 成功接收的报文计数。 */ + uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */ + uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */ + uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */ + uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */ + uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */ + uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */ + uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */ +} canfd_diag_t; + +/** + * @brief CANFD 状态发送载荷。 + * @note 用于封装温度与供电电压相关的状态数据,供状态帧打包发送。 + */ +typedef struct { + uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */ + uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */ + float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */ + float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */ +} canfd_status_payload_t; + +/** + * @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void canfd_init(void); + +/** + * @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 若当前没有待接收报文,函数直接返回。 + */ +void canfd_poll(void); + +/** + * @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。 + * @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void canfd_prepare_status(const canfd_status_payload_t *status); + +/** + * @brief 发送已准备好的 CANFD 状态报文。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 若当前没有有效待发送报文,函数直接返回。 + */ +void canfd_send_prepared(void); + +/** + * @brief 查询是否存在尚未消费的接收报文。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint8_t 返回非 0 表示存在待消费报文,返回 0 表示没有待消费报文。 + */ +uint8_t canfd_has_rx_frame(void); + +/** + * @brief 读取并消费最近一次缓存的接收报文。 + * @param[out] id 输出报文标识符的指针;允许为空。 + * @param[out] data 输出报文数据缓存区指针;允许为空。 + * @param[out] length 输出报文数据长度的指针;允许为空。 + * @retval uint8_t 返回 1 表示成功消费一帧报文,返回 0 表示当前无有效缓存报文。 + */ +uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length); + +/** + * @brief 获取 CANFD 诊断统计信息只读指针。 + * @param[in] 无。 + * @param[out] 无。 + * @retval const canfd_diag_t* 指向内部诊断统计结构体的只读指针。 + */ +const canfd_diag_t *canfd_get_diag(void); + +#endif /* DEVICE_CANFD_H */ diff --git a/GD32E508_DCU_APP/device/temp_sensor.cpp b/GD32E508_DCU_APP/device/temp_sensor.cpp new file mode 100644 index 0000000..ecae9ea --- /dev/null +++ b/GD32E508_DCU_APP/device/temp_sensor.cpp @@ -0,0 +1,17 @@ +/** + * @file temp_sensor.cpp + * @brief 温度传感模块实现。 + */ + +#include "device/adc.h" +#include "device/temp_sensor.h" + +void TempSensor::init() +{ + Adc::init(); +} + +void TempSensor::sample(Data *data) +{ + Adc::collectSample(data); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/temp_sensor.cpp.bak_oop b/GD32E508_DCU_APP/device/temp_sensor.cpp.bak_oop new file mode 100644 index 0000000..31d5fad --- /dev/null +++ b/GD32E508_DCU_APP/device/temp_sensor.cpp.bak_oop @@ -0,0 +1,25 @@ +/** + * @file temp_sensor.cpp + * @brief 温度传感采样实现。 + */ + +#include "device/adc.h" +#include "device/temp_sensor.h" + +/** + * @brief 初始化温度传感采样所需硬件资源。 + * @note 当前通过初始化 ADC 模块完成内部温度通道准备。 + */ +void temp_sensor_init(void) +{ + adc_hw_init(); +} + +/** + * @brief 采集一次温度传感样本并输出结果。 + * @param[out] data 指向输出样本结构体的指针;其语义与 adc_collect_sample() 一致。 + */ +void temp_sensor_sample(temp_sensor_data_t *data) +{ + adc_collect_sample(data); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/temp_sensor.h b/GD32E508_DCU_APP/device/temp_sensor.h new file mode 100644 index 0000000..03d3291 --- /dev/null +++ b/GD32E508_DCU_APP/device/temp_sensor.h @@ -0,0 +1,20 @@ +#ifndef DEVICE_TEMP_SENSOR_H +#define DEVICE_TEMP_SENSOR_H + +#include "device/adc.h" + +/** + * @brief 温度采样模块。 + * @note 当前作为 ADC 模块的轻量门面,复用 Adc::Sample 作为结果类型。 + */ +class TempSensor { +public: + using Data = Adc::Sample; + + static void init(); + static void sample(Data *data); +}; + +using temp_sensor_data_t = TempSensor::Data; + +#endif /* DEVICE_TEMP_SENSOR_H */ diff --git a/GD32E508_DCU_APP/device/temp_sensor.h.bak_oop b/GD32E508_DCU_APP/device/temp_sensor.h.bak_oop new file mode 100644 index 0000000..75712a4 --- /dev/null +++ b/GD32E508_DCU_APP/device/temp_sensor.h.bak_oop @@ -0,0 +1,33 @@ +/** + * @file temp_sensor.h + * @brief 温度传感采样接口定义。 + */ +#ifndef DEVICE_TEMP_SENSOR_H +#define DEVICE_TEMP_SENSOR_H + +#include +#include "device/adc.h" + +/** + * @brief 温度传感采样数据类型别名。 + * @note 复用 ADC 采样结果结构,统一保存温度与参考电压原始值及换算值。 + */ +typedef adc_sample_t temp_sensor_data_t; + +/** + * @brief 初始化温度传感采样所需硬件资源。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 当前通过初始化 ADC 模块完成内部温度通道准备。 + */ +void temp_sensor_init(void); + +/** + * @brief 采集一次温度传感样本并输出结果。 + * @param[out] data 指向输出样本结构体的指针;其语义与 adc_collect_sample() 一致。 + * @retval 无返回值。 + */ +void temp_sensor_sample(temp_sensor_data_t *data); + +#endif /* DEVICE_TEMP_SENSOR_H */ diff --git a/GD32E508_DCU_APP/device/usart0.cpp b/GD32E508_DCU_APP/device/usart0.cpp new file mode 100644 index 0000000..7336501 --- /dev/null +++ b/GD32E508_DCU_APP/device/usart0.cpp @@ -0,0 +1,77 @@ +/** + * @file usart0.cpp + * @brief USART0 串口模块实现与 printf 重定向。 + */ + +#include "main.h" +#include "device/usart0.h" +#include "sys/config.h" + +uint8_t Usart0::s_txBuf[Usart0::kTxBufSize]; +volatile uint16_t Usart0::s_txHead = 0U; +volatile uint16_t Usart0::s_txTail = 0U; + +uint16_t Usart0::nextIndex(uint16_t idx) +{ + return (idx + 1U) % kTxBufSize; +} + +bool Usart0::isBufferFull() +{ + return nextIndex(s_txHead) == s_txTail; +} + +bool Usart0::isBufferEmpty() +{ + return s_txHead == s_txTail; +} + +int Usart0::putChar(int ch, FILE *f) +{ + (void)f; + uint16_t next = nextIndex(s_txHead); + while(next == s_txTail) { /* 缓冲满,忙等重试 */ } + + s_txBuf[s_txHead] = static_cast(ch); + s_txHead = next; + usart_interrupt_enable(USART0, USART_INT_TBE); + return ch; +} + +void Usart0::onInterrupt() +{ + if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_TBE) != RESET) { + if(!isBufferEmpty()) { + usart_data_transmit(USART0, s_txBuf[s_txTail]); + s_txTail = nextIndex(s_txTail); + } + if(isBufferEmpty()) { + usart_interrupt_disable(USART0, USART_INT_TBE); + } + } +} + +void Usart0::configurePins() +{ + rcu_periph_clock_enable(RCU_GPIOA); + gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); + gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); +} + +void Usart0::init() +{ + rcu_periph_clock_enable(RCU_USART0); + usart_deinit(USART0); + usart_baudrate_set(USART0, CONFIG_USART0_BAUDRATE); + usart_word_length_set(USART0, USART_WL_8BIT); + usart_stop_bit_set(USART0, USART_STB_1BIT); + usart_parity_config(USART0, USART_PM_NONE); + usart_receive_config(USART0, USART_RECEIVE_ENABLE); + usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); + usart_enable(USART0); + nvic_irq_enable(USART0_IRQn, 2U, 0U); +} + +/* ----- 中断入口 / printf 重定向的 C 包装 --------------------------- */ +extern "C" int fputc(int ch, FILE *f) { return Usart0::putChar(ch, f); } +extern "C" void USART0_IRQHandler(void) { Usart0::onInterrupt(); } \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/usart0.cpp.bak_oop b/GD32E508_DCU_APP/device/usart0.cpp.bak_oop new file mode 100644 index 0000000..7696620 --- /dev/null +++ b/GD32E508_DCU_APP/device/usart0.cpp.bak_oop @@ -0,0 +1,109 @@ +/** + * @file usart0.cpp + * @brief USART0 串口配置与 printf 重定向实现。 + * @note 使用 TX 中断 + 环形缓冲替代逐字节忙等 TBE,降低 CPU 占用。 + */ + +#include "main.h" +#include "device/usart0.h" +#include "sys/config.h" + +#define USARTx USART0 +#define USART_TX_BUF_SIZE 256U + +namespace { +uint8_t g_tx_buf[USART_TX_BUF_SIZE]; +volatile uint16_t g_tx_head = 0U; +volatile uint16_t g_tx_tail = 0U; + +inline uint16_t tx_buf_next(uint16_t idx) +{ + return (idx + 1U) % USART_TX_BUF_SIZE; +} + +inline bool tx_buf_full(void) +{ + return tx_buf_next(g_tx_head) == g_tx_tail; +} + +inline bool tx_buf_empty(void) +{ + return g_tx_head == g_tx_tail; +} +} // namespace + +/** + * @brief 重定向标准输出字符到 USART0。 + * @param[in] ch 待输出的字符。 + * @param[in] f 标准流指针;当前实现中未实际使用。 + * @retval 返回已发送的字符值。 + * @note 将字符写入环形发送缓冲,由 USART0 TXE 中断负责实际发送。 + */ +extern "C" int fputc(int ch, FILE *f) +{ + (void)f; + uint16_t next = tx_buf_next(g_tx_head); + + while(next == g_tx_tail) { + /* 缓冲满,忙等一小段时间后重试; + * 在 115200 波特率下通常不会发生 */ + } + g_tx_buf[g_tx_head] = static_cast(ch); + g_tx_head = next; + + /* 启动 TXE 中断以触发实际硬件发送 */ + usart_interrupt_enable(USARTx, USART_INT_TBE); + + return ch; +} + +/** + * @brief USART0 中断服务例程。 + * @note 处理 TXE 发送缓冲区空中断,从环形缓冲取下一字节发送。 + */ +extern "C" void USART0_IRQHandler(void) +{ + if(usart_interrupt_flag_get(USARTx, USART_INT_FLAG_TBE) != RESET) { + if(!tx_buf_empty()) { + usart_data_transmit(USARTx, g_tx_buf[g_tx_tail]); + g_tx_tail = tx_buf_next(g_tx_tail); + } + /* 缓冲已空,关 TXE 中断,等下次 fputc 再开 */ + if(tx_buf_empty()) { + usart_interrupt_disable(USARTx, USART_INT_TBE); + } + } +} + +/** + * @brief 配置 USART0 相关 GPIO 引脚。 + */ +void usart0_gpio_config(void) +{ + rcu_periph_clock_enable(RCU_GPIOA); + + gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); + gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); +} + +/** + * @brief 配置并使能 USART0 外设。 + * @note 当前使用 8N1 格式,115200 波特率,收发同时使能。 + * 同步配置 NVIC 以便 TXE 中断驱动发送。 + */ +void usart0_config(void) +{ + rcu_periph_clock_enable(RCU_USART0); + + usart_deinit(USARTx); + usart_baudrate_set(USARTx, CONFIG_USART0_BAUDRATE); + usart_word_length_set(USARTx, USART_WL_8BIT); + usart_stop_bit_set(USARTx, USART_STB_1BIT); + usart_parity_config(USARTx, USART_PM_NONE); + usart_receive_config(USARTx, USART_RECEIVE_ENABLE); + usart_transmit_config(USARTx, USART_TRANSMIT_ENABLE); + usart_enable(USARTx); + + /* 使能 USART0 NVIC,用于 TXE 中断驱动发送 */ + nvic_irq_enable(USART0_IRQn, 2U, 0U); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/usart0.cpp.bak_txisr b/GD32E508_DCU_APP/device/usart0.cpp.bak_txisr new file mode 100644 index 0000000..1e51cc6 --- /dev/null +++ b/GD32E508_DCU_APP/device/usart0.cpp.bak_txisr @@ -0,0 +1,54 @@ +/** + * @file usart0.cpp + * @brief USART0 串口配置与 printf 重定向实现。 + */ + +#include "main.h" +#include "device/usart0.h" +#include "sys/config.h" + +#define USARTx USART0 + +/** + * @brief 重定向标准输出字符到 USART0。 + * @param[in] ch 待输出的字符。 + * @param[in] f 标准流指针;当前实现中未实际使用。 + * @retval 返回已发送的字符值。 + */ +extern "C" int fputc(int ch, FILE *f) +{ + (void)f; + while(RESET == usart_flag_get(USARTx, USART_FLAG_TBE)) { + } + usart_data_transmit(USARTx, (uint16_t)ch); + return ch; +} + +/** + * @brief 配置 USART0 相关 GPIO 引脚。 + */ +void usart0_gpio_config(void) +{ + rcu_periph_clock_enable(RCU_GPIOA); + + gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); + gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); +} + +/** + * @brief 配置并使能 USART0 外设。 + * @note 当前使用 8N1 格式,115200 波特率,收发同时使能。 + */ +void usart0_config(void) +{ + rcu_periph_clock_enable(RCU_USART0); + + usart_deinit(USARTx); + usart_baudrate_set(USARTx, CONFIG_USART0_BAUDRATE); + usart_word_length_set(USARTx, USART_WL_8BIT); + usart_stop_bit_set(USARTx, USART_STB_1BIT); + usart_parity_config(USARTx, USART_PM_NONE); + usart_receive_config(USARTx, USART_RECEIVE_ENABLE); + usart_transmit_config(USARTx, USART_TRANSMIT_ENABLE); + usart_enable(USARTx); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/usart0.h b/GD32E508_DCU_APP/device/usart0.h new file mode 100644 index 0000000..6c67c68 --- /dev/null +++ b/GD32E508_DCU_APP/device/usart0.h @@ -0,0 +1,29 @@ +#ifndef DEVICE_USART0_H +#define DEVICE_USART0_H + +#include +#include + +/** + * @brief USART0 串口模块。 + * @note 负责 GPIO 初始化、串口配置、TX 环形缓冲与中断驱动发送。 + */ +class Usart0 { +public: + static void configurePins(); + static void init(); + static int putChar(int ch, FILE *f); + static void onInterrupt(); + +private: + static uint16_t nextIndex(uint16_t idx); + static bool isBufferFull(); + static bool isBufferEmpty(); + + static constexpr uint16_t kTxBufSize = 256U; + static uint8_t s_txBuf[kTxBufSize]; + static volatile uint16_t s_txHead; + static volatile uint16_t s_txTail; +}; + +#endif /* DEVICE_USART0_H */ diff --git a/GD32E508_DCU_APP/device/usart0.h.bak_oop b/GD32E508_DCU_APP/device/usart0.h.bak_oop new file mode 100644 index 0000000..1e5f910 --- /dev/null +++ b/GD32E508_DCU_APP/device/usart0.h.bak_oop @@ -0,0 +1,25 @@ +/** + * @file usart0.h + * @brief USART0 串口打印与基础配置接口定义。 + */ +#ifndef DEVICE_USART0_H +#define DEVICE_USART0_H + +/** + * @brief 配置 USART0 相关 GPIO 引脚。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void usart0_gpio_config(void); + +/** + * @brief 配置并使能 USART0 外设。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 当前串口参数由实现文件中的默认宏定义决定。 + */ +void usart0_config(void); + +#endif /* DEVICE_USART0_H */ diff --git a/GD32E508_DCU_APP/main.cpp b/GD32E508_DCU_APP/main.cpp new file mode 100644 index 0000000..5fff4d7 --- /dev/null +++ b/GD32E508_DCU_APP/main.cpp @@ -0,0 +1,37 @@ +#include "main.h" +#include "sys/initial.h" +#include "sys/task_runner.h" +#include "sys/config.h" + +/** + * @brief 打印应用启动横幅与系统时钟信息。 + * @note 该函数仅负责串口输出启动提示,不修改系统控制状态。 + */ +void App::printBanner() const +{ + printf("\r\n%s\r\n", APP_BANNER_TEXT); + printf("SystemCoreClock=%u Hz\r\n", SystemCoreClock); +} + +/** + * @brief 运行应用主循环。 + * @retval int 固定返回 0;在嵌入式常驻循环场景下通常不会真正返回。 + */ +int App::run() +{ + AppInitializer::init(); + TaskRunner::init(); + printBanner(); + + while(1) { + TaskRunner::runPending(); + } + + return 0; +} + +int main(void) +{ + App app; + return app.run(); +} diff --git a/GD32E508_DCU_APP/main.cpp.bak_config b/GD32E508_DCU_APP/main.cpp.bak_config new file mode 100644 index 0000000..95a6e28 --- /dev/null +++ b/GD32E508_DCU_APP/main.cpp.bak_config @@ -0,0 +1,34 @@ +#include "main.h" +#include "sys/initial.h" +#include "sys/task_runner.h" + +/** + * @brief 打印应用启动横幅与系统时钟信息。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 该函数仅负责串口输出启动提示,不修改系统控制状态。 + */ +static void app_banner(void) +{ + printf("\r\nGD32E508 DCU task scheduler start\r\n"); + printf("SystemCoreClock=%u Hz\r\n", SystemCoreClock); +} + +/** + * @brief 应用程序主入口函数。 + * @param[in] 无。 + * @param[out] 无。 + * @retval int 固定返回 0;在嵌入式常驻循环场景下通常不会真正返回。 + * @note 初始化硬件与任务运行环境后,进入永久任务执行循环。 + */ +int main(void) +{ + app_init(); + task_runner_init(); + app_banner(); + + while(1) { + task_runner_run_pending(); + } +} diff --git a/GD32E508_DCU_APP/main.cpp.bak_oop b/GD32E508_DCU_APP/main.cpp.bak_oop new file mode 100644 index 0000000..c92b7fd --- /dev/null +++ b/GD32E508_DCU_APP/main.cpp.bak_oop @@ -0,0 +1,35 @@ +#include "main.h" +#include "sys/initial.h" +#include "sys/task_runner.h" +#include "sys/config.h" + +/** + * @brief 打印应用启动横幅与系统时钟信息。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 该函数仅负责串口输出启动提示,不修改系统控制状态。 + */ +static void app_banner(void) +{ + printf("\r\n%s\r\n", APP_BANNER_TEXT); + printf("SystemCoreClock=%u Hz\r\n", SystemCoreClock); +} + +/** + * @brief 应用程序主入口函数。 + * @param[in] 无。 + * @param[out] 无。 + * @retval int 固定返回 0;在嵌入式常驻循环场景下通常不会真正返回。 + * @note 初始化硬件与任务运行环境后,进入永久任务执行循环。 + */ +int main(void) +{ + app_init(); + task_runner_init(); + app_banner(); + + while(1) { + task_runner_run_pending(); + } +} diff --git a/GD32E508_DCU_APP/main.h b/GD32E508_DCU_APP/main.h new file mode 100644 index 0000000..f9773bc --- /dev/null +++ b/GD32E508_DCU_APP/main.h @@ -0,0 +1,59 @@ +#ifndef MAIN_H +#define MAIN_H + +#include "RTE_Components.h" +#include CMSIS_device_header + +#include +#include + +/** + * @brief 系统任务枚举定义。 + * @note 每个枚举值对应任务调度器中的一个任务位,用于驱动主循环中的任务分发。 + */ +enum system_task_t { + TASK_ADC_VREF_SAMPLE = 0, /**< @brief 采样 VIN/VREF 原始值任务。 */ + TASK_ADC_TEMP_SAMPLE, /**< @brief 采样内部温度通道原始值任务。 */ + TASK_ADC_PROCESS, /**< @brief 对 ADC 原始值进行电压与温度计算任务。 */ + TASK_CAN_RX_POLL, /**< @brief 兼容保留的 CANFD 接收轮询任务。 */ + TASK_CAN_RX_PARSE, /**< @brief 解析并消费已接收 CANFD 帧任务。 */ + TASK_CAN_TX_PREPARE, /**< @brief 准备 CANFD 状态发送帧任务。 */ + TASK_CAN_TX_SEND, /**< @brief 发送已准备好的 CANFD 帧任务。 */ + TASK_STATUS_LED, /**< @brief 状态灯维护任务。 */ + TASK_HEARTBEAT, /**< @brief 系统心跳维护任务。 */ + TASK_DIAGNOSTIC_PRINT, /**< @brief 诊断信息打印任务。 */ + TASK_COUNT /**< @brief 任务总数,用于边界或统计场景。 */ +}; + +/** + * @brief 任务调度器状态结构体。 + * @note 保存各类节拍计数器与待执行任务标志位,由中断与主循环共享访问。 + */ +struct task_scheduler_t { + volatile uint32_t task_flags; /**< @brief 待执行任务位图,每一位对应一个任务。 */ + volatile uint32_t tick_100us; /**< @brief 100us 基准节拍累计计数。 */ + volatile uint32_t tick_1ms; /**< @brief 1ms 节拍累计计数。 */ + volatile uint32_t tick_10ms; /**< @brief 10ms 节拍累计计数。 */ + volatile uint32_t tick_100ms; /**< @brief 100ms 节拍累计计数。 */ + volatile uint32_t tick_1s; /**< @brief 1s 节拍累计计数。 */ +}; + +/** + * @brief 全局任务调度器状态实例。 + * @note 由 TaskManager 管理,由中断与调试逻辑共享观察。 + */ +extern task_scheduler_t g_scheduler; + +/** + * @brief 应用顶层对象。 + * @note 负责组织初始化、横幅输出与主循环执行。 + */ +class App { +public: + int run(); + +private: + void printBanner() const; +}; + +#endif /* MAIN_H */ diff --git a/GD32E508_DCU_APP/main.h.bak_oop b/GD32E508_DCU_APP/main.h.bak_oop new file mode 100644 index 0000000..b25cc70 --- /dev/null +++ b/GD32E508_DCU_APP/main.h.bak_oop @@ -0,0 +1,47 @@ +#ifndef MAIN_H +#define MAIN_H + +#include "RTE_Components.h" +#include CMSIS_device_header + +#include +#include + +/** + * @brief 系统任务枚举定义。 + * @note 每个枚举值对应任务调度器中的一个任务位,用于驱动主循环中的任务分发。 + */ +typedef enum { + TASK_ADC_VREF_SAMPLE = 0, /**< @brief 采样内部参考电压原始值任务。 */ + TASK_ADC_TEMP_SAMPLE, /**< @brief 采样内部温度通道原始值任务。 */ + TASK_ADC_PROCESS, /**< @brief 对 ADC 原始值进行电压与温度计算任务。 */ + TASK_CAN_RX_POLL, /**< @brief 轮询 CANFD 接收 FIFO 任务。 */ + TASK_CAN_RX_PARSE, /**< @brief 解析并消费已接收 CANFD 帧任务。 */ + TASK_CAN_TX_PREPARE, /**< @brief 准备 CANFD 状态发送帧任务。 */ + TASK_CAN_TX_SEND, /**< @brief 发送已准备好的 CANFD 帧任务。 */ + TASK_STATUS_LED, /**< @brief 状态灯维护任务。 */ + TASK_HEARTBEAT, /**< @brief 系统心跳维护任务。 */ + TASK_DIAGNOSTIC_PRINT, /**< @brief 诊断信息打印任务。 */ + TASK_COUNT /**< @brief 任务总数,用于边界或统计场景。 */ +} system_task_t; + +/** + * @brief 任务调度器状态结构体。 + * @note 该结构体保存各类节拍计数器与待执行任务标志位,由中断与主循环共享访问。 + */ +typedef struct { + volatile uint32_t task_flags; /**< @brief 待执行任务位图,每一位对应一个任务。 */ + volatile uint32_t tick_100us; /**< @brief 100us 基准节拍累计计数。 */ + volatile uint32_t tick_1ms; /**< @brief 1ms 节拍累计计数。 */ + volatile uint32_t tick_10ms; /**< @brief 10ms 节拍累计计数。 */ + volatile uint32_t tick_100ms; /**< @brief 100ms 节拍累计计数。 */ + volatile uint32_t tick_1s; /**< @brief 1s 节拍累计计数。 */ +} task_scheduler_t; + +/** + * @brief 全局任务调度器实例。 + * @note 由任务管理模块定义,由其他模块引用以观察或维护调度状态。 + */ +extern task_scheduler_t g_scheduler; + +#endif /* MAIN_H */ diff --git a/GD32E508_DCU_APP/sys/config.h b/GD32E508_DCU_APP/sys/config.h new file mode 100644 index 0000000..600f9cb --- /dev/null +++ b/GD32E508_DCU_APP/sys/config.h @@ -0,0 +1,79 @@ +/** + * @file config.h + * @brief 应用级集中配置项定义。 + * @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。 + */ +#ifndef SYS_CONFIG_H +#define SYS_CONFIG_H + +/** @brief 应用启动横幅字符串。 */ +#define APP_BANNER_TEXT "GD32E508 DCU task scheduler start" + +/** @brief USART0 默认波特率。 */ +#define CONFIG_USART0_BAUDRATE 115200U + +/** @brief ADC 外设实例。 */ +#define CONFIG_ADC_INSTANCE ADC0 +/** @brief ADC 外部 VIN 检测通道号(原理图:PA6 -> VIN_ADC)。 */ +#define CONFIG_ADC_VIN_CHANNEL ADC_CHANNEL_6 +/** @brief ADC 内部温度通道号。 */ +#define CONFIG_ADC_TEMP_CHANNEL ADC_CHANNEL_16 +/** @brief ADC 内部参考电压通道号。 */ +#define CONFIG_ADC_VREFINT_CHANNEL ADC_CHANNEL_17 +/** @brief ADC 满量程计数值。 */ +#define CONFIG_ADC_MAX_VALUE 4095.0f +/** @brief 内部参考电压典型值,单位 V。 */ +#define CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE 1.20f +/** @brief 温度传感器 25℃ 对应电压,单位 V。 */ +#define CONFIG_ADC_TEMP_V25 1.43f +/** @brief 温度传感器平均斜率,单位 V/℃。 */ +#define CONFIG_ADC_TEMP_AVG_SLOPE 0.0043f +/** @brief ADC 上电后稳定等待时间,单位 ms。 */ +#define CONFIG_ADC_STABILIZE_DELAY_MS 2U +/** @brief ADC 规则通道采样时间配置。 */ +#define CONFIG_ADC_SAMPLE_TIME ADC_SAMPLETIME_239POINT5 +/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */ +#define CONFIG_ADC_DEFAULT_VDDA 3.3f + +/** @brief CAN 外设实例。 */ +#define CONFIG_CAN_INSTANCE CAN0 +/** @brief CAN 外设时钟使能枚举。 */ +#define CONFIG_CAN_RCU RCU_CAN0 +/** @brief CAN 状态报文标准帧 ID。 */ +#define CONFIG_CAN_STATUS_ID 0x321U +/** @brief CAN 状态载荷中调试标识字符 0。 */ +#define CONFIG_CAN_STATUS_TAG0 0x43U +/** @brief CAN 状态载荷中调试标识字符 1。 */ +#define CONFIG_CAN_STATUS_TAG1 0x41U +/** @brief CAN 状态载荷中调试标识字符 2。 */ +#define CONFIG_CAN_STATUS_TAG2 0x4EU +/** @brief CAN 状态载荷中调试标识字符 3。 */ +#define CONFIG_CAN_STATUS_TAG3 0x46U + +/** @brief 用户调度定时器实例。 */ +#define CONFIG_SCHED_TIMER_INSTANCE TIMER2 +/** @brief 用户调度定时器中断号。 */ +#define CONFIG_SCHED_TIMER_IRQn TIMER2_IRQn +/** @brief 用户调度定时器时钟使能宏参数。 */ +#define CONFIG_SCHED_TIMER_RCU RCU_TIMER2 +/** @brief 用户调度定时器预分频值。 */ +#define CONFIG_SCHED_TIMER_PRESCALER 179U +/** @brief 用户调度定时器周期重装值。 */ +#define CONFIG_SCHED_TIMER_PERIOD 99U +/** @brief 用户调度定时器 NVIC 抢占优先级。 */ +#define CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO 1U +/** @brief 用户调度定时器 NVIC 子优先级。 */ +#define CONFIG_SCHED_TIMER_IRQ_SUB_PRIO 0U + +/** @brief 调度基准节拍:ADC VREF 采样周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS 5U +/** @brief 调度基准节拍:ADC 温度采样/处理周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS 10U +/** @brief 调度基准节拍:CAN 接收轮询/解析周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_CAN_RX_TICKS 100U +/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_CAN_TX_TICKS 1000U +/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U + +#endif /* SYS_CONFIG_H */ diff --git a/GD32E508_DCU_APP/sys/config.h.bak_canrate b/GD32E508_DCU_APP/sys/config.h.bak_canrate new file mode 100644 index 0000000..3fea9a4 --- /dev/null +++ b/GD32E508_DCU_APP/sys/config.h.bak_canrate @@ -0,0 +1,79 @@ +/** + * @file config.h + * @brief 应用级集中配置项定义。 + * @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。 + */ +#ifndef SYS_CONFIG_H +#define SYS_CONFIG_H + +/** @brief 应用启动横幅字符串。 */ +#define APP_BANNER_TEXT "GD32E508 DCU task scheduler start" + +/** @brief USART0 默认波特率。 */ +#define CONFIG_USART0_BAUDRATE 115200U + +/** @brief ADC 外设实例。 */ +#define CONFIG_ADC_INSTANCE ADC0 +/** @brief ADC 内部温度通道号。 */ +#define CONFIG_ADC_TEMP_CHANNEL ADC_CHANNEL_16 +/** @brief ADC 内部参考电压通道号。 */ +#define CONFIG_ADC_VREFINT_CHANNEL ADC_CHANNEL_17 +/** @brief ADC 满量程计数值。 */ +#define CONFIG_ADC_MAX_VALUE 4095.0f +/** @brief 内部参考电压典型值,单位 V。 */ +#define CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE 1.20f +/** @brief 温度传感器 25℃ 对应电压,单位 V。 */ +#define CONFIG_ADC_TEMP_V25 1.43f +/** @brief 温度传感器平均斜率,单位 V/℃。 */ +#define CONFIG_ADC_TEMP_AVG_SLOPE 0.0043f +/** @brief ADC 上电后稳定等待时间,单位 ms。 */ +#define CONFIG_ADC_STABILIZE_DELAY_MS 2U +/** @brief ADC 规则通道采样时间配置。 */ +#define CONFIG_ADC_SAMPLE_TIME ADC_SAMPLETIME_239POINT5 +/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */ +#define CONFIG_ADC_DEFAULT_VDDA 3.3f + +/** @brief CAN 外设实例。 */ +#define CONFIG_CAN_INSTANCE CAN0 +/** @brief CAN 外设时钟使能枚举。 */ +#define CONFIG_CAN_RCU RCU_CAN0 +/** @brief CAN 状态报文标准帧 ID。 */ +#define CONFIG_CAN_STATUS_ID 0x321U +/** @brief CAN 标称波特率预分频值。 */ +#define CONFIG_CAN_NOMINAL_BAUD_PRESCALER 12U +/** @brief CAN 状态载荷中调试标识字符 0。 */ +#define CONFIG_CAN_STATUS_TAG0 0x43U +/** @brief CAN 状态载荷中调试标识字符 1。 */ +#define CONFIG_CAN_STATUS_TAG1 0x41U +/** @brief CAN 状态载荷中调试标识字符 2。 */ +#define CONFIG_CAN_STATUS_TAG2 0x4EU +/** @brief CAN 状态载荷中调试标识字符 3。 */ +#define CONFIG_CAN_STATUS_TAG3 0x46U + +/** @brief 用户调度定时器实例。 */ +#define CONFIG_SCHED_TIMER_INSTANCE TIMER2 +/** @brief 用户调度定时器中断号。 */ +#define CONFIG_SCHED_TIMER_IRQn TIMER2_IRQn +/** @brief 用户调度定时器时钟使能宏参数。 */ +#define CONFIG_SCHED_TIMER_RCU RCU_TIMER2 +/** @brief 用户调度定时器预分频值。 */ +#define CONFIG_SCHED_TIMER_PRESCALER 179U +/** @brief 用户调度定时器周期重装值。 */ +#define CONFIG_SCHED_TIMER_PERIOD 99U +/** @brief 用户调度定时器 NVIC 抢占优先级。 */ +#define CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO 1U +/** @brief 用户调度定时器 NVIC 子优先级。 */ +#define CONFIG_SCHED_TIMER_IRQ_SUB_PRIO 0U + +/** @brief 调度基准节拍:ADC VREF 采样周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS 5U +/** @brief 调度基准节拍:ADC 温度采样/处理周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS 10U +/** @brief 调度基准节拍:CAN 接收轮询/解析周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_CAN_RX_TICKS 100U +/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_CAN_TX_TICKS 1000U +/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U + +#endif /* SYS_CONFIG_H */ diff --git a/GD32E508_DCU_APP/sys/config.h.bak_canrcu b/GD32E508_DCU_APP/sys/config.h.bak_canrcu new file mode 100644 index 0000000..ec08ca5 --- /dev/null +++ b/GD32E508_DCU_APP/sys/config.h.bak_canrcu @@ -0,0 +1,77 @@ +/** + * @file config.h + * @brief 应用级集中配置项定义。 + * @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。 + */ +#ifndef SYS_CONFIG_H +#define SYS_CONFIG_H + +/** @brief 应用启动横幅字符串。 */ +#define APP_BANNER_TEXT "GD32E508 DCU task scheduler start" + +/** @brief USART0 默认波特率。 */ +#define CONFIG_USART0_BAUDRATE 115200U + +/** @brief ADC 外设实例。 */ +#define CONFIG_ADC_INSTANCE ADC0 +/** @brief ADC 内部温度通道号。 */ +#define CONFIG_ADC_TEMP_CHANNEL ADC_CHANNEL_16 +/** @brief ADC 内部参考电压通道号。 */ +#define CONFIG_ADC_VREFINT_CHANNEL ADC_CHANNEL_17 +/** @brief ADC 满量程计数值。 */ +#define CONFIG_ADC_MAX_VALUE 4095.0f +/** @brief 内部参考电压典型值,单位 V。 */ +#define CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE 1.20f +/** @brief 温度传感器 25℃ 对应电压,单位 V。 */ +#define CONFIG_ADC_TEMP_V25 1.43f +/** @brief 温度传感器平均斜率,单位 V/℃。 */ +#define CONFIG_ADC_TEMP_AVG_SLOPE 0.0043f +/** @brief ADC 上电后稳定等待时间,单位 ms。 */ +#define CONFIG_ADC_STABILIZE_DELAY_MS 2U +/** @brief ADC 规则通道采样时间配置。 */ +#define CONFIG_ADC_SAMPLE_TIME ADC_SAMPLETIME_239POINT5 +/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */ +#define CONFIG_ADC_DEFAULT_VDDA 3.3f + +/** @brief CAN 外设实例。 */ +#define CONFIG_CAN_INSTANCE CAN0 +/** @brief CAN 状态报文标准帧 ID。 */ +#define CONFIG_CAN_STATUS_ID 0x321U +/** @brief CAN 标称波特率预分频值。 */ +#define CONFIG_CAN_NOMINAL_BAUD_PRESCALER 12U +/** @brief CAN 状态载荷中调试标识字符 0。 */ +#define CONFIG_CAN_STATUS_TAG0 0x43U +/** @brief CAN 状态载荷中调试标识字符 1。 */ +#define CONFIG_CAN_STATUS_TAG1 0x41U +/** @brief CAN 状态载荷中调试标识字符 2。 */ +#define CONFIG_CAN_STATUS_TAG2 0x4EU +/** @brief CAN 状态载荷中调试标识字符 3。 */ +#define CONFIG_CAN_STATUS_TAG3 0x46U + +/** @brief 用户调度定时器实例。 */ +#define CONFIG_SCHED_TIMER_INSTANCE TIMER2 +/** @brief 用户调度定时器中断号。 */ +#define CONFIG_SCHED_TIMER_IRQn TIMER2_IRQn +/** @brief 用户调度定时器时钟使能宏参数。 */ +#define CONFIG_SCHED_TIMER_RCU RCU_TIMER2 +/** @brief 用户调度定时器预分频值。 */ +#define CONFIG_SCHED_TIMER_PRESCALER 179U +/** @brief 用户调度定时器周期重装值。 */ +#define CONFIG_SCHED_TIMER_PERIOD 99U +/** @brief 用户调度定时器 NVIC 抢占优先级。 */ +#define CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO 1U +/** @brief 用户调度定时器 NVIC 子优先级。 */ +#define CONFIG_SCHED_TIMER_IRQ_SUB_PRIO 0U + +/** @brief 调度基准节拍:ADC VREF 采样周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS 5U +/** @brief 调度基准节拍:ADC 温度采样/处理周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS 10U +/** @brief 调度基准节拍:CAN 接收轮询/解析周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_CAN_RX_TICKS 100U +/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_CAN_TX_TICKS 1000U +/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U + +#endif /* SYS_CONFIG_H */ diff --git a/GD32E508_DCU_APP/sys/config.h.bak_sch b/GD32E508_DCU_APP/sys/config.h.bak_sch new file mode 100644 index 0000000..33a88f7 --- /dev/null +++ b/GD32E508_DCU_APP/sys/config.h.bak_sch @@ -0,0 +1,77 @@ +/** + * @file config.h + * @brief 应用级集中配置项定义。 + * @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。 + */ +#ifndef SYS_CONFIG_H +#define SYS_CONFIG_H + +/** @brief 应用启动横幅字符串。 */ +#define APP_BANNER_TEXT "GD32E508 DCU task scheduler start" + +/** @brief USART0 默认波特率。 */ +#define CONFIG_USART0_BAUDRATE 115200U + +/** @brief ADC 外设实例。 */ +#define CONFIG_ADC_INSTANCE ADC0 +/** @brief ADC 内部温度通道号。 */ +#define CONFIG_ADC_TEMP_CHANNEL ADC_CHANNEL_16 +/** @brief ADC 内部参考电压通道号。 */ +#define CONFIG_ADC_VREFINT_CHANNEL ADC_CHANNEL_17 +/** @brief ADC 满量程计数值。 */ +#define CONFIG_ADC_MAX_VALUE 4095.0f +/** @brief 内部参考电压典型值,单位 V。 */ +#define CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE 1.20f +/** @brief 温度传感器 25℃ 对应电压,单位 V。 */ +#define CONFIG_ADC_TEMP_V25 1.43f +/** @brief 温度传感器平均斜率,单位 V/℃。 */ +#define CONFIG_ADC_TEMP_AVG_SLOPE 0.0043f +/** @brief ADC 上电后稳定等待时间,单位 ms。 */ +#define CONFIG_ADC_STABILIZE_DELAY_MS 2U +/** @brief ADC 规则通道采样时间配置。 */ +#define CONFIG_ADC_SAMPLE_TIME ADC_SAMPLETIME_239POINT5 +/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */ +#define CONFIG_ADC_DEFAULT_VDDA 3.3f + +/** @brief CAN 外设实例。 */ +#define CONFIG_CAN_INSTANCE CAN0 +/** @brief CAN 外设时钟使能枚举。 */ +#define CONFIG_CAN_RCU RCU_CAN0 +/** @brief CAN 状态报文标准帧 ID。 */ +#define CONFIG_CAN_STATUS_ID 0x321U +/** @brief CAN 状态载荷中调试标识字符 0。 */ +#define CONFIG_CAN_STATUS_TAG0 0x43U +/** @brief CAN 状态载荷中调试标识字符 1。 */ +#define CONFIG_CAN_STATUS_TAG1 0x41U +/** @brief CAN 状态载荷中调试标识字符 2。 */ +#define CONFIG_CAN_STATUS_TAG2 0x4EU +/** @brief CAN 状态载荷中调试标识字符 3。 */ +#define CONFIG_CAN_STATUS_TAG3 0x46U + +/** @brief 用户调度定时器实例。 */ +#define CONFIG_SCHED_TIMER_INSTANCE TIMER2 +/** @brief 用户调度定时器中断号。 */ +#define CONFIG_SCHED_TIMER_IRQn TIMER2_IRQn +/** @brief 用户调度定时器时钟使能宏参数。 */ +#define CONFIG_SCHED_TIMER_RCU RCU_TIMER2 +/** @brief 用户调度定时器预分频值。 */ +#define CONFIG_SCHED_TIMER_PRESCALER 179U +/** @brief 用户调度定时器周期重装值。 */ +#define CONFIG_SCHED_TIMER_PERIOD 99U +/** @brief 用户调度定时器 NVIC 抢占优先级。 */ +#define CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO 1U +/** @brief 用户调度定时器 NVIC 子优先级。 */ +#define CONFIG_SCHED_TIMER_IRQ_SUB_PRIO 0U + +/** @brief 调度基准节拍:ADC VREF 采样周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS 5U +/** @brief 调度基准节拍:ADC 温度采样/处理周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS 10U +/** @brief 调度基准节拍:CAN 接收轮询/解析周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_CAN_RX_TICKS 100U +/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_CAN_TX_TICKS 1000U +/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U + +#endif /* SYS_CONFIG_H */ diff --git a/GD32E508_DCU_APP/sys/config.h.bak_txisr b/GD32E508_DCU_APP/sys/config.h.bak_txisr new file mode 100644 index 0000000..33a88f7 --- /dev/null +++ b/GD32E508_DCU_APP/sys/config.h.bak_txisr @@ -0,0 +1,77 @@ +/** + * @file config.h + * @brief 应用级集中配置项定义。 + * @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。 + */ +#ifndef SYS_CONFIG_H +#define SYS_CONFIG_H + +/** @brief 应用启动横幅字符串。 */ +#define APP_BANNER_TEXT "GD32E508 DCU task scheduler start" + +/** @brief USART0 默认波特率。 */ +#define CONFIG_USART0_BAUDRATE 115200U + +/** @brief ADC 外设实例。 */ +#define CONFIG_ADC_INSTANCE ADC0 +/** @brief ADC 内部温度通道号。 */ +#define CONFIG_ADC_TEMP_CHANNEL ADC_CHANNEL_16 +/** @brief ADC 内部参考电压通道号。 */ +#define CONFIG_ADC_VREFINT_CHANNEL ADC_CHANNEL_17 +/** @brief ADC 满量程计数值。 */ +#define CONFIG_ADC_MAX_VALUE 4095.0f +/** @brief 内部参考电压典型值,单位 V。 */ +#define CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE 1.20f +/** @brief 温度传感器 25℃ 对应电压,单位 V。 */ +#define CONFIG_ADC_TEMP_V25 1.43f +/** @brief 温度传感器平均斜率,单位 V/℃。 */ +#define CONFIG_ADC_TEMP_AVG_SLOPE 0.0043f +/** @brief ADC 上电后稳定等待时间,单位 ms。 */ +#define CONFIG_ADC_STABILIZE_DELAY_MS 2U +/** @brief ADC 规则通道采样时间配置。 */ +#define CONFIG_ADC_SAMPLE_TIME ADC_SAMPLETIME_239POINT5 +/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */ +#define CONFIG_ADC_DEFAULT_VDDA 3.3f + +/** @brief CAN 外设实例。 */ +#define CONFIG_CAN_INSTANCE CAN0 +/** @brief CAN 外设时钟使能枚举。 */ +#define CONFIG_CAN_RCU RCU_CAN0 +/** @brief CAN 状态报文标准帧 ID。 */ +#define CONFIG_CAN_STATUS_ID 0x321U +/** @brief CAN 状态载荷中调试标识字符 0。 */ +#define CONFIG_CAN_STATUS_TAG0 0x43U +/** @brief CAN 状态载荷中调试标识字符 1。 */ +#define CONFIG_CAN_STATUS_TAG1 0x41U +/** @brief CAN 状态载荷中调试标识字符 2。 */ +#define CONFIG_CAN_STATUS_TAG2 0x4EU +/** @brief CAN 状态载荷中调试标识字符 3。 */ +#define CONFIG_CAN_STATUS_TAG3 0x46U + +/** @brief 用户调度定时器实例。 */ +#define CONFIG_SCHED_TIMER_INSTANCE TIMER2 +/** @brief 用户调度定时器中断号。 */ +#define CONFIG_SCHED_TIMER_IRQn TIMER2_IRQn +/** @brief 用户调度定时器时钟使能宏参数。 */ +#define CONFIG_SCHED_TIMER_RCU RCU_TIMER2 +/** @brief 用户调度定时器预分频值。 */ +#define CONFIG_SCHED_TIMER_PRESCALER 179U +/** @brief 用户调度定时器周期重装值。 */ +#define CONFIG_SCHED_TIMER_PERIOD 99U +/** @brief 用户调度定时器 NVIC 抢占优先级。 */ +#define CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO 1U +/** @brief 用户调度定时器 NVIC 子优先级。 */ +#define CONFIG_SCHED_TIMER_IRQ_SUB_PRIO 0U + +/** @brief 调度基准节拍:ADC VREF 采样周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS 5U +/** @brief 调度基准节拍:ADC 温度采样/处理周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS 10U +/** @brief 调度基准节拍:CAN 接收轮询/解析周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_CAN_RX_TICKS 100U +/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_CAN_TX_TICKS 1000U +/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */ +#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U + +#endif /* SYS_CONFIG_H */ diff --git a/GD32E508_DCU_APP/sys/initial.cpp b/GD32E508_DCU_APP/sys/initial.cpp new file mode 100644 index 0000000..9f7f1f1 --- /dev/null +++ b/GD32E508_DCU_APP/sys/initial.cpp @@ -0,0 +1,26 @@ +/** + * @file initial.cpp + * @brief 应用启动初始化实现。 + */ + +#include "sys/initial.h" + +#include "device/canfd.h" +#include "device/temp_sensor.h" +#include "device/usart0.h" +#include "device/Timer.h" +#include "device/adc.h" +#include "sys/systick.h" +#include "sys/task_manager.h" + +void AppInitializer::init() +{ + SysTickTimer::initMs(); + Usart0::configurePins(); + Usart0::init(); + TempSensor::init(); + while(!Adc::dmaReady()) { } + CanFd::init(); + TaskManager::init(); + SchedulerTimer::init(); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/initial.cpp.bak_dma b/GD32E508_DCU_APP/sys/initial.cpp.bak_dma new file mode 100644 index 0000000..2eb750c --- /dev/null +++ b/GD32E508_DCU_APP/sys/initial.cpp.bak_dma @@ -0,0 +1,28 @@ +/** + * @file initial.cpp + * @brief 应用启动初始化实现。 + */ + +#include "sys/initial.h" + +#include "device/canfd.h" +#include "device/temp_sensor.h" +#include "device/usart0.h" +#include "device/Timer.h" +#include "sys/systick.h" +#include "sys/task_manager.h" + +/** + * @brief 执行应用启动所需的底层模块初始化。 + * @note 初始化顺序:系统时基 → 串口 → 温度采集 → CAN 通信 → 任务调度 → 定时器。 + */ +void app_init(void) +{ + systick_config_ms(); + usart0_gpio_config(); + usart0_config(); + temp_sensor_init(); + canfd_init(); + task_manager_init(); + user_timer_init(); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/initial.cpp.bak_fixinclude b/GD32E508_DCU_APP/sys/initial.cpp.bak_fixinclude new file mode 100644 index 0000000..2eb750c --- /dev/null +++ b/GD32E508_DCU_APP/sys/initial.cpp.bak_fixinclude @@ -0,0 +1,28 @@ +/** + * @file initial.cpp + * @brief 应用启动初始化实现。 + */ + +#include "sys/initial.h" + +#include "device/canfd.h" +#include "device/temp_sensor.h" +#include "device/usart0.h" +#include "device/Timer.h" +#include "sys/systick.h" +#include "sys/task_manager.h" + +/** + * @brief 执行应用启动所需的底层模块初始化。 + * @note 初始化顺序:系统时基 → 串口 → 温度采集 → CAN 通信 → 任务调度 → 定时器。 + */ +void app_init(void) +{ + systick_config_ms(); + usart0_gpio_config(); + usart0_config(); + temp_sensor_init(); + canfd_init(); + task_manager_init(); + user_timer_init(); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/initial.cpp.bak_oop b/GD32E508_DCU_APP/sys/initial.cpp.bak_oop new file mode 100644 index 0000000..f923ed2 --- /dev/null +++ b/GD32E508_DCU_APP/sys/initial.cpp.bak_oop @@ -0,0 +1,31 @@ +/** + * @file initial.cpp + * @brief 应用启动初始化实现。 + */ + +#include "sys/initial.h" + +#include "device/canfd.h" +#include "device/temp_sensor.h" +#include "device/usart0.h" +#include "device/Timer.h" +#include "device/adc.h" +#include "sys/systick.h" +#include "sys/task_manager.h" + +/** + * @brief 执行应用启动所需的底层模块初始化。 + * @note 初始化顺序:系统时基 → 串口 → 温度采集 → CAN 通信 → 任务调度 → 定时器。 + */ +void app_init(void) +{ + systick_config_ms(); + usart0_gpio_config(); + usart0_config(); + temp_sensor_init(); + while(adc_dma_ready() == 0U) { + } + canfd_init(); + task_manager_init(); + user_timer_init(); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/initial.cpp.bak_restore b/GD32E508_DCU_APP/sys/initial.cpp.bak_restore new file mode 100644 index 0000000..5fef8ca --- /dev/null +++ b/GD32E508_DCU_APP/sys/initial.cpp.bak_restore @@ -0,0 +1,28 @@ +/** + * @file initial.cpp + * @brief 应用启动初始化实现。 + */ + +#include "initial.h" + +#include "../device/canfd.h" +#include "../device/temp_sensor.h" +#include "../device/usart0.h" +#include "../device/Timer.h" +#include "systick.h" +#include "task_manager.h" + +/** + * @brief 执行应用启动所需的底层模块初始化。 + * @note 初始化顺序:系统时基 → 串口 → 温度采集 → CAN 通信 → 任务调度 → 定时器。 + */ +void app_init(void) +{ + systick_config_ms(); + usart0_gpio_config(); + usart0_config(); + temp_sensor_init(); + canfd_init(); + task_manager_init(); + user_timer_init(); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/initial.h b/GD32E508_DCU_APP/sys/initial.h new file mode 100644 index 0000000..6c6e912 --- /dev/null +++ b/GD32E508_DCU_APP/sys/initial.h @@ -0,0 +1,13 @@ +#ifndef SYS_INITIAL_H +#define SYS_INITIAL_H + +/** + * @brief 应用初始化器。 + * @note 负责组织系统时基、串口、采样、通信、调度器等模块的启动顺序。 + */ +class AppInitializer { +public: + static void init(); +}; + +#endif /* SYS_INITIAL_H */ diff --git a/GD32E508_DCU_APP/sys/initial.h.bak_oop b/GD32E508_DCU_APP/sys/initial.h.bak_oop new file mode 100644 index 0000000..9c7ae7d --- /dev/null +++ b/GD32E508_DCU_APP/sys/initial.h.bak_oop @@ -0,0 +1,17 @@ +/** + * @file initial.h + * @brief 应用初始化入口接口定义。 + */ +#ifndef SYS_INITIAL_H +#define SYS_INITIAL_H + +/** + * @brief 执行应用启动所需的底层模块初始化。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 按既定顺序初始化系统时基、串口、采样、通信与任务调度相关模块。 + */ +void app_init(void); + +#endif /* SYS_INITIAL_H */ diff --git a/GD32E508_DCU_APP/sys/systick.cpp b/GD32E508_DCU_APP/sys/systick.cpp new file mode 100644 index 0000000..93d56a0 --- /dev/null +++ b/GD32E508_DCU_APP/sys/systick.cpp @@ -0,0 +1,29 @@ +/** + * @file systick.cpp + * @brief SysTick 时基模块实现。 + */ + +#include "main.h" +#include "sys/systick.h" + +volatile uint32_t SysTickTimer::s_ms = 0U; + +void SysTickTimer::onInterrupt() +{ + s_ms++; +} + +void SysTickTimer::delayMs(uint32_t ms) +{ + uint32_t start = s_ms; + while((s_ms - start) < ms) { } +} + +void SysTickTimer::initMs() +{ + SystemCoreClockUpdate(); + SysTick_Config(SystemCoreClock / 1000U); +} + +/* ----- 中断入口 C 包装 ---------------------------------------------- */ +void SysTick_Handler(void) { SysTickTimer::onInterrupt(); } \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/systick.cpp.bak_fixinclude b/GD32E508_DCU_APP/sys/systick.cpp.bak_fixinclude new file mode 100644 index 0000000..a8c2363 --- /dev/null +++ b/GD32E508_DCU_APP/sys/systick.cpp.bak_fixinclude @@ -0,0 +1,38 @@ +/** + * @file systick.cpp + * @brief SysTick 时基配置、中断处理与毫秒延时实现。 + */ + +#include "main.h" +#include "sys/systick.h" + +/** @brief 自启动后累计的系统节拍毫秒数。 */ +static volatile uint32_t systick_ms = 0U; + +/** + * @brief SysTick 中断服务例程,每毫秒递增一次节拍计数器。 + */ +void SysTick_Handler(void) +{ + systick_ms++; +} + +/** + * @brief 执行阻塞式毫秒延时。 + * @param[in] ms 需要延时的毫秒数。 + */ +void delay_ms(uint32_t ms) +{ + uint32_t start = systick_ms; + while((systick_ms - start) < ms) { + } +} + +/** + * @brief 配置 SysTick 为 1ms 系统节拍。 + */ +void systick_config_ms(void) +{ + SystemCoreClockUpdate(); + SysTick_Config(SystemCoreClock / 1000U); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/systick.cpp.bak_oop b/GD32E508_DCU_APP/sys/systick.cpp.bak_oop new file mode 100644 index 0000000..a8c2363 --- /dev/null +++ b/GD32E508_DCU_APP/sys/systick.cpp.bak_oop @@ -0,0 +1,38 @@ +/** + * @file systick.cpp + * @brief SysTick 时基配置、中断处理与毫秒延时实现。 + */ + +#include "main.h" +#include "sys/systick.h" + +/** @brief 自启动后累计的系统节拍毫秒数。 */ +static volatile uint32_t systick_ms = 0U; + +/** + * @brief SysTick 中断服务例程,每毫秒递增一次节拍计数器。 + */ +void SysTick_Handler(void) +{ + systick_ms++; +} + +/** + * @brief 执行阻塞式毫秒延时。 + * @param[in] ms 需要延时的毫秒数。 + */ +void delay_ms(uint32_t ms) +{ + uint32_t start = systick_ms; + while((systick_ms - start) < ms) { + } +} + +/** + * @brief 配置 SysTick 为 1ms 系统节拍。 + */ +void systick_config_ms(void) +{ + SystemCoreClockUpdate(); + SysTick_Config(SystemCoreClock / 1000U); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/systick.cpp.bak_restore b/GD32E508_DCU_APP/sys/systick.cpp.bak_restore new file mode 100644 index 0000000..9b064b1 --- /dev/null +++ b/GD32E508_DCU_APP/sys/systick.cpp.bak_restore @@ -0,0 +1,38 @@ +/** + * @file systick.cpp + * @brief SysTick 时基配置、中断处理与毫秒延时实现。 + */ + +#include "../main.h" +#include "systick.h" + +/** @brief 自启动后累计的系统节拍毫秒数。 */ +static volatile uint32_t systick_ms = 0U; + +/** + * @brief SysTick 中断服务例程,每毫秒递增一次节拍计数器。 + */ +void SysTick_Handler(void) +{ + systick_ms++; +} + +/** + * @brief 执行阻塞式毫秒延时。 + * @param[in] ms 需要延时的毫秒数。 + */ +void delay_ms(uint32_t ms) +{ + uint32_t start = systick_ms; + while((systick_ms - start) < ms) { + } +} + +/** + * @brief 配置 SysTick 为 1ms 系统节拍。 + */ +void systick_config_ms(void) +{ + SystemCoreClockUpdate(); + SysTick_Config(SystemCoreClock / 1000U); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/systick.h b/GD32E508_DCU_APP/sys/systick.h new file mode 100644 index 0000000..623a988 --- /dev/null +++ b/GD32E508_DCU_APP/sys/systick.h @@ -0,0 +1,20 @@ +#ifndef SYS_SYSTICK_H +#define SYS_SYSTICK_H + +#include + +/** + * @brief SysTick 时基模块。 + * @note 负责 1ms 系统节拍维护与阻塞式毫秒延时。 + */ +class SysTickTimer { +public: + static void initMs(); + static void delayMs(uint32_t ms); + static void onInterrupt(); + +private: + static volatile uint32_t s_ms; +}; + +#endif /* SYS_SYSTICK_H */ diff --git a/GD32E508_DCU_APP/sys/systick.h.bak_oop b/GD32E508_DCU_APP/sys/systick.h.bak_oop new file mode 100644 index 0000000..a0172a5 --- /dev/null +++ b/GD32E508_DCU_APP/sys/systick.h.bak_oop @@ -0,0 +1,26 @@ +/** + * @file systick.h + * @brief SysTick 时基配置与毫秒延时接口定义。 + */ +#ifndef SYS_SYSTICK_H +#define SYS_SYSTICK_H + +#include + +/** + * @brief 配置 SysTick 为 1ms 系统节拍。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void systick_config_ms(void); + +/** + * @brief 执行阻塞式毫秒延时。 + * @param[in] ms 需要延时的毫秒数。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void delay_ms(uint32_t ms); + +#endif /* SYS_SYSTICK_H */ diff --git a/GD32E508_DCU_APP/sys/task_manager.c.bak_20260510_0448 b/GD32E508_DCU_APP/sys/task_manager.c.bak_20260510_0448 new file mode 100644 index 0000000..a853f4b --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_manager.c.bak_20260510_0448 @@ -0,0 +1,63 @@ +#include "main.h" +#include "sys/task_manager.h" + +task_scheduler_t g_scheduler = {0}; + +void task_manager_init(void) +{ + g_scheduler.task_flags = 0U; + g_scheduler.tick_100us = 0U; + g_scheduler.tick_1ms = 0U; + g_scheduler.tick_10ms = 0U; + g_scheduler.tick_100ms = 0U; + g_scheduler.tick_1s = 0U; +} + +void task_manager_set(uint32_t task_mask) +{ + g_scheduler.task_flags |= task_mask; +} + +uint32_t task_manager_take_pending(void) +{ + uint32_t pending; + + __disable_irq(); + pending = g_scheduler.task_flags; + g_scheduler.task_flags &= ~pending; + __enable_irq(); + + return pending; +} + +void task_manager_tick_100us(void) +{ + g_scheduler.tick_100us++; + + if((g_scheduler.tick_100us % 5U) == 0U) { + task_manager_set(1UL << TASK_ADC_VREF_SAMPLE); + } + + if((g_scheduler.tick_100us % 10U) == 0U) { + g_scheduler.tick_1ms++; + task_manager_set((1UL << TASK_ADC_TEMP_SAMPLE) | + (1UL << TASK_ADC_PROCESS)); + } + + if((g_scheduler.tick_100us % 100U) == 0U) { + g_scheduler.tick_10ms++; + task_manager_set(1UL << TASK_CAN_POLL); + } + + if((g_scheduler.tick_100us % 1000U) == 0U) { + g_scheduler.tick_100ms++; + task_manager_set((1UL << TASK_CAN_REPORT) | + (1UL << TASK_STATUS_LED)); + } + + if((g_scheduler.tick_100us % 10000U) == 0U) { + g_scheduler.tick_1s++; + task_manager_set((1UL << TASK_HEARTBEAT) | + (1UL << TASK_DIAGNOSTIC_PRINT)); + } +} diff --git a/GD32E508_DCU_APP/sys/task_manager.cpp b/GD32E508_DCU_APP/sys/task_manager.cpp new file mode 100644 index 0000000..e1f8d59 --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_manager.cpp @@ -0,0 +1,69 @@ +/** + * @file task_manager.cpp + * @brief 任务调度管理器实现。 + */ + +#include "main.h" +#include "sys/task_manager.h" +#include "sys/config.h" + +task_scheduler_t g_scheduler = {0}; + +void TaskManager::init() +{ + g_scheduler.task_flags = 0U; + g_scheduler.tick_100us = 0U; + g_scheduler.tick_1ms = 0U; + g_scheduler.tick_10ms = 0U; + g_scheduler.tick_100ms = 0U; + g_scheduler.tick_1s = 0U; +} + +void TaskManager::set(uint32_t taskMask) +{ + g_scheduler.task_flags |= taskMask; +} + +uint32_t TaskManager::takePending() +{ + uint32_t pending; + __disable_irq(); + pending = g_scheduler.task_flags; + g_scheduler.task_flags &= ~pending; + __enable_irq(); + return pending; +} + +void TaskManager::tick100us() +{ + g_scheduler.tick_100us++; + + if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS) == 0U) { + set(1UL << TASK_ADC_VREF_SAMPLE); + } + + if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS) == 0U) { + g_scheduler.tick_1ms++; + set((1UL << TASK_ADC_TEMP_SAMPLE) | (1UL << TASK_ADC_PROCESS)); + } + + if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_CAN_RX_TICKS) == 0U) { + g_scheduler.tick_10ms++; + set((1UL << TASK_CAN_RX_POLL) | (1UL << TASK_CAN_RX_PARSE)); + } + + if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_CAN_TX_TICKS) == 0U) { + g_scheduler.tick_100ms++; + set((1UL << TASK_CAN_TX_PREPARE) | (1UL << TASK_CAN_TX_SEND) | (1UL << TASK_STATUS_LED)); + } + + if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_HEARTBEAT_TICKS) == 0U) { + g_scheduler.tick_1s++; + set((1UL << TASK_HEARTBEAT) | (1UL << TASK_DIAGNOSTIC_PRINT)); + } +} + +task_scheduler_t &TaskManager::state() +{ + return g_scheduler; +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/task_manager.cpp.bak_config b/GD32E508_DCU_APP/sys/task_manager.cpp.bak_config new file mode 100644 index 0000000..61bc513 --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_manager.cpp.bak_config @@ -0,0 +1,85 @@ +/** + * @file task_manager.cpp + * @brief 任务标志调度与时基驱动实现。 + */ + +#include "main.h" +#include "sys/task_manager.h" + +task_scheduler_t g_scheduler = {0}; + +/** + * @brief 初始化任务管理器内部状态。 + */ +void task_manager_init(void) +{ + g_scheduler.task_flags = 0U; + g_scheduler.tick_100us = 0U; + g_scheduler.tick_1ms = 0U; + g_scheduler.tick_10ms = 0U; + g_scheduler.tick_100ms = 0U; + g_scheduler.tick_1s = 0U; +} + +/** + * @brief 设置一个或多个待执行任务标志。 + * @param[in] task_mask 待置位的任务位图掩码。 + */ +void task_manager_set(uint32_t task_mask) +{ + g_scheduler.task_flags |= task_mask; +} + +/** + * @brief 原子地取出当前所有待执行任务标志并清零。 + * @retval 返回当前待执行任务位图。 + */ +uint32_t task_manager_take_pending(void) +{ + uint32_t pending; + + __disable_irq(); + pending = g_scheduler.task_flags; + g_scheduler.task_flags &= ~pending; + __enable_irq(); + + return pending; +} + +/** + * @brief 推进一次 100us 调度节拍并按周期置位待执行任务。 + * @note 按 500us / 1ms / 10ms / 100ms / 1s 档次分别置位对应任务组。 + */ +void task_manager_tick_100us(void) +{ + g_scheduler.tick_100us++; + + if((g_scheduler.tick_100us % 5U) == 0U) { + task_manager_set(1UL << TASK_ADC_VREF_SAMPLE); + } + + if((g_scheduler.tick_100us % 10U) == 0U) { + g_scheduler.tick_1ms++; + task_manager_set((1UL << TASK_ADC_TEMP_SAMPLE) | + (1UL << TASK_ADC_PROCESS)); + } + + if((g_scheduler.tick_100us % 100U) == 0U) { + g_scheduler.tick_10ms++; + task_manager_set((1UL << TASK_CAN_RX_POLL) | + (1UL << TASK_CAN_RX_PARSE)); + } + + if((g_scheduler.tick_100us % 1000U) == 0U) { + g_scheduler.tick_100ms++; + task_manager_set((1UL << TASK_CAN_TX_PREPARE) | + (1UL << TASK_CAN_TX_SEND) | + (1UL << TASK_STATUS_LED)); + } + + if((g_scheduler.tick_100us % 10000U) == 0U) { + g_scheduler.tick_1s++; + task_manager_set((1UL << TASK_HEARTBEAT) | + (1UL << TASK_DIAGNOSTIC_PRINT)); + } +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/task_manager.cpp.bak_fixinclude b/GD32E508_DCU_APP/sys/task_manager.cpp.bak_fixinclude new file mode 100644 index 0000000..61bc513 --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_manager.cpp.bak_fixinclude @@ -0,0 +1,85 @@ +/** + * @file task_manager.cpp + * @brief 任务标志调度与时基驱动实现。 + */ + +#include "main.h" +#include "sys/task_manager.h" + +task_scheduler_t g_scheduler = {0}; + +/** + * @brief 初始化任务管理器内部状态。 + */ +void task_manager_init(void) +{ + g_scheduler.task_flags = 0U; + g_scheduler.tick_100us = 0U; + g_scheduler.tick_1ms = 0U; + g_scheduler.tick_10ms = 0U; + g_scheduler.tick_100ms = 0U; + g_scheduler.tick_1s = 0U; +} + +/** + * @brief 设置一个或多个待执行任务标志。 + * @param[in] task_mask 待置位的任务位图掩码。 + */ +void task_manager_set(uint32_t task_mask) +{ + g_scheduler.task_flags |= task_mask; +} + +/** + * @brief 原子地取出当前所有待执行任务标志并清零。 + * @retval 返回当前待执行任务位图。 + */ +uint32_t task_manager_take_pending(void) +{ + uint32_t pending; + + __disable_irq(); + pending = g_scheduler.task_flags; + g_scheduler.task_flags &= ~pending; + __enable_irq(); + + return pending; +} + +/** + * @brief 推进一次 100us 调度节拍并按周期置位待执行任务。 + * @note 按 500us / 1ms / 10ms / 100ms / 1s 档次分别置位对应任务组。 + */ +void task_manager_tick_100us(void) +{ + g_scheduler.tick_100us++; + + if((g_scheduler.tick_100us % 5U) == 0U) { + task_manager_set(1UL << TASK_ADC_VREF_SAMPLE); + } + + if((g_scheduler.tick_100us % 10U) == 0U) { + g_scheduler.tick_1ms++; + task_manager_set((1UL << TASK_ADC_TEMP_SAMPLE) | + (1UL << TASK_ADC_PROCESS)); + } + + if((g_scheduler.tick_100us % 100U) == 0U) { + g_scheduler.tick_10ms++; + task_manager_set((1UL << TASK_CAN_RX_POLL) | + (1UL << TASK_CAN_RX_PARSE)); + } + + if((g_scheduler.tick_100us % 1000U) == 0U) { + g_scheduler.tick_100ms++; + task_manager_set((1UL << TASK_CAN_TX_PREPARE) | + (1UL << TASK_CAN_TX_SEND) | + (1UL << TASK_STATUS_LED)); + } + + if((g_scheduler.tick_100us % 10000U) == 0U) { + g_scheduler.tick_1s++; + task_manager_set((1UL << TASK_HEARTBEAT) | + (1UL << TASK_DIAGNOSTIC_PRINT)); + } +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/task_manager.cpp.bak_oop b/GD32E508_DCU_APP/sys/task_manager.cpp.bak_oop new file mode 100644 index 0000000..eb9be32 --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_manager.cpp.bak_oop @@ -0,0 +1,86 @@ +/** + * @file task_manager.cpp + * @brief 任务标志调度与时基驱动实现。 + */ + +#include "main.h" +#include "sys/task_manager.h" +#include "sys/config.h" + +task_scheduler_t g_scheduler = {0}; + +/** + * @brief 初始化任务管理器内部状态。 + */ +void task_manager_init(void) +{ + g_scheduler.task_flags = 0U; + g_scheduler.tick_100us = 0U; + g_scheduler.tick_1ms = 0U; + g_scheduler.tick_10ms = 0U; + g_scheduler.tick_100ms = 0U; + g_scheduler.tick_1s = 0U; +} + +/** + * @brief 设置一个或多个待执行任务标志。 + * @param[in] task_mask 待置位的任务位图掩码。 + */ +void task_manager_set(uint32_t task_mask) +{ + g_scheduler.task_flags |= task_mask; +} + +/** + * @brief 原子地取出当前所有待执行任务标志并清零。 + * @retval 返回当前待执行任务位图。 + */ +uint32_t task_manager_take_pending(void) +{ + uint32_t pending; + + __disable_irq(); + pending = g_scheduler.task_flags; + g_scheduler.task_flags &= ~pending; + __enable_irq(); + + return pending; +} + +/** + * @brief 推进一次 100us 调度节拍并按周期置位待执行任务。 + * @note 按 500us / 1ms / 10ms / 100ms / 1s 档次分别置位对应任务组。 + */ +void task_manager_tick_100us(void) +{ + g_scheduler.tick_100us++; + + if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS) == 0U) { + task_manager_set(1UL << TASK_ADC_VREF_SAMPLE); + } + + if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS) == 0U) { + g_scheduler.tick_1ms++; + task_manager_set((1UL << TASK_ADC_TEMP_SAMPLE) | + (1UL << TASK_ADC_PROCESS)); + } + + if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_CAN_RX_TICKS) == 0U) { + g_scheduler.tick_10ms++; + task_manager_set((1UL << TASK_CAN_RX_POLL) | + (1UL << TASK_CAN_RX_PARSE)); + } + + if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_CAN_TX_TICKS) == 0U) { + g_scheduler.tick_100ms++; + task_manager_set((1UL << TASK_CAN_TX_PREPARE) | + (1UL << TASK_CAN_TX_SEND) | + (1UL << TASK_STATUS_LED)); + } + + if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_HEARTBEAT_TICKS) == 0U) { + g_scheduler.tick_1s++; + task_manager_set((1UL << TASK_HEARTBEAT) | + (1UL << TASK_DIAGNOSTIC_PRINT)); + } +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/task_manager.cpp.bak_restore b/GD32E508_DCU_APP/sys/task_manager.cpp.bak_restore new file mode 100644 index 0000000..c1e0c0f --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_manager.cpp.bak_restore @@ -0,0 +1,85 @@ +/** + * @file task_manager.cpp + * @brief 任务标志调度与时基驱动实现。 + */ + +#include "../main.h" +#include "task_manager.h" + +task_scheduler_t g_scheduler = {0}; + +/** + * @brief 初始化任务管理器内部状态。 + */ +void task_manager_init(void) +{ + g_scheduler.task_flags = 0U; + g_scheduler.tick_100us = 0U; + g_scheduler.tick_1ms = 0U; + g_scheduler.tick_10ms = 0U; + g_scheduler.tick_100ms = 0U; + g_scheduler.tick_1s = 0U; +} + +/** + * @brief 设置一个或多个待执行任务标志。 + * @param[in] task_mask 待置位的任务位图掩码。 + */ +void task_manager_set(uint32_t task_mask) +{ + g_scheduler.task_flags |= task_mask; +} + +/** + * @brief 原子地取出当前所有待执行任务标志并清零。 + * @retval 返回当前待执行任务位图。 + */ +uint32_t task_manager_take_pending(void) +{ + uint32_t pending; + + __disable_irq(); + pending = g_scheduler.task_flags; + g_scheduler.task_flags &= ~pending; + __enable_irq(); + + return pending; +} + +/** + * @brief 推进一次 100us 调度节拍并按周期置位待执行任务。 + * @note 按 500us / 1ms / 10ms / 100ms / 1s 档次分别置位对应任务组。 + */ +void task_manager_tick_100us(void) +{ + g_scheduler.tick_100us++; + + if((g_scheduler.tick_100us % 5U) == 0U) { + task_manager_set(1UL << TASK_ADC_VREF_SAMPLE); + } + + if((g_scheduler.tick_100us % 10U) == 0U) { + g_scheduler.tick_1ms++; + task_manager_set((1UL << TASK_ADC_TEMP_SAMPLE) | + (1UL << TASK_ADC_PROCESS)); + } + + if((g_scheduler.tick_100us % 100U) == 0U) { + g_scheduler.tick_10ms++; + task_manager_set((1UL << TASK_CAN_RX_POLL) | + (1UL << TASK_CAN_RX_PARSE)); + } + + if((g_scheduler.tick_100us % 1000U) == 0U) { + g_scheduler.tick_100ms++; + task_manager_set((1UL << TASK_CAN_TX_PREPARE) | + (1UL << TASK_CAN_TX_SEND) | + (1UL << TASK_STATUS_LED)); + } + + if((g_scheduler.tick_100us % 10000U) == 0U) { + g_scheduler.tick_1s++; + task_manager_set((1UL << TASK_HEARTBEAT) | + (1UL << TASK_DIAGNOSTIC_PRINT)); + } +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/task_manager.h b/GD32E508_DCU_APP/sys/task_manager.h new file mode 100644 index 0000000..8c37329 --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_manager.h @@ -0,0 +1,20 @@ +#ifndef SYS_TASK_MANAGER_H +#define SYS_TASK_MANAGER_H + +#include +#include "main.h" + +/** + * @brief 任务调度管理器。 + * @note 负责维护节拍计数器与待执行任务位图。 + */ +class TaskManager { +public: + static void init(); + static void tick100us(); + static uint32_t takePending(); + static void set(uint32_t taskMask); + static task_scheduler_t &state(); +}; + +#endif /* SYS_TASK_MANAGER_H */ diff --git a/GD32E508_DCU_APP/sys/task_manager.h.bak_20260510_0448 b/GD32E508_DCU_APP/sys/task_manager.h.bak_20260510_0448 new file mode 100644 index 0000000..3c9c51a --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_manager.h.bak_20260510_0448 @@ -0,0 +1,11 @@ +#ifndef SYS_TASK_MANAGER_H +#define SYS_TASK_MANAGER_H + +#include + +void task_manager_init(void); +void task_manager_tick_100us(void); +uint32_t task_manager_take_pending(void); +void task_manager_set(uint32_t task_mask); + +#endif /* SYS_TASK_MANAGER_H */ diff --git a/GD32E508_DCU_APP/sys/task_manager.h.bak_oop b/GD32E508_DCU_APP/sys/task_manager.h.bak_oop new file mode 100644 index 0000000..9b17811 --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_manager.h.bak_oop @@ -0,0 +1,42 @@ +/** + * @file task_manager.h + * @brief 任务标志调度与时基驱动接口定义。 + */ +#ifndef SYS_TASK_MANAGER_H +#define SYS_TASK_MANAGER_H + +#include + +/** + * @brief 初始化任务管理器内部状态。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void task_manager_init(void); + +/** + * @brief 推进一次 100us 调度节拍并按周期置位待执行任务。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void task_manager_tick_100us(void); + +/** + * @brief 原子地取出当前所有待执行任务标志。 + * @param[in] 无。 + * @param[out] 无。 + * @retval uint32_t 返回当前待执行任务位图,并在内部清除对应标志。 + */ +uint32_t task_manager_take_pending(void); + +/** + * @brief 设置一个或多个待执行任务标志。 + * @param[in] task_mask 待置位的任务位图掩码。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void task_manager_set(uint32_t task_mask); + +#endif /* SYS_TASK_MANAGER_H */ diff --git a/GD32E508_DCU_APP/sys/task_runner.cpp b/GD32E508_DCU_APP/sys/task_runner.cpp new file mode 100644 index 0000000..f59adba --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_runner.cpp @@ -0,0 +1,106 @@ +/** + * @file task_runner.cpp + * @brief 任务执行器实现。 + */ + +#include "main.h" +#include "device/adc.h" +#include "device/canfd.h" +#include "device/temp_sensor.h" +#include "sys/task_manager.h" +#include "sys/task_runner.h" + +struct TaskRunner::Runtime { + temp_sensor_data_t sample{}; + uint16_t last_vin_raw; + uint16_t last_vref_raw; + uint16_t last_temp_raw; + uint32_t last_rx_id; + uint8_t last_rx_len; + uint8_t last_rx_data[64]; +}; + +TaskRunner::Runtime TaskRunner::s_runtime; + +void TaskRunner::taskAdcVrefSample() +{ + s_runtime.last_vin_raw = Adc::readVinRaw(); + s_runtime.last_vref_raw = Adc::readVrefRaw(); +} + +void TaskRunner::taskAdcTempSample() +{ + s_runtime.last_temp_raw = Adc::readTempRaw(); +} + +void TaskRunner::taskAdcProcess() +{ + auto &s = s_runtime.sample; + s.vin_raw = s_runtime.last_vin_raw; + s.vref_raw = s_runtime.last_vref_raw; + s.temp_raw = s_runtime.last_temp_raw; + s.vdda = Adc::calculateVdda(s.vref_raw); + s.vin_adc_v = Adc::calculateVinAdcVoltage(s.vin_raw, s.vdda); + s.temperature = Adc::calculateTemperature(s.temp_raw, s.vdda); +} + +void TaskRunner::taskCanRxPoll() { CanFd::poll(); } +void TaskRunner::taskCanTxSend() { CanFd::sendPrepared(); } + +void TaskRunner::taskCanRxParse() +{ + if(CanFd::hasRxFrame()) + CanFd::consumeRxFrame(&s_runtime.last_rx_id, s_runtime.last_rx_data, &s_runtime.last_rx_len); +} + +void TaskRunner::taskCanTxPrepare() +{ + CanFd::StatusPayload status; + status.temp_raw = s_runtime.sample.temp_raw; + status.vref_raw = s_runtime.sample.vref_raw; + status.vdda = s_runtime.sample.vdda; + status.temperature = s_runtime.sample.temperature; + CanFd::prepareStatus(&status); +} + +void TaskRunner::taskStatusLed() { /* 预留 */ } +void TaskRunner::taskHeartbeat() { /* 预留 */ } + +void TaskRunner::taskDiagnosticPrint() +{ + const auto *diag = CanFd::diag(); + const auto &s = s_runtime.sample; + + printf("vin_raw=%u, vin_adc=%.3f V, temp_raw=%u, vref_raw=%u, vdda=%.3f V, temp=%.2f C, rx_id=0x%08lX, rx_len=%u, can_tx=%lu, can_rx=%lu, can_err=%lu\r\n", + s.vin_raw, s.vin_adc_v, s.temp_raw, s.vref_raw, s.vdda, s.temperature, + static_cast(s_runtime.last_rx_id), + static_cast(s_runtime.last_rx_len), + static_cast(diag->tx_count), + static_cast(diag->rx_count), + static_cast(diag->error_count)); +} + +void TaskRunner::init() +{ + Adc::collectSample(&s_runtime.sample); + s_runtime.last_vin_raw = s_runtime.sample.vin_raw; + s_runtime.last_vref_raw = s_runtime.sample.vref_raw; + s_runtime.last_temp_raw = s_runtime.sample.temp_raw; +} + +void TaskRunner::runPending() +{ + const uint32_t pending = TaskManager::takePending(); + if(pending == 0U) return; + + if(pending & (1UL << TASK_ADC_VREF_SAMPLE)) taskAdcVrefSample(); + if(pending & (1UL << TASK_ADC_TEMP_SAMPLE)) taskAdcTempSample(); + if(pending & (1UL << TASK_ADC_PROCESS)) taskAdcProcess(); + if(pending & (1UL << TASK_CAN_RX_POLL)) taskCanRxPoll(); + if(pending & (1UL << TASK_CAN_RX_PARSE)) taskCanRxParse(); + if(pending & (1UL << TASK_CAN_TX_PREPARE)) taskCanTxPrepare(); + if(pending & (1UL << TASK_CAN_TX_SEND)) taskCanTxSend(); + if(pending & (1UL << TASK_STATUS_LED)) taskStatusLed(); + if(pending & (1UL << TASK_HEARTBEAT)) taskHeartbeat(); + if(pending & (1UL << TASK_DIAGNOSTIC_PRINT)) taskDiagnosticPrint(); +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/task_runner.cpp.bak_fixinclude b/GD32E508_DCU_APP/sys/task_runner.cpp.bak_fixinclude new file mode 100644 index 0000000..342188b --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_runner.cpp.bak_fixinclude @@ -0,0 +1,179 @@ +/** + * @file task_runner.cpp + * @brief 任务执行器初始化与待执行任务分发实现。 + */ + +#include "main.h" +#include "device/adc.h" +#include "device/canfd.h" +#include "device/temp_sensor.h" +#include "sys/task_manager.h" +#include "sys/task_runner.h" + +namespace { +/** @brief 应用运行时上下文,保存各任务间共享的最新数据。 */ +struct AppRuntime { + temp_sensor_data_t sample{}; + uint16_t last_vref_raw = 0U; + uint16_t last_temp_raw = 0U; + uint32_t last_rx_id = 0U; + uint8_t last_rx_len = 0U; + uint8_t last_rx_data[64] = {0}; +}; + +AppRuntime g_app{}; + +/** + * @brief 采样内部参考电压原始值。 + */ +inline void task_adc_vref_sample() +{ + g_app.last_vref_raw = adc_read_vref_raw(); +} + +/** + * @brief 采样内部温度通道原始值。 + */ +inline void task_adc_temp_sample() +{ + g_app.last_temp_raw = adc_read_temp_raw(); +} + +/** + * @brief 根据采样原始值换算 VDDA 与芯片温度。 + */ +inline void task_adc_process() +{ + g_app.sample.vref_raw = g_app.last_vref_raw; + g_app.sample.temp_raw = g_app.last_temp_raw; + g_app.sample.vdda = adc_calculate_vdda(g_app.sample.vref_raw); + g_app.sample.temperature = adc_calculate_temperature(g_app.sample.temp_raw, g_app.sample.vdda); +} + +/** + * @brief 轮询 CANFD 接收 FIFO。 + */ +inline void task_can_rx_poll() +{ + canfd_poll(); +} + +/** + * @brief 解析并消费已接收的 CANFD 帧。 + */ +inline void task_can_rx_parse() +{ + if(canfd_has_rx_frame() != 0U) { + canfd_consume_rx_frame(&g_app.last_rx_id, g_app.last_rx_data, &g_app.last_rx_len); + } +} + +/** + * @brief 准备 CANFD 状态发送帧。 + */ +inline void task_can_tx_prepare() +{ + const canfd_status_payload_t status { + g_app.sample.temp_raw, + g_app.sample.vref_raw, + g_app.sample.vdda, + g_app.sample.temperature, + }; + canfd_prepare_status(&status); +} + +/** + * @brief 发送已准备好的 CANFD 帧。 + */ +inline void task_can_tx_send() +{ + canfd_send_prepared(); +} + +/** + * @brief 状态灯维护任务(当前为预留占位)。 + */ +inline void task_status_led() +{ +} + +/** + * @brief 系统心跳维护任务(当前为预留占位)。 + */ +inline void task_heartbeat() +{ +} + +/** + * @brief 诊断信息打印任务,输出当前所有运行时数据。 + */ +inline void task_diagnostic_print() +{ + const canfd_diag_t *diag = canfd_get_diag(); + + printf("temp_raw=%u, vref_raw=%u, vdda=%.3f V, temp=%.2f C, rx_id=0x%08lX, rx_len=%u, can_tx=%lu, can_rx=%lu, can_err=%lu\r\n", + g_app.sample.temp_raw, + g_app.sample.vref_raw, + g_app.sample.vdda, + g_app.sample.temperature, + static_cast(g_app.last_rx_id), + static_cast(g_app.last_rx_len), + static_cast(diag->tx_count), + static_cast(diag->rx_count), + static_cast(diag->error_count)); +} +} // namespace + +/** + * @brief 初始化任务执行器运行时上下文。 + * @note 执行一次初始采样,填充运行时数据起点。 + */ +void task_runner_init(void) +{ + adc_collect_sample(&g_app.sample); + g_app.last_vref_raw = g_app.sample.vref_raw; + g_app.last_temp_raw = g_app.sample.temp_raw; +} + +/** + * @brief 执行当前所有已置位的待处理任务。 + * @note 若当前没有待执行任务,直接返回。 + */ +void task_runner_run_pending(void) +{ + const uint32_t pending = task_manager_take_pending(); + if(pending == 0U) { + return; + } + + if((pending & (1UL << TASK_ADC_VREF_SAMPLE)) != 0U) { + task_adc_vref_sample(); + } + if((pending & (1UL << TASK_ADC_TEMP_SAMPLE)) != 0U) { + task_adc_temp_sample(); + } + if((pending & (1UL << TASK_ADC_PROCESS)) != 0U) { + task_adc_process(); + } + if((pending & (1UL << TASK_CAN_RX_POLL)) != 0U) { + task_can_rx_poll(); + } + if((pending & (1UL << TASK_CAN_RX_PARSE)) != 0U) { + task_can_rx_parse(); + } + if((pending & (1UL << TASK_CAN_TX_PREPARE)) != 0U) { + task_can_tx_prepare(); + } + if((pending & (1UL << TASK_CAN_TX_SEND)) != 0U) { + task_can_tx_send(); + } + if((pending & (1UL << TASK_STATUS_LED)) != 0U) { + task_status_led(); + } + if((pending & (1UL << TASK_HEARTBEAT)) != 0U) { + task_heartbeat(); + } + if((pending & (1UL << TASK_DIAGNOSTIC_PRINT)) != 0U) { + task_diagnostic_print(); + } +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/task_runner.cpp.bak_oop b/GD32E508_DCU_APP/sys/task_runner.cpp.bak_oop new file mode 100644 index 0000000..eb2f9a1 --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_runner.cpp.bak_oop @@ -0,0 +1,186 @@ +/** + * @file task_runner.cpp + * @brief 任务执行器初始化与待执行任务分发实现。 + */ + +#include "main.h" +#include "device/adc.h" +#include "device/canfd.h" +#include "device/temp_sensor.h" +#include "sys/task_manager.h" +#include "sys/task_runner.h" + +namespace { +/** @brief 应用运行时上下文,保存各任务间共享的最新数据。 */ +struct AppRuntime { + temp_sensor_data_t sample{}; + uint16_t last_vin_raw = 0U; + uint16_t last_vref_raw = 0U; + uint16_t last_temp_raw = 0U; + uint32_t last_rx_id = 0U; + uint8_t last_rx_len = 0U; + uint8_t last_rx_data[64] = {0}; +}; + +AppRuntime g_app{}; + +/** + * @brief 采样内部参考电压原始值。 + */ +inline void task_adc_vref_sample() +{ + g_app.last_vin_raw = adc_read_vin_raw(); + g_app.last_vref_raw = adc_read_vref_raw(); +} + +/** + * @brief 采样内部温度通道原始值。 + */ +inline void task_adc_temp_sample() +{ + g_app.last_temp_raw = adc_read_temp_raw(); +} + +/** + * @brief 根据采样原始值换算 VDDA 与芯片温度。 + */ +inline void task_adc_process() +{ + g_app.sample.vin_raw = g_app.last_vin_raw; + g_app.sample.vref_raw = g_app.last_vref_raw; + g_app.sample.temp_raw = g_app.last_temp_raw; + g_app.sample.vdda = adc_calculate_vdda(g_app.sample.vref_raw); + g_app.sample.vin_adc_v = adc_calculate_vin_adc_voltage(g_app.sample.vin_raw, g_app.sample.vdda); + g_app.sample.temperature = adc_calculate_temperature(g_app.sample.temp_raw, g_app.sample.vdda); +} + +/** + * @brief 轮询 CANFD 接收 FIFO。 + */ +inline void task_can_rx_poll() +{ + canfd_poll(); +} + +/** + * @brief 解析并消费已接收的 CANFD 帧。 + */ +inline void task_can_rx_parse() +{ + if(canfd_has_rx_frame() != 0U) { + canfd_consume_rx_frame(&g_app.last_rx_id, g_app.last_rx_data, &g_app.last_rx_len); + } +} + +/** + * @brief 准备 CANFD 状态发送帧。 + */ +inline void task_can_tx_prepare() +{ + const canfd_status_payload_t status { + g_app.sample.temp_raw, + g_app.sample.vref_raw, + g_app.sample.vdda, + g_app.sample.temperature, + }; + canfd_prepare_status(&status); +} + +/** + * @brief 发送已准备好的 CANFD 帧。 + */ +inline void task_can_tx_send() +{ + canfd_send_prepared(); +} + +/** + * @brief 状态灯维护任务(当前为预留占位)。 + */ +inline void task_status_led() +{ +} + +/** + * @brief 系统心跳维护任务(当前为预留占位)。 + */ +inline void task_heartbeat() +{ +} + +/** + * @brief 诊断信息打印任务,输出当前所有运行时数据。 + */ +inline void task_diagnostic_print() +{ + const canfd_diag_t *diag = canfd_get_diag(); + + printf("vin_raw=%u, vin_adc=%.3f V, temp_raw=%u, vref_raw=%u, vdda=%.3f V, temp=%.2f C, rx_id=0x%08lX, rx_len=%u, can_tx=%lu, can_rx=%lu, can_err=%lu\r\n", + g_app.sample.vin_raw, + g_app.sample.vin_adc_v, + g_app.sample.temp_raw, + g_app.sample.vref_raw, + g_app.sample.vdda, + g_app.sample.temperature, + static_cast(g_app.last_rx_id), + static_cast(g_app.last_rx_len), + static_cast(diag->tx_count), + static_cast(diag->rx_count), + static_cast(diag->error_count)); +} +} // namespace + +/** + * @brief 初始化任务执行器运行时上下文。 + * @note 执行一次初始采样,填充运行时数据起点。 + */ +void task_runner_init(void) +{ + adc_collect_sample(&g_app.sample); + g_app.last_vin_raw = g_app.sample.vin_raw; + g_app.last_vref_raw = g_app.sample.vref_raw; + g_app.last_temp_raw = g_app.sample.temp_raw; +} + +/** + * @brief 执行当前所有已置位的待处理任务。 + * @note 若当前没有待执行任务,直接返回。 + */ +void task_runner_run_pending(void) +{ + const uint32_t pending = task_manager_take_pending(); + if(pending == 0U) { + return; + } + + if((pending & (1UL << TASK_ADC_VREF_SAMPLE)) != 0U) { + task_adc_vref_sample(); + } + if((pending & (1UL << TASK_ADC_TEMP_SAMPLE)) != 0U) { + task_adc_temp_sample(); + } + if((pending & (1UL << TASK_ADC_PROCESS)) != 0U) { + task_adc_process(); + } + if((pending & (1UL << TASK_CAN_RX_POLL)) != 0U) { + task_can_rx_poll(); + } + if((pending & (1UL << TASK_CAN_RX_PARSE)) != 0U) { + task_can_rx_parse(); + } + if((pending & (1UL << TASK_CAN_TX_PREPARE)) != 0U) { + task_can_tx_prepare(); + } + if((pending & (1UL << TASK_CAN_TX_SEND)) != 0U) { + task_can_tx_send(); + } + if((pending & (1UL << TASK_STATUS_LED)) != 0U) { + task_status_led(); + } + if((pending & (1UL << TASK_HEARTBEAT)) != 0U) { + task_heartbeat(); + } + if((pending & (1UL << TASK_DIAGNOSTIC_PRINT)) != 0U) { + task_diagnostic_print(); + } +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/task_runner.cpp.bak_restore b/GD32E508_DCU_APP/sys/task_runner.cpp.bak_restore new file mode 100644 index 0000000..e709cf1 --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_runner.cpp.bak_restore @@ -0,0 +1,179 @@ +/** + * @file task_runner.cpp + * @brief 任务执行器初始化与待执行任务分发实现。 + */ + +#include "../main.h" +#include "../device/adc.h" +#include "../device/canfd.h" +#include "../device/temp_sensor.h" +#include "task_manager.h" +#include "task_runner.h" + +namespace { +/** @brief 应用运行时上下文,保存各任务间共享的最新数据。 */ +struct AppRuntime { + temp_sensor_data_t sample{}; + uint16_t last_vref_raw = 0U; + uint16_t last_temp_raw = 0U; + uint32_t last_rx_id = 0U; + uint8_t last_rx_len = 0U; + uint8_t last_rx_data[64] = {0}; +}; + +AppRuntime g_app{}; + +/** + * @brief 采样内部参考电压原始值。 + */ +inline void task_adc_vref_sample() +{ + g_app.last_vref_raw = adc_read_vref_raw(); +} + +/** + * @brief 采样内部温度通道原始值。 + */ +inline void task_adc_temp_sample() +{ + g_app.last_temp_raw = adc_read_temp_raw(); +} + +/** + * @brief 根据采样原始值换算 VDDA 与芯片温度。 + */ +inline void task_adc_process() +{ + g_app.sample.vref_raw = g_app.last_vref_raw; + g_app.sample.temp_raw = g_app.last_temp_raw; + g_app.sample.vdda = adc_calculate_vdda(g_app.sample.vref_raw); + g_app.sample.temperature = adc_calculate_temperature(g_app.sample.temp_raw, g_app.sample.vdda); +} + +/** + * @brief 轮询 CANFD 接收 FIFO。 + */ +inline void task_can_rx_poll() +{ + canfd_poll(); +} + +/** + * @brief 解析并消费已接收的 CANFD 帧。 + */ +inline void task_can_rx_parse() +{ + if(canfd_has_rx_frame() != 0U) { + canfd_consume_rx_frame(&g_app.last_rx_id, g_app.last_rx_data, &g_app.last_rx_len); + } +} + +/** + * @brief 准备 CANFD 状态发送帧。 + */ +inline void task_can_tx_prepare() +{ + const canfd_status_payload_t status { + g_app.sample.temp_raw, + g_app.sample.vref_raw, + g_app.sample.vdda, + g_app.sample.temperature, + }; + canfd_prepare_status(&status); +} + +/** + * @brief 发送已准备好的 CANFD 帧。 + */ +inline void task_can_tx_send() +{ + canfd_send_prepared(); +} + +/** + * @brief 状态灯维护任务(当前为预留占位)。 + */ +inline void task_status_led() +{ +} + +/** + * @brief 系统心跳维护任务(当前为预留占位)。 + */ +inline void task_heartbeat() +{ +} + +/** + * @brief 诊断信息打印任务,输出当前所有运行时数据。 + */ +inline void task_diagnostic_print() +{ + const canfd_diag_t *diag = canfd_get_diag(); + + printf("temp_raw=%u, vref_raw=%u, vdda=%.3f V, temp=%.2f C, rx_id=0x%08lX, rx_len=%u, can_tx=%lu, can_rx=%lu, can_err=%lu\r\n", + g_app.sample.temp_raw, + g_app.sample.vref_raw, + g_app.sample.vdda, + g_app.sample.temperature, + static_cast(g_app.last_rx_id), + static_cast(g_app.last_rx_len), + static_cast(diag->tx_count), + static_cast(diag->rx_count), + static_cast(diag->error_count)); +} +} // namespace + +/** + * @brief 初始化任务执行器运行时上下文。 + * @note 执行一次初始采样,填充运行时数据起点。 + */ +void task_runner_init(void) +{ + adc_collect_sample(&g_app.sample); + g_app.last_vref_raw = g_app.sample.vref_raw; + g_app.last_temp_raw = g_app.sample.temp_raw; +} + +/** + * @brief 执行当前所有已置位的待处理任务。 + * @note 若当前没有待执行任务,直接返回。 + */ +void task_runner_run_pending(void) +{ + const uint32_t pending = task_manager_take_pending(); + if(pending == 0U) { + return; + } + + if((pending & (1UL << TASK_ADC_VREF_SAMPLE)) != 0U) { + task_adc_vref_sample(); + } + if((pending & (1UL << TASK_ADC_TEMP_SAMPLE)) != 0U) { + task_adc_temp_sample(); + } + if((pending & (1UL << TASK_ADC_PROCESS)) != 0U) { + task_adc_process(); + } + if((pending & (1UL << TASK_CAN_RX_POLL)) != 0U) { + task_can_rx_poll(); + } + if((pending & (1UL << TASK_CAN_RX_PARSE)) != 0U) { + task_can_rx_parse(); + } + if((pending & (1UL << TASK_CAN_TX_PREPARE)) != 0U) { + task_can_tx_prepare(); + } + if((pending & (1UL << TASK_CAN_TX_SEND)) != 0U) { + task_can_tx_send(); + } + if((pending & (1UL << TASK_STATUS_LED)) != 0U) { + task_status_led(); + } + if((pending & (1UL << TASK_HEARTBEAT)) != 0U) { + task_heartbeat(); + } + if((pending & (1UL << TASK_DIAGNOSTIC_PRINT)) != 0U) { + task_diagnostic_print(); + } +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/task_runner.cpp.bak_sch b/GD32E508_DCU_APP/sys/task_runner.cpp.bak_sch new file mode 100644 index 0000000..342188b --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_runner.cpp.bak_sch @@ -0,0 +1,179 @@ +/** + * @file task_runner.cpp + * @brief 任务执行器初始化与待执行任务分发实现。 + */ + +#include "main.h" +#include "device/adc.h" +#include "device/canfd.h" +#include "device/temp_sensor.h" +#include "sys/task_manager.h" +#include "sys/task_runner.h" + +namespace { +/** @brief 应用运行时上下文,保存各任务间共享的最新数据。 */ +struct AppRuntime { + temp_sensor_data_t sample{}; + uint16_t last_vref_raw = 0U; + uint16_t last_temp_raw = 0U; + uint32_t last_rx_id = 0U; + uint8_t last_rx_len = 0U; + uint8_t last_rx_data[64] = {0}; +}; + +AppRuntime g_app{}; + +/** + * @brief 采样内部参考电压原始值。 + */ +inline void task_adc_vref_sample() +{ + g_app.last_vref_raw = adc_read_vref_raw(); +} + +/** + * @brief 采样内部温度通道原始值。 + */ +inline void task_adc_temp_sample() +{ + g_app.last_temp_raw = adc_read_temp_raw(); +} + +/** + * @brief 根据采样原始值换算 VDDA 与芯片温度。 + */ +inline void task_adc_process() +{ + g_app.sample.vref_raw = g_app.last_vref_raw; + g_app.sample.temp_raw = g_app.last_temp_raw; + g_app.sample.vdda = adc_calculate_vdda(g_app.sample.vref_raw); + g_app.sample.temperature = adc_calculate_temperature(g_app.sample.temp_raw, g_app.sample.vdda); +} + +/** + * @brief 轮询 CANFD 接收 FIFO。 + */ +inline void task_can_rx_poll() +{ + canfd_poll(); +} + +/** + * @brief 解析并消费已接收的 CANFD 帧。 + */ +inline void task_can_rx_parse() +{ + if(canfd_has_rx_frame() != 0U) { + canfd_consume_rx_frame(&g_app.last_rx_id, g_app.last_rx_data, &g_app.last_rx_len); + } +} + +/** + * @brief 准备 CANFD 状态发送帧。 + */ +inline void task_can_tx_prepare() +{ + const canfd_status_payload_t status { + g_app.sample.temp_raw, + g_app.sample.vref_raw, + g_app.sample.vdda, + g_app.sample.temperature, + }; + canfd_prepare_status(&status); +} + +/** + * @brief 发送已准备好的 CANFD 帧。 + */ +inline void task_can_tx_send() +{ + canfd_send_prepared(); +} + +/** + * @brief 状态灯维护任务(当前为预留占位)。 + */ +inline void task_status_led() +{ +} + +/** + * @brief 系统心跳维护任务(当前为预留占位)。 + */ +inline void task_heartbeat() +{ +} + +/** + * @brief 诊断信息打印任务,输出当前所有运行时数据。 + */ +inline void task_diagnostic_print() +{ + const canfd_diag_t *diag = canfd_get_diag(); + + printf("temp_raw=%u, vref_raw=%u, vdda=%.3f V, temp=%.2f C, rx_id=0x%08lX, rx_len=%u, can_tx=%lu, can_rx=%lu, can_err=%lu\r\n", + g_app.sample.temp_raw, + g_app.sample.vref_raw, + g_app.sample.vdda, + g_app.sample.temperature, + static_cast(g_app.last_rx_id), + static_cast(g_app.last_rx_len), + static_cast(diag->tx_count), + static_cast(diag->rx_count), + static_cast(diag->error_count)); +} +} // namespace + +/** + * @brief 初始化任务执行器运行时上下文。 + * @note 执行一次初始采样,填充运行时数据起点。 + */ +void task_runner_init(void) +{ + adc_collect_sample(&g_app.sample); + g_app.last_vref_raw = g_app.sample.vref_raw; + g_app.last_temp_raw = g_app.sample.temp_raw; +} + +/** + * @brief 执行当前所有已置位的待处理任务。 + * @note 若当前没有待执行任务,直接返回。 + */ +void task_runner_run_pending(void) +{ + const uint32_t pending = task_manager_take_pending(); + if(pending == 0U) { + return; + } + + if((pending & (1UL << TASK_ADC_VREF_SAMPLE)) != 0U) { + task_adc_vref_sample(); + } + if((pending & (1UL << TASK_ADC_TEMP_SAMPLE)) != 0U) { + task_adc_temp_sample(); + } + if((pending & (1UL << TASK_ADC_PROCESS)) != 0U) { + task_adc_process(); + } + if((pending & (1UL << TASK_CAN_RX_POLL)) != 0U) { + task_can_rx_poll(); + } + if((pending & (1UL << TASK_CAN_RX_PARSE)) != 0U) { + task_can_rx_parse(); + } + if((pending & (1UL << TASK_CAN_TX_PREPARE)) != 0U) { + task_can_tx_prepare(); + } + if((pending & (1UL << TASK_CAN_TX_SEND)) != 0U) { + task_can_tx_send(); + } + if((pending & (1UL << TASK_STATUS_LED)) != 0U) { + task_status_led(); + } + if((pending & (1UL << TASK_HEARTBEAT)) != 0U) { + task_heartbeat(); + } + if((pending & (1UL << TASK_DIAGNOSTIC_PRINT)) != 0U) { + task_diagnostic_print(); + } +} \ No newline at end of file diff --git a/GD32E508_DCU_APP/sys/task_runner.h b/GD32E508_DCU_APP/sys/task_runner.h new file mode 100644 index 0000000..6870bcd --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_runner.h @@ -0,0 +1,29 @@ +#ifndef SYS_TASK_RUNNER_H +#define SYS_TASK_RUNNER_H + +/** + * @brief 任务执行器。 + * @note 负责消费 TaskManager 置位的任务,并驱动各模块完成实际工作。 + */ +class TaskRunner { +public: + static void init(); + static void runPending(); + +private: + struct Runtime; + static Runtime s_runtime; + + static void taskAdcVrefSample(); + static void taskAdcTempSample(); + static void taskAdcProcess(); + static void taskCanRxPoll(); + static void taskCanRxParse(); + static void taskCanTxPrepare(); + static void taskCanTxSend(); + static void taskStatusLed(); + static void taskHeartbeat(); + static void taskDiagnosticPrint(); +}; + +#endif /* SYS_TASK_RUNNER_H */ diff --git a/GD32E508_DCU_APP/sys/task_runner.h.bak_oop b/GD32E508_DCU_APP/sys/task_runner.h.bak_oop new file mode 100644 index 0000000..13c3530 --- /dev/null +++ b/GD32E508_DCU_APP/sys/task_runner.h.bak_oop @@ -0,0 +1,25 @@ +/** + * @file task_runner.h + * @brief 任务执行器初始化与待执行任务分发接口定义。 + */ +#ifndef SYS_TASK_RUNNER_H +#define SYS_TASK_RUNNER_H + +/** + * @brief 初始化任务执行器运行时上下文。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + */ +void task_runner_init(void); + +/** + * @brief 执行当前所有已置位的待处理任务。 + * @param[in] 无。 + * @param[out] 无。 + * @retval 无返回值。 + * @note 若当前没有待执行任务,函数直接返回。 + */ +void task_runner_run_pending(void); + +#endif /* SYS_TASK_RUNNER_H */ diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json new file mode 100644 index 0000000..f93b888 --- /dev/null +++ b/vcpkg-configuration.json @@ -0,0 +1,16 @@ +{ + "registries": [ + { + "name": "arm", + "kind": "artifact", + "location": "https://artifacts.tools.arm.com/vcpkg-registry" + } + ], + "requires": { + "arm:tools/open-cmsis-pack/cmsis-toolbox": "2.13.0", + "arm:tools/kitware/cmake": "3.31.5", + "arm:tools/ninja-build/ninja": "1.13.2", + "arm:tools/arm/mdk-toolbox": "1.1.0", + "arm:compilers/arm/armclang": "6.24.0" + } +} \ No newline at end of file