- 2025-01-27
-
回复了主题帖:
在越南出差时登陆不了
okhxyyo 发表于 2025-1-27 11:17
原来出差去了呀!新年快乐呀!
我遇到过这种故障,做网站一定要使用带时区的时间值。
- 2025-01-24
-
回复了主题帖:
【FRDM-MCXN947测评】CAN FD通讯测试
freebsder 发表于 2025-1-24 17:59
can用起来还是各种麻烦,我们现在没必要的地方用以太网,很多复杂的事就完全没事了。
我的感觉和你一样,以太网似乎更方便。CAN 可能是不了解的缘故,感觉很复杂
-
发表了主题帖:
【FRDM-MCXN947测评】相移PWM输出测试
本帖最后由 bigbat 于 2025-1-24 16:02 编辑
1、测试介绍
MCXN947系列MCU的FlexPWM 模块,可以通过独立的时钟控制两个通道输出:具有可控制相位输出的PWM波形。每个通道A相或B相可以独立的任意调节PWM相位。该功能可以实现:
中心对齐PWM:
边缘对齐PWM:
相移PWM:
双开关 PWM:
其中的相移输出是我工作中需要的输出,旧有的设计需要多个计数器相互配合实现复杂,而且输出精度和实时性较差,不能实现较为精准的输出和控制。我的要求是:两个通道,A先开启进行预充电,然后在开启输出功率管工作。原有的设计经常出现中断的现象。就是由于相位控制不精确造成的。所以该功能正是我需要的。本次测试也是对该功能进行测试。
硬件:
FRDM-MCXN947开发板一块
双通道100M示波器一台
USB TYPE-C电缆
软件:
GCC ARM Embedded 13.2.1编译器。
测试实现的效果如:
PWM_A相:输出
PWM_B相:输出
2、设置及程序分析
该测试程序参考MCXN947的SDK PWM程序。
NXP的配置工具MCUXpresso Config Tools v16.1除了可以配置芯片引脚路由外,其它的我到现在都没有发现其它的更详细的配置项目。
第一段:
程序的思路是通过MCU PWM的两个寄存器来控制PWM的脉宽位置。参考下面的初始化代码。
/*******************************************************************************
* Code
******************************************************************************/
static void PWM_DRV_Init3PhPwm(void)
{
uint16_t deadTimeVal;
pwm_signal_param_t pwmSignal[2];
uint32_t pwmSourceClockInHz;
uint32_t pwmFrequencyInHz = APP_DEFAULT_PWM_FREQUENCY;
pwmSourceClockInHz = PWM_SRC_CLK_FREQ;
/* Set deadtime count, we set this to about 650ns */
deadTimeVal = ((uint64_t)pwmSourceClockInHz * 650) / 1000000000;
pwmSignal[0].pwmChannel = kPWM_PwmA;
pwmSignal[0].level = kPWM_HighTrue;
pwmSignal[0].dutyCyclePercent = 50; /* 1 percent dutycycle */
pwmSignal[0].deadtimeValue = deadTimeVal;
pwmSignal[0].faultState = kPWM_PwmFaultState0;
pwmSignal[0].pwmchannelenable = true;
pwmSignal[1].pwmChannel = kPWM_PwmB;
pwmSignal[1].level = kPWM_HighTrue;
/* Dutycycle field of PWM B does not matter as we are running in PWM A complementary mode */
pwmSignal[1].dutyCyclePercent = 50;
pwmSignal[1].deadtimeValue = deadTimeVal;
pwmSignal[1].faultState = kPWM_PwmFaultState0;
pwmSignal[1].pwmchannelenable = true;
/*********** PWMA_SM0 - phase A, configuration, setup 2 channel as an example ************/
PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_0, pwmSignal, 2, kPWM_SignedCenterAligned, pwmFrequencyInHz,
pwmSourceClockInHz);
/*********** PWMA_SM1 - phase B configuration, setup PWM A channel only ************/
#ifdef DEMO_PWM_CLOCK_DEVIDER
PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_1, pwmSignal, 1, kPWM_SignedCenterAligned, pwmFrequencyInHz,
pwmSourceClockInHz / (1 << DEMO_PWM_CLOCK_DEVIDER));
#else
PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_1, pwmSignal, 1, kPWM_SignedCenterAligned, pwmFrequencyInHz,
pwmSourceClockInHz);
#endif
/*********** PWMA_SM2 - phase C configuration, setup PWM A channel only ************/
#ifdef DEMO_PWM_CLOCK_DEVIDER
PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_2, pwmSignal, 1, kPWM_SignedCenterAligned, pwmFrequencyInHz,
pwmSourceClockInHz / (1 << DEMO_PWM_CLOCK_DEVIDER));
#else
PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_2, pwmSignal, 1, kPWM_SignedCenterAligned, pwmFrequencyInHz,
pwmSourceClockInHz);
#endif
}
整个参数配置为
一个结构体 pwm_signal_param_t pwmSignal[2];
一个配置函数PWM_SetupPwm();
函数设计有点过于抽象,函数的设计思路是按照工程化的思维模式设计的,例如:占空比(pwmSignal[0].dutyCyclePercent = 50;)、相位角(pwmSignal[0].deadtimeValue = deadTimeVal;)等概念来输入。对于我这种嵌入式程序思维的人来说,总是觉得不够直接(好像没有掌控感,呵呵!好像NXP不能够让我装了,有点不爽呃)
第二段:
这个思路主要是控制PWM的输出。参考下面代码
/*!
* [url=home.php?mod=space&uid=159083]@brief[/url] Main function
*/
int main(void)
{
/* Structure of initialize PWM */
pwm_config_t pwmConfig;
pwm_fault_param_t faultConfig;
uint32_t pwmVal = 4;
/* Board pin, clock, debug console init */
/* attach FRO 12M to FLEXCOMM4 (debug console) */
CLOCK_SetClkDiv(kCLOCK_DivFlexcom4Clk, 1u);
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
BOARD_InitPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();
/* Enable PWM1 SUB Clockn */
SYSCON->PWM1SUBCTL |=
(SYSCON_PWM1SUBCTL_CLK0_EN_MASK | SYSCON_PWM1SUBCTL_CLK1_EN_MASK | SYSCON_PWM1SUBCTL_CLK2_EN_MASK);
PRINTF("FlexPWM driver example\n");
/*
* pwmConfig.enableDebugMode = false;
* pwmConfig.enableWait = false;
* pwmConfig.reloadSelect = kPWM_LocalReload;
* pwmConfig.clockSource = kPWM_BusClock;
* pwmConfig.prescale = kPWM_Prescale_Divide_1;
* pwmConfig.initializationControl = kPWM_Initialize_LocalSync;
* pwmConfig.forceTrigger = kPWM_Force_Local;
* pwmConfig.reloadFrequency = kPWM_LoadEveryOportunity;
* pwmConfig.reloadLogic = kPWM_ReloadImmediate;
* pwmConfig.pairOperation = kPWM_Independent;
*/
PWM_GetDefaultConfig(&pwmConfig);
#ifdef DEMO_PWM_CLOCK_DEVIDER
pwmConfig.prescale = DEMO_PWM_CLOCK_DEVIDER;
#endif
/* Use full cycle reload */
pwmConfig.reloadLogic = kPWM_ReloadPwmFullCycle;
/* PWM A & PWM B form a complementary PWM pair */
pwmConfig.pairOperation = kPWM_ComplementaryPwmA;
pwmConfig.enableDebugMode = true;
/* Initialize submodule 0 */
if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_0, &pwmConfig) == kStatus_Fail)
{
PRINTF("PWM initialization failed\n");
return 1;
}
/* Initialize submodule 1, make it use same counter clock as submodule 0. */
pwmConfig.clockSource = kPWM_Submodule0Clock;
pwmConfig.prescale = kPWM_Prescale_Divide_1;
pwmConfig.initializationControl = kPWM_Initialize_MasterSync;
if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_1, &pwmConfig) == kStatus_Fail)
{
PRINTF("PWM initialization failed\n");
return 1;
}
/* Initialize submodule 2 the same way as submodule 1 */
if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_2, &pwmConfig) == kStatus_Fail)
{
PRINTF("PWM initialization failed\n");
return 1;
}
/*
* config->faultClearingMode = kPWM_Automatic;
* config->faultLevel = false;
* config->enableCombinationalPath = true;
* config->recoverMode = kPWM_NoRecovery;
*/
PWM_FaultDefaultConfig(&faultConfig);
#ifdef DEMO_PWM_FAULT_LEVEL
faultConfig.faultLevel = DEMO_PWM_FAULT_LEVEL;
#endif
/* Sets up the PWM fault protection */
PWM_SetupFaults(BOARD_PWM_BASEADDR, kPWM_Fault_0, &faultConfig);
PWM_SetupFaults(BOARD_PWM_BASEADDR, kPWM_Fault_1, &faultConfig);
PWM_SetupFaults(BOARD_PWM_BASEADDR, kPWM_Fault_2, &faultConfig);
PWM_SetupFaults(BOARD_PWM_BASEADDR, kPWM_Fault_3, &faultConfig);
/* Set PWM fault disable mapping for submodule 0/1/2 */
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_1, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_2, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
/* Call the init function with demo configuration */
PWM_DRV_Init3PhPwm();
/* Set the load okay bit for all submodules to load registers from their buffer */
PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0 | kPWM_Control_Module_1 | kPWM_Control_Module_2, true);
/* Start the PWM generation from Submodules 0, 1 and 2 */
PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0 | kPWM_Control_Module_1 | kPWM_Control_Module_2);
while (1U)
{
/* Delay at least 100 PWM periods. */
SDK_DelayAtLeastUs((1000000U / APP_DEFAULT_PWM_FREQUENCY) * 100, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
pwmVal = pwmVal + 4;
/* Reset the duty cycle percentage */
if (pwmVal > 100)
{
pwmVal = 4;
}
/* Update duty cycles for all 3 PWM signals */
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_SignedCenterAligned, pwmVal);
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_1, kPWM_PwmA, kPWM_SignedCenterAligned, (pwmVal >> 1));
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_2, kPWM_PwmA, kPWM_SignedCenterAligned, (pwmVal >> 2));
/* Set the load okay bit for all submodules to load registers from their buffer */
PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0 | kPWM_Control_Module_1 | kPWM_Control_Module_2, true);
}
主要代码是:
(1)初始化模块(submodule 0/1/2)
/* Set PWM fault disable mapping for submodule 0/1/2 */
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_1, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_2, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
(2)开始定时器
PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0 | kPWM_Control_Module_1 | kPWM_Control_Module_2);
(3)程序循环中控制PWM脉冲的量值
/* Update duty cycles for all 3 PWM signals */
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_SignedCenterAligned, pwmVal);
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_1, kPWM_PwmA, kPWM_SignedCenterAligned, (pwmVal >> 1));
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_2, kPWM_PwmA, kPWM_SignedCenterAligned, (pwmVal >> 2));
3、测试过程
(1) 将A0相输出和A1输出连接到示波器上去。
(2)将USB Type-C电缆连接到PC主机
(3)运行程序
调节示波器,可以查看输出波形。
波形输出为设计的方式,但是波形出现了很多的“过冲”现象,不知道是什么原因造成的。
4、总结
通过测试可以更加深入的了解NXP FlexPWM的控制原理,NXP使用较为工程化的函数封装来简化编程的复杂度。我个人觉得如果想很好的了解FlexPWM的模块工作原理还是需要了解各种寄存器和模块的设计原理。
- 2025-01-23
-
发表了主题帖:
【MCXN947开发板测评】I2C总线设备的兼容性测试
1、测试简介
测试如何使用 CMSIS i2c 驱动作为主机,通过I2C总线控制SSD1306 OLED显示图像。通过轮询方式进行I2C控制通讯。
硬件要求:
Type-C USB 线
FRDM - MCXN947 开发板
SSD1306 OLED屏
个人计算机
软件:
GCC ARM 嵌入式 13.2.1
Putty串口终端
2、设置与程序
OLED屏需按以下方式连接:
主机(LPI2C2) 连接 SSD1306
开发板 OLED
SCL J2 引脚 20 <----------> SCL
SDA J2 引脚 18 <----------> SDA
GND J3 引脚 14 <----------> GND
P3.3V J3 引脚 4 <----------> 3.3V
/*
* Copyright 2017 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* Standard C Included Files */
#include <stdio.h>
#include <string.h>
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "fsl_debug_console.h"
#include "fsl_lpi2c.h"
#include "fsl_SSD1306_I2C.h"
/*******************************************************************************
* Definitions
******************************************************************************/
//#define EXAMPLE_I2C_MASTER_BASE (LPI2C2_BASE)
#define LPI2C_MASTER_CLOCK_FREQUENCY CLOCK_GetLPFlexCommClkFreq(2u)
#define WAIT_TIME 10U
//#define EXAMPLE_I2C_MASTER ((LPI2C_Type *)EXAMPLE_I2C_MASTER_BASE)
#define LPI2C_MASTER_SLAVE_ADDR_7BIT 0x3CU
#define LPI2C_BAUDRATE 100000U
#define LPI2C_DATA_LENGTH 33U
/*******************************************************************************
* Prototypes
******************************************************************************/
/*******************************************************************************
* Variables
******************************************************************************/
uint8_t g_master_txBuff[LPI2C_DATA_LENGTH];
uint8_t g_master_rxBuff[LPI2C_DATA_LENGTH];
/*******************************************************************************
* Code
******************************************************************************/
/*!
* [url=home.php?mod=space&uid=159083]@brief[/url] Main function
*/
int main(void)
{
lpi2c_master_config_t masterConfig;
//status_t reVal = kStatus_Fail;
//uint8_t deviceAddress = 0x01U;
size_t txCount = 0xFFU;
/* attach FRO 12M to FLEXCOMM4 (debug console) */
CLOCK_SetClkDiv(kCLOCK_DivFlexcom4Clk, 1u);
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
/* attach FRO 12M to FLEXCOMM2 */
CLOCK_SetClkDiv(kCLOCK_DivFlexcom2Clk, 1u);
CLOCK_AttachClk(kFRO12M_to_FLEXCOMM2);
BOARD_InitBootPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();
PRINTF("\r\nLPI2C board2board polling example -- Master transfer.\r\n");
LPI2C_MasterGetDefaultConfig(&masterConfig);
/* Change the default baudrate configuration */
masterConfig.baudRate_Hz = LPI2C_BAUDRATE;
/* Initialize the LPI2C master peripheral */
LPI2C_MasterInit(EXAMPLE_I2C_MASTER, &masterConfig, LPI2C_MASTER_CLOCK_FREQUENCY);
/* Initialize the SSD1306 display*/
OLED_Init();
OLED_Refresh();
OLED_Clear();
/*Print welcome message*/
OLED_Copy_Image(&logo_nxp[0], sizeof(logo_nxp));
OLED_Refresh();
OLED_Set_Text(10, 52, kOLED_Pixel_Set, "MCXN947 I2C", 2);
OLED_Refresh();
PRINTF("\r\nEnd of LPI2C example .\r\n");
while (1)
{
}
}
OLED SSD1306驱动程序fsl_SSD1306_I2C.c
/*----esta modificada para usar el I2C1, en caso de utilizar otro cambiar todo
* lo que diga I2C1 por la base del I2C usado--- */
/*---- BASADO EN fsl_SSD1306.h para SPI de NXP ------*/
#include "fsl_gpio.h"
#include "fsl_lpi2c.h"
#include <fsl_SSD1306_I2C.h>
// #include "fsl_Systick_Delay.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/*******************************************************************************
* Variables
******************************************************************************/
/*! @brief OLED buffer */
static uint8_t OLED_Buffer[(OLED_WIDTH * OLED_HEIGHT) / 8];
/*******************************************************************************
* Code
******************************************************************************/
static void OLED_Command(uint8_t Cmd) {
lpi2c_master_transfer_t xfer = {0};
xfer.data = (uint8_t *)&Cmd;
xfer.dataSize = sizeof(Cmd);
xfer.flags = kLPI2C_TransferDefaultFlag;
xfer.slaveAddress = SSD1306_ADDRESS_1;
xfer.direction = kLPI2C_Write;
xfer.subaddress = 0x0;
xfer.subaddressSize = 1;
LPI2C_MasterTransferBlocking(EXAMPLE_I2C_MASTER, &xfer);
}
static void OLED_Data(uint8_t *Data) {
lpi2c_master_transfer_t xfer = {0};
/*Start Transfer*/
xfer.data = Data;
xfer.dataSize = sizeof(OLED_Buffer);
xfer.flags = kLPI2C_TransferDefaultFlag;
xfer.slaveAddress = SSD1306_ADDRESS_1;
xfer.direction = kLPI2C_Write;
xfer.subaddress = 0x40;
xfer.subaddressSize = 1;
LPI2C_MasterTransferBlocking(EXAMPLE_I2C_MASTER, &xfer);
}
static void OLED_Reset(void) {
OLED_Command(OLED_DISPLAYON);
// for(int i=0; i<5000; i++); //delay
//1ms
// SysTick_Delay_ms(1);
OLED_Command(OLED_DISPLAYOFF);
// for(int i=0; i<5000; i++); //delay
//1ms
// SysTick_Delay_ms(1);
OLED_Command(OLED_DISPLAYON);
// for(int i=0; i<50000; i++); //delay
//10ms
// SysTick_Delay_ms(10);
}
static void OLED_Config_Display(void) {
OLED_Reset();
// Turn the OLED Display off
OLED_Command(OLED_DISPLAYOFF);
// Configure the display for 128x64 pixels, KS0108 mode
OLED_Command(OLED_SETDISPLAYCLOCKDIV);
OLED_Command(0x80);
OLED_Command(OLED_SETMULTIPLEX);
OLED_Command(OLED_HEIGHT - 1); // LCD Height
OLED_Command(OLED_SETDISPLAYOFFSET);
OLED_Command(0x0);
OLED_Command(OLED_SETSTARTLINE | 0x0);
OLED_Command(OLED_CHARGEPUMP);
OLED_Command(0x14); // Use 3.3V supply to generate high voltage supply
OLED_Command(OLED_MEMORYMODE);
OLED_Command(0x00);
OLED_Command(OLED_SEGREMAP | 0x1);
OLED_Command(OLED_COMSCANDEC);
OLED_Command(OLED_SETCOMPINS);
OLED_Command(0x12);
OLED_Command(OLED_SETCONTRAST);
OLED_Command(0xCF);
OLED_Command(OLED_SETPRECHARGE);
OLED_Command(0xF1);
OLED_Command(OLED_SETVCOMDETECT);
OLED_Command(0x40);
OLED_Command(OLED_DISPLAYALLON_RESUME);
OLED_Command(OLED_NORMALDISPLAY);
OLED_Command(OLED_DEACTIVATE_SCROLL);
OLED_Command(OLED_COLUMNADDR);
OLED_Command(0);
OLED_Command(OLED_WIDTH - 1);
OLED_Command(OLED_PAGEADDR);
OLED_Command(0);
OLED_Command(OLED_HEIGHT / 8 - 1);
// Turn the OLED display on!
OLED_Command(OLED_DISPLAYON);
OLED_Command(OLED_SETLOWCOLUMN | 0x0); // low col = 0
OLED_Command(OLED_SETHIGHCOLUMN | 0x0); // hi col = 0
OLED_Command(OLED_SETSTARTLINE | 0x0); // line #0
}
static int OLED_Render_Char(uint8_t X_axis, uint8_t Y_axis, uint8_t SC,
int8_t String, uint8_t Scale) {
uint8_t px, py;
uint16_t start_pos;
if ((X_axis >= OLED_WIDTH) || (Y_axis >= OLED_HEIGHT)) {
return 1;
}
if (String > 127) {
return 2;
}
if (Scale > 3) {
return 3;
}
start_pos = ((uint8_t)String) * 7; // Characters have a 7 row offset
for (px = 0; px < 5; px++) {
for (py = 0; py < 7; py++) {
if ((font5x7[start_pos + py] >> (7 - px)) & 1) {
switch (Scale) {
case 3:
OLED_Set_Pixel(X_axis + (px * Scale), Y_axis + (py * Scale), SC);
OLED_Set_Pixel(X_axis + (px * Scale) + 1, Y_axis + (py * Scale), SC);
OLED_Set_Pixel(X_axis + (px * Scale) + 2, Y_axis + (py * Scale), SC);
OLED_Set_Pixel(X_axis + (px * Scale), Y_axis + (py * Scale) + 1, SC);
OLED_Set_Pixel(X_axis + (px * Scale) + 1, Y_axis + (py * Scale) + 1,
SC);
OLED_Set_Pixel(X_axis + (px * Scale) + 2, Y_axis + (py * Scale) + 1,
SC);
OLED_Set_Pixel(X_axis + (px * Scale), Y_axis + (py * Scale) + 2, SC);
OLED_Set_Pixel(X_axis + (px * Scale) + 1, Y_axis + (py * Scale) + 2,
SC);
OLED_Set_Pixel(X_axis + (px * Scale) + 2, Y_axis + (py * Scale) + 2,
SC);
break;
case 2:
OLED_Set_Pixel(X_axis + (px * Scale), Y_axis + (py * Scale), SC);
OLED_Set_Pixel(X_axis + (px * Scale) + 1, Y_axis + (py * Scale), SC);
OLED_Set_Pixel(X_axis + (px * Scale), Y_axis + (py * Scale) + 1, SC);
OLED_Set_Pixel(X_axis + (px * Scale) + 1, Y_axis + (py * Scale) + 1,
SC);
break;
case 1:
default:
OLED_Set_Pixel(X_axis + px, Y_axis + py, SC);
break;
}
}
}
}
return 0;
}
void OLED_Init(void) {
/*Give the display a reset*/
OLED_Reset();
/* Clear the framebuffer*/
OLED_Clear();
/*Configure the OLED display controller*/
OLED_Config_Display();
}
void OLED_Refresh(void) {
OLED_Command(0xb0);
OLED_Command(((0 & 0xf0) >> 4) | 0x10);
OLED_Command((0 & 0x0f) | 0x01);
OLED_Data(&OLED_Buffer[0]);
}
void OLED_Clear(void) { memset(OLED_Buffer, 0, sizeof(OLED_Buffer)); }
void OLED_Fill(uint8_t Pattern) {
memset(OLED_Buffer, Pattern, sizeof(OLED_Buffer));
}
void OLED_Display_Mode(uint8_t Mode) {
if (Mode) {
OLED_Command(OLED_INVERTDISPLAY);
} else {
OLED_Command(OLED_NORMALDISPLAY);
}
}
void OLED_Set_Pixel(uint8_t X_axis, uint8_t Y_axis, uint8_t SC) {
if ((X_axis >= OLED_WIDTH) || (Y_axis >= OLED_HEIGHT)) {
// Do nothing
} else {
switch (SC) {
case kOLED_Pixel_Clear:
OLED_Buffer[X_axis + (Y_axis / 8) * OLED_WIDTH] &= ~(1 << (Y_axis & 7));
break;
case kOLED_Pixel_Set:
OLED_Buffer[X_axis + (Y_axis / 8) * OLED_WIDTH] |= (1 << (Y_axis & 7));
break;
}
}
}
void OLED_Set_Text(uint8_t X_axis, uint8_t Y_axis, uint8_t SC, char *String,
uint8_t Scale) {
uint16_t Cont;
uint16_t xscaled;
if ((X_axis >= OLED_WIDTH) || (Y_axis >= OLED_HEIGHT)) {
// Do nothing
} else {
if (Scale > 3) {
// Do nothing
} else {
for (Cont = 0; String[Cont] != '\0'; Cont++) {
// Catch overflow when scaling!
xscaled = X_axis + (Cont * 5 * Scale);
if (xscaled > OLED_WIDTH) {
// Do nothing
}
else {
OLED_Render_Char(xscaled, Y_axis, SC, String[Cont], Scale);
}
}
}
}
}
void OLED_Copy_Image(const uint8_t *Img, uint16_t size) {
uint16_t CpyBuffer;
OLED_Clear();
for (CpyBuffer = 0; CpyBuffer < size - 1; CpyBuffer++) {
OLED_Buffer[CpyBuffer] = *(Img + CpyBuffer);
}
}
3、测试过程
将开发板与OLED相连接
4、总结
MCXN的I2C编程使用查询模式对MCU不断查询。所以对于MCU有一定的压力,对于双核的系统可以发挥效能。
-
回复了主题帖:
【FRDM-MCXN947测评】CAN FD通讯测试
freebsder 发表于 2025-1-23 16:35
有开源的CANFD的canopen协议栈吗?
是的,我之前了解过该项目,但是项目的资料不是很多,而且这类项目也不多。远不如以太网
-
发表了主题帖:
【FRDM-MCXN947测评】使用NPU对象检测测试
1、测试介绍
测试是基于 TensorFlow Lite 模型的“对象检测器”实现对象检测测试。 此测试是使用eQI工具经过调整后的TensorFlow Lite模型,转换后的模型可在带有NPU的MCXN MCU 上运行。
程序将三通道彩色图像转化为量化 Mobilenet 的卷积神经网络模型输入,该输入通过“对象检测器模型”在NPU运算后输出为1000种图像分类之一。
程序静态图像为输入。NPU运算结果通过UART进行输出。
待识别的图像为:
该图像需要预先转换为C语言的数组文件image_data.h,该文件代替摄像头输入。
硬件:
FRDM-MCXN947开发板一块
USB Type-C电缆一条
软件:
GCC ARM Embedded 13.2.1编译器
PUTTY 串口终端软件
eIQ_Toolkit 模型工具软件
2、模型转换
测试的模型可以从http://download.tensorflow.org/models/mobilenet_v1_2018_08_02/mobilenet_v1_0.25_128.tgz处下载。
输入文件 https://commons.wikimedia.org/wiki/File:Stopwatch2.jpg
(1)将Stopwatch2.jpg文件使用python脚本转成OpenCV格式数组
import cv2
import numpy as np
img = cv2.imread('test/Stopwatch2.jpg')
img = cv2.resize(img,(128,128))
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
with open('image_data.h','w') as fout:
print('#define STATIC_IMAGE_NAME "stopwatch"',file=fout)
print('static const uint8_t image_data[] = {',file=fout)
img.tofile(fout,', ','0x%02X')
print('};\n',file=fout)
(2)使用eIQ_Toolkit软件将TensorFlow Lite 模型模型转换成MCXN947 NPU能够使用的int8结构模型
使用MODEL TOOL将模型转换成MCXN NPU格式
模型的层次信息
使用软件左上角Covert功能转换格式
目标文件custom_model_converted.tflite
转后的文件格式为int8格式,该格式可以运行在MCXN NPU上面。
3、模型引入
将custom_model_converted.tflite文件放入model_data.h格式当中。
整个文件成为uint8_t model_data[]数组。
总结一下就是将eIQ转换的模型放到一个C语言数组当中。这是我看了好几遍文档才明白过来的。
4、项目介绍
使用MCUXpresso Config Tools 引入项目
整个程序主要为两部分
一、对使用的模型进行初始化;
二、将输入数据转换成NPU数组;
int main(void)
{
BOARD_Init();
TIMER_Init();
DEMO_PrintInfo();
if (MODEL_Init() != kStatus_Success)
{
PRINTF("Failed initializing model" EOL);
for (;;) {}
}
tensor_dims_t inputDims;
tensor_type_t inputType;
uint8_t* inputData = MODEL_GetInputTensorData(&inputDims, &inputType);
tensor_dims_t outputDims;
tensor_type_t outputType;
uint8_t* outputData = MODEL_GetOutputTensorData(&outputDims, &outputType);
while (1)
{
/* Expected tensor dimensions: [batches, height, width, channels] */
if (IMAGE_GetImage(inputData, inputDims.data[2], inputDims.data[1], inputDims.data[3]) != kStatus_Success)
{
PRINTF("Failed retrieving input image" EOL);
for (;;) {}
}
MODEL_ConvertInput(inputData, &inputDims, inputType);
auto startTime = TIMER_GetTimeInUS();
MODEL_RunInference();
auto endTime = TIMER_GetTimeInUS();
MODEL_ProcessOutput(outputData, &outputDims, outputType, endTime - startTime);
}
}
5、测试过程
使用USB type-c连接开发板和PC
打开Putty终端
测试结果:
秒表 stopwatch
置信度:(86%)
6、总结
该测试是为了测评进行的,我之前尝试了好多次的摄像头实时取数的程序,但是摄像头的调试关于复杂,而且我没有显示屏也不能实时看到结果。但是整个NPU项目的流程已经搞清楚了。
非常期待手头阔绰的网友能够给出更加精彩的测评。
-
发表了主题帖:
【FRDM-MCXN947测评】CAN FD通讯测试
本帖最后由 bigbat 于 2025-1-23 11:40 编辑
1、测试介绍
MCXN947VDF MCU具有CAN FD外设。本次测试CAN 通讯测试。测试需要两块具有CAN FD通讯的设备。我这里使用一块FRM-MCXA156开发板作为从机进行通讯。两块开发板通过 CAN 总线连接。当用户在终端输入要发送的 CAN 消息数量时,端点 A(开发板 A)会向端点 B(开发板 B)发送 CAN/CANFD 消息。端点 B 使用两个接收队列轮流接收消息,并且在任意一个队列满时,将消息内容和接收队列编号打印到终端。
测试硬件:
FRDM-MCXN947 开发板一块
FRDM-MCXA156 开发板 一块
PC计算机一台
USB type-c电缆线两条
杜邦连接一组
软件:
GCC ARM Embedded 13.2.1编译器
PUTTY串口终端
2、测试配置与程序
将引脚引脚·P1_10和·P1_11引脚设置为CAN FD功能。
cAN通讯程序较为复杂,直接构建较为麻烦,所以通常使用工具自动生成程序。
if ((node_type == 'A') || (node_type == 'a'))
{
uint8_t index = 0;
uint32_t times = 0;
LOG_INFO("Please input the number of CAN/CANFD messages to be send and end with enter.\r\n");
while (index != 0x0D)
{
index = GETCHAR();
if ((index >= '0') && (index <= '9'))
{
(void)PUTCHAR(index);
times = times * 10 + (index - 0x30U);
}
}
LOG_INFO("\r\n");
txFrame.id = FLEXCAN_ID_STD(TX_MB_ID);
txFrame.format = (uint8_t)kFLEXCAN_FrameFormatStandard;
txFrame.type = (uint8_t)kFLEXCAN_FrameTypeData;
txFrame.length = (uint8_t)DLC;
#if (defined(USE_CANFD) && USE_CANFD)
txFrame.brs = 1U;
txFrame.edl = 1U;
#endif
for (i = 1; i <= times; i++)
{
#if (defined(USE_CANFD) && USE_CANFD)
(void)FLEXCAN_TransferFDSendBlocking(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, &txFrame);
#else
(void)FLEXCAN_TransferSendBlocking(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, &txFrame);
#endif
/* Wait for 200ms after every 2 RX_QUEUE_BUFFER_SIZE transmissions. */
if ((TxCount % (RX_QUEUE_BUFFER_SIZE * 2U)) == 0U)
SDK_DelayAtLeastUs(200000U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
txFrame.dataByte0++;
TxCount++;
}
LOG_INFO("Transmission done.\r\n\r\n");
}
else
{
/* Wait until Rx queue 1 full. */
while (rxQueue1Flag != 1U)
{
};
rxQueue1Flag = 0;
LOG_INFO("Read Rx MB from Queue 1.\r\n");
for (i = 0; i < RX_QUEUE_BUFFER_SIZE; i++)
{
LOG_INFO("Rx MB ID: 0x%3x, Rx MB data: 0x%x, Time stamp: %d\r\n", rxFrame[i].id >> CAN_ID_STD_SHIFT,
rxFrame[i].dataByte0, rxFrame[i].timestamp);
}
/* Wait until Rx queue 2 full. */
while (rxQueue2Flag != 1U)
{
};
rxQueue2Flag = 0;
LOG_INFO("Read Rx MB from Queue 2.\r\n");
for (; i < (RX_QUEUE_BUFFER_SIZE * 2U); i++)
{
LOG_INFO("Rx MB ID: 0x%3x, Rx MB data: 0x%x, Time stamp: %d\r\n", rxFrame[i].id >> CAN_ID_STD_SHIFT,
rxFrame[i].dataByte0, rxFrame[i].timestamp);
}
if (rxStatus == kStatus_FLEXCAN_RxOverflow)
{
rxStatus = 0;
LOG_INFO("The data in the last MB %d in the queue 2 is overwritten\r\n", RX_QUEUE_BUFFER_END_2);
}
LOG_INFO("Wait Node A to trigger the next 8 messages!\r\n\r\n");
}
主要思路为设置一个贞缓冲,使用FLEXCAN_TransferSendBlocking进行数据发送。接收数据使用中断进行接收数据。将接受数据放置到接收缓冲当中。
void EXAMPLE_FLEXCAN_IRQHandler(void)
{
User_TransferHandleIRQ(EXAMPLE_CAN);
SDK_ISR_EXIT_BARRIER;
}
3、测试过程
将两个开发板使用杜邦线相连。将开发板FRDM-MCXA156 USB线缆插入PC主机。
打开Putty中断,将开发板设置为节点NODE B
将FRDM-MCXN947 USB线缆插入PC,打开终端软件,将开发板设置为NODE A
在终端中输入数据缓冲大小,可以发现由NODE A向NODE B发送的测试数据。
4、总结
从程序的编写角度来看,CAN的通讯较为复杂,调用的API也较多。虽然NXP对SDK进行了较大的简化,但是在复杂的应用中还是较为困难。如果业界推出一些较为程式化的封装库,则可以大大简化编程。就如iwIP之类的应用。
- 2025-01-21
-
发表了主题帖:
【FRDM-MCXN947测评】AD采集和设置测试及功耗测量
1、测试介绍
本测试使用ADC外设中断模式采集电压数据。MCXN947VDF MCU有两组16bit ADC核心。
1)每个 ADC 可用作两个单端输入 ADC 或一个差分输入 ADC。
2)在 16 位模式下高达 2 Msps,在 12 位模式下高达 3.15 Msps。
3)多达 75 个 ADC 输入通道。
4)每个 ADC 外设集成一个温度传感器作为温度补偿使用。
通常的MCU中多数为12bit的ADC,很少集成为16bit的ADC。所以MCXN947VDF使用为高档仪表较为理想。
硬件:
FRDM-MCXN947一块。
USB type-c电缆线一条。
简易电压信号发生器一台。
万用表一块。
软件:
GCC ARM Embedded 13.2.1编译器。
测试通过J8端口 28引脚采集电压。该引脚与MCU P4_23通道连接,本次测试使用14bit模式进行采集。
2、程序与测试设置
将MCU的ADC0的通道A2打开。
编写中断和初始化程序
中断服务程序。程序中使用LPADC_GetConvResult()函数获得ADC转化值。并且将g_LpadcConversionCompletedFlag = true;标记置为true用来提示采集完成。
void DEMO_LPADC_IRQ_HANDLER_FUNC(void)
{
g_LpadcInterruptCounter++;
#if (defined(FSL_FEATURE_LPADC_FIFO_COUNT) && (FSL_FEATURE_LPADC_FIFO_COUNT == 2U))
if (LPADC_GetConvResult(DEMO_LPADC_BASE, &g_LpadcResultConfigStruct, 0U))
#else
if (LPADC_GetConvResult(DEMO_LPADC_BASE, &g_LpadcResultConfigStruct))
#endif /* FSL_FEATURE_LPADC_FIFO_COUNT */
{
g_LpadcConversionCompletedFlag = true;
}
SDK_ISR_EXIT_BARRIER;
}
ADC初始化代码,代码的配置是通过mLpadcConfigStruct数据结构在各配置函数中进行传递。
1)LPADC_GetDefaultConfig(&mLpadcConfigStruct);函数取得默认配置,填充默认值。
2)对mLpadcConfigStruct各域进行赋值,即进行设置。
将预制值打开
选择参考电压源
选择均值模式
初始化ADC
LPADC_GetDefaultConfig(&mLpadcConfigStruct);
mLpadcConfigStruct.enableAnalogPreliminary = true;
#if defined(DEMO_LPADC_VREF_SOURCE)
mLpadcConfigStruct.referenceVoltageSource = DEMO_LPADC_VREF_SOURCE;
#endif /* DEMO_LPADC_VREF_SOURCE */
#if defined(FSL_FEATURE_LPADC_HAS_CTRL_CAL_AVGS) && FSL_FEATURE_LPADC_HAS_CTRL_CAL_AVGS
mLpadcConfigStruct.conversionAverageMode = kLPADC_ConversionAverage128;
#endif /* FSL_FEATURE_LPADC_HAS_CTRL_CAL_AVGS */
LPADC_Init(DEMO_LPADC_BASE, &mLpadcConfigStruct);
设置采集模式和校准ADC。
/* Request LPADC calibration. */
#if defined(FSL_FEATURE_LPADC_HAS_CTRL_CALOFSMODE) && FSL_FEATURE_LPADC_HAS_CTRL_CALOFSMODE
LPADC_SetOffsetCalibrationMode(DEMO_LPADC_BASE, DEMO_LPADC_OFFSET_CALIBRATION_MODE);
#endif /* FSL_FEATURE_LPADC_HAS_CTRL_CALOFSMODE */
#if defined(FSL_FEATURE_LPADC_HAS_CTRL_CALOFS) && FSL_FEATURE_LPADC_HAS_CTRL_CALOFS
#if defined(DEMO_LPADC_DO_OFFSET_CALIBRATION) && DEMO_LPADC_DO_OFFSET_CALIBRATION
LPADC_DoOffsetCalibration(DEMO_LPADC_BASE); /* Request offset calibration, automatic update OFSTRIM register. */
#else /* Update OFSTRIM register manually. */
#if defined(FSL_FEATURE_LPADC_HAS_OFSTRIM) && FSL_FEATURE_LPADC_HAS_OFSTRIM
#if defined(FSL_FEATURE_LPADC_OFSTRIM_COUNT) && (FSL_FEATURE_LPADC_OFSTRIM_COUNT == 2U)
LPADC_SetOffsetValue(DEMO_LPADC_BASE, DEMO_LPADC_OFFSET_VALUE_A, DEMO_LPADC_OFFSET_VALUE_B);
#elif defined(FSL_FEATURE_LPADC_OFSTRIM_COUNT) && (FSL_FEATURE_LPADC_OFSTRIM_COUNT == 1U)
LPADC_SetOffsetValue(DEMO_LPADC_BASE, DEMO_LPADC_OFFSET_VALUE);
#endif /* FSL_FEATURE_LPADC_OFSTRIM_COUNT */
#else /* For other OFSTRIM register type. */
if (DEMO_LPADC_OFFSET_CALIBRATION_MODE == kLPADC_OffsetCalibration12bitMode)
{
LPADC_SetOffset12BitValue(DEMO_LPADC_BASE, DEMO_LPADC_OFFSET_VALUE_A, DEMO_LPADC_OFFSET_VALUE_B);
}
else
{
LPADC_SetOffset16BitValue(DEMO_LPADC_BASE, DEMO_LPADC_OFFSET_VALUE_A, DEMO_LPADC_OFFSET_VALUE_B);
}
#endif /* FSL_FEATURE_LPADC_HAS_OFSTRIM */
#endif /* DEMO_LPADC_DO_OFFSET_CALIBRATION */
#endif /* FSL_FEATURE_LPADC_HAS_CTRL_CALOFS */
#if defined(FSL_FEATURE_LPADC_HAS_CTRL_CAL_REQ) && FSL_FEATURE_LPADC_HAS_CTRL_CAL_REQ
/* Request auto calibration (including gain error calibration and linearity error calibration). */
LPADC_DoAutoCalibration(DEMO_LPADC_BASE);
#endif /* FSL_FEATURE_LPADC_HAS_CTRL_CAL_REQ */
#if (defined(FSL_FEATURE_LPADC_HAS_CFG_CALOFS) && FSL_FEATURE_LPADC_HAS_CFG_CALOFS)
/* Do auto calibration. */
LPADC_DoAutoCalibration(DEMO_LPADC_BASE);
#endif /* FSL_FEATURE_LPADC_HAS_CFG_CALOFS */
#if (defined(FSL_FEATURE_LPADC_HAS_CFG_CALOFS) && FSL_FEATURE_LPADC_HAS_CFG_CALOFS)
/* Do auto calibration. */
LPADC_DoAutoCalibration(DEMO_LPADC_BASE);
#endif /* FSL_FEATURE_LPADC_HAS_CFG_CALOFS */
/* Set conversion CMD configuration. */
LPADC_GetDefaultConvCommandConfig(&mLpadcCommandConfigStruct);
mLpadcCommandConfigStruct.channelNumber = DEMO_LPADC_USER_CHANNEL;
#if defined(DEMO_LPADC_USE_HIGH_RESOLUTION) && DEMO_LPADC_USE_HIGH_RESOLUTION
mLpadcCommandConfigStruct.conversionResolutionMode = kLPADC_ConversionResolutionHigh;
#endif /* DEMO_LPADC_USE_HIGH_RESOLUTION */
LPADC_SetConvCommandConfig(DEMO_LPADC_BASE, DEMO_LPADC_USER_CMDID, &mLpadcCommandConfigStruct);
/* Set trigger configuration. */
LPADC_GetDefaultConvTriggerConfig(&mLpadcTriggerConfigStruct);
mLpadcTriggerConfigStruct.targetCommandId = DEMO_LPADC_USER_CMDID; /* CMD15 is executed. */
mLpadcTriggerConfigStruct.enableHardwareTrigger = false;
LPADC_SetConvTriggerConfig(DEMO_LPADC_BASE, 0U, &mLpadcTriggerConfigStruct); /* Configurate the trigger0. */
/* Enable the watermark interrupt. */
#if (defined(FSL_FEATURE_LPADC_FIFO_COUNT) && (FSL_FEATURE_LPADC_FIFO_COUNT == 2U))
LPADC_EnableInterrupts(DEMO_LPADC_BASE, kLPADC_FIFO0WatermarkInterruptEnable);
#else
LPADC_EnableInterrupts(DEMO_LPADC_BASE, kLPADC_FIFOWatermarkInterruptEnable);
#endif /* FSL_FEATURE_LPADC_FIFO_COUNT */
EnableIRQ(DEMO_LPADC_IRQn);
PRINTF("ADC Full Range: %d\r\n", g_LpadcFullRange);
3、测试过程
将J8的28pin和GND短接进行测试。
经过多次测试,发现电压的范围变化不是很大。最高不超过2,2*3.3/4096=1.6mV毫伏。如果做平均则可以几乎接近为0V电压。
将J8接口28pin与信号发生器输出相接,信号输出设置为1.5V,输出如下:
大致在1961附近跳动,理论电压为1961*3.3/4096=1.579V,由于参考电压VDD_ANA为电源电压直接连接。所以对实际的电压进行测量为:3.27V
1961*3.27/4096=1.5655V,从测量来看,电压的精度一般。信号发生器的输出1.5014V(万用表测量值)。
使用的是lpADC设备,顺便测量了一下功耗,16.49*3.27=53.9mW,ADC和串口的共同功耗为53.9毫瓦。
4、总结
本次测试ADC从数值分析ADC的精度不是十分理想(也可以是无法获得实际参考电压的原因),功耗也较低功耗应用有一定的差距。本次的测试也可以受限测量方法的原因,所以有知道的请告知。
- 2025-01-19
-
发表了主题帖:
【FRDM-MCXN947测评】DAC数模转换测试
1、测试介绍
MCXN947VDF MCU有两组DAC通道,12bit 2个,14bit 1个本次使用测试的是DAC0通道,测试通过中断进行设置操作,通过串口进行修改测试电压值的操作。
硬件:FRDM-MCXN947开发板,万用表。
软件:GCC ARM Embedded 13.2.1编译器。不知道这个编译器版本是绑定的,在VScode中无法更换。
Putty串口终端软件。
通过J1接口的4_2pin输出。
2、测试过程
程序项目参考dac_1_buffer_interrupt样例
使用vscode打开项目
使用Putty软件连接串口。
通过putty终端进行修改电压值
电压最低值:0.0095,最高值:2.484,输出基本上稳定。
3、软件设计
软件步骤:
1、初始化DAC设备
2、启动DAC设备
3、启动DAC中断
/*
* Copyright (c) 2016, Freescale Semiconductor, Inc.
* Copyright 2016-2017 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_debug_console.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "fsl_dac.h"
/*******************************************************************************
* Definitions
******************************************************************************/
#define DEMO_DAC_BASEADDR DAC0
#define DEMO_DAC_IRQ_ID DAC0_IRQn
#define DEMO_DAC_IRQ_HANDLER_FUNC DAC0_IRQHandler
#define DEMO_DAC_VREF kDAC_ReferenceVoltageSourceAlt1
#define DEMO_DAC_VALUE_ARRAY_SIZE 32U
/*******************************************************************************
* Prototypes
******************************************************************************/
/*******************************************************************************
* Variables
******************************************************************************/
volatile uint32_t g_DacInputIndex = 0U;
volatile uint32_t g_DacOutputIndex = 0U;
volatile uint32_t g_DacInterruptDone = false;
/* User-defined wave for DAC output. */
const uint32_t g_DacValues[DEMO_DAC_VALUE_ARRAY_SIZE] = {
0U, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500,
1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100};
/*******************************************************************************
* Code
******************************************************************************/
/*!
* [url=home.php?mod=space&uid=159083]@brief[/url] Main function
*/
int main(void)
{
dac_config_t dacConfigStruct;
/* attach FRO 12M to FLEXCOMM4 (debug console) */
CLOCK_SetClkDiv(kCLOCK_DivFlexcom4Clk, 1u);
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
/* attach FRO HF to DAC0 */
CLOCK_SetClkDiv(kCLOCK_DivDac0Clk, 1u);
CLOCK_AttachClk(kFRO_HF_to_DAC0);
/* enable DAC0 and VREF */
SPC0->ACTIVE_CFG1 |= 0x11;
BOARD_InitPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();
EnableIRQ(DEMO_DAC_IRQ_ID); /* Enable interrupt in NVIC. */
PRINTF("\r\nDAC buffer interrupt Example.\r\n");
/* Configure the DAC. */
DAC_GetDefaultConfig(&dacConfigStruct);
dacConfigStruct.referenceVoltageSource = DEMO_DAC_VREF;
dacConfigStruct.fifoTriggerMode = kDAC_FIFOTriggerBySoftwareMode; /* Software trigger. */
dacConfigStruct.fifoWorkMode = kDAC_FIFOWorkAsNormalMode; /* Normal FIFO mode. */
dacConfigStruct.fifoWatermarkLevel = 4U; /* Watermark. */
DAC_Init(DEMO_DAC_BASEADDR, &dacConfigStruct);
DAC_Enable(DEMO_DAC_BASEADDR, true); /* Enable output. */
PRINTF("Press any key to trigger the DAC...\r\n");
/* Enable DAC interrupts. */
DAC_EnableInterrupts(DEMO_DAC_BASEADDR, kDAC_FIFOEmptyInterruptEnable);
while (1)
{
/* Wait */
while (!g_DacInterruptDone)
{
}
g_DacInterruptDone = false;
/* Trigger the buffer and move the pointer. */
GETCHAR();
DAC_DoSoftwareTriggerFIFO(DEMO_DAC_BASEADDR);
PRINTF("DAC next output: %d\r\n", g_DacValues[g_DacOutputIndex]);
if (g_DacOutputIndex >= DEMO_DAC_VALUE_ARRAY_SIZE - 1U)
{
g_DacOutputIndex = 0U;
}
else
{
g_DacOutputIndex++;
}
}
}
/*!
* @brief IRQ function for DAC buffer interrupt
*/
void DEMO_DAC_IRQ_HANDLER_FUNC(void)
{
uint32_t flags = DAC_GetStatusFlags(DEMO_DAC_BASEADDR);
if (0U != (kDAC_FIFOEmptyFlag & flags))
{
DAC_SetData(DEMO_DAC_BASEADDR, g_DacValues[g_DacInputIndex]);
if (g_DacInputIndex >= (DEMO_DAC_VALUE_ARRAY_SIZE - 1U))
{
g_DacInputIndex = 0U;
}
else
{
g_DacInputIndex++;
}
}
g_DacInterruptDone = true;
SDK_ISR_EXIT_BARRIER;
}
4、刷新DAC_DoSoftwareTriggerFIFO(DEMO_DAC_BASEADDR);输入输出堆列。
中断中修改value
/*!
* @brief IRQ function for DAC buffer interrupt
*/
void DEMO_DAC_IRQ_HANDLER_FUNC(void)
{
uint32_t flags = DAC_GetStatusFlags(DEMO_DAC_BASEADDR);
if (0U != (kDAC_FIFOEmptyFlag & flags))
{
DAC_SetData(DEMO_DAC_BASEADDR, g_DacValues[g_DacInputIndex]);
if (g_DacInputIndex >= (DEMO_DAC_VALUE_ARRAY_SIZE - 1U))
{
g_DacInputIndex = 0U;
}
else
{
g_DacInputIndex++;
}
}
g_DacInterruptDone = true;
SDK_ISR_EXIT_BARRIER;
}
通过 DAC_SetData(DEMO_DAC_BASEADDR, g_DacValues[g_DacInputIndex]);数组修改DAC的输出值。
4、总结
本次测试通过中断修改DAC的输出数值,当DAC完成输出后即使通过FIFO队列输出数据。NXP的DAC中断设计相当的完善,可以防止DAC输出出现间断的可能。
- 2025-01-08
-
回复了主题帖:
【测评入围名单(最后1批)】年终回炉:FPGA、AI、高性能MCU、书籍等65个测品邀你来~
个人信息无误,确认可以完成测评计划
-
发表了主题帖:
NXP MCUXpresso for VSCode插件安装编译工具故障
故障描述:
因为在我的PC上有其它友商的环境,所以预先安装了arm-gnu-toolchain-13.3.rel1-mingw-w64-i686-arm-none-eabi编译环境,并且设置了环境变量,%ARM_GCC_TOOLCHAIN_PATH%添加到PATH当中,而且工作良好,可以编译。使用MCUXpresso Installer安装工具安装软件,
ARM_GCC_TOOLCHAIN_PATH
D:\arm-gnu-toolchain-13.3.rel1-mingw-w64-i686-arm-none-eabi\bin
%ARM_GCC_TOOLCHAIN_PATH%
MCUXpresso Installer安装工具显示如下:
MCUXpresso Installer安装工具也没有重新安装编译工具。
安装vscode插件
已经可以使用。使用配置工具
也输出正常。
但是,在使用插件导入项目时出现故障。
无法找到编译工具。即使指定目录也不行
出现故障
项目无法引入。
先后进行了两次安装均未成功。
但是如果将编译工具删除后,使用上述步骤可以安装成功。不知道是何原因。
- 2025-01-07
-
回复了主题帖:
最近“具身智能”的概念很火,车厂们纷纷布局,大家怎么看?
AI数学模型没有突破,机器人的时代就还很遥远
- 2025-01-06
-
加入了学习《Follow me第二季第3期 任务汇总》,观看 Follow me第二季第3期 任务汇总
- 2025-01-01
-
回复了主题帖:
电源PCB设计上,有哪些常见的坑?
1、尽可能的不要使用PCB走220V等这样的信号,即时不得已也要注意趴电的距离或挖孔处理
2、即使是低速的板子中PCB的走线距离也不应该非常的长,长的走线可能带来干扰,例如:DC/DC的FB反馈线尽量不要原理芯片,其实这个信号的速度非常的高。因为,上升或下降沿非常的抖,就不应该看作是低速信号
-
回复了主题帖:
祝福2025!回帖即有奖!选取最有心的送5块国产开发板!
感谢EEWORLD陪我度过了2024年,在2024年里个人生活起起伏伏经历了太多的故事,苦楚中有着不屈的奋斗。EEWORLD就像一杯清茶,虽然不能果腹但却给生活带来了许多的清新。祝愿各位网友2025大吉大利!EEWORLD越来越好!
- 2024-12-31
-
回复了主题帖:
Linux下克隆github项目失败怎么办?
linux也可以使用zip包的,wget 、curl都可以使用。get下包之后。如果实在不行就找人代劳
- 2024-12-25
-
回复了主题帖:
固态继电器关断不彻底的原因是什么?
交流固态继电器是使用双向可控硅结构的,可以控制交流不可以控制直流,直流固态继电器可以控制直流,而不能控制交流,互不通用。
-
回复了主题帖:
KEIL编译USB功能异常IAR编译正常
shipeng 发表于 2024-12-25 11:20
不对吧,如果是代码问题那IAR编译生成的目标文件也应该有问题才对,现在是用IAR编译功能是正常的。
可能原始项目是由IAR编写,所以在项目中的编译或链接参数是被修改了,keil就不一定了
-
加入了学习《【Follow me第二季第4期】ARDUINO NANO RP2040 CONNECT》,观看 Arduino NANO RP2040演示合集
-
回复了主题帖:
#源来如此#讨论场来啦~征集电源话题,说说你最关心最想知道的电源内容~
变压器设计中电感对变压器的影响