电机应用-无刷直流电机

目录

无刷直流电机

KV值

工作原理

直流无刷电机驱动设计与分析

霍尔传感器模式

野火无刷电机驱动板

直流无刷电机控制实现

速度控制原理

定时器8配置

定时器TIM5配置

定时器相关函数

电机初始化及调用函数

测试环节

直流无刷驱动板温度电压采集

硬件接线

ADC配置

ADC采集函数

测试环节

直流无刷驱动板温度、电源电压、三相电流读取

ADC配置

ADC采集函数

测试环节


无刷直流电机

本篇总结:

无刷直流电机(BLDC)由电动机和驱动器组成,无碳刷和无换向器(电子换向器去替代,如霍尔传感器)。和有刷直流电机相反,定子为线圈,转子为永磁铁。

实际转速 = KV值 * 工作电压。无刷直流电机的转速和电压成正比。

获取转子位置信息的方法:使用传感器(霍尔传感器成本低、光栅编码器精度高、旋转变压器可靠性高)、不使用传感器(直接使用反电动势来估计转子位置)

霍尔效应传感器是测量转子上永磁体产生的磁场,通常摆放在离转子很近的地方。一般来说,我们需要三个霍尔传感器。

无刷直流电机(Brushless Dirent Current Motor,简称BLDC)由电动机主体和驱动器组成,无电刷和无换向器,是除了有刷电机外用得最多的一种电机。

无刷直流电机不使用机械的电刷装置,采用方波自控式永磁同步电机。与有刷电机相比,无刷直流电机将转子和定子交换(即使用电枢绕组作为定子,使用钕铁硼的永磁材料作为转子),以霍尔传感器取代碳刷换向器,性能上比一般的直流电机有很大优势。广泛应用于航模、高速车模和船模。

优点:高效率、低功耗、低噪音、超长寿命、高可靠性、可伺服控制、无极变频调速等。

缺点:贵、比有刷直流电机开发复杂。转矩小,功率不能做太大。

普通的有刷电机旋转的是绕组,而无刷电机无论是外转子结构还是内转子结构,旋转的都是磁铁。

无刷直流电机(BLDC)和永磁同步电机(PMSM)区别在于:反电动势为梯形波的是BLDC,反电动势为正弦波的是PMSM。

KV值

有刷直流电机是根据额定工作电压来标注额定转速的,无刷电机引入了KV值让用户可以直观的知道无刷电机在具体的工作电压下的具体转速。

实际转速=KV值*工作电压。无刷直流电机的转速和电压成正比。

工作原理

电机应用-无刷直流电机_第1张图片

直流无刷电机驱动设计与分析

根据上面的工作原理,我们可以知道怎么导通就可以让无刷电机转动,因为单片机的引脚驱动能力有限,所以使用三相六臂全桥驱动电路来驱动无刷电机。

电机应用-无刷直流电机_第2张图片

Q1和Q4导通,其它不导通,则电流将从Q1流经U相绕组,再从V相绕组流到Q4.这完成了工作原理中描述的第一步。

同理,依次导通Q5Q4、Q5Q2、Q3Q2、Q3Q6、Q1Q6。这就完成了六拍工作方式。

但是单片机的引脚不能直接驱动MOS管,所以需要专用的IC来驱动MOS管。

想让一对MOS管导通时,是需要知道上一步导通的是哪两个MOS管,而且在第一步中MOS管导通时转子的位置是自己规定的,但在实际使用中启动时转子的位置是不可知的,所以需要知道转子的位置信息,但不需要连续的位置信息,只需知道换向点的位置即可。

获取转子位置信息的方法:使用传感器(霍尔传感器成本低、光栅编码器精度高、旋转变压器可靠性高)、不使用传感器(直接使用反电动势来估计转子位置)

直接使用反电动势来估计转子位置有:通过AD检测(精准换向)、通过比较器检测(快速换向)。

霍尔传感器模式

霍尔传感器是根据霍尔效应制作的一种磁场传感器。霍尔效应传感器是测量转子上永磁体产生的磁场,通常摆放在离转子很近的地方。

霍尔效应:当电流垂直于外磁场通过半导体时,载流子发生偏转,垂直于电流和磁场的方向会产生附加电场,从而在半导体的两端产生电势差(霍尔电势差)。

一般来说,我们需要三个霍尔传感器。

在BLDC中一般采用3个开关型霍尔穿传感器测量转子的位置,由其输出的3位二进制编码去控制三相六臂全桥中的6个MOS管的导通实现换相。

如果将一个霍尔传感器安装在靠近转子的位置,当N极逐渐靠近霍尔传感器(即磁感应达到一定值时),输出导通状态;当极逐渐远离霍尔传感器(即磁感应逐渐减小时),仍然输出导通状态;只有磁场转变为S极(即磁感应逐渐减小到一定值时),输出截止状态。在S和N交替变化下传感器输出波形占高、低电平各占50%。如果转子是一对极,则电机旋转一周霍尔传感器输出一个周期的电压波形;如果转子是两对极,则电机旋转一周霍尔传感器输出两个周期的电压波形。

在BLDC中一般把3个霍尔传感器按间隔120°或60°圆周分布来安装。如果按间隔120°来安装,则3个霍尔传感器输出波形相差120°电度角,输出信号中高低电平各占180°电度角。

电机应用-无刷直流电机_第3张图片

当按真值表对应的霍尔值导通MOS管后就保持状态不变,此时电机会旋转到对应位置保持不变,此时电路的电能只能转换为热能,不能转化为机械能。而电机绕组是漆包铜线,内阻非常小,电路非常大,将会产生大量的热而导致电源或电机被烧毁。

在上方的三相六臂全桥驱动如果同时导通Q1Q2或Q3Q4或Q5Q6,都会导致电路的电机不能正常工作,而MOS管直接将电源的正负极接通,将会导致烧毁电源或MOS管。

野火无刷电机驱动板

