基于纳芯微NSSinePad-NS800RT5039评估板输出一对互补PWM波形
一、项目介绍
ePWM(Enhanced Pulse Width Modulator)模块是电机控制项目用来生成PWM信号的核心外设。它的灵活性和高精度,使你可以用它生成不同频率、占空比、相位的PWM波,驱动电机、控制电源等复杂系统。纳芯微NS800RT系列是与芯弦半导体联合推出的实时控制MCU,在一系列高精度外设的加持下,NS800RT系列可实现100皮秒的高精度PWM控制,从而支持各种对高效率、高细分、高控制精度应用的需求,更加适配基于SiC和GaN功率器件打造的数字电源和电机控制系统。
本项目通过使用原厂提供的NSSinePad-NS800RT5039开发板完成调用Uart串口输出 “Hello, NOVOSENSE Wedesign project.” 字符串,以及调用ePWM外设,实现在对应输出端口,输出一对互补PWM波形,波形频率2MHz,占空比50%。并使用12指神探捕获对应波形截图。

二、项目设计思路
1、项目实现思路
该项目可以分解为三部分,一是调用Uart串口,完成在串口输出 “Hello, NOVOSENSE Wedesign project.” 字符串任务。二是调用ePWM外设,实现在对应输出端口,输出一对互补PWM波形,波形频率2MHz,占空比50%。三是使用12指神探捕获对应波形截图。
对于调用Uart串口输出指定的字符串比较简单,只需初始化串口后,调用uart内置相关输出函数即可;对于输出一对互补PWM波形,则需要在ePWM初始化上下功夫,配置EPWM1两个输出为互补输出;对于捕获对应波形截图,则需要12指神探调试器烧录示波器的功能,并使用上位机软件pulseview接收显示输出。
2、项目设备特点
该项目主要使用了两个硬件,一个是NSSinePad-NS800RT5039开发板,是基于 NS800RT5039 实时微控制器设计的,用户可以方便 使用NS800RT5039/49 实时微控制器的所有外设、内存。除实时微控制器芯片外,该开发板还具有板载 CAN 收发器(支持 CAN2.0B 及 CAN-FD)、两个 5V 编码器(eQEP)接口芯片及板载 DAPLink 调试/仿真器。另一个是基于RP2040多功能嵌入式编程学习、硬件调试平台,又称为12指神探,基于RP2040微控制器、搭配240*240分辨率的LCD彩屏、两个轻触按键和一个拨轮,12根管脚支持对外供电、3个ADC模拟信号输入、最多9根通用数字IO,功能灵活,通过搭配不同程序可以做成各种调试器。此次调试器烧录了逻辑分析仪功能的固件实现了一个简单的逻辑分析和示波器的功能。
3、项目电路连接
a.项目可使用typec口上的GPIO28、GPIO29口直接接入USB虚拟串口的RXD和TXD。

b.项目使用12指神探的ADC0、ADC1分别接入J11引出的GPIO0和GPIO1引脚,接收开发板产生的PWM互补信号。



4、项目所用软件简介
一是MCU嵌入式代码,使用C语言,keil开发环境,主要功能包括:
- 通过UART发送指定字符串:"Hello, NOVOSENSE Wedesign project."
- 生成一对互补的PWM波形,频率为2MHz,占空比为50%
具体代码见下一节关键代码及说明。
二是上位机软件pulseview,PulseView 是一个开源的示波器工具,它是 Sigrok 项目的一部分。PulseView 能够实现逻辑分析仪和示波器的波形显示功能,支持多种硬件设备,用户可以通过它来捕获、显示和分析数字和模拟信号。该项目旨在打造一个功能强大、易于使用的界面,让电子爱好者、工程师和研究者在开发和测试过程中能够轻松地可视化信号。

