使用恩智浦MCXN947 PWM控制直流电机驱动模块L9110S

描述

前言    

恩智浦“FRDM-MCXN947”评测活动由安富利和与非网协同举办。本篇内容由与非网用户发布,已获转载许可。原文可在与非网(eefocus)工程师社区查看。

背景

01         直流电机驱动模块L9110S

一个L9110S驱动可以控制一个电机,下图中的GroundStudio L9110s模块板载两个L9110s芯片,可以驱动两个直流电机。

直流电机

02         引脚说明

此模块有6根引脚,如下:

直流电机

简单来说L9110S的输入输出有以下四种情形:

直流电机

从上图可知,只需要给IN1IN2管脚输入不同的电平就能实现正转、反转,输入相同的电平就能停转。

需要注意,不要直接由开发板来给模块供电,因为L9110S模块可能因为需求的驱动功率太高而导致板子上的供电不平衡。

03         MCXN947 PWM

从上表可知,使用GPIO给IN1IN2也能驱动电机,但是不能控制速度。现在我们需要实现能控制速度的电机驱动。自然而然想到了PWM调速。

假设给IN2管脚用GPIO控制,拉低电平,只需要PWM往IN1灌高电平就可以驱动电机正转,调节PWM占空比就可以实现电机转速;把IN2管脚拉高电平,用PMW往IN1灌低电平就可以实现电机反转,注意此时的占空比和转速是反着的,即占空比越大,转速越慢。

这里选择eFlexPWM产生PWM1_A通道的PWM波来控制电机,另外一个管脚用GPIO来控制电平调节正反转。

04         代码实现

为了方便调试,先实现命令行接口,可以方便的通过串口输入命令调节参数控制电机正反转和转速。然后实现对应的电机驱动接口。

05         命令行接口

FreeRTOS-CLI新增命令行,支持如下的3种命令:

(滑动查看)

 

motor  stop       // 电机停转
motor  left   speed // 电机正转,speed 表示速度,取值范围 [0, 100]
motor  right  speed // 电机反转,speed 表示范围,取值范围 [0, 100]

 

当前命令行的解析函数prvMotorCommand()如下,其实最终调用的函数是分别是motor_stop(),motor_left(speed)motor_right(speed)这三个函数。

(滑动查看)

 

/**
 * @brief 直流电机命令的实现
 *
 * motor left/right speed 其中 speed 取值范围是 [0, 100]
 * motor stop
 *
 * 示例:
 * motor left 0
 * motor left 100
 * motor right 20
 * motor right 80
 * motor stop 停转
 *
 * @param pcWriteBuffer
 * @param xWriteBufferLen
 * @param pcCommandString
 * @return BaseType_t
 */