野火无刷电机驱动板是使用MOS管搭建的大功率无刷电机驱动板。驱动板可支持12V~70V的宽电压输入,10A过电流保护电路(超过10A可自动禁用电机控制信号),最高功率支持700W。实际使用输入电压需要根据电机进行选择,同时还具有三相电流和反电动势采样电路、编码器(霍尔)接口和电源电压检测电路等。

野火使用MOS管搭建的直流无刷电机驱动板做到了信号完全隔离,其它驱动板基本都只是使用光耦隔离了控制信号,并没有对ADC采样电路进行隔离。野火不仅使用光耦对控制信号进行了隔离,还使用AMC1200SDUBR隔离运放对ADC采样电路进行了隔离。

PWM控制信号使用TLP2362高速光耦进行了隔离,SD控制信号使用EL357N光耦进行了隔离。如下图。

电机应用-无刷直流电机_第4张图片

为了防止出现同一侧高端MOS管和低端MOS管同时导通的情况,在电路上增加了异或门和与门。分析U相和上图中最下方的与门,如下图。

电机应用-无刷直流电机_第5张图片

下图是使用MOS管搭建的U相半桥电路。IR2110S主要功能是逻辑信号输入处理、电平转换功能和悬浮自举电源结构等。可以使MCU输出逻辑信号直接连接到IR2110S的输入通道上。IR2110S芯片有一个SHUTDOWN引脚(SD),逻辑输入控制信号高电平有效。

电机应用-无刷直流电机_第6张图片

从真值表可以看出,IR2110S的输入逻辑信号SD为H时,驱动器控制输出HO、LO同时为L,即上、下功率管同时关断。

IR2110S的输入逻辑信号SD连接的是光耦输出信号Motor_SD_IN,并且在上上图中可知道,Motor_SD_IN 由 Motor_SD_A 和 Motor_SD_B 控制,而且经过了一个光耦的反相。

Motor_SD_B 是驱动板的硬件过流保护信号,只要不过流,正常时都保持高电平。所以在驱动板不过流的前提下,由 Motor_SD_A(即为驱动板接口的真正SD引脚) 来控制驱动器的输出HO、LO。

直流无刷电机控制实现

速度控制原理

影响电机转速的三个参量:调整电枢回路的总电阻Ra(改变难度大)、调整电枢绕组的供电电压、调整励磁磁通(改变难度大)。

电机应用-无刷直流电机_第7张图片

在一般情况下可以对无刷直流电机的供电电压适当调整,从而降低线圈绕组通过电流的大小,以达到控制电机转速的目的。

无刷直流电机也可以使用PWM来进行速度控制,通常PWM频率为十几kHz或几十kHz(不得超过MOS管的开关频率),这样把需要通电的MOS管使用PWM来控制就可以实现速度的控制。

使用PWM控制直流无刷电机的策略包括:PWM-ON、ON-PWM、H_PWM-L_ON、H_ON-L_PWM、H_PWM-L_PWM。均是在120°运行方式下进行

电机应用-无刷直流电机_第8张图片

定时器8配置

高级定时器,属于APB2总线,定时器内部时钟为168MHz。配置后为15kHz频率。

TIM_HandleTypeDef  htimx_bldcm;
TIM_OC_InitTypeDef TIM_OCInitStructure;

static void TIMx_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

	// OCPWM1-PI5 OCPWM2-PI6 OCPWM3-PI7
	// OCNPWM1-PH13 OCNPWM2-PH14 OCNPWM3-PH15
    __GPIOI_CLK_ENABLE();
    __GPIOH_CLK_ENABLE();

    // OCNPWM1-PH13
	GPIO_InitStructure.Pin 		= GPIO_PIN_13;
    GPIO_InitStructure.Pull 	= GPIO_NOPULL;
    GPIO_InitStructure.Speed 	= GPIO_SPEED_HIGH;
    GPIO_InitStructure.Mode 	= GPIO_MODE_OUTPUT_PP;   // 推挽输出模式
    HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);

	// OCNPWM2-PH14
    GPIO_InitStructure.Pin = GPIO_PIN_14;
    HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);

	// OCNPWM3-PH15
    GPIO_InitStructure.Pin = GPIO_PIN_15;
    HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);

	// OCPWM1-PI5
	GPIO_InitStructure.Pin 			= GPIO_PIN_5;
	GPIO_InitStructure.Alternate 	= MOTOR_OCPWM1_AF;
    GPIO_InitStructure.Mode 		= GPIO_MODE_AF_PP;
    HAL_GPIO_Init(GPIOI, &GPIO_InitStructure);

	// OCPWM2-PI6
    GPIO_InitStructure.Pin 			= GPIO_PIN_6;
    GPIO_InitStructure.Alternate 	= MOTOR_OCPWM2_AF;
    HAL_GPIO_Init(GPIOI, &GPIO_InitStructure);

    // OCPWM3-PI7
    GPIO_InitStructure.Pin 			= GPIO_PIN_7;
    GPIO_InitStructure.Alternate 	= MOTOR_OCPWM3_AF;
    HAL_GPIO_Init(GPIOI, &GPIO_InitStructure);
}

