这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【FRDM-MCXA366】⑤项目实战:电机PWM调速系统

共1条 1/1 1 跳转至

【FRDM-MCXA366】⑤项目实战:电机PWM调速系统

菜鸟
2026-09-17 21:34:39     打赏

本例要实现的电机PWM调速系统系统,基于前面的几个帖子。

分别涉及到PWM 输出、按键中断、LCD液晶。


PWM 输出 https://forum.eepw.com.cn/thread/401543/1

按键中断 https://forum.eepw.com.cn/thread/401475/1

LCD液晶 https://forum.eepw.com.cn/thread/401332/1


硬件部分,使用了基于TB6612的直流电机驱动板。

 TB6612FNG


是东芝半导体公司生产的一款直流电机驱动器件,它具有大电流 MOSFET-H 桥结构,双通道电路输出,可同时驱动 2 个电机。可以实现控制电机的正反转,和PWM调速。


电路大概如下图所示,使用一个编码电机,可以通过输出对应的矩形波来测速(本例接逻辑分析仪分析波形)。

模块的14 、15脚用于控制电机正反转,本例直接将15脚接正极。

PWM接FRDM-MCXA366的J1-15(P3_12)


image.png

实物图如下:

da7e95fe0e408b7358af014871cb6c3e.jpg


接下来是软件部分

第一部分PWM

在原有的基础上增加了 更新PWM波的占空比 函数

CTIMER_UpdatePwmDutycycle(CTIMER,CTIMER_MAT_PWM_PERIOD_CHANNEL,CTIMER_MAT_OUT,duty_set);

其中最关键的变量就是duty_set,是0~100的整数。

image.png

第二部分 按键中断

在第一部分的基础上,增加SW2(调高PWM占空比)和SW3(调低PWM占空比)功能

由于前面提到的 MCUXpresso IDE v25.6.136还不支持MCXA366的图形化硬件配置。

详见:https://forum.eepw.com.cn/thread/401424/1

那就只好参考SDK手搓硬件配置了。

以SW2(P1_7)为例

首先初始化IO并配置中断:打开PORT1和GPIO1的时钟开启复位后释放功能配置为GPIO方向设置为输入输入缓冲打开配置下降沿中断

这里最容易忽略的就是【输入缓冲打开


pin_mux.c

//SW2   P1_7配置输入&中断
	/* PORT1: Peripheral clock is enabled */
	CLOCK_EnableClock(kCLOCK_GatePORT1);
	CLOCK_EnableClock(kCLOCK_GateGPIO1);
	/* PORT1 peripheral is released from reset */
	RESET_ReleasePeripheralReset(kPORT1_RST_SHIFT_RSTn);
	// GPIO也需要释放复位!!
	RESET_ReleasePeripheralReset(kGPIO1_RST_SHIFT_RSTn);


/* PORT is configured as GPIO */
PORT_SetPinMux(PORT1, 7U, kPORT_MuxAlt0);
gpio_pin_config_t SW2_config = {
.pinDirection = kGPIO_DigitalInput,
};
/* Initialize GPIO functionality   */
GPIO_PinInit(GPIO1, 7U, &SW2_config);

//输入缓冲打开
PORT1->PCR[7] = ((PORT1->PCR[7] &
/* Mask bits to zero which are setting */
(~(PORT_PCR_IBE_MASK)))

/* Input Buffer Enable: Enables. */
| PORT_PCR_IBE(0x01u));

/* Interrupt configuration : Interrupt on falling edge */
GPIO_SetPinInterruptConfig(GPIO1, 7U, kGPIO_InterruptFallingEdge);



其次,开启中断

peripherals.c

/* Interrupt vector GPIO0_IRQn priority settings in the NVIC. */
NVIC_SetPriority(INT_0_IRQN, INT_0_IRQ_PRIORITY);
/* Enable interrupt INT_0_IRQN request in the NVIC */
EnableIRQ(INT_0_IRQN);


最后,在中断函数中,写入对应功能:加减PWM占空比

main.c

/* GPIO1_IRQn interrupt handler */
void INT_1_IRQHANDLER(void) {
/*  Place your code here */
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
     Store immediate overlapping exception return operation might vector to incorrect interrupt. */
	uint32_t flag1 = GPIO_GpioGetInterruptFlags(GPIO1);   //GPIO_PinGetInterruptFlag(GPIO_Type *base, uint32_t pin);
	if (flag1 & (1u << 7))
	{
		PRINTF("SW2 INT TRIGGER!rn");
		duty_set += 5;
		if(duty_set>95)duty_set=95;
		GPIO_PinClearInterruptFlag(GPIO1,7);
	}

}