static BaseType_t prvMotorCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
{
  configASSERT(pcWriteBuffer);
br
  /* param1: left/right/stop */
  const char *paramMotorCmd1 = NULL;
  BaseType_t paramMotorCmd1Length = 0;
br
  /* param2: speed */
  const char *paramMotorCmd2 = NULL;
  BaseType_t paramMotorCmd2Length = 0;
  uint32_t speed = 0;
br
  // 首先清除输出缓冲区旧的内容
  memset(pcWriteBuffer, 0, xWriteBufferLen);
br
  /*          arg0  arg1       arg2   */
  /* 命令形式1:motor left/right speed  */
  /* 命令形式2:motor stop              */
  paramMotorCmd1 = FreeRTOS_CLIGetParameter(pcCommandString, 1, ¶mMotorCmd1Length);
br
  if (strncmp("stop", paramMotorCmd1, strlen("stop")) == 0) {
    motor_stop();
    sprintf(pcWriteBuffer, "
 motor_stop() 
");
  } else {
    /* 获取 speed */
    paramMotorCmd2 = FreeRTOS_CLIGetParameter(pcCommandString, 2, ¶mMotorCmd2Length);
    if (paramMotorCmd2 != NULL) {
      speed = strtoul(paramMotorCmd2, NULL, 10);
br
      if (speed >= 100) {
        speed = 99; /* NOTE: PWM 占空比 <= 100 但由于两个高电平导致电机停转,所以占空比应该 < 100 */
      }
    }
br
    if (strncmp("left", paramMotorCmd1, strlen("left")) == 0) {
      sprintf(pcWriteBuffer, "
 motor_left(%u) 
", speed);
      motor_left(speed);
    } else if (strncmp("right", paramMotorCmd1, strlen("right")) == 0) {
      sprintf(pcWriteBuffer, "
 motor_right(%u) 
", speed);
      motor_right(speed);
    } else {
      sprintf(pcWriteBuffer, "
Error: arg1 should be left/right
");
    }
  }
br
  /* There is no more data to return after this single string, so return pdFALSE. */
  return pdFALSE;
}

 

06         电机驱动实现

PWM和GPIO初始化

通过MCUXpresso Config Tools来实现,配置J3.15PWM1_A0,配置J3.13为GPIO输出模式。生成代码,自动保存在pin_mux.c文件中。

直流电机

在MCUXpresso Config Tools中,点击(1)处新建分组BOARD_MOTOR_Init,把电机相关的管脚都放在同一个组里初始化;

在(2)处可以看到J3.15配置为PWM1_A0信号,J3.13配置为PIO2_7且为输出;

在(3)处给管脚添加标识符,会生成相对应的宏定义。

对应的管脚初始化代码如下:

(滑动查看)

 

void BOARD_MOTOR_Init(void)
{
    /* Enables the clock for GPIO2: Enables clock */
    CLOCK_EnableClock(kCLOCK_Gpio2);
    /* Enables the clock for PORT2: Enables clock */
    CLOCK_EnableClock(kCLOCK_Port2);


    gpio_pin_config_t MOTOR_DIR_config = {
        .pinDirection = kGPIO_DigitalOutput,
        .outputLogic = 0U
    };
    /* Initialize GPIO functionality on pin PIO2_7 (pin L2)  */
    GPIO_PinInit(MOTOR_MOTOR_DIR_GPIO, MOTOR_MOTOR_DIR_PIN, &MOTOR_DIR_config);


    /* PORT2_6 (pin K2) is configured as PWM1_A0 */
    PORT_SetPinMux(MOTOR_MOTOR_SPEED_PORT, MOTOR_MOTOR_SPEED_PIN, kPORT_MuxAlt5);


    PORT2->PCR[6] = ((PORT2->PCR[6] &
                      /* Mask bits to zero which are setting */
                      (~(PORT_PCR_IBE_MASK)))


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


    /* PORT2_7 (pin L2) is configured as PIO2_7 */
    PORT_SetPinMux(MOTOR_MOTOR_DIR_PORT, MOTOR_MOTOR_DIR_PIN, kPORT_MuxAlt0);


    PORT2->PCR[7] = ((PORT2->PCR[7] &
                      /* Mask bits to zero which are setting */
                      (~(PORT_PCR_IBE_MASK)))


                     /* Input Buffer Enable: Enables. */
                     | PORT_PCR_IBE(PCR_IBE_ibe1));
}

 

电机控制

需要实现4个函数,分别是:

motor_init()

motor_stop()

motor_left()

motor_right()

公共的类型和变量

(滑动查看)

 

#include "pin_mux.h"
#include "fsl_gpio.h"
#include "fsl_pwm.h"
#include "bsp_motor.h"
#include "fsl_debug_console.h"
br
/*******************************************************************************
 * Definitions
 ******************************************************************************/
/* The PWM base address */
#define BOARD_PWM_BASEADDR PWM1
#define PWM_SRC_CLK_FREQ CLOCK_GetFreq(kCLOCK_BusClk)
#define DEMO_PWM_FAULT_LEVEL true
#define APP_DEFAULT_PWM_FREQUENCY (10000UL)
/* Definition for default PWM frequence in hz. */
#ifndef APP_DEFAULT_PWM_FREQUENCY
#define APP_DEFAULT_PWM_FREQUENCY (1000UL)
#endif
br
/* Macros ----------------------------------------------------------------------------------------*/
#define MOTOR_DIR_SET_LEFT()      GPIO_PinWrite(MOTOR_MOTOR_DIR_GPIO, MOTOR_MOTOR_DIR_GPIO_PIN, 0)
#define MOTOR_DIR_SET_RIGHT()     GPIO_PinWrite(MOTOR_MOTOR_DIR_GPIO, MOTOR_MOTOR_DIR_GPIO_PIN, 1)
br
br
/* Data Types ------------------------------------------------------------------------------------*/
br
typedef enum {
  MOTOR_DIR_LEFT = 0,
  MOTOR_DIR_RIGHT = 1,
} motor_dir_e;
br
static uint32_t pwmSourceClockInHz;
static uint32_t pwmFrequencyInHz;
static pwm_signal_param_t pwmSignal = { 0 };
static motor_dir_e m_motor_dir = MOTOR_DIR_LEFT;

 

motor_init()

(滑动查看)

 

static void PWM_DRV_Init2PhPwm(void)
{
  uint16_t deadTimeVal;
br
  /* Set deadtime count, we set this to about 650ns */
  deadTimeVal = ((uint64_t)pwmSourceClockInHz * 650) / 1000000000;
br
  pwmSignal.pwmChannel = kPWM_PwmA;
  pwmSignal.level = kPWM_HighTrue;
  pwmSignal.dutyCyclePercent = 30; /* x percent dutycycle */
  pwmSignal.deadtimeValue = deadTimeVal;
  pwmSignal.faultState = kPWM_PwmFaultState0;
  pwmSignal.pwmchannelenable = true;
br
  /*********** PWMA_SM0 - phase A, configuration, setup 2 channel as an example ************/
  PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_0, &pwmSignal, 1,
    kPWM_SignedCenterAligned, pwmFrequencyInHz, pwmSourceClockInHz);
}
br
/**
 * @brief 直流电机初始化,即 PWM1 的通道A和通道B初始化
 *
 * @return int 0 on success, others on failure.
 */
int motor_init(void)
{
  pwm_config_t pwmConfig;
  pwm_fault_param_t faultConfig;
br
  pwmSourceClockInHz = PWM_SRC_CLK_FREQ;
  pwmFrequencyInHz = APP_DEFAULT_PWM_FREQUENCY;
br
  SYSCON->PWM1SUBCTL |= SYSCON_PWM1SUBCTL_CLK0_EN_MASK; /* 只使能子模块0 */
br
  PWM_GetDefaultConfig(&pwmConfig);
br
  pwmConfig.reloadLogic = kPWM_ReloadPwmFullCycle;  /* use full cycle reload */
  pwmConfig.pairOperation = kPWM_Independent;       /* PWM A & PWM B operate as 2 independent channels */
  pwmConfig.enableDebugMode = true;
br
  if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_0, &pwmConfig) == kStatus_Fail)
  {
    PRINTF("PWM initialization failed
");
    return 1;
  }
br
  PWM_FaultDefaultConfig(&faultConfig);
br
#ifdef DEMO_PWM_FAULT_LEVEL
  faultConfig.faultLevel = DEMO_PWM_FAULT_LEVEL;
#endif
br
  /* 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);
br
  /* Set PWM fault disable mapping for submodule 0 */
  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);
br
  /* Call the init function with demo configuration */
  PWM_DRV_Init2PhPwm();
br
  /* Set the load okay bit for all submodules to load registers from their buffer */
  PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true);
br
  PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0);
br
  motor_stop();
br
  return 0;
}

 

