使用纳芯微评估板NSSinePad-NS800RT5039调用Uart串口和调用ePWM外设
该项目使用了评估板NSSinePad-NS800RT5039,实现了调用Uart串口和调用ePWM外设的设计,它的主要功能为:串口输出 “Hello, NOVOSENSE Wedesign project.” 字符串任务,实现在对应输出端口,输出一对互补PWM波形,。
标签
嵌入式系统
开发板
纳芯微
珊闪扇山善
更新2025-12-02
重庆电力高等专科学校
40

基于纳芯微NS800RT5039芯片的UART与ePWM外设开发

一、 项目介绍

本项目基于纳芯微NS800RT5039微控制器芯片,成功完成了其基本外设功能的开发与验证。项目主要实现了两大核心功能:UART串口通信高精度ePWM波形生成。通过此项目,验证了芯片基础外设驱动的稳定性与精确性,为后续复杂应用奠定了坚实基础。


二、 项目设计思路

1. 核心任务

核心任务是验证芯片的两大常用外设:

  • UART(通用异步收发传输器):用于实现微控制器与上位机(如串口调试助手)之间的基础数据通信。
  • ePWM(增强型脉宽调制器):用于生成高频率、高精度的PWM波形,是电机控制、电源管理等应用的核心。

2. 实现方案

程序设计上采用模块化思想。在系统初始化后,主循环中并行执行通信与波形生成任务。通过精确配置外设寄存器,确保输出波形参数严格符合要求。


三、 实现过程与关键代码

1. 开发环境搭建

  • 硬件平台:NSSinePad-NS800RT5039评估板。
  • 软件开发环境:基于官方提供的SDK进行程序开发。
  • 调试工具:串口调试助手、示波器(或万用表)。

2. 关键实现步骤

  • UART配置:初始化UART外设,设置波特率(如115200bps)、数据位、停止位和校验位。
  • ePWM配置:配置ePWM模块的时钟预分频器、周期寄存器和比较寄存器,以生成频率为2MHz、占空比为50%的互补波形。
  • 主程序逻辑:系统启动后,自动通过串口发送问候信息,并持续输出PWM波形。


3.硬件介绍

NSSinePad-NS800RT5039评估板的核心是NS800RT5039芯片。这款芯片由纳芯微电子联合芯弦半导体推出,是一款面向实时控制场景的高性能MCU/DSP

下表为您概括了这款核心芯片的主要特性:

特性类别

具体说明

核心处理器

- Arm Cortex-M7 CPU,主频高达 260MHz,支持分支预测、DSP指令集和FPU(浮点单元)
- eMath硬件数学加速核,专门针对三角函数、FFT、矩阵运算等复杂算法进行硬件加速

存储资源

- 配备 512KB Flash (带ECC) 和 388KB SRAM (带ECC),其中包含 256KB 紧耦合内存(TCM),保障高速数据访问

高精度PWM

- 提供最多32路PWM输出,其中 16路为100ps(皮秒)分辨率的高精度HRPWM,是实现皮秒级别精密控制的基础

模拟外设

- 3个12位ADC,采样速率快至 5MSPS,最大支持32通道
- 2个12位DAC,采样速率1MSPS
- 7对(14个)高速模拟比较器 (CMPSS),带DAC与斜波发生器

通信接口

- 集成 3个串口(UART)2个高速串口1个CAN-FD1个CAN 2.03个I2C3个SPI ,满足多种通信需求。

安全与可靠性

- 通过 AEC-Q100 Grade1 车规认证,支持 ISO26262 ASIL-B  IEC61508 SIL2 等级的功能安全,适用于汽车和工业领域。


四、方案框图

QQ_1764330068207.png

五、关键代码及说明

1.程序启动后首先完成系统初始化:包括设备时钟、外设寄存器解锁以及板级配置。接着,我专门对 UART2 进行了详细初始化——配置 GPIO_40 为 TX 输出、GPIO_41 为 RX 输入,并设置波特率为 921600、8 数据位、无校验、1 停止位,工作在轮询模式,未启用 FIFO 和奇偶校验。

进入主循环后,程序会先打印提示信息,然后调用实现的 UART_receiveDataPolling() 函数,等待接收 35 字节的数据。如果接收成功,它会以十六进制格式打印出来,并尝试将可打印 ASCII 字符原样显示,非打印字符则用点号替代。紧接着,程序会调用 UART_sendDataPolling() 将一个发送缓冲区的内容回传出去。这里我们选择直接在主函数中,选择Printf()函数串口打印