三、关键代码及说明
1、头文件和定义
包含必要的头文件,并定义了PWM频率和占空比常量。
#include "device.h"
#include "board.h"
#include "uart.h"
#include "epwm.h"
#include "rcc.h"
#define PWM_FREQUENCY 2000000U // 2MHz
#define PWM_DUTY_CYCLE 50U // 50% duty cycle
2、全局变量
声明并初始化了指向UART1和EPWM1外设的指针。
/*******************************************************************************
* Variables
******************************************************************************/
UART_TypeDef *uart = UART1;
EPWM_TypeDef *epwm = EPWM1;
/*******************************************************************************
* Functions Declaration
******************************************************************************/
static void uart_init(UART_TypeDef *uart);
static void epwm_init(EPWM_TypeDef *epwm);
3、主函数流程
主函数 main 执行以下步骤:
- 初始化设备时钟和外设
- 禁用外设寄存器锁定
- 禁用同步(冻结PWM时钟)
- 板级初始化
- 初始化UART和ePWM模块
- 启用同步和PWM时钟
- 发送指定字符串
- 进入无限循环
int main(void)
{
/* Initialize device clock and peripherals */
Device_init();
/* Disable peripheral register locks */
Device_unlockPeriphReg();
/* Disable sync(Freeze clock to PWM as well) */
SYSCON_UNLOCK;
SYSCON_enableTbclkSync(SYSCON, false);
SYSCON_LOCK;
/* Board Initialization */
Board_init();
/* UART Initialization */
uart_init(uart);
/* EPWM Initialization */
epwm_init(epwm);
/* Enable sync and clock to PWM */
SYSCON_UNLOCK;
SYSCON_enableTbclkSync(SYSCON, true);
SYSCON_LOCK;
/* Output the required string */
const char *hello_str = "Hello, NOVOSENSE Wedesign project.\r\n";
uint16_t str_len = 0;
while (hello_str[str_len] != '\0') {
str_len++;
}
UART_writeCharArray(uart, (const uint8_t *)hello_str, str_len);
/* Loop indefinitely */
while(1)
{
}
}
4、UART初始化
uart_init 函数配置了GPIO引脚用于UART通信:
- GPIO28配置为UART1 RX输入
- GPIO29配置为UART1 TX输出
- 设置波特率为115200
- 配置数据位、停止位和使能收发器
static void uart_init(UART_TypeDef *uart)
{
/* UART1 RX pinmux control */
GPIO_setPinConfig(GPIO_28_SCIA_RX);
GPIO_setAnalogMode(GPIO_28, GPIO_ANALOG_DISABLED);
GPIO_setPadConfig(GPIO_28, GPIO_PIN_TYPE_PULLUP);
GPIO_setQualificationMode(GPIO_28, GPIO_QUAL_SYNC);
GPIO_setQualificationPeriod(GPIO_28, GPIO_SMP_SYSCLK_DIV_1);
GPIO_setDirectionMode(GPIO_28, GPIO_DIR_MODE_IN);
/* UART1 TX pinmux control */
GPIO_setPinConfig(GPIO_29_SCIA_TX);
GPIO_setAnalogMode(GPIO_29, GPIO_ANALOG_DISABLED);
GPIO_setPadConfig(GPIO_29, GPIO_PIN_TYPE_STD);
GPIO_setDriveLevel(GPIO_29, GPIO_DRV_LOW);
GPIO_setPin(GPIO_29);
GPIO_setDirectionMode(GPIO_29, GPIO_DIR_MODE_OUT);
/* Reset uart before configure it */
UART_resetModule(uart);
/* Set baudrate */
UART_setBaud(uart, 115200);
/* Set the number of bits per char in UART controller */
UART_setBitCountPerChar(uart, UART_8_BITS_PER_CHAR, false);
/* Set the number of stop bits */
UART_setStopBitCount(uart, UART_ONE_STOP_BIT);
/* Enable transmitter */
UART_enableTxModule(uart);
/* Enable receiver */
UART_enableRxModule(uart);
}
5、ePWM初始化
epwm_init 函数完成了复杂的PWM配置:
- 配置GPIO0和GPIO1作为EPWM1A和EPWM1B输出
- 根据系统时钟计算周期值以获得2MHz频率:
- 设置计数器模式为向上计数
- 配置比较值以实现50%占空比
- 设置动作限定符,使EPWM1A在计数器为0时变高,在达到比较值时变低
- 配置EPWM1B为互补输出(与EPWM1A相反)
- 配置死区控制以确保互补特性
static void epwm_init(EPWM_TypeDef *epwm)
{
/* Configure GPIO pins for EPWM1. */
/* EPWM1A - GPIO0 */
GPIO_setAnalogMode(GPIO_0, GPIO_ANALOG_DISABLED);
GPIO_setPinConfig(GPIO_0_EPWM1_A);
GPIO_setPadConfig(GPIO_0, GPIO_PIN_TYPE_STD);
GPIO_setQualificationMode(GPIO_0, GPIO_QUAL_SYNC);
/* EPWM1B - GPIO1 */
GPIO_setAnalogMode(GPIO_1, GPIO_ANALOG_DISABLED);
GPIO_setPinConfig(GPIO_1_EPWM1_B);
GPIO_setPadConfig(GPIO_1, GPIO_PIN_TYPE_STD);
GPIO_setQualificationMode(GPIO_1, GPIO_QUAL_SYNC);
/* Get system clock frequency */
uint32_t sysClk = RCC_getSysClockFrequency();
/* Calculate period value for 2MHz frequency */
/* Period = (sysClk / (prescaler * frequency)) - 1 */
/* For 2MHz, using no prescaler: Period = (sysClk / 2000000) - 1 */
uint16_t period = (sysClk / PWM_FREQUENCY) - 1;
uint16_t cmpValue = period * PWM_DUTY_CYCLE / 100;
/* Set EPWM clock prescaler - no division */
EPWM_setClockPrescaler(epwm, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1);
/* Set time base period */
EPWM_setTimeBasePeriod(epwm, period);
/* Set time base counter */
EPWM_setTimeBaseCounter(epwm, 0);
/* Set counter mode - up count */
EPWM_setTimeBaseCounterMode(epwm, EPWM_COUNTER_MODE_UP);
/* Disable phase shift */
EPWM_disablePhaseShiftLoad(epwm);
EPWM_setPhaseShift(epwm, 0);
/* Set counter compare value for 50% duty cycle */
EPWM_setCounterCompareValue(epwm, EPWM_COUNTER_COMPARE_A, cmpValue);
/* Set counter compare shadow load mode */
EPWM_setCounterCompareShadowLoadMode(epwm, EPWM_COUNTER_COMPARE_A, EPWM_COMP_LOAD_ON_CNTR_ZERO);
/* Set action qualifier for EPWM1A */
EPWM_setActionQualifierAction(epwm, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);
EPWM_setActionQualifierAction(epwm, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
/* Set action qualifier for EPWM1B (complementary) */
EPWM_setActionQualifierAction(epwm, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);
EPWM_setActionQualifierAction(epwm, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
/* Configure dead band for complementary PWM */
/* Enable dead band */
EPWM_setDeadBandDelayMode(epwm, EPWM_DB_RED, true);
EPWM_setDeadBandDelayMode(epwm, EPWM_DB_FED, true);
/* Set rising edge delay and falling edge delay to 0 for true complementary */
EPWM_setRisingEdgeDelayCount(epwm, 0);
EPWM_setFallingEdgeDelayCount(epwm, 0);
/* Set dead band polarity for complementary outputs */
EPWM_setDeadBandDelayPolarity(epwm, EPWM_DB_FED, EPWM_DB_POLARITY_ACTIVE_LOW);
}
四、实物演示及说明

硬件连接如图所示。

pulseview需要手动选择连接到设备:


最终显示的波形如上图。图上一对PWM互补实际还不是准确的50%占空比,暂时还没有想到解决的办法。
五、开发过程中遇到的问题与解决方式
主要在使用Keil5时候要注意一些细节:
1、Arm Compiler 报错 ,error: use of LTO is disallowed in this variant of Arm Compiler for Embedded
经查,该问题通常发生在使用较新版本编译器时启用了链接时优化(LTO)。
解决方案:禁用LTO选项:打开Keil工程,右键点击目标(如 XINJIAN)选择 Options for Target。进入 C/C++ 选项卡,找到 Optimization 部分。取消勾选LTO相关选项(如 Use Link-Time Optimization)。
2、Arm Compiler 警告,warning: L6329W: Pattern *(.vt_dtcm) only matches removed unused sections.
经查,该问题通常由链接脚本(如 .sct 文件)中定义的段(Section)未被实际代码使用导致。编译器在优化时移除了未使用的段,从而触发警告。
解决办法:调整编译器优化选项:禁用特定优化:在 Keil 的 Options for Target → C/C++ → Optimization 中,将优化等级改为 -O0(不优化),或添加标志 --no_remove 禁止移除未使用段。若确认警告无害,可在链接器选项中添加 --diag_suppress=L6329W 屏蔽该警告。
六、对本次活动的心得体会
NS800RT系列是中端算力实时控制MCU,相比我们学习用的STM32系列MCU,增加了许多新的特性,尤其是配合自研的eMath浮点数学运算加速核,可以实现迅速的环路控制和高效的实时控制运算。高速高精度的ADC、100ps的高精度PWM、专用的PWM事件管理器跟高速比较器的快速联动可以保障每个高速环路的精准控制。由于拿的比较晚,先从逐一体验其基本功能开始,后续再使用纳芯微传感器再深入试用一下该MCU。PWM互补实际还不是准确的50%占空比,暂时还没有想到解决的办法,后续还要进一步研究解决。另外,12指神探也是一个非常小巧方便的工具,还可以有更多的功能扩展潜力,也可以把玩一阵子了。