static void TIM_Mode_Config(void)
{
    __TIM8_CLK_ENABLE();
	
    htimx_bldcm.Instance 			= TIM8;
    htimx_bldcm.Init.Period 		= 5600 - 1;
    htimx_bldcm.Init.Prescaler 		= 2 - 1;
    htimx_bldcm.Init.ClockDivision 	= TIM_CLOCKDIVISION_DIV1;
    htimx_bldcm.Init.CounterMode 	= TIM_COUNTERMODE_UP;
    htimx_bldcm.Init.RepetitionCounter = 0;
    HAL_TIM_PWM_Init(&htimx_bldcm);

    TIM_OCInitStructure.OCMode 			= TIM_OCMODE_PWM1;
    TIM_OCInitStructure.Pulse 			= 0;                         // 默认必须要初始为0
    TIM_OCInitStructure.OCPolarity 		= TIM_OCPOLARITY_HIGH;
    TIM_OCInitStructure.OCNPolarity 	= TIM_OCNPOLARITY_HIGH;
    TIM_OCInitStructure.OCIdleState 	= TIM_OCIDLESTATE_SET;
    TIM_OCInitStructure.OCNIdleState 	= TIM_OCNIDLESTATE_RESET;

    HAL_TIM_PWM_ConfigChannel(&htimx_bldcm, &TIM_OCInitStructure, TIM_CHANNEL_1);  // 初始化通道 1 输出 PWM
    HAL_TIM_PWM_ConfigChannel(&htimx_bldcm, &TIM_OCInitStructure, TIM_CHANNEL_2);  // 初始化通道 2 输出 PWM
    HAL_TIM_PWM_ConfigChannel(&htimx_bldcm, &TIM_OCInitStructure, TIM_CHANNEL_3);  // 初始化通道 3 输出 PWM

    /* 配置触发源,失步检测 */
    HAL_TIMEx_ConfigCommutationEvent(&htimx_bldcm, TIM_TS_ITR3, TIM_COMMUTATION_SOFTWARE);

    /* 开启定时器通道输出PWM */
    HAL_TIM_PWM_Start(&htimx_bldcm, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htimx_bldcm, TIM_CHANNEL_2);
    HAL_TIM_PWM_Start(&htimx_bldcm, TIM_CHANNEL_3);
}

void TIMx_Configuration(void)
{
    TIMx_GPIO_Config();
    TIM_Mode_Config();
}

定时器TIM5配置

通用定时器,属于APB1总线,定时器内部时钟为84MHz。配置后约为10Hz频率,计时器溢出周期为100ms,这个时间要设置到电机正常旋转时足够一路霍尔传感器产生变化。因为任何一相霍尔传感器发生变化都需要换相,所以输入捕获极性设置为双边沿触发。

/* 霍尔传感器相关定时器初始出 */
TIM_HandleTypeDef htimx_hall;

static void hall_gpio_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

	// PH10-U PH11-V PH12-W
    __GPIOH_CLK_ENABLE();

	// PH10-U
    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Alternate = HALL_INPUTU_AF;
    HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);

	// PH11-V
    GPIO_InitStruct.Pin = GPIO_PIN_11;
    HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);

	// PH12-W
    GPIO_InitStruct.Pin = GPIO_PIN_12;
    HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
}

/**
  * @brief  霍尔传感器定时器初始化
  * @param  无
  * @retval 无
  */
static void hall_tim_init(void)
{
    TIM_HallSensor_InitTypeDef  hall_sensor_cfg;

    /* 基本定时器外设时钟使能 */
    __TIM5_CLK_ENABLE();

    /* 定时器基本功能配置 */
    htimx_hall.Instance 			= TIM5;
    htimx_hall.Init.Prescaler 		= 128 - 1;
	htimx_hall.Init.Period 			= 0xFFFF - 1;
    htimx_hall.Init.CounterMode 	= TIM_COUNTERMODE_UP;
    htimx_hall.Init.ClockDivision 	= TIM_CLOCKDIVISION_DIV1;

    hall_sensor_cfg.IC1Prescaler 		= TIM_ICPSC_DIV1;           // 输入捕获分频
    hall_sensor_cfg.IC1Polarity 		= TIM_ICPOLARITY_BOTHEDGE;  // 输入捕获极性
    hall_sensor_cfg.IC1Filter 			= 10;                       // 输入滤波
    hall_sensor_cfg.Commutation_Delay 	= 0U;                   	// 不使用延迟触发
    HAL_TIMEx_HallSensor_Init(&htimx_hall, &hall_sensor_cfg);

    HAL_NVIC_SetPriority(TIM5_IRQn, 0, 0);    // 设置中断优先级
    HAL_NVIC_EnableIRQ(TIM5_IRQn);            // 使能中断
}

void hall_tim_config(void)
{
    hall_gpio_init();	  // 初始化引脚
    hall_tim_init();      // 初始化定时器
}

定时器相关函数

/**
  * @brief  停止pwm输出
  * @param  无
  * @retval 无
  */
void stop_pwm_output(void)
{
    /* 关闭定时器通道输出PWM */
    __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);
    __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);
    __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);

    HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂
    HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂
    HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂
}

static uint16_t bldcm_pulse = 0;

/**
  * @brief  设置pwm输出的占空比
  * @param  pulse:要设置的占空比
  * @retval 无
  */
void set_pwm_pulse(uint16_t pulse)
{
    bldcm_pulse = pulse;
} 

/**
  * @brief  使能霍尔传感器
  * @param  无
  * @retval 无
  */
void hall_enable(void)
{
    /* 使能霍尔传感器接口 */
    __HAL_TIM_ENABLE_IT(&htimx_hall, TIM_IT_TRIGGER);	// 触发中断
    __HAL_TIM_ENABLE_IT(&htimx_hall, TIM_IT_UPDATE);	// 更新中断
    HAL_TIMEx_HallSensor_Start(&htimx_hall);
    LED1_OFF;	// LED1作为电机堵转超时的指示灯
	// 执行一次换相,因为需要根据当前霍尔传感器的位置让电机旋转到下一个位置,同时霍尔传感器状态也发生了变化
    HAL_TIM_TriggerCallback(&htimx_hall);   			
}

/**
  * @brief  禁用霍尔传感器
  * @param  无
  * @retval 无
  */
void hall_disable(void)
{
    /* 禁用霍尔传感器接口 */
    __HAL_TIM_DISABLE_IT(&htimx_hall, TIM_IT_TRIGGER);
    __HAL_TIM_DISABLE_IT(&htimx_hall, TIM_IT_UPDATE);
    HAL_TIMEx_HallSensor_Stop(&htimx_hall);
}