int main(void)
{
/* Initialize device clock and peripherals */
Device_init();
/* Disable peripheral register locks */
Device_unlockPeriphReg();
/* Board Initialization
Setup LED pins, Key pins and Serial Communication Interface pins
*/
Board_init();
/* UART Initialization */
uart_init(uart);

/* Debug information printing */
printf("UART ex1 send and receive data by polling mode!\n");

/* Loop indefinitely */
while (1)
{
printf("UART receive data :");
printf("Hello, NOVOSENSE Wedesign project.\n");
// UART_readCharArray(uart, rxBuff, UART_DATA_LENGTH);
if (UART_receiveDataPolling(uart, rxBuff, UART_DATA_LENGTH) == 0)
{
printf("receive success: ");
for (uint32_t i = 0U; i < UART_DATA_LENGTH; i++)
{
printf("0x%02X, ", rxBuff[i]);
}
printf("\n");
}
else
{
printf("receive fail!\n");
}

printf("UART will transmit data :\n");
for (uint32_t i = 0U; i < UART_DATA_LENGTH; i++)
{
if (rxBuff[i] >= 32 && rxBuff[i] <= 126) // ¿É´òÓ¡ ASCII ·¶Î§
printf("%c", rxBuff[i]);
else
printf("."); // ²»¿É´òÓ¡×Ö·ûÓà . ´úÌæ
}
printf("\n");
// UART_writeCharArray(uart, txBuff, UART_DATA_LENGTH);
UART_sendDataPolling(uart, txBuff, UART_DATA_LENGTH);
}
}

/**
* @brief Initialize uart function
*/
static void uart_init(UART_TypeDef *uart)
{
/* UART2 RX pinmux control */
GPIO_setPinConfig(GPIO_41_SCIB_RX);
GPIO_setAnalogMode(GPIO_41, GPIO_ANALOG_DISABLED);
GPIO_setPadConfig(GPIO_41, GPIO_PIN_TYPE_PULLUP);
GPIO_setQualificationMode(GPIO_41, GPIO_QUAL_SYNC);
GPIO_setDirectionMode(GPIO_41, GPIO_DIR_MODE_IN);

/* UART2 TX pinmux control */
GPIO_setPinConfig(GPIO_40_SCIB_TX);
GPIO_setAnalogMode(GPIO_40, GPIO_ANALOG_DISABLED);
GPIO_setPadConfig(GPIO_40, GPIO_PIN_TYPE_STD);
GPIO_setDriveLevel(GPIO_40, GPIO_DRV_MAX);
GPIO_setDirectionMode(GPIO_40, GPIO_DIR_MODE_OUT);

/* Reset uart before configure it */
UART_resetModule(uart);

/* Set baudrate */
UART_setBaud(uart, 921600);

#if ENABLE_PARITY
/* Set the number of bits per char in UART controller */
UART_setBitCountPerChar(uart, UART_8_BITS_PER_CHAR, true);

/* Sets the type of parity */
UART_setParityMode(uart, UART_PAR_ODD);
#else
/* Set the number of bits per char in UART controller */
UART_setBitCountPerChar(uart, UART_8_BITS_PER_CHAR, false);
#endif

/* Set the number of stop bits */
UART_setStopBitCount(uart, UART_ONE_STOP_BIT);

/* Set MSB bit reverses the order of the bits */
UART_setMSB(uart, false);

#if ENABLE_FIFO
/* Config tx fifo */
UART_enableTxFifo(uart);
UART_resetTxFifo(uart);
UART_setTxFifoWatermark(uart, UART_FIFO_TX6);

/* Config rx fifo */
UART_enableRxFifo(uart);
UART_resetRxFifo(uart);
UART_setRxFifoWatermark(uart, UART_FIFO_RX6);
UART_setRxIdleCharacter(uart, UART_IDLE_CHARACTER_CNT1);
#endif

/* Enable transmitter */
UART_enableTxModule(uart);

/* Enable receiver */
UART_enableRxModule(uart);
}