SW3(P0_6)配置方法同样如此。

我们在main.c中的while(1)函数中,增加每200ms更新PWM占空比的函数,就可以实现按键中断调速了。

SDK_DelayMs(200);               //延时
CTIMER_UpdatePwmDutycycle(CTIMER,CTIMER_MAT_PWM_PERIOD_CHANNEL,CTIMER_MAT_OUT,duty_set);
PRINTF("Duty is %drn",duty_set);


第三部分 液晶显示

液晶显示部分,直接用官方函数,不细究代码了。

关键部分是将PWM占空比通过处理,显示在液晶屏上。

		SLCD_Engine_Show_Num(&slcdEngine, duty_set/10 ,1, 1);
		SLCD_Engine_Show_Num(&slcdEngine, duty_set%10 ,0, 1);


占空比95%时,编码器频率为1.2KHZ

image.png

占空比10%时,编码器频率为74HZ

image.png


最后是本项目的视频展示:



完整main.c代码

/*
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * Copyright 2016-2019,2025 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "board.h"
#include "fsl_common.h"
#include "app.h"
#include "fsl_slcd.h"
#include "slcd_engine.h"
#include "fsl_debug_console.h"


#include "fsl_ctimer.h"
#include "peripherals.h"


/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define SDK_DelayMs(ms)  SDK_DelayAtLeastUs((ms)*1000U, 100000000U)
volatile uint8_t duty_set = 50;  //中断和main循环共享的全局变量,必须加volatile


/*******************************************************************************
 * 宏定义
 ******************************************************************************/
#ifndef CTIMER_MAT_PWM_PERIOD_CHANNEL
// PWM周期使用的CTimer匹配通道
#define CTIMER_MAT_PWM_PERIOD_CHANNEL kCTIMER_Match_3
#endif

/*******************************************************************************
 * 函数声明
 ******************************************************************************/

/*******************************************************************************
 * 全局变量
 ******************************************************************************/
volatile uint32_t g_pwmPeriod   = 0U;    // PWM周期对应的匹配计数值
volatile uint32_t g_pulsePeriod = 0U;    // PWM高/低电平对应的匹配计数值

/*******************************************************************************
 * 代码实现
 ******************************************************************************/
/*!
 * @brief 计算PWM周期与脉冲宽度对应的CTimer匹配值
 * @param pwmFreqHz PWM输出频率,单位Hz
 * @param dutyCyclePercent 占空比百分比 0~100
 * @param timerClock_Hz CTimer定时器时钟频率,单位Hz
 * @return 函数执行状态,kStatus_Success表示成功
 */
status_t CTIMER_GetPwmPeriodValue(uint32_t pwmFreqHz, uint8_t dutyCyclePercent, uint32_t timerClock_Hz)
{
/* 计算PWM周期匹配值:定时器计数到该值时完成一个PWM周期 */
g_pwmPeriod = (timerClock_Hz / pwmFreqHz) - 1U;

/*
     * 计算脉冲宽度匹配值
     * 公式:总周期计数 * (100 - 占空比) /100
     * 注:此处逻辑为匹配到时输出翻转,代表低电平时间,可根据硬件输出极性调整理解
     */
g_pulsePeriod = (g_pwmPeriod + 1U) * (100 - dutyCyclePercent) / 100;

return kStatus_Success;
}
/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*!
 * @brief SLCD time delay.
 * @param ms Milli-second
 */
static void SLCD_TimeDelay(uint32_t ms);

/* Demonstrate how to show number. */
static void SLCD_Show_Digital(void);

/* Demonstrate how to show icon. */
static void SLCD_Show_Icon(void);

#if SLCD_PANEL_SUPPORT_LETTER
/* Demonstrate how to show letter. */
static void SLCD_Show_Letter(void);
#endif

/* Demonstrate the blink feature. */
static void SLCD_Blink(void);

/*******************************************************************************
 * Variables
 ******************************************************************************/
tSLCD_Engine slcdEngine;

/*******************************************************************************
 * Code
 ******************************************************************************/