uint8_t get_hall_state(void)
{
    uint8_t state = 0;

#if 1

    /* 读取霍尔传感器 U 的状态 */
    if (HAL_GPIO_ReadPin(GPIOH, GPIO_PIN_10) != GPIO_PIN_RESET)
    {
        state |= 0x01U << 0;
    }

    /* 读取霍尔传感器 V 的状态 */
    if (HAL_GPIO_ReadPin(GPIOH, GPIO_PIN_11) != GPIO_PIN_RESET)
    {
        state |= 0x01U << 1;
    }

    /* 读取霍尔传感器 W 的状态 */
    if (HAL_GPIO_ReadPin(GPIOH, GPIO_PIN_12) != GPIO_PIN_RESET)
    {
        state |= 0x01U << 2;
    }

#else
    state = (GPIOH->IDR >> 10) & 7;    // 读 3 个霍尔传感器的状态
#endif

    return state;    // 返回传感器状态
}

int update = 0;     // 定时器更新计数

/**
  * @brief  霍尔传感器触发回调函数
  * @param  htim:定时器句柄
  * @retval 无
  */
void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim)
{
    /* 获取霍尔传感器引脚状态,根据厂家给出的真值表进行换相 */
	/* 上桥臂采用PWM输出,下桥臂直接输出高电平,即H_PWM-L_ON模式 */
	/* 霍尔传感器每变化一次都会进该中断处理 */
    uint8_t step = 0;
    step = get_hall_state();

#if 0

    if (get_bldcm_direction() == MOTOR_FWD)
    {
        step = 7 - step;        // 根据换向表的规律可知: REV = 7 - FWD;
    }

#endif

    if (get_bldcm_direction() == MOTOR_FWD)
    {
        switch (step)
        {
            case 1:    /* U+ W- */
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, bldcm_pulse);                  // 通道 1 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_SET);      // 开启下桥臂
                break;

            case 2:     /* V+ U- */
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, bldcm_pulse);                  // 通道 2 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_SET);      // 开启下桥臂

                break;

            case 3:    /* V+ W- */
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, bldcm_pulse);                  // 通道 2 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_SET);      // 开启下桥臂
                break;

            case 4:     /* W+ V- */
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, bldcm_pulse);                  // 通道 3 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_SET);      // 开启下桥臂
                break;

            case 5:     /* U+  V -*/
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, bldcm_pulse);                  // 通道 1 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_SET);      // 开启下桥臂
                break;

            case 6:     /* W+ U- */
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, bldcm_pulse);                  // 通道 3 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_SET);      // 开启下桥臂
                break;
        }
    }
    else
    {
        switch (step)
        {
            case 1:   /* W+ U- */
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, bldcm_pulse);                  // 通道 3 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_SET);      // 开启下桥臂
                break;

            case 2:    /* U+  V -*/
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, bldcm_pulse);                  // 通道 1 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_SET);      // 开启下桥臂
                break;

            case 3:   /* W+ V- */
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, bldcm_pulse);                  // 通道 3 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_SET);      // 开启下桥臂

                break;

            case 4:    /* V+ W- */
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, bldcm_pulse);                  // 通道 2 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_SET);      // 开启下桥臂
                break;

            case 5:    /* V+ U- */
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, bldcm_pulse);                  // 通道 2 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_SET);      // 开启下桥臂
                break;

            case 6:    /* U+ W- */
                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0
                HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂

                __HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, bldcm_pulse);                  // 通道 1 配置的占空比
                HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_SET);      // 开启下桥臂
                break;
        }
    }

    HAL_TIM_GenerateEvent(&htimx_bldcm, TIM_EVENTSOURCE_COM);    // 软件产生换相事件,此时才将配置写入
    update = 0;
}

/**
  * @brief  定时器更新中断回调函数
  * @param  htim:定时器句柄
  * @retval 无
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	// 有一次在产生更新中断前霍尔传感器没有捕获到值,说明电机堵转了并至少堵转了100ms
    if (update++ > 1)    		
    {
        printf("堵转超时\r\n");
        update = 0;
        LED1_ON;     			// 点亮LED1表示堵转超时停止
        /* 堵转超时停止 PWM 输出 */
        hall_disable();       	// 禁用霍尔传感器接口
        stop_pwm_output();    	// 停止 PWM 输出
    }
}

电机初始化及调用函数

typedef struct
{
  motor_dir_t direction;    // 电机方向
  uint16_t dutyfactor;      // PWM 输出占空比
  uint8_t is_enable;        // 使能电机
  uint32_t lock_timeout;    // 电机堵转计时
}bldcm_data_t;
static bldcm_data_t bldcm_data;

/**
  * @brief  电机 SD 控制引脚初始化
  * @param  无
  * @retval 无
  */
static void sd_gpio_config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

	// SD-PE6
    __GPIOE_CLK_ENABLE();

	GPIO_InitStruct.Pin 	= GPIO_PIN_6;
    GPIO_InitStruct.Mode 	= GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Speed 	= GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(SHUTDOWN_GPIO_PORT, &GPIO_InitStruct);
}

void bldcm_init(void)
{
    TIMx_Configuration();    // 电机控制定时器,引脚初始化
    hall_tim_config();       // 霍尔传感器初始化
    sd_gpio_config();        // sd 引脚初始化
}

/**
  * @brief  设置电机速度
  * @param  v: 速度(占空比)
  * @retval 无
  */
void set_bldcm_speed(uint16_t v)
{
    bldcm_data.dutyfactor = v;
    set_pwm_pulse(v);     // 设置速度
}

/**
  * @brief  设置电机方向
  * @param  无
  * @retval 无
  */
void set_bldcm_direction(motor_dir_t dir)
{
    bldcm_data.direction = dir;
}

/**
  * @brief  获取电机当前方向
  * @param  无
  * @retval 无
  */
motor_dir_t get_bldcm_direction(void)
{
    return bldcm_data.direction;
}

/**
  * @brief  使能电机
  * @param  无
  * @retval 无
  */
void set_bldcm_enable(void)
{
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_SET);     // PE6-高电平使能 
    HAL_Delay(1);
    hall_enable();											// 霍尔传感器使能
}

/**
  * @brief  禁用电机
  * @param  无
  * @retval 无
  */
void set_bldcm_disable(void)
{
    hall_disable();											/* 禁用霍尔传感器接口 */
    stop_pwm_output();										/* 停止 PWM 输出 */
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_RESET);   // PE6-低电平失能
}