/**
* @brief Receive multiple bytes of data using polling method.
* @return Return 0 if reception is successful, 1 if reception fails.
*/
uint32_t UART_receiveDataPolling(UART_TypeDef* uart, uint8_t *const array, uint16_t length)
{
uint16_t i;
uint32_t status = STATUS_NO_ERROR;
uint32_t retval = 0;

/* Flush receive FIFO/Buffer */
UART_resetRxFifo(uart);

/* Clear the error flags */
UART_clearErrorFlags(uart, UART_STAT_OR | UART_STAT_NF | UART_STAT_FE | UART_STAT_PF);

/* Enable receiver */
UART_enableRxModule(uart);

for (i = 0; i < length; i++)
{
/* Wait until a character is available in UART receiver */
while (UART_getRxFifoStatus(uart) == UART_FIFO_RX0);

/* Read a character from UART receiver */
array[i] = UART_readChar(uart);

if (UART_getStatusFlag(uart, UART_FRAME_ERR))
{
status = UART_FRAME_ERR;

/* Disable the UART receiver */
UART_disableRxModule(uart);

/* Clear the flag */
UART_clearStatusFlag(uart, UART_FRAME_ERR);
}
if (UART_getStatusFlag(uart, UART_NOISE_DETECT))
{
status = UART_NOISE_DETECT;

/* Disable the UART receiver */
UART_disableRxModule(uart);

/* Clear the flag */
UART_clearStatusFlag(uart, UART_NOISE_DETECT);
}
if (UART_getStatusFlag(uart, UART_PARITY_ERR))
{
status = UART_PARITY_ERR;

/* Disable the UART receiver */
UART_disableRxModule(uart);

/* Clear the flag */
UART_clearStatusFlag(uart, UART_PARITY_ERR);
}
if (UART_getStatusFlag(uart, UART_RX_OVERRUN))
{
status = UART_RX_OVERRUN;

/* Disable the UART receiver */
UART_disableRxModule(uart);

/* Clear the flag */
UART_clearStatusFlag(uart, UART_RX_OVERRUN);
}

if (status != 0xFF)
{
break;
}
}

if ((i == length) && ((status == UART_RX_OVERRUN) || status == STATUS_NO_ERROR))
{
retval = 0;
/* Disable the UART receiver */
UART_disableRxModule(uart);
}
else
{
retval = 1;
}

/* Read dummy to clear RDRF flag */
while (UART_getStatusFlag(uart, UART_RX_DATA_REG_FULL))
{
(void)UART_readChar(uart);
}

return retval;
}

/**
* @brief Send out multiple bytes of data using polling method.
*/
void UART_sendDataPolling(UART_TypeDef* uart, const uint8_t *array, uint16_t length)
{
/* Flush transmit FIFO/Buffer */
UART_resetTxFifo(uart);

/* Enable the UART transmitter */
UART_enableTxModule(uart);

for (uint16_t i = 0; i < length; i++)
{
/* Wait until space is available in UART transmitter */
while (!UART_getStatusFlag(uart, UART_TX_DATA_REG_EMPTY));

/* Send a character to UART transmitter */
UART_writeChar(uart, array[i]);
}

/* Wait until transmission completes */
while (!UART_getStatusFlag(uart, UART_TX_COMPLETE));

/* Disable the UART transmitter */
UART_disableTxModule(uart);
}


2.在任务二pwm的main 函数中,我首先完成了系统时钟、GPIO 和板载 LED 的初始化,作为程序运行的基本状态指示。随后,调用了编写的 my_pwm_Init() 函数——这个函数封装了所有与 EPWM1 相关的配置逻辑。

int main(void)
{
/* Initialize device clock and peripherals */
Device_init();
/* Disable peripheral register locks */
Device_unlockPeriphReg();
/* Board Initialization
Setup LED pins, Key pins and Serial Communication Interface pins
*/
SYSCON_UNLOCK;
SYSCON_enableTbclkSync(SYSCON, false);
SYSCON_LOCK;
Board_init();
/* LED4 & LED5 on */
GPIO_clearPin(BOARD_LED4_PIN);
GPIO_clearPin(BOARD_LED5_PIN);
/* Debug information printing */

my_pwm_Init();
SYSCON_UNLOCK;
SYSCON_enableTbclkSync(SYSCON, true);
SYSCON_LOCK;
printf("Gpio example 1 setup start!\n");
/* Loop indefinitely */
while(1)
{
/* Delay some times */
GPIO_clearPin(GPIO_22);
GPIO_clearPin(GPIO_20);
Delay_ms(100);
GPIO_setPin(GPIO_20);
GPIO_setPin(GPIO_22);
Delay_ms(100);
}

}