motor_stop()

(滑动查看)

 

void motor_stop(void)
{
  if (m_motor_dir == MOTOR_DIR_LEFT) {
    MOTOR_DIR_SET_LEFT();
    PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_SignedCenterAligned, 0);
    /* Set the load okay bit for all submodules to load registers from their buffer */
    PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true);
  } else {
    MOTOR_DIR_SET_RIGHT();
    PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_SignedCenterAligned, 100);
    /* Set the load okay bit for all submodules to load registers from their buffer */
    PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true);
  }
}

 

motor_left(speed)

(滑动查看)

 

/**
 * @brief 正转
 * @param speed 占空比,[0~100]
 */
void motor_left(uint32_t speed)
{
  MOTOR_DIR_SET_LEFT();
  m_motor_dir = MOTOR_DIR_LEFT;
br
  PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_SignedCenterAligned, speed);
  /* Set the load okay bit for all submodules to load registers from their buffer */
  PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true);
  PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0);
}

 

motor_right(speed)

(滑动查看)

 

/**
 * @brief 反转
 * @param speed
 */
void motor_right(uint32_t speed)
{
  MOTOR_DIR_SET_RIGHT();
  m_motor_dir = MOTOR_DIR_RIGHT;
  speed = 100 - speed;
br
  PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_SignedCenterAligned, speed);
  // PWM_SetChannelOutput(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_InvertState);
  /* Set the load okay bit for all submodules to load registers from their buffer */
  PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true);
br
  PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0);
}

 

验证

01         示波器测量

上面黄色的是CH2,即PWM1_A信号(J3.15)

下面绿色的是CH1,即GPIO信号(J3.13)

电机正转motor left时CH1输出低电平

电机反转motor right时CH1输出高电平。

motor stop

两路信号都是低电平。电机停转。

直流电机

motor left 5

电机正转,速度为5。示波器观测PWM1_A占空比为5。

直流电机

motor left 70

电机正转,速度为70。示波器观测PWM1_A占空比为70。

直流电机

motor left 100

虽然PWM占空比可以设置成100%,但是两个高电平导致电机停转,所以这里把占空比合理的范围是[0,99]。

直流电机

motor right 5

直流电机

motor right 70

直流电机

motor right 100

直流电机

关于安富利  

安富利是全球领先的技术分销商和解决方案提供商,在过去一个多世纪里一直秉持初心,致力于满足客户不断变化的需求。通过遍布全球的专业化和区域化业务覆盖,安富利可在产品生命周期的每个阶段为客户和供应商提供支持。安富利能够帮助各种类型的公司适应不断变化的市场环境,在产品开发过程中加快设计和供应速度。安富利在整个技术价值链中处于中心位置,这种独特的地位和视角让其成为了值得信赖的合作伙伴,能够帮助客户解决复杂的设计和供应链难题,从而更快地实现营收。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分