测试环节

void test(void)
{
	__IO uint16_t ChannelPulse = 5500 / 10;
    uint8_t i = 0;
	
	初始化
	bldcm_init();
	
	while(1)
	{
		/* 扫描KEY1 */
        if (Key_Scan(KEY1_GPIO_PORT, KEY1_PIN) == KEY_ON)
        {
            /* 使能电机 设置电机的速度为百分之十的占空比*/
            set_bldcm_speed(ChannelPulse);
            set_bldcm_enable();
        }

        /* 扫描KEY2 */
        if (Key_Scan(KEY2_GPIO_PORT, KEY2_PIN) == KEY_ON)
        {
            /* 停止电机 */
            set_bldcm_disable();
        }

        /* 扫描KEY3 */
        if (Key_Scan(KEY3_GPIO_PORT, KEY3_PIN) == KEY_ON)
        {
            /* 增大占空比 */
            ChannelPulse += 5500 / 10;

            if (ChannelPulse > 5500)
            {
                ChannelPulse = 5500;
            }

            set_bldcm_speed(ChannelPulse);
        }

        /* 扫描KEY4 */
        if (Key_Scan(KEY4_GPIO_PORT, KEY4_PIN) == KEY_ON)
        {
            /* 减少占空比 */
            if (ChannelPulse < 5500 / 10)
            {
                ChannelPulse = 0;
            }
            else
            {
                ChannelPulse -= 5500 / 10;
            }

            set_bldcm_speed(ChannelPulse);
        }

        /* 扫描KEY5 */
        if (Key_Scan(KEY5_GPIO_PORT, KEY5_PIN) == KEY_ON)
        {
            /* 转换方向 即Forward,正转运行;相对地,REV,即Reverse,反转运行 */
            set_bldcm_direction((++i % 2) ? MOTOR_FWD : MOTOR_REV);
        }
	}
}

直流无刷驱动板温度电压采集

硬件接线

在NTC接口上插入NTC采样电阻,并将另一头贴于电机表面。

电机应用-无刷直流电机_第9张图片

ADC配置

DMA_HandleTypeDef DMA_Init_Handle;
ADC_HandleTypeDef ADC_Handle;

static int16_t adc_buff[128];   	// 电压采集缓冲区

/**
  * @brief  ADC 通道引脚初始化
  * @param  无
  * @retval 无
  */
static void ADC_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    __GPIOF_CLK_ENABLE();
	
    // PF10-温度
    GPIO_InitStructure.Pin 	= GPIO_PIN_10;
    GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStructure);

	// PF9-电压
    GPIO_InitStructure.Pin = GPIO_PIN_9;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStructure);
}

void adc_dma_init(void)
{
    // ------------------DMA Init 结构体参数 初始化--------------------------
    // ADC3使用DMA2,数据流0,通道0,这个是手册固定死的
    __DMA2_CLK_ENABLE();
	
    DMA_Init_Handle.Instance 					= DMA2_Stream0;
    DMA_Init_Handle.Init.Direction 				= DMA_PERIPH_TO_MEMORY;
    DMA_Init_Handle.Init.PeriphInc 				= DMA_PINC_DISABLE;
    DMA_Init_Handle.Init.MemInc 				= DMA_MINC_ENABLE;
    DMA_Init_Handle.Init.PeriphDataAlignment 	= DMA_PDATAALIGN_HALFWORD;
    DMA_Init_Handle.Init.MemDataAlignment 		= DMA_MDATAALIGN_HALFWORD;
    DMA_Init_Handle.Init.Mode 					= DMA_CIRCULAR;
    DMA_Init_Handle.Init.Priority 				= DMA_PRIORITY_HIGH;
    DMA_Init_Handle.Init.FIFOMode 				= DMA_FIFOMODE_DISABLE;
    DMA_Init_Handle.Init.FIFOThreshold 			= DMA_FIFO_THRESHOLD_HALFFULL;
    DMA_Init_Handle.Init.MemBurst 				= DMA_MBURST_SINGLE;
    DMA_Init_Handle.Init.PeriphBurst 			= DMA_PBURST_SINGLE;
    DMA_Init_Handle.Init.Channel 				= TEMP_ADC_DMA_CHANNEL;
	
    //初始化DMA流,流相当于一个大的管道,管道里面有很多通道
    HAL_DMA_Init(&DMA_Init_Handle);

    __HAL_LINKDMA(&ADC_Handle, DMA_Handle, DMA_Init_Handle);
}

/**
  * @brief  ADC 模式配置
  * @param  无
  * @retval 无
  */
static void ADC_Mode_Config(void)
{
    TEMP_ADC_CLK_ENABLE();
	
    // -------------------ADC Init 结构体 参数 初始化------------------------
    ADC_Handle.Instance 					= ADC3;
    ADC_Handle.Init.ClockPrescaler 			= ADC_CLOCKPRESCALER_PCLK_DIV4;
    ADC_Handle.Init.Resolution 				= ADC_RESOLUTION_12B;
    ADC_Handle.Init.ScanConvMode 			= ENABLE;
    ADC_Handle.Init.ContinuousConvMode 		= ENABLE;
    ADC_Handle.Init.DiscontinuousConvMode 	= DISABLE;
    ADC_Handle.Init.NbrOfDiscConversion   	= 0;
    ADC_Handle.Init.ExternalTrigConvEdge 	= ADC_EXTERNALTRIGCONVEDGE_NONE;
    ADC_Handle.Init.ExternalTrigConv 		= ADC_SOFTWARE_START;
    ADC_Handle.Init.DataAlign 				= ADC_DATAALIGN_RIGHT;
    ADC_Handle.Init.NbrOfConversion 		= 2;
    ADC_Handle.Init.DMAContinuousRequests 	= ENABLE;
    ADC_Handle.Init.EOCSelection          	= ADC_EOC_SINGLE_CONV;
    HAL_ADC_Init(&ADC_Handle);

    //---------------------------------------------------------------------------
    ADC_ChannelConfTypeDef ADC_Config;

	// 温度
    ADC_Config.Channel      = ADC_CHANNEL_8;
    ADC_Config.Rank         = 1;
    ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
    ADC_Config.Offset       = 0;
    HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);

    /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
    */
	// 电压
    ADC_Config.Channel 		= ADC_CHANNEL_7;
    ADC_Config.Rank 		= 2;
    ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
    ADC_Config.Offset       = 0;
    if (HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config) != HAL_OK)
    {
        while (1);
    }

    // 外设中断优先级配置和使能中断配置
    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 1);
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

    HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t *)&adc_buff, 128);
}