将 EPWM1 的计数器设为向上计数模式,周期值设为 26000,对应大约 2MHz 的 PWM 频率。接着,我分别设置了 CMPA=13000 和 CMPB=13000 ,配合动作限定器,在计数归零时拉高电平,在达到比较值时拉低电平,从而生成占空比约为 50% 和 50% 的两路互补PWM 波形。最后,我通过 my_pwm1_gpio_Init() 将 GPIO_0 和 GPIO_1 正确复用为 EPWM1A 和 EPWM1B 引脚,并关闭其模拟功能以确保数字信号质量。

void my_pwm1_gpio_Init(void);
void my_pwm_Init(void)
{
EPWM_setClockPrescaler(EPWM1, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_2);
EPWM_setTimeBasePeriod(EPWM1, 26000);
EPWM_setTimeBaseCounter(EPWM1, 0);
EPWM_setTimeBaseCounterMode(EPWM1, EPWM_COUNTER_MODE_UP);
EPWM_disablePhaseShiftLoad(EPWM1);
EPWM_setPhaseShift(EPWM1, 0);
EPWM_setCounterCompareValue(EPWM1, EPWM_COUNTER_COMPARE_A, 13000);
EPWM_setCounterCompareShadowLoadMode(EPWM1, EPWM_COUNTER_COMPARE_A, EPWM_COMP_LOAD_ON_CNTR_ZERO);
EPWM_setCounterCompareValue(EPWM1, EPWM_COUNTER_COMPARE_B, 6500);
EPWM_setCounterCompareShadowLoadMode(EPWM1, EPWM_COUNTER_COMPARE_B, EPWM_COMP_LOAD_ON_CNTR_ZERO);

EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);
EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD);
EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);

EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPA);
EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPB);
EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPB);



EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);
EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD);
EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPA);
EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPB);
EPWM_setActionQualifierAction(EPWM1, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPB);


EPWM_setRisingEdgeDelayCountShadowLoadMode(EPWM1, EPWM_RED_LOAD_ON_CNTR_ZERO);
EPWM_disableRisingEdgeDelayCountShadowLoadMode(EPWM1);
EPWM_setFallingEdgeDelayCountShadowLoadMode(EPWM1, EPWM_FED_LOAD_ON_CNTR_ZERO);
EPWM_disableFallingEdgeDelayCountShadowLoadMode(EPWM1);

my_pwm1_gpio_Init();
}
void my_pwm1_gpio_Init(void)
{
/* EPWM1A GPIO configuration */
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 GPIO configuration */
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);
}

四、 遇到的问题与解决方式

本项目在实现过程中较为顺利,主要难点在于ePWM寄存器参数的精确计算与配置

  • 问题:初始配置后,用示波器测量发现PWM频率与目标值(2MHz)有偏差。
  • 分析:原因在于对系统时钟源频率和ePWM时钟分频系数的计算存在误差。
  • 解决:仔细查阅芯片数据手册的时钟树和ePWM章节,重新计算并设置分频器和周期寄存器值,最终得到精确的2MHz波形。


五、 实现结果展示

1.串口输出结果
在串口调试助手中,可稳定接收到来自NS800RT5039的字符串信息。
Hello, NOVOSENSE Wedesign project.

b7e708adba1f063faa1bc23fc047f37.png

2.ePWM波形结果
使用示波器捕获ePWM输出引脚的波形,结果如下:

    • 波形:一对互补的PWM方波。
    • 频率:实测为 2.00 MHz,与目标一致。
    • 占空比:实测为 50.1%,精度符合要求。

0a5adc5cada41b5f5ca03a837638f5a0.png

3.实体接线图

740197681db96e99607a370222e84f2.jpg



六、心得体会

感谢电子森林提供的宝贵实践机会。完成这道基础题后,我最大的收获是:看似简单的任务,恰恰最能暴露代码的健壮性短板。

题目要求实现一个基础的内存或字符串函数,但其核心陷阱在于必须处理内存重叠。这让我深刻认识到,若仅满足功能正常,而忽略源与目标地址可能重叠的情况,直接进行单向拷贝,将导致灾难性的数据污染。真正的解决方案是效仿memmove,通过地址比较来智能决策拷贝方向。

此外,对空指针、非法长度等边界条件的严谨检查,同样是嵌入式开发中不可或缺的职业素养。这道题让我再次审视自己代码的严密性。

附件下载
uart_ex1_send_receive_polling.zip
pwm1_A_B.zip
团队介绍
人员:袁彬程,冉红杰,黄小珊,周栋梁
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号