这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【过程贴】Let'sdo2025年第4期-基于单片机的直流电机控制

共1条 1/1 1 跳转至

【过程贴】Let'sdo2025年第4期-基于单片机的直流电机控制

助工
2026-02-02 05:32:55     打赏

设置好了开发环境,就着手开始开发控制电机的代码。

一、 硬件接线

  首先要弄清楚驱动板 TB6612FNG 的接线要求,如下图所示。

驱动板的接线图.png

PWMA ---- 控制第1路电机的 PWM调速信号

AIN1、AIN2 ---- 依次为LH为反转,为HL是正转(控制方向的)。

VM  ---- 要接 电机的动力电源。

VCC、GND -----接控制的 3.3V 和 0V

STBY ------  接高电平,才能输出,否则电机都不输出

AO1、AO2--------接到电机上,没有正负极,接反电机会反转。

 

根据以上要求,我将开发板上的信号分配如下:

P1_4 :  调速用PWM信号

P3_9 和 P3_10:控制方向的 AIN1 和 AIN2

P3_6 和 P3_7:  OLED屏的I2C通讯线,SDA 和 SCL

P3_12: 板载的LED 红色灯。

 

实际的接线情况如下:

1426060892-815504094.jpg

 

二、 软件开发

 

从下载的 SDK 中 \boards\frdmmcxa153\driver_examples\ctimer\ 目录下有一个 PWM的例子工程:simple_pwm。 将此工程导入到 VSCode 中,编译运行下载,会在 P1_4 产生一个占空比 20% 的PWM信号,正是我想要的。

测试下PWM有没有输出,手头没有示波器,用万用表量了下,有0.65V左右电压,刚好是 3.3V 的 20%,看来PWM输出正常!

接下来需要做的,就是:

(1)添加 GPIO 控制2个AIN1 和 AIN2 输出,用于控制电机的方向。

(2)添加两个GPIO 开漏输出,用于驱动I2C的OLED屏幕显示。

(3)添加板载的LED_RED 红色小灯的输出。

打开 config 工具,添加以下5个针脚并配置好功能:

config.png

  

代码里封装一个控制电机的文件 pwm_motor.c 文件, 代码如下:

#include "fsl_gpio.h"
#include "fsl_port.h"
#include "fsl_ctimer.h"
#include "fsl_clock.h"
#include "fsl_common.h"

#define MOTOR_CTL_PORT      PORT3
#define MOTOR_CTL_GPIO      GPIO3
#define MOTOR_CTL_CLOCK     kCLOCK_GateGPIO3
#define MOTOR_IN1_PIN       9U      //针脚定义
#define MOTOR_IN2_PIN       10U     //针脚定义
#define MOTOR_PWM_PIN       11U     //针脚定义

/* PWM 常量*/

#define MOTOR_PWM_CTIMER      CTIMER1
#define MOTOR_PWM_MATCH_PERIOD  kCTIMER_Match_0  // 周期
#define MOTOR_PWM_MATCH_DUTY    kCTIMER_Match_1  // MAT1 输出
#define MOTOR_PWM_MUX         kPORT_MuxAlt3     // PWM 功能复用
#define PWM_FREQ_HZ           20000U            // 20 kHz
#define PWM_RESOLUTION        100U              // 0–99/

//保存PWM周期
volatile static uint32_t g_pwmPeriodCount = 0;

// 设置占空比 0-99
void MotorPwm_SetDuty(uint8_t duty)
{
    if (duty > 99) duty = 99;

    CTIMER1->MR[1] = (CTIMER1->MR[0] + 1) * duty / 100; // 更新占空比
    // 不需要 LER
}

void motor_pwm_init();

//motor控制线3根:初始化
void motor_ctl_init()
{
    /* ---------------------------
     * 方向控制:两个输出
     * ---------------------------*/
    //初始化电机 MOTOR_IN1_PIN 和 MOTOR_IN2_PIN 为推挽输出
    //CLOCK_EnableClock(MOTOR_CTL_CLOCK);
    gpio_pin_config_t motor_pin_config = {
        kGPIO_DigitalOutput, 0,
    };
    GPIO_PinInit(MOTOR_CTL_GPIO, MOTOR_IN1_PIN, &motor_pin_config);
    GPIO_PinInit(MOTOR_CTL_GPIO, MOTOR_IN2_PIN, &motor_pin_config);
    //输出IN1 和 IN2 初始值
    GPIO_PinWrite(MOTOR_CTL_GPIO, MOTOR_IN1_PIN, 0);
    GPIO_PinWrite(MOTOR_CTL_GPIO, MOTOR_IN2_PIN, 1);

    /* ---------------------------
     * 速度控制:1个PWM输出
     * ---------------------------*/
    //motor_pwm_init();
   
}