/**
  * @brief  ADC 采集初始化
  * @param  无
  * @retval 无
  */
void ADC_Init(void)
{
    ADC_GPIO_Config();
    adc_dma_init();
    ADC_Mode_Config();
}

ADC采集函数

/**
  * @brief  常规转换在非阻塞模式下完成回调
  * @param  hadc: ADC  句柄.
  * @retval 无
  */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    int32_t adc_mean = 0;

    HAL_ADC_Stop_DMA(hadc);       		// 停止 ADC 采样,处理完一次数据在继续采样

    /* 计算温度通道采样的平均值 */
    for (uint32_t count = 0; count < 128; count += 2)
    {
        adc_mean += (int32_t)adc_buff[count];
    }

    adc_mean_t = adc_mean / (128 / 2);  // 保存平均值

    adc_mean = 0;

    /* 计算电压通道采样的平均值 */
    for (uint32_t count = 1; count < 128; count += 2)
    {
        adc_mean += (int32_t)adc_buff[count];
    }

    vbus_adc_mean = adc_mean / (128 / 2);    // 保存平均值

    HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t *)&adc_buff, 128);   // 开始 ADC 采样
}

/**
  * @brief  获取温度传感器端的电压值
  * @param  无
  * @retval 转换得到的电压值
  */
float get_ntc_v_val(void)
{
    float vdc = (float)adc_mean_t / 4096.0f * 3.3f;      // 获取电压值
    return vdc;
}

/**
  * @brief  获取温度传感器端的电阻值
  * @param  无
  * @retval 转换得到的电阻值
  */
float get_ntc_r_val(void)
{
    float r = 0;
    float vdc = get_ntc_v_val();

    r = (3.3f - vdc) / (vdc / (float)4700.0);

    return r;
}

/**
  * @brief  获取温度传感器的温度
  * @param  无
  * @retval 转换得到的温度,单位:(℃)
  */
float get_ntc_t_val(void)
{
    float t = 0;             // 测量温度
    float Rt = 0;            // 测量电阻
    float Ka = 273.15;       // 0℃ 时对应的温度(开尔文)
    float R25 = 10000.0;     // 25℃ 电阻值
    float T25 = Ka + 25;     // 25℃ 时对应的温度(开尔文)
    float B = 3950.0;        /* B-常数:B = ln(R25 / Rt) / (1 / T – 1 / T25),其中 T = 25 + 273.15 */

    Rt = get_ntc_r_val();    // 获取当前电阻值

    t = B * T25 / (B + log(Rt / R25) * T25) - Ka ;    	// 使用公式计算

    return t;
}

/**
  * @brief  获取电源电压值
  * @param  无
  * @retval 转换得到的电压值
  */
float get_vbus_val(void)
{
    float vdc = (float)vbus_adc_mean / 4096.0f * 3.3f;	// 获取电压值
    return ((float)vdc - 1.24f) * 37.0f;
}

测试环节

void test(void)
{
	初始化
	
	while(1)
	{
		if (HAL_GetTick() % 50 == 0 && flag == 0)  // 每50毫秒读取一次温度、电压
        {
            flag = 1;
            printf("电源电压=%0.1fV, NTC=%0.0fΩ, T=%0.1f℃.\r\n", get_vbus_val(), get_ntc_r_val(), get_ntc_t_val());
        }
        else if (HAL_GetTick() % 50 != 0 && flag == 1)
        {
            flag = 0;
        }
	}
}

直流无刷驱动板温度、电源电压、三相电流读取

ADC配置

DMA_HandleTypeDef DMA_Init_Handle;
ADC_HandleTypeDef ADC_Handle;

static int16_t adc_buff[320];	// 电压采集缓冲区

/**
  * @brief  ADC 通道引脚初始化
  * @param  无
  * @retval 无
  */
static void ADC_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

	// PF10-温度 PF9-电压 PF6-U PF7-V PF8-W
    __GPIOF_CLK_ENABLE();
	
    GPIO_InitStructure.Pin 	= GPIO_PIN_10;
    GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStructure);

    GPIO_InitStructure.Pin 	= GPIO_PIN_9;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStructure);

    GPIO_InitStructure.Pin 	= GPIO_PIN_6;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStructure);

    GPIO_InitStructure.Pin 	= GPIO_PIN_7;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStructure);

    GPIO_InitStructure.Pin 	= GPIO_PIN_8;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStructure);
}

void adc_dma_init(void)
{
    // ------------------DMA Init 结构体参数 初始化--------------------------
    // ADC3使用DMA2,数据流0,通道0,这个是手册固定死的
    __DMA2_CLK_ENABLE();
	
    DMA_Init_Handle.Instance 					= DMA2_Stream0;
    DMA_Init_Handle.Init.Direction 				= DMA_PERIPH_TO_MEMORY;
    DMA_Init_Handle.Init.PeriphInc 				= DMA_PINC_DISABLE;
    DMA_Init_Handle.Init.MemInc 				= DMA_MINC_ENABLE;
    DMA_Init_Handle.Init.PeriphDataAlignment 	= DMA_PDATAALIGN_HALFWORD;
    DMA_Init_Handle.Init.MemDataAlignment 		= DMA_MDATAALIGN_HALFWORD;
    DMA_Init_Handle.Init.Mode 					= DMA_CIRCULAR;
    DMA_Init_Handle.Init.Priority 				= DMA_PRIORITY_HIGH;
    DMA_Init_Handle.Init.FIFOMode 				= DMA_FIFOMODE_DISABLE;
    DMA_Init_Handle.Init.FIFOThreshold 			= DMA_FIFO_THRESHOLD_HALFFULL;
    DMA_Init_Handle.Init.MemBurst 				= DMA_MBURST_SINGLE;
    DMA_Init_Handle.Init.PeriphBurst 			= DMA_PBURST_SINGLE;
    DMA_Init_Handle.Init.Channel 				= DMA_CHANNEL_2;
	
    //初始化DMA流,流相当于一个大的管道,管道里面有很多通道
    HAL_DMA_Init(&DMA_Init_Handle);

    __HAL_LINKDMA(&ADC_Handle, DMA_Handle, DMA_Init_Handle);
}