static void SLCD_SetLCDPin(lcd_set_type_t type, uint32_t lcd_pin, uint8_t pin_val, int32_t on)
{
assert(lcd_pin > 0);

uint8_t gpio_pin = 0;
uint8_t bit_val  = 0;
uint8_t i        = 0;

/* lcd _pin starts from 1. */
gpio_pin = slcd_lcd_gpio_seg_pin[lcd_pin - 1];

if (type == SLCD_Set_Num)
{
SLCD_SetFrontPlaneSegments(LCD, gpio_pin, (on ? pin_val : 0));
}
else
{
for (i = 0; i < 8; ++i)
{
bit_val = (uint8_t)(pin_val >> i) & 0x1U;
if (bit_val)
{
SLCD_SetFrontPlaneOnePhase(LCD, gpio_pin, (slcd_phase_index_t)i, on);
}
}
}
}

static void SLCD_Show_Digital(void)
{
uint8_t digital;
int32_t position;

PRINTF("\r\nShow digital numbers\r\n");

for (digital = 0; digital < 10; digital++)
{
for (position = 0; position < NUM_POSEND; position++)
{
SLCD_Engine_Show_Num(&slcdEngine, digital, position, 1);
}

SLCD_TimeDelay(500);

for (position = 0; position < NUM_POSEND; position++)
{
SLCD_Engine_Show_Num(&slcdEngine, digital, position, 0);
}
}

PRINTF("\r\nShow digital numbers finished\r\n");
}

#if SLCD_PANEL_SUPPORT_LETTER
static void SLCD_Show_Letter(void)
{
int8_t letter;
int8_t position;

PRINTF("\r\nShow letter\r\n");

for (letter = 'A'; letter <= 'z'; letter++)
{
for (position = 0; position < NUM_POSEND; position++)
{
SLCD_Engine_Show_Letter(&slcdEngine, letter, position, 1);
}

SLCD_TimeDelay(500);

for (position = 0; position < NUM_POSEND; position++)
{
SLCD_Engine_Show_Letter(&slcdEngine, letter, position, 0);
}

if (letter == 'Z')
{
letter = 'a';
}
}

PRINTF("\r\nShow letter finished\r\n");
}
#endif

static void SLCD_Show_Icon(void)
{
int32_t icon;

PRINTF("\r\nTurn on and off the icons one by one\r\n");

for (icon = 0; icon < ICON_END; icon++)
{
SLCD_Engine_Show_Icon(&slcdEngine, icon, 1);

SLCD_TimeDelay(500);

SLCD_Engine_Show_Icon(&slcdEngine, icon, 0);
}

PRINTF("\r\nShow icons finished\r\n");
}

static void SLCD_Blink(void)
{
int32_t icon;

for (icon = 0; icon < ICON_END; icon++)
{
SLCD_Engine_Show_Icon(&slcdEngine, icon, 1);
}

PRINTF("\r\nSLCD Displays All Segments.\r\n");
SLCD_TimeDelay(2000);

PRINTF("\r\nSLCD Starts Blink Mode.\r\n");

/* Blink mode Display. */
SLCD_StartBlinkMode(LCD, kSLCD_BlankDisplayBlink, kSLCD_BlinkRate01);
SLCD_TimeDelay(4000);

PRINTF("\r\nSLCD Stops Blink Mode.\r\n");
/* Stops SLCD blink display mode. */
SLCD_StopBlinkMode(LCD);
SLCD_TimeDelay(2000);
}

static void SLCD_TimeDelay(uint32_t ms)
{
SDK_DelayAtLeastUs(1000 * ms, SystemCoreClock);
}

int main(void)
{
slcd_config_t config;
ctimer_config_t config1;       // CTimer配置结构体
uint32_t srcClock_Hz;         // CTimer源时钟频率(Hz)
uint32_t timerClock;          // 经过预分频后的定时器工作时钟(Hz)

/* Hardware initialize. */
BOARD_InitHardware();
BOARD_InitBootPeripherals(); //开启中断

PRINTF("MCUX SDK version: %s\r\n", MCUXSDK_VERSION_FULL_STR);

PRINTF("\r\nSLCD Example Starts.\r\n");

/* CTimer0计数器使用AHB时钟 */
srcClock_Hz = CTIMER_CLK_FREQ;
/* 获取CTimer默认配置参数 */
CTIMER_GetDefaultConfig(&config1);
/* 计算预分频之后的定时器时钟频率 */
timerClock = srcClock_Hz / (config1.prescale + 1);

/* 初始化CTimer外设 */
CTIMER_Init(CTIMER, &config1);

/* 获取10KHz、指定占空比的PWM周期与脉冲匹配计数值 */
CTIMER_GetPwmPeriodValue(10000, (uint8_t)DUTY_CYCLE, timerClock);
/* 配置CTimer生成PWM:周期通道、输出通道、周期计数值、脉冲计数值,不开启中断 */
CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT_OUT, g_pwmPeriod, g_pulsePeriod, false);
/* 启动CTimer定时器开始计数,输出PWM波形 */
CTIMER_StartTimer(CTIMER);
/* SLCD get default configure. */
/*
     * config.displayMode = kSLCD_NormalMode;
     * config.frameFreqIntEnable = false;
     * config.faultConfig = NULL;
     */
SLCD_GetDefaultConfig(&config);

/* Verify and Complete the configuration structure. */
#if !(defined(FSL_FEATURE_SLCD_LP_CONTROL) && FSL_FEATURE_SLCD_LP_CONTROL)
/*
     * config.powerSupply = kSLCD_InternalVll3UseChargePump;
     * config.voltageTrim = kSLCD_RegulatedVolatgeTrim08;
     * config.lowPowerBehavior = kSLCD_EnabledInWaitStop;
     */
config.clkConfig          = &slcdClkConfig;
config.loadAdjust         = kSLCD_HighLoadOrSlowestClkSrc;
#endif
config.dutyCycle          = APP_SLCD_DUTY_CYCLE;
config.slcdLowPinEnabled  = APP_SLCD_LOW_PIN_ENABLED;
config.slcdHighPinEnabled = APP_SLCD_HIGH_PIN_ENABLED;
config.backPlaneLowPin    = APP_SLCD_BACK_PANEL_LOW_PIN;
config.backPlaneHighPin   = APP_SLCD_BACK_PANEL_HIGH_PIN;

config.faultConfig = NULL;
/* SLCD Initialize. */
SLCD_Init(LCD, &config);

BOARD_SetSlcdBackPlanePhase();

memset(&slcdEngine, 0, sizeof(tSLCD_Engine));

SLCD_Engine_Init(&slcdEngine, SLCD_SetLCDPin);

/* Starts SLCD display. */
SLCD_StartDisplay(LCD);
//    SLCD_Engine_Show_Letter(&slcdEngine, 'd' ,5, 1);
//    SLCD_Engine_Show_Letter(&slcdEngine, 'u' ,4, 1);
//    SLCD_Engine_Show_Letter(&slcdEngine, 't' ,3, 1);
//    SLCD_Engine_Show_Letter(&slcdEngine, 'y' ,2, 1);
//SLCD_Show_Digital();

#if SLCD_PANEL_SUPPORT_LETTER
//SLCD_Show_Letter();
#endif

//SLCD_Show_Icon();

//SLCD_Blink();

PRINTF("\r\nSLCD Stops Display.\r\n");
/* Stops SLCD display. */
//SLCD_StopDisplay(LCD);

PRINTF("\r\nSLCD Example Ends.\r\n");

while (1)
{
		SDK_DelayMs(200);               //延时500ms
		CTIMER_UpdatePwmDutycycle(CTIMER,CTIMER_MAT_PWM_PERIOD_CHANNEL,CTIMER_MAT_OUT,duty_set);
		PRINTF("Duty is %d\r\n",duty_set);

		SLCD_Engine_Show_Num(&slcdEngine, duty_set/10 ,1, 1);
		SLCD_Engine_Show_Num(&slcdEngine, duty_set%10 ,0, 1);

}
}



/* GPIO0_IRQn interrupt handler */
void INT_0_IRQHANDLER(void) {
/*  Place your code here */
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
     Store immediate overlapping exception return operation might vector to incorrect interrupt. */
	uint32_t flag0 = GPIO_GpioGetInterruptFlags(GPIO0);   //GPIO_PinGetInterruptFlag(GPIO_Type *base, uint32_t pin);
	if (flag0 & (1u << 6))
	{
		PRINTF("SW3 INT TRIGGER!\r\n");
		duty_set -= 5;
		if(duty_set<0)duty_set=0;
		GPIO_PinClearInterruptFlag(GPIO0,6);
	}

}

/* GPIO1_IRQn interrupt handler */
void INT_1_IRQHANDLER(void) {
/*  Place your code here */
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
     Store immediate overlapping exception return operation might vector to incorrect interrupt. */
	uint32_t flag1 = GPIO_GpioGetInterruptFlags(GPIO1);   //GPIO_PinGetInterruptFlag(GPIO_Type *base, uint32_t pin);
	if (flag1 & (1u << 7))
	{
		PRINTF("SW2 INT TRIGGER!\r\n");
		duty_set += 5;
		if(duty_set>95)duty_set=95;
		GPIO_PinClearInterruptFlag(GPIO1,7);
	}

}










共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]