//设置电机的速度,speed范围0~100
//方向用正负表示,IN1和IN2控制方向:LH=反转,HL=正转
void motor_set_speed(int8_t speed)
{
    //限位到 -100 到 100
    if(speed < -100) speed = -100;
    if(speed > 100) speed = 100;

    //设置方向
    if(speed >= 0) {
        GPIO_PinWrite(MOTOR_CTL_GPIO, MOTOR_IN1_PIN, 1);
        GPIO_PinWrite(MOTOR_CTL_GPIO,MOTOR_IN2_PIN, 0);
    } else if(speed < 0) {
        GPIO_PinWrite(MOTOR_CTL_GPIO,MOTOR_IN1_PIN, 0);
        GPIO_PinWrite(MOTOR_CTL_GPIO,MOTOR_IN2_PIN, 1);
        speed = -speed; //取正值用于占空比计算
    }

    //设置PWM占空比
    CTIMER_UpdatePwmDutycycle(
        MOTOR_PWM_CTIMER,
        kCTIMER_Match_3,
        kCTIMER_Match_2,
        speed
    );

}

 

头文件也导出下 pwm_motor.h, 代码如下:

#pragma once
#include <stdint.h>

void motor_set_speed(int8_t speed);
void motor_ctl_init();

 

主函数里加上 对OLED初始化和 输出string的函数,并且在 while(1) 循环中加入 LED红灯闪烁的代码,和 设置占空比的函数,完整代码如下:

/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright 2016-2020, 2025 NXP
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/*******************************************************************************
 * Includes
 ******************************************************************************/

#include "fsl_debug_console.h"
#include "board.h"
#include "app.h"
#include "fsl_ctimer.h"
#include "hardware/OLED.h"
#include "hardware/pwm_motor.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#ifndef CTIMER_MAT_PWM_PERIOD_CHANNEL
#define CTIMER_MAT_PWM_PERIOD_CHANNEL kCTIMER_Match_3
#endif

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/
volatile uint32_t g_pwmPeriod   = 0U;
volatile uint32_t g_pulsePeriod = 0U;

/*******************************************************************************
 * Code
 ******************************************************************************/
status_t CTIMER_GetPwmPeriodValue(uint32_t pwmFreqHz, uint8_t dutyCyclePercent, uint32_t timerClock_Hz)
{
    /* Calculate PWM period match value */
    g_pwmPeriod = (timerClock_Hz / pwmFreqHz) - 1U;

    /* Calculate pulse width match value */
    g_pulsePeriod = (g_pwmPeriod + 1U) * (100 - dutyCyclePercent) / 100;

    return kStatus_Success;
}

/*!
 * @brief Main function
 */
int main(void)
{
    ctimer_config_t config;
    uint32_t srcClock_Hz;
    uint32_t timerClock;

    /* Init hardware*/
    BOARD_InitHardware();

    __disable_irq();
    OLED_Init();
    OLED_ShowString(1,1,"Hello OLED~~~");
    __enable_irq();


    // /* CTimer0 counter uses the AHB clock, some CTimer1 modules use the Aysnc clock */
    srcClock_Hz = CTIMER_CLK_FREQ;

    PRINTF("CTimer example to generate a PWM signal\r\n");

    CTIMER_GetDefaultConfig(&config);
    timerClock = srcClock_Hz / (config.prescale + 1);

    CTIMER_Init(CTIMER, &config);

    /* Get the PWM period match value and pulse width match value of 20Khz PWM signal with 20% dutycycle */
    CTIMER_GetPwmPeriodValue(20000, (uint8_t)DUTY_CYCLE, timerClock);
    CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT_OUT, g_pwmPeriod, g_pulsePeriod, false);
    CTIMER_StartTimer(CTIMER);

    while (1)
    {
        SDK_DelayAtLeastUs(2000 * 1000, CLOCK_GetCoreSysClkFreq());
        GPIO_PortToggle(BOARD_LED_RED_GPIO, 1u << BOARD_LED_RED_GPIO_PIN);

        //更改占空比(最大45)
        motor_set_speed(-15);
    }
}

 

最后测试一下,刚开始占空比10%,小风扇居然不转。是不是阻力太大了?直接改为占空比30%,小风扇就呼呼呼的转起来了~~~~~ 速度改成负数,方向输出也相应的改变,风扇风向果然就不一样了!开心~

 

至此,基础任务已完成。后面想办法让风声占空比 自动变化起来。





关键词: 单片机     PWM     TB6612     NXP    

共1条 1/1 1 跳转至

回复

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