/**
  * @brief  ADC 模式配置
  * @param  无
  * @retval 无
  */
static void ADC_Mode_Config(void)
{
    __ADC3_CLK_ENABLE();
	
    // -------------------ADC Init 结构体 参数 初始化------------------------
    ADC_Handle.Instance 					= ADC3;
    ADC_Handle.Init.ClockPrescaler 			= ADC_CLOCKPRESCALER_PCLK_DIV4;
    ADC_Handle.Init.Resolution 				= ADC_RESOLUTION_12B;
    ADC_Handle.Init.ScanConvMode 			= ENABLE;
    ADC_Handle.Init.ContinuousConvMode 		= ENABLE;
    ADC_Handle.Init.DiscontinuousConvMode 	= DISABLE;
    ADC_Handle.Init.NbrOfDiscConversion   	= 0;
    ADC_Handle.Init.ExternalTrigConvEdge 	= ADC_EXTERNALTRIGCONVEDGE_NONE;
    ADC_Handle.Init.ExternalTrigConv 		= ADC_SOFTWARE_START;
    ADC_Handle.Init.DataAlign 				= ADC_DATAALIGN_RIGHT;
    ADC_Handle.Init.NbrOfConversion 		= 5;
    ADC_Handle.Init.DMAContinuousRequests 	= ENABLE;
    ADC_Handle.Init.EOCSelection          	= ADC_EOC_SINGLE_CONV;
    HAL_ADC_Init(&ADC_Handle);

    //---------------------------------------------------------------------------
    ADC_ChannelConfTypeDef ADC_Config;

	// 温度
    ADC_Config.Channel      = ADC_CHANNEL_8;
    ADC_Config.Rank         = 1;
    ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
    ADC_Config.Offset       = 0;
    HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);

	// 电压
    ADC_Config.Channel 		= ADC_CHANNEL_7;
    ADC_Config.Rank			= 2;
    ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
    ADC_Config.Offset       = 0;
    HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);

	// U
    ADC_Config.Channel      = ADC_CHANNEL_4;
    ADC_Config.Rank         = 3;
    ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
    ADC_Config.Offset       = 0;
    HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);

	// V
    ADC_Config.Channel      = ADC_CHANNEL_5;
    ADC_Config.Rank         = 4;
    ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
    ADC_Config.Offset       = 0;
    HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);

	// W
    ADC_Config.Channel      = ADC_CHANNEL_6;
    ADC_Config.Rank         = 5;
    ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
    ADC_Config.Offset       = 0;
    if (HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config) != HAL_OK)
    {
        while (1);
    }

    // 外设中断优先级配置和使能中断配置
    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 1);
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

    HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t *)&adc_buff, 320);
}

/**
  * @brief  ADC 采集初始化
  * @param  无
  * @retval 无
  */
void ADC_Init(void)
{
    ADC_GPIO_Config();
    adc_dma_init();
    ADC_Mode_Config();
}

ADC采集函数

static int16_t vbus_adc_mean = 0;       // 电源电压 ACD 采样结果平均值
static uint32_t adc_mean_t = 0;        	// 平均值累加
static uint32_t adc_mean_sum_u = 0;     // 平均值累加
static uint32_t adc_mean_sum_v = 0;     // 平均值累加
static uint32_t adc_mean_sum_w = 0;     // 平均值累加
static uint32_t adc_mean_count_u = 0;   // 累加计数
static uint32_t adc_mean_count_v = 0;   // 累加计数
static uint32_t adc_mean_count_w = 0;   // 累加计数

/**
  * @brief  常规转换在非阻塞模式下完成回调
  * @param  hadc: ADC  句柄.
  * @retval 无
  */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    int32_t adc_mean = 0;
    HAL_ADC_Stop_DMA(hadc);       // 停止 ADC 采样,处理完一次数据在继续采样

    /* 计算温度通道采样的平均值 */
    for (uint32_t count = 0; count < 320; count += 5)
    {
        adc_mean += (int32_t)adc_buff[count];
    }

    adc_mean_t = adc_mean / (320 / 5);    		// 保存平均值  
	adc_mean = 0;


    /* 计算电压通道采样的平均值 */
    for (uint32_t count = 1; count < 320; count += 5)
    {
        adc_mean += (int32_t)adc_buff[count];
    }

    vbus_adc_mean = adc_mean / (320 / 5);    	// 保存平均值
    adc_mean = 0;

    /* 计算电流通道采样的平均值 */
    for (uint32_t count = 2; count < 320; count += 5)
    {
        adc_mean += (uint32_t)adc_buff[count];
    }

    adc_mean_sum_u += adc_mean / (320 / 5);    // 累加电压
    adc_mean_count_u++;
    adc_mean = 0;

    /* 计算电流通道采样的平均值 */
    for (uint32_t count = 3; count < 320; count += 5)
    {
        adc_mean += (uint32_t)adc_buff[count];
    }

    adc_mean_sum_v += adc_mean / (320 / 5);    // 累加电压
    adc_mean_count_v++;
    adc_mean = 0;

    /* 计算电流通道采样的平均值 */
    for (uint32_t count = 4; count < 320; count += 5)
    {
        adc_mean += (uint32_t)adc_buff[count];
    }

    adc_mean_sum_w += adc_mean / (320 / 5);    // 累加电压
    adc_mean_count_w++;
    adc_mean = 0;

    HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t *)&adc_buff, 320);   // 开始 ADC 采样
}

/**
  * @brief  获取温度传感器端的电压值
  * @param  无
  * @retval 转换得到的电压值
  */
float get_ntc_v_val(void)
{
    float vdc = (float)adc_mean_t / 4096.0f * 3.3f;      // 获取电压值
    return vdc;
}

/**
  * @brief  获取温度传感器端的电阻值
  * @param  无
  * @retval 转换得到的电阻值
  */
float get_ntc_r_val(void)
{
    float r = 0;
    float vdc = get_ntc_v_val();

    r = (3.3f - vdc) / (vdc / (float)4700.0);

    return r;
}

/**
  * @brief  获取温度传感器的温度
  * @param  无
  * @retval 转换得到的温度,单位:(℃)
  */
float get_ntc_t_val(void)
{
    float t = 0;             // 测量温度
    float Rt = 0;            // 测量电阻
    float Ka = 273.15;       // 0℃ 时对应的温度(开尔文)
    float R25 = 10000.0;     // 25℃ 电阻值
    float T25 = Ka + 25;     // 25℃ 时对应的温度(开尔文)
    float B = 3950.0;        /* B-常数:B = ln(R25 / Rt) / (1 / T – 1 / T25),其中 T = 25 + 273.15 */

    Rt = get_ntc_r_val();    // 获取当前电阻值

    t = B * T25 / (B + log(Rt / R25) * T25) - Ka ;    // 使用公式计算

    return t;
}

/**
  * @brief  获取V相的电流值
  * @param  无
  * @retval 转换得到的电流值
  */
int32_t get_curr_val_v(void)
{
    static uint8_t flag = 0;
    static uint32_t adc_offset = 0;    // 偏置电压
    int16_t curr_adc_mean = 0;         // 电流 ACD 采样结果平均值

    curr_adc_mean = adc_mean_sum_v / adc_mean_count_v;    // 保存平均值

    adc_mean_count_v = 0;
    adc_mean_sum_v = 0;

    if (flag < 17)
    {
        adc_offset = curr_adc_mean;    // 多次记录偏置电压,待系统稳定偏置电压才为有效值
        flag += 1;
    }

    if (curr_adc_mean >= adc_offset)
    {
        curr_adc_mean -= adc_offset;                     // 减去偏置电压
    }
    else
    {
        curr_adc_mean = 0;
    }

    float vdc = (float)curr_adc_mean / 4096.0f * 3.3f;   // 获取电压值

	// 得到电流值,电压放大8倍,0.02是采样电阻,单位mA
    return ((float)vdc)/(float)8.0/(float)0.02*(float)1000.0;
}

/**
  * @brief  获取U相的电流值
  * @param  无
  * @retval 转换得到的电流值
  */
int32_t get_curr_val_u(void)
{
    static uint8_t flag = 0;
    static uint32_t adc_offset = 0;    // 偏置电压
    int16_t curr_adc_mean = 0;         // 电流 ACD 采样结果平均值

    curr_adc_mean = adc_mean_sum_u / adc_mean_count_u;    // 保存平均值


    adc_mean_count_u = 0;
    adc_mean_sum_u = 0;

    if (flag < 17)
    {
        adc_offset = curr_adc_mean;    // 多次记录偏置电压,待系统稳定偏置电压才为有效值
        flag += 1;
    }

    if (curr_adc_mean >= adc_offset)
    {
        curr_adc_mean -= adc_offset;                     // 减去偏置电压
    }
    else
    {
        curr_adc_mean = 0;
    }

    float vdc = (float)curr_adc_mean / 4096.0f * 3.3f;   // 获取电压值

    // 得到电流值,电压放大8倍,0.02是采样电阻,单位mA
    return ((float)vdc)/(float)8.0/(float)0.02*(float)1000.0;
}

/**
  * @brief  获取W相的电流值
  * @param  无
  * @retval 转换得到的电流值
  */
int32_t get_curr_val_w(void)
{
    static uint8_t flag = 0;
    static uint32_t adc_offset = 0;    // 偏置电压
    int16_t curr_adc_mean = 0;         // 电流 ACD 采样结果平均值

    curr_adc_mean = adc_mean_sum_w / adc_mean_count_w;   // 保存平均值


    adc_mean_count_w = 0;
    adc_mean_sum_w = 0;

    if (flag < 17)
    {
        adc_offset = curr_adc_mean;    // 多次记录偏置电压,待系统稳定偏置电压才为有效值
        flag += 1;
    }

    if (curr_adc_mean >= adc_offset)
    {
        curr_adc_mean -= adc_offset;                    // 减去偏置电压
    }
    else
    {
        curr_adc_mean = 0;
    }

    float vdc = (float)curr_adc_mean / 4096.0f * 3.3f;  // 获取电压值

    // 得到电流值,电压放大8倍,0.02是采样电阻,单位mA
    return ((float)vdc)/(float)8.0/(float)0.02*(float)1000.0;
}

/**
  * @brief  获取电源电压值
  * @param  无
  * @retval 转换得到的电压值
  */
float get_vbus_val(void)
{
    float vdc = (float)vbus_adc_mean / 4096.0f * 3.3f;  // 获取电压值
    return ((float)vdc - 1.24f) * 37.0f;
}

测试环节

void test(void)
{
	初始化
	
	while(1)
	{
		if (HAL_GetTick() % 50 == 0 && flag == 0)  // 每50毫秒读取一次温度、电压
        {
            flag = 1;
            int32_t current_v = get_curr_val_v();
            int32_t current_u = get_curr_val_u();
            int32_t current_w = get_curr_val_w();
            printf("电源电压=%0.1fV, T=%0.1f℃, U相电流=%dmA, V相电流=%dmA, W相电流=%dmA\r\n", get_vbus_val(),  get_ntc_t_val(), current_v, current_u, current_w);
        }
        else if (HAL_GetTick() % 50 != 0 && flag == 1)
        {
            flag = 0;
        }
	}
}

你可能感兴趣的:(#,电机应用开发,stm32)