BLDC的基本控制

news/2024/11/15 19:42:13/

目录

1.直流无刷电机简介

2.无刷电机的分类

3.无刷电机的主要参数

4.无刷电机的应用

5.无刷电机驱动原理

6.无刷电机驱动板介绍

7.基本控制代码

8.总结


1.直流无刷电机简介

说到直流无刷电机(BLDC)就不得不说一下直流有刷电机(BDC)。直流有刷电机顾名思义就是有电刷与换向器。换相是通过电刷完成的。而直流无刷电机是没有电刷的。换相如果是有感的话利用霍尔传感器与编码器检测转子的位置来换相。无感的话通过反电动势的读取。

有刷直流电机与无刷电机的最大结构区别:无刷没有电刷以及换向器;转子与定子反过来了。

如下是对比图:

2.直流无刷电机分类 

BLDC都是方波驱动的。分为外转子式BLDC与内转子式BLDC。

1.外转子式BLDC:转轴与外壳固定。

2.内转子式BLDC:转轴与外壳不固定固定。

3.直流无刷电机的主要参数(主要的)

极对数:转子磁铁NS级的对数,此参数和电机旋转速度有关:电子速度 =  电机实际速度 * 极对数。

KV值:值越大电机转速越大。电机转速 = KV值*工作电压。

额定转速:额定的电流下的空载转速,通常单位用RPM表示。

转矩:电机中转子产生的可以带动机械负载的驱动力矩。通常单位为:N-M。

4.直流无刷电机的应用

无刷电机的应用场景很广泛,如电动车、无人机、风扇、鼓风机、抽油烟机等等。

5.直流无刷电机的驱动原理(重点)

内部BLDC的图如下:

简化如下:可以看出此电机有四对极。而A,B,C绕组各自的四个绕组都是串联在一起的。串联起来的绕组一端都到公共端。

为了方便分析只用一对磁对极。这并不影响。

这样,通电的线圈会产生各自的磁场,他们的合成磁场满足矢量合成的原则。如下图:

 直流无刷电机的6拍工作方式,线圈产生旋转磁场,见图

通过上图我们可以明显看出,想要控制BLDC旋转,根本的问题就是产生这6拍工作方式的电压信号(称为BLDC的六步控制)。举个例子来说明,假定一个BLDC的额定电压为24V,电机三根线就定义为A、B、C:

(1)为A接24V、B悬空、C接GND,此时对应图中的①,电机转轴被固定在一个位置;

(2)在(1)的基础上,我们修改接线方式,为A接24V、B接GND、C悬空,此时对应图中的            ②,电机转轴就在(1)基础上旋转一个角度,达到另外一个位置;

(3)在(2)的基础上,我们修改接线方式,为A悬空、B接GND、C接24V,此时对应图中的③,电机转轴就在(2)基础上旋转一个角度,达到另外一个位置;

(4)在(3)的基础上,我们修改接线方式,为A接GND、B悬空、C接24V,此时对应图中的④,电机转轴就在(3)基础上旋转一个角度,达到另外一个位置;

(5)在(4)的基础上,我们修改接线方式,为A接GND、B接24V、C悬空,此时对应图中的⑤,电机转轴就在(4)基础上旋转一个角度,达到另外一个位置;

(6)在(5)的基础上,我们修改接线方式,为A悬空、B接24V、C接GND,此时对应图中的⑥,电机转轴就在(6)基础上旋转一个角度,达到另外一个位置。

(7)然后又是以(1)方式给电,如此循环,电机就可以转动起来。

但是如何实现上面所描述的三相极性的切换呢?

有了上面的原理分析,现在想让BLDC旋转起来的一个问题就是如何任意的控制A、B、C线的电压,参考之前的直流有刷电机驱动设计,就会马上想到可以用三个半桥(6个桥臂:3个上桥臂+3个下桥臂)构成的一个三相逆变器,这里的每个桥臂都有一个电子开关,电子开关可以选择是功率MOSFET或者IGBT,IGBT用于超大功率电机驱动。最终搭建起来的电路见图:

只要控制六个电子开关的导通或截止就可以实现六步换相。如下图:

 

把A,B,C三相不是公共端的各自分别接到每个上下半桥的中间。并且每个电子开关用PWM来驱动。但是还会多一个MOS驱动器这是因为控制器(MCU)输出的占空比电压是5v或者3.3V。而电子开关如果是MOSFET管的话是不能使其导通。相当于电压不够。所以MOS驱动器可以提升电压到15V左右。使其满足MOSFET管的导通条件。

下面引出另一个问题什么时候换相。要怎么准确的得到转子的位置。假如现在从0度转到60度怎么知道到60度了。这样才能在到60度时准确换相。如果换快了。电机可能就会乱转。换慢了就可能会停一下又转一下。这样会使电机很不平稳。所以转子的位置显得很关键。

方法如下:

有感方案:通过安装霍尔传感器与编码器得到转子反馈位置。

无感方案:比如读取反电动势的大小来得到转子的位置。

下面介绍安装霍尔传感器。霍尔传感器就是利用根据霍尔效应制作的一种磁场传感器。有正向磁场时输出1,反相磁场输出0。如图:

 

霍尔传感器与BLDC电机安装示意图见图。

安装时霍尔传感器要与转子的南极,北极对应好。否则可能不准。并且是要有三个才能判断出转子的位置。

安装方法有两种三个霍尔传感器电角度相差120度与60度。如图:

 看波形可以找到120度安装H1,H2,H3不会同时都为0或者同时为1.而60度安装会有。其实60度安装的H3等于120度的H2。H2等于120安装H3的反相。

当只有一对磁对数时。每个霍尔传感器会经过六次磁极对应。

电机按一定方向转动时,3个霍尔的输出会按照6步的规律变化如下:

 结合之前介绍的BLDC六步控制,在每个霍尔信号都对应一个BLDC控制步,使得BLDC旋转一个角度,这样可以制作下表

 特别注意,一般BLDC厂家都会给出一个霍尔传感器和绕组得电情况对应关系表,不一定跟上面两个表都完全对应一致,但是原理分析都是一致的。

接下来一个问题怎么控制电机的速度?

答案当然是控制占空比的大小。但PWM控制直流无刷电机的方法有不同的模式。

如下图的四种方法。不同控制方式在性能上有不同的效果,当然针对实际的应用场合可以尝试多种调制方式,然后选择最优方式。

 H_on-L_pwm:就是上桥臂用高电平,下桥臂用PWM。H_pwm-L_on正好与H_on-L_pwm相反。

6.无刷驱动板的介绍。

无论直流有刷电机还是无刷电机都会有专门的驱动板去控制。因为MCU的输出电压,电流是有限的。而电机的控制一般需要较高的电压或者电流。下面介绍一个半桥电路。另外的半桥都是一样的。

Ctrl_PWM_U_H与Ctrl_PWM_U_L接MCU一个定时器的互补通道。

首先TLP715是5M的高速隔离光耦:作用是隔离保护以及把PWM的电压抬升到15V。

当TLP715输入高电平那么Vout就输出15v。如果输入低电平那么输出0v。有两个TLP715的输出到IR2110S.一个做高(HIN)一个做低端输入(LIN)。接着IR2110S的有两个输出一个高端输出(HO)去控制上桥臂(Q1)。而低端输出(LO)作为下桥臂(Q2)。对于Q2来说要导通是比较容易的。因为S是接地的所以G点电压只要大于VGS(看手册)的就可以导通。但对于Q1来说S极是没有接地的。而D极与S极之间的电阻比较小。可以近似短路。相当于S极就是VCC_POWER=24V。所以G极必须要大于VCC_POWER+VGS才能导通。从而引出自举电路。就是把HO的电压抬高到VCC_POWER+VGS使得Q1导通。IR2110S还有一个使能引脚SD并且高电平有效。当为高电平无输出。

采样电流采集电路:只是其中一相。

 Current_V_amp通过ADC采集得到然后计算出Isensor_V的电压。在除以采样电阻就是采样电流的值。MCP6024是运算放大因为采样电阻两端的电压太小了只是一个放大功能。而D16是把电压钳位在0~3.3v。

总线电压的检测:

 其实VCC_POWER=24v是固定的。但是但上下两个桥臂短路就会变化。通过检测ADC采集V_DC-BUS就可以计算出VCC_POWER是否有变化。

温度的检测:

热敏电阻阻值是随温度变化的。通ADC采集V_Thermal就可以计算出温度。

过流保护:

LMV331是电压比较器:如果Isensor的电压大于Vref那么Ctrl_SHUTDOWN输出低电平。否则高电平。从而去判断是否过流。

7.基本控制代码

思路:用定时器5的霍尔传感器接口每当其中任意一相发生边沿跳变捕获并且产生中断。在中断里

 先获取引脚的相位接着进行对应的相位切换。定时器8用来产生6路PWM。

定时器5.h

#ifndef __BSP_HALL_H_
#define __BSP_HALL_H_/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f4xx_hal.h"/* 类型定义 ------------------------------------------------------------------*//* 宏定义 --------------------------------------------------------------------*//**************** 定时器霍尔接口TIM参数定义,TIMx CH1,CH2,CH3 *****************//* 注意:更换 "霍尔接口定时器"的时候需要修改 "PWM输出定时器(TIM1/TIM8)" 的触发源  */#define HALL_TIMx                        TIM5
#define HALL_TIM_RCC_CLK_ENABLE()        __HAL_RCC_TIM5_CLK_ENABLE()#define HALL_TIM_GPIO_AF                 GPIO_AF2_TIM5
#define HALL_TIM_GPIO_RCC_CLK_ENABLE()   __HAL_RCC_GPIOH_CLK_ENABLE()#define HALL_TIM_CH1_PORT                GPIOH          // CH1的引脚
#define HALL_TIM_CH1_PIN                 GPIO_PIN_10
#define HALL_TIM_CH1                     TIM_CHANNEL_1#define HALL_TIM_CH2_PORT                GPIOH          // CH2的引脚
#define HALL_TIM_CH2_PIN                 GPIO_PIN_11
#define HALL_TIM_CH2                     TIM_CHANNEL_2#define HALL_TIM_CH3_PORT                GPIOH          // CH3的引脚
#define HALL_TIM_CH3_PIN                 GPIO_PIN_12
#define HALL_TIM_CH3                     TIM_CHANNEL_3#define HALL_TIM_IRQn                    TIM5_IRQn
#define HALL_TIM_IRQHanler               TIM5_IRQHandler// 定义定时器预分频,定时器实际时钟频率为:84MHz/(HALL_TIMx_PRESCALER+1)
#define HALL_TIM_PRESCALER               83  // 实际时钟频率为:1MHz// 定义定时器周期,当定时器开始计数到HALL_TIMx_PERIOD值是更新定时器并生成对应事件和中断
#define HALL_TIM_PERIOD                  0xFFFF ///* 扩展变量 ------------------------------------------------------------------*/
extern TIM_HandleTypeDef htimx_hall;/* 函数声明 ------------------------------------------------------------------*/
void HALLSensor_TIMx_Init(void); // 初始化
int32_t HALL_GetPhase(void);     // 获取相位

定时器5.c

#include "hall/bsp_hall.h"
#include "bldc/bsp_bldc.h"/* 私有类型定义 --------------------------------------------------------------*/
TIM_HandleTypeDef htimx_hall;/*** 函数功能: 定时器HALL接口引脚初始化* 输入参数: 无* 返 回 值: 无* 说    明: 无*/
void YS_TIM_HALL_Base_MspInit(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};HALL_TIM_GPIO_RCC_CLK_ENABLE();GPIO_InitStruct.Pin       = HALL_TIM_CH1_PIN;GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;GPIO_InitStruct.Alternate = HALL_TIM_GPIO_AF;GPIO_InitStruct.Pull      = GPIO_PULLUP;HAL_GPIO_Init(HALL_TIM_CH1_PORT, &GPIO_InitStruct);GPIO_InitStruct.Pin       = HALL_TIM_CH2_PIN;HAL_GPIO_Init(HALL_TIM_CH2_PORT, &GPIO_InitStruct);GPIO_InitStruct.Pin       = HALL_TIM_CH3_PIN;HAL_GPIO_Init(HALL_TIM_CH3_PORT, &GPIO_InitStruct);HAL_NVIC_SetPriority(HALL_TIM_IRQn, 1, 1);HAL_NVIC_EnableIRQ(HALL_TIM_IRQn);
}/*** 函数功能: 定时器霍尔传感器接口初始化* 输入参数: 无* 返 回 值: 无* 说    明: 无*/
void HALLSensor_TIMx_Init(void)
{TIM_HallSensor_InitTypeDef sHallConfig = {0};/* 通用定时器外设时钟使能 */HALL_TIM_RCC_CLK_ENABLE();/* 初始化板载设备,定时器通道引脚,使能时钟 */YS_TIM_HALL_Base_MspInit();///* 配置定时器基础计数功能 */htimx_hall.Instance           = HALL_TIMx;          // 定时器TIMhtimx_hall.Init.Prescaler     = HALL_TIM_PRESCALER; // PSC设置预分频值htimx_hall.Init.CounterMode   = TIM_COUNTERMODE_CENTERALIGNED1;// 中心对齐模式htimx_hall.Init.Period        = HALL_TIM_PERIOD;    // 计数周期htimx_hall.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;sHallConfig.Commutation_Delay = 0x06; // 延迟触发,7us,实际测试需要7ussHallConfig.IC1Filter         = 0x0F; // 输入滤波;sHallConfig.IC1Polarity       = TIM_ICPOLARITY_RISING;// 输入捕获极性//不起作用sHallConfig.IC1Prescaler      = TIM_ICPSC_DIV1;       // 输入捕获预分频HAL_TIMEx_HallSensor_Init(&htimx_hall, &sHallConfig);
}
/*** 函数功能: 读取霍尔引脚状态* 输入参数: 无* 返 回 值: 霍尔引脚状态* 说    明: 直接读取引脚的状态,数据字节的低三位分别对应UVW(HALL)的电平状态*/
int32_t HALL_GetPhase()
{int32_t tmp = 0;tmp |= HAL_GPIO_ReadPin(HALL_TIM_CH1_PORT, HALL_TIM_CH1_PIN);//U(A)tmp <<= 1;tmp |= HAL_GPIO_ReadPin(HALL_TIM_CH2_PORT, HALL_TIM_CH2_PIN);//V(B)tmp <<= 1;tmp |= HAL_GPIO_ReadPin(HALL_TIM_CH3_PORT, HALL_TIM_CH3_PIN);//W(C)return (tmp & 0x0007); // 取低三位
}/*** 函数功能: 霍尔传感器回调函数* 输入参数: @htim,霍尔传感器接口定时器* 返 回 值: 无* 说    明:*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{int32_t RT_hallPhase = 0; // 霍尔信号RT_hallPhase = HALL_GetPhase();     // 获取霍尔引脚的相位/* 换相控制 */BLDCMotor_PhaseCtrl(RT_hallPhase);
}

 定时器8.h

#ifndef __BLDCMOTOR_TIM_H__
#define __BLDCMOTOR_TIM_H__/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f4xx_hal.h"/* 类型定义 ------------------------------------------------------------------*/
/* 电机方向定义 */
typedef enum 
{MOTOR_DIR_CW = 0, // 顺时针转动MOTOR_DIR_CCW     // 逆时针转动
}MotorDir_Typedef;  // 方向定义/* 电机使能定义 */
typedef enum 
{MOTOR_ENABLE  = 0, MOTOR_DISABLE
}MotorSta_Typedef ;/* 宏定义 --------------------------------------------------------------------*/
#define USE_PMSMMOTOR   // 定义使用的是PMSM电机/******************** 通用定时器TIM参数定义,TIM8 CH1,CH2,CH3 *****************/#define BLDCMOTOR_TIMx                        TIM8
#define BLDCMOTOR_TIM_RCC_CLK_ENABLE()        __HAL_RCC_TIM8_CLK_ENABLE()#define BLDCMOTOR_TIM_GPIO_AF                 GPIO_AF3_TIM8
#define BLDCMOTOR_TIM_GPIO_RCC_CLK_ENABLE()   {__HAL_RCC_GPIOI_CLK_ENABLE();\__HAL_RCC_GPIOH_CLK_ENABLE();}#define BLDCMOTOR_TIM_CH1_PORT                GPIOI          // TIM8_CH1的引脚
#define BLDCMOTOR_TIM_CH1_PIN                 GPIO_PIN_5
#define BLDCMOTOR_TIM_CH1                     TIM_CHANNEL_1#define BLDCMOTOR_TIM_CH2_PORT                GPIOI          // TIM8_CH2的引脚
#define BLDCMOTOR_TIM_CH2_PIN                 GPIO_PIN_6
#define BLDCMOTOR_TIM_CH2                     TIM_CHANNEL_2#define BLDCMOTOR_TIM_CH3_PORT                GPIOI          // TIM8_CH3的引脚
#define BLDCMOTOR_TIM_CH3_PIN                 GPIO_PIN_7
#define BLDCMOTOR_TIM_CH3                     TIM_CHANNEL_3/* 三个互补通道 */
#define BLDCMOTOR_TIM_CH1N_PORT               GPIOH          // TIM8_CH1N的引脚
#define BLDCMOTOR_TIM_CH1N_PIN                GPIO_PIN_13#define BLDCMOTOR_TIM_CH2N_PORT               GPIOH          // TIM8_CH2N的引脚
#define BLDCMOTOR_TIM_CH2N_PIN                GPIO_PIN_14#define BLDCMOTOR_TIM_CH3N_PORT               GPIOH          // TIM8_CH3N的引脚
#define BLDCMOTOR_TIM_CH3N_PIN                GPIO_PIN_15/* short down 控制 */
#define BLDCMOTOR_SHORTDOWN_RCC_CLK_ENABLE()  __HAL_RCC_GPIOH_CLK_ENABLE()
#define BLDCMOTOR_SHORTDOWN_PORT              GPIOH
#define BLDCMOTOR_SHORTDOWN_PIN               GPIO_PIN_9#define BLDCMOTOR_ENABLE()                   HAL_GPIO_WritePin(BLDCMOTOR_SHORTDOWN_PORT,BLDCMOTOR_SHORTDOWN_PIN,GPIO_PIN_SET)
#define BLDCMOTOR_DISABLE()                  HAL_GPIO_WritePin(BLDCMOTOR_SHORTDOWN_PORT,BLDCMOTOR_SHORTDOWN_PIN,GPIO_PIN_RESET)/** 触发源选择,TIM1作为从定时器,可以连接到TIM3,TIM4的TRGO,由主定时器触发com事件* TIM5   TIM_TS_ITR3* 选择TIM2的时候,触发源为TIR1,选择TIM4则触发源是TIM_TS_ITR2*/
#define BLDMOTOR_TIM_TS                       TIM_TS_ITR3 // 定时器触发源,ITR3  TIM5->TIM8// 定义定时器预分频,定时器实际时钟频率为:168MHz/(BLDCMOTOR_TIMx_PRESCALER+1)
#define BLDCMOTOR_TIM_PRESCALER            0  // 实际时钟频率为:168MHz// 定义定时器周期,当定时器开始计数到BLDCMOTOR_TIMx_PERIOD值是更新定时器并生成对应事件和中断
#define BLDCMOTOR_TIM_PERIOD               4200  // 定时器更新频率为:168MHz/(8400)=20KHz,即50us定时周期// 定时器通道1初始占空比,实际并没有意义,在代码中实时修改了
#define BLDCMOTOR_TIM_CH1_PULSE            0   // 初始占空比/* 扩展变量 ------------------------------------------------------------------*/
extern TIM_HandleTypeDef htimx_BLDCM;
extern MotorSta_Typedef Motor_State ; // 电机使能状态
extern MotorDir_Typedef Motor_Dir  ;  // 电机方向 /* 函数声明 ------------------------------------------------------------------*/
void BLDCMOTOR_TIMx_Init(void);
void BLDCMotor_Start(void);
void BLDCM_Inertia_brake(void);
void BLDCMotor_braking_LowerShort(void);
void BLDCMotor_unbraking_LS(void);void BLDCMotor_SetSpeed(float speed);void BLDCMotor_PhaseCtrl(int32_t HALLPhase );#endif	/* __BLDCMOTOR_TIM_H__ */

定时器8.c

#include "bldc/bsp_bldc.h" 
#include "hall/bsp_hall.h"/* 私有类型定义 --------------------------------------------------------------*//* 私有宏定义 ----------------------------------------------------------------*//* 私有变量 ------------------------------------------------------------------*/
TIM_HandleTypeDef htimx_BLDCM;/* 扩展变量 ------------------------------------------------------------------*/
MotorSta_Typedef Motor_State = MOTOR_DISABLE; // 电机使能状态
MotorDir_Typedef Motor_Dir   = MOTOR_DIR_CCW;  // 电机方向 ,顺时针
float PWM_Duty   = 0.25f;        // 25%占空比/* 私有函数原形 --------------------------------------------------------------*//* 函数体 --------------------------------------------------------------------*//*** 函数功能: 通用定时器硬件初始化配置* 输入参数: 无* 返 回 值: 无* 说    明: 无*/
void YS_TIM_Base_MspInit(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* 定时器通道功能引脚端口时钟使能 */BLDCMOTOR_TIM_GPIO_RCC_CLK_ENABLE();/* 定时器通道1功能引脚IO初始化 */GPIO_InitStruct.Pin       = BLDCMOTOR_TIM_CH1_PIN;GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;GPIO_InitStruct.Alternate = BLDCMOTOR_TIM_GPIO_AF;HAL_GPIO_Init(BLDCMOTOR_TIM_CH1_PORT, &GPIO_InitStruct);/* 定时器通道2功能引脚IO初始化 */GPIO_InitStruct.Pin       = BLDCMOTOR_TIM_CH2_PIN;GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;GPIO_InitStruct.Alternate = BLDCMOTOR_TIM_GPIO_AF;HAL_GPIO_Init(BLDCMOTOR_TIM_CH2_PORT, &GPIO_InitStruct);/* 定时器通道3功能引脚IO初始化 */GPIO_InitStruct.Pin       = BLDCMOTOR_TIM_CH3_PIN;GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;GPIO_InitStruct.Alternate = BLDCMOTOR_TIM_GPIO_AF;HAL_GPIO_Init(BLDCMOTOR_TIM_CH3_PORT, &GPIO_InitStruct);/* 定时器互补通道1功能引脚IO初始化 */GPIO_InitStruct.Pin       = BLDCMOTOR_TIM_CH1N_PIN;HAL_GPIO_Init(BLDCMOTOR_TIM_CH3N_PORT, &GPIO_InitStruct);/* 定时器互补通道1功能引脚IO初始化 */GPIO_InitStruct.Pin       = BLDCMOTOR_TIM_CH2N_PIN;HAL_GPIO_Init(BLDCMOTOR_TIM_CH3N_PORT, &GPIO_InitStruct);/* 定时器互补通道1功能引脚IO初始化 */GPIO_InitStruct.Pin       = BLDCMOTOR_TIM_CH3N_PIN;HAL_GPIO_Init(BLDCMOTOR_TIM_CH3N_PORT, &GPIO_InitStruct);/* shortdown控制引脚初始化 */BLDCMOTOR_SHORTDOWN_RCC_CLK_ENABLE();BLDCMOTOR_ENABLE(); // 将引脚设置为有效电平,是PWM能控制电机GPIO_InitStruct.Pin       = BLDCMOTOR_SHORTDOWN_PIN;GPIO_InitStruct.Mode      = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;GPIO_InitStruct.Alternate = 0;GPIO_InitStruct.Pull      = GPIO_NOPULL;HAL_GPIO_Init(BLDCMOTOR_SHORTDOWN_PORT, &GPIO_InitStruct);BLDCMOTOR_ENABLE(); // 将引脚设置为有效电平,使PWM能控制电机
}/*** 函数功能: 高级定时器初始化* 输入参数: 无* 返 回 值: 无* 说    明: 无*/
void BLDCMOTOR_TIMx_Init(void)
{TIM_OC_InitTypeDef sConfigOC = {0};/* 通用定时器外设时钟使能 */BLDCMOTOR_TIM_RCC_CLK_ENABLE();/* 初始化板载设备,定时器通道引脚,使能时钟 */ YS_TIM_Base_MspInit();/* 配置定时器基础计数功能 */htimx_BLDCM.Instance           = BLDCMOTOR_TIMx;          // 定时器TIMhtimx_BLDCM.Init.Prescaler     = BLDCMOTOR_TIM_PRESCALER; // PSC设置预分频值htimx_BLDCM.Init.CounterMode   = TIM_COUNTERMODE_CENTERALIGNED1;// 中心对齐模式htimx_BLDCM.Init.Period        = BLDCMOTOR_TIM_PERIOD;    // 计数周期htimx_BLDCM.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;HAL_TIM_Base_Init(&htimx_BLDCM);/* 配置比较输出为PWM1模式 */sConfigOC.OCMode       = TIM_OCMODE_PWM1;        // PWM1 模式 sConfigOC.Pulse        = BLDCMOTOR_TIM_CH1_PULSE;// CHx的输出脉冲宽度sConfigOC.OCPolarity   = TIM_OCPOLARITY_HIGH;    // 输出极性sConfigOC.OCNPolarity  = TIM_OCNPOLARITY_HIGH;   // 互补通道输出极性sConfigOC.OCIdleState  = TIM_OCIDLESTATE_RESET;  // 空闲状态极性sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; // 互补空闲状态极性sConfigOC.OCFastMode   = TIM_OCFAST_DISABLE;     // 快速输出模式,禁止  /* 这里使用的是HAL_TIM_OC_ConfigChannel,而不是HAL_TIM_PWM_ConfigChannel* 如果使用HAL_TIM_PWM_ConfigChannel会使能比较器的预装载功能,也就是修改比较值* 之后不是立即生效,这将会造成换相之后的第一个脉冲脉宽不固定* 这两个函数实际的配置几乎完全一样,只是HAL_TIM_OC_ConfigChannel不会去配置* OCFastmode和比较器的预装载使能.* */HAL_TIM_OC_ConfigChannel(&htimx_BLDCM, &sConfigOC, BLDCMOTOR_TIM_CH1);/* CH2 CH2N*/HAL_TIM_OC_ConfigChannel(&htimx_BLDCM, &sConfigOC, BLDCMOTOR_TIM_CH2);/* CH3 CH3N */HAL_TIM_OC_ConfigChannel(&htimx_BLDCM, &sConfigOC, BLDCMOTOR_TIM_CH3);/* 配置COM事件(commutation)* 由主定时器的TRGO触发TIM1的COM事件* 当触发COM事件的时候将会统一更新定时器通道输出的使能控制位,避免由于程序执行* 的延迟导致换相时刻下桥臂出现负窄脉冲.*///HAL_TIMEx_ConfigCommutationEvent(&htimx_BLDCM, BLDMOTOR_TIM_TS, TIM_COMMUTATION_TRGI);
}/*** 函数功能: 无刷电机相位控制* 输入参数: @HALLPhase 霍尔信号相位* 返 回 值: 无* 说    明: 控制定时器输出PWM波形换相,定义定时器输出通道CH1为A相(U) *           CH2为B相(V),CH3为C相(W),配置下一次霍尔换相的时候的波形*/
void BLDCMotor_PhaseCtrl(int32_t HALLPhase )
{#ifndef USE_PMSMMOTOR  /* 顺时针的霍尔顺序与逆时针的顺序是关于7互补,所以可以使用7减逆时针的顺序得到* 顺时针的霍尔顺序,这里使用异或代替减法*/if(MOTOR_DIR_CW == Motor_Dir)HALLPhase = 0x07 ^ HALLPhase;// 将低三位异或 111b ^ 010b -> 101b
#else/* PMSM的旋转顺序跟BLDC刚好是相反的,设定为MOTOR_DIR_CCW的时候BLDC是逆时针旋转* PMSM是顺时针旋转,如果需要设定为MOTOR_DIR_CCW的时候PMSM为逆时针旋转,则使用* 下面语句代替上面的语句*/if(MOTOR_DIR_CCW == Motor_Dir)HALLPhase = 0x07 ^ HALLPhase;// 将低三位异或 111b ^ 010b -> 101b
#endifswitch(HALLPhase){/* 定义电机的U(A),V(B),W(C)三相分别对应是CH1,CH2,CH3;*  A+,A-分别表示CH1控制的上,下桥臂导通*/case 5: //B+  A-{/*  Channe3 configuration */ HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);/*  Channe2 configuration  */__HAL_TIM_SET_COMPARE(&htimx_BLDCM, BLDCMOTOR_TIM_CH2,BLDCMOTOR_TIM_PERIOD * PWM_Duty);HAL_TIM_PWM_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);  /*  Channe1 configuration */__HAL_TIM_SET_COMPARE(&htimx_BLDCM, BLDCMOTOR_TIM_CH1,BLDCMOTOR_TIM_PERIOD +1);HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);HAL_TIMEx_PWMN_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);}break;case 4:// C+ A-{/*  Channe2 configuration */ HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);/*  Channe3 configuration  */__HAL_TIM_SET_COMPARE(&htimx_BLDCM, BLDCMOTOR_TIM_CH3,BLDCMOTOR_TIM_PERIOD * PWM_Duty);HAL_TIM_PWM_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);  /*  Channe1 configuration  */__HAL_TIM_SET_COMPARE(&htimx_BLDCM,BLDCMOTOR_TIM_CH1,BLDCMOTOR_TIM_PERIOD +1);HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);HAL_TIMEx_PWMN_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);}break;case 6://C+ B-{/*  Channe1 configuration  */ HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);/*  Channe3 configuration */__HAL_TIM_SET_COMPARE(&htimx_BLDCM,BLDCMOTOR_TIM_CH3,BLDCMOTOR_TIM_PERIOD * PWM_Duty);HAL_TIM_PWM_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);  /*  Channe2 configuration  */__HAL_TIM_SET_COMPARE(&htimx_BLDCM,BLDCMOTOR_TIM_CH2,BLDCMOTOR_TIM_PERIOD +1);HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);HAL_TIMEx_PWMN_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);}break;case 2: // A+ B-{/*  Channe3 configuration */       HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);/*  Channe1 configuration */__HAL_TIM_SET_COMPARE(&htimx_BLDCM,BLDCMOTOR_TIM_CH1,BLDCMOTOR_TIM_PERIOD * PWM_Duty);HAL_TIM_PWM_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);  /*  Channe2 configuration */__HAL_TIM_SET_COMPARE(&htimx_BLDCM,BLDCMOTOR_TIM_CH2,BLDCMOTOR_TIM_PERIOD +1);HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);HAL_TIMEx_PWMN_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);}break;case 3:// A+ C-{/*  Channe2 configuration */ HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);/*  Channe1 configuration */__HAL_TIM_SET_COMPARE(&htimx_BLDCM,BLDCMOTOR_TIM_CH1,BLDCMOTOR_TIM_PERIOD * PWM_Duty);HAL_TIM_PWM_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);  /*  Channe3 configuration */__HAL_TIM_SET_COMPARE(&htimx_BLDCM,BLDCMOTOR_TIM_CH3,BLDCMOTOR_TIM_PERIOD +1);    HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);HAL_TIMEx_PWMN_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);}break;case 1: // B+ C-{/*  Channe1 configuration */ HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);/*  Channe2 configuration */__HAL_TIM_SET_COMPARE(&htimx_BLDCM,BLDCMOTOR_TIM_CH2, BLDCMOTOR_TIM_PERIOD * PWM_Duty);HAL_TIM_PWM_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);  /*  Channe3 configuration */__HAL_TIM_SET_COMPARE(&htimx_BLDCM, BLDCMOTOR_TIM_CH3, BLDCMOTOR_TIM_PERIOD +1);HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);HAL_TIMEx_PWMN_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);}break;}
}
/*** 函数功能: 启动电机* 输入参数: 无* 返 回 值: 无* 说    明: 启动无刷电机*/
void  BLDCMotor_Start()
{int32_t hallPhase = 0;BLDCMOTOR_ENABLE();// 使能允许输出PWM/* 下桥臂导通,给自举电容充电 */BLDCMotor_braking_LowerShort();HAL_Delay(9); //充电时间, 大概值 不需要准确/* 启动HALL传感器接口中断 */__HAL_TIM_CLEAR_FLAG(&htimx_hall,TIM_FLAG_CC1);HAL_TIMEx_HallSensor_Start_IT(&htimx_hall);hallPhase = HALL_GetPhase(); // 获取霍尔信号相位/* 配置当前霍尔信号对应的PWM相位 */BLDCMotor_PhaseCtrl(hallPhase);  // 配置输出PWMHAL_TIM_GenerateEvent(&htimx_BLDCM, TIM_EVENTSOURCE_COM); // 软件生成COM事件__HAL_TIM_CLEAR_FLAG(&htimx_BLDCM, TIM_FLAG_COM);Motor_State = MOTOR_ENABLE;
}
/*** 函数功能: 惯性刹车* 输入参数: 无* 返 回 值: 无* 说    明: 自由停机,就是直接切断输出,依靠惯性是电机停下来*/
void BLDCM_Inertia_brake()
{BLDCMOTOR_DISABLE(); // 使用驱动芯片的shutdown引脚切断输出Motor_State = MOTOR_DISABLE;
}
/*** 函数功能: 刹车制动* 输入参数: 无* 返 回 值: 无* 说    明: 使用下桥臂使电机短路,属于能耗制动的一种方式*/
void BLDCMotor_braking_LowerShort()
{/* 先禁止中断,防止刹车过程触发COM事件重新输出 */HAL_TIMEx_HallSensor_Stop_IT(&htimx_hall);/*** 直接关闭MOE,使下桥臂输出高电平* 直接控制MOS管下桥臂导通,上桥臂关闭*//* 下桥臂导通 */__HAL_TIM_SET_COMPARE(&htimx_BLDCM, BLDCMOTOR_TIM_CH1, 0);// 如果需要导通上桥臂__HAL_TIM_SET_COMPARE(&htimx_BLDCM, BLDCMOTOR_TIM_CH2, 0);// 占空比设置为100%__HAL_TIM_SET_COMPARE(&htimx_BLDCM, BLDCMOTOR_TIM_CH3, 0);// 即可HAL_TIM_PWM_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);HAL_TIM_PWM_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);HAL_TIM_PWM_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);HAL_TIMEx_PWMN_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);HAL_TIMEx_PWMN_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);HAL_TIMEx_PWMN_Start(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);HAL_TIM_GenerateEvent(&htimx_BLDCM, TIM_EVENTSOURCE_COM);  __HAL_TIM_CLEAR_FLAG(&htimx_BLDCM, TIM_FLAG_COM);Motor_State = MOTOR_DISABLE;
}
/*** 函数功能: 取消刹车制动* 输入参数: 无* 返 回 值: 无* 说    明: 刹车的时候使用下桥臂导通的方式,确认停下来之后取消刹车制动模式*/
void BLDCMotor_unbraking_LS()
{/* 关闭输出 */HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);HAL_TIM_PWM_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH1);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH2);HAL_TIMEx_PWMN_Stop(&htimx_BLDCM, BLDCMOTOR_TIM_CH3);//HAL_TIM_GenerateEvent(&htimx_BLDCM, TIM_EVENTSOURCE_COM);  
}
/*** 函数功能: 设置电机转速* 输入参数: @speed PWM的占空比* 返 回 值: 无* 说    明: 无*/
void BLDCMotor_SetSpeed(float speed)
{if(speed > 1.0f){speed = 1.0f;}else if(speed < 0.0f){speed = 0.0f;}PWM_Duty = speed;
}

main.c

#include "main.h"
#include "stm32f4xx_hal.h"
#include "bldc/bsp_bldc.h"
#include "hall/bsp_hall.h"
#include "key/bsp_key.h"/* 私有类型定义 --------------------------------------------------------------*/
float MotorSpeed = 0.0f ;// 电机转速,这里是占空比,/* 私有宏定义 ----------------------------------------------------------------*//* 私有变量 ------------------------------------------------------------------*//* 扩展变量 ------------------------------------------------------------------*/
extern MotorSta_Typedef Motor_State; // 电机使能状态
extern MotorDir_Typedef Motor_Dir;  // 电机方向 ,顺时针
extern float PWM_Duty;        // 25%占空比/* 私有函数原形 --------------------------------------------------------------*//* 函数体 --------------------------------------------------------------------*//*** 函数功能: 系统时钟配置* 输入参数: 无* 返 回 值: 无* 说    明: 无*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};__HAL_RCC_PWR_CLK_ENABLE();                                     // 使能PWR时钟__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);  // 设置调压器输出电压级别1RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;      // 外部晶振,8MHzRCC_OscInitStruct.HSEState       = RCC_HSE_ON;                  // 打开HSERCC_OscInitStruct.PLL.PLLState   = RCC_PLL_ON;                  // 打开PLLRCC_OscInitStruct.PLL.PLLSource  = RCC_PLLSOURCE_HSE;           // PLL时钟源选择HSERCC_OscInitStruct.PLL.PLLM = 8;                                 // 8分频MHzRCC_OscInitStruct.PLL.PLLN = 336;                               // 336倍频RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;                     // 2分频,得到168MHz主时钟RCC_OscInitStruct.PLL.PLLQ = 7;                                 // USB/SDIO/随机数产生器等的主PLL分频系数HAL_RCC_OscConfig(&RCC_OscInitStruct);RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;     // 系统时钟:168MHzRCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;             // AHB时钟: 168MHzRCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;               // APB1时钟:42MHzRCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;               // APB2时钟:84MHzHAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);HAL_RCC_EnableCSS();                                            // 使能CSS功能,优先使用外部晶振,内部时钟源为备用// HAL_RCC_GetHCLKFreq()/1000    1ms中断一次// HAL_RCC_GetHCLKFreq()/100000	 10us中断一次// HAL_RCC_GetHCLKFreq()/1000000 1us中断一次HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);               // 配置并启动系统滴答定时器/* 系统滴答定时器时钟源 */HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);/* 系统滴答定时器中断优先级配置 */HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}/*** 函数功能: 主函数.* 输入参数: 无* 返 回 值: 无* 说    明: 无*/
int main(void)
{/* 复位所有外设,初始化Flash接口和系统滴答定时器 */HAL_Init();/* 配置系统时钟 */SystemClock_Config();HAL_Delay(100);/* 按键初始化 */KEY_GPIO_Init();/* 初始化定时器各通道输出 */BLDCMOTOR_TIMx_Init();/* 霍尔传感器初始化 */HALLSensor_TIMx_Init();__HAL_DBGMCU_FREEZE_TIM8(); // debug的时候停止定时器时钟__HAL_DBGMCU_FREEZE_TIM5(); // debug的时候停止定时器时钟Motor_Dir = MOTOR_DIR_CW;/* 无限循环 */while (1){/* 启动电机 */if(KEY1_StateRead() == KEY_DOWN){MotorSpeed = 0.15f;//15%BLDCMotor_SetSpeed( MotorSpeed );BLDCMotor_Start();}/* 改变方向 */if(KEY2_StateRead() == KEY_DOWN){/* 必须先停止,然后再重新启动 */if(  Motor_State == MOTOR_DISABLE){if(Motor_Dir == MOTOR_DIR_CW){ Motor_Dir = MOTOR_DIR_CCW; }else{ Motor_Dir = MOTOR_DIR_CW; }}}/* 加速 */if(KEY3_StateRead() == KEY_DOWN){if( MotorSpeed == 0.0f ){MotorSpeed += 0.15f; //  15% 启动BLDCMotor_SetSpeed( MotorSpeed );BLDCMotor_Start();}else{MotorSpeed += 0.05f; // 速度递增 5%if( MotorSpeed >= 1.0f ){MotorSpeed = 1.0f;}BLDCMotor_SetSpeed( MotorSpeed );}      }/* 减速 */if(KEY4_StateRead() == KEY_DOWN){MotorSpeed -= 0.05f; // 速度递减 5%if( MotorSpeed <= 0.0f ){MotorSpeed = 0.0f;}BLDCMotor_SetSpeed( MotorSpeed );}/* 刹车 */if(KEY5_StateRead() == KEY_DOWN){BLDCMotor_braking_LowerShort();HAL_Delay(100);// 等100ms, 电机停下来之后才取消刹车控制BLDCMotor_unbraking_LS();}}
}

8.总结

按下KEY1:电机开始旋转

按下KEY2:改变方向(只能在电机停止的时候按KEY2才有效,禁止在转动过程中突变方向)

按下KEY3:加速,速度递增5%

按下KEY4:减速, 速度递减5%

按下KEY5:IR2110S的SD引脚使能无输出。电机停止。

按键程序太简单就不粘贴了。

直流无刷的实物图

对于六步换相来说。每次转60度就换一次相精度是比较差的。所以一般很少有位置环的控制。并且每次换相前。都要先断开上一次的相。所以电流会不连续。就会导致力矩的不平稳。这也是六步换相所不足的地方。但FOC算法就很好的克服了。

 

 

 

 

 

 

 

 

 

 


http://www.ppmy.cn/news/717151.html

相关文章

控制BLDC资料汇总

过去曾由在其它地方发过关于BLDC控制资料的帖子,看起来反映不错,帖子算是自己的原创,整理一下,这里也一并收录到自己的博客中。 最近打算将自己在其它地方发过的一些帖子收集整理一下,算是留个备份,省得自己以后找一些资料也麻烦。这个帖子得原来位置在马达控制应用笔记和…

[node.js总结]

fs模块 //写入文件 //异步写入文件 const fsrequire("fs"); fs.writeFile("1.txt","温故而知新",err>{ if(err){ console.log("写入失败"); return; } console.log("写入成功"); }); //同步写入 fs.writeFil…

java学生宿舍管理系统

学生宿舍管理系统 java swing宿舍管理系统 java学生宿舍管理系统 技术&#xff1a; 基于javamysql学生宿舍管理系统的设计与实现 运行环境&#xff1a; JAVA版本&#xff1a;JDK1.8 IDE类型&#xff1a;IDEA、Eclipse都可运行 数据库类型&#xff1a;MySql&#xff08;8.x…

新生儿喝奶后不要马上放回床上睡觉,为宝宝健康着想,先做1件事

看到一个问题&#xff0c;题主问&#xff0c;新生儿喝完奶能马上放回床上睡觉吗&#xff1f;可以吗&#xff1f;也许每个人都认为照顾新生儿是一件非常简单的事情&#xff0c;因为新生儿几乎整天都在睡觉。当他们饿的时候&#xff0c;他们会让他们的宝宝吃牛奶。他们吃饱了就要…

美德乐吸奶器怎么样-

美德乐吸奶器中国官网地址为&#xff1a;http://medela.wang&#xff0c;旨在为中国妈妈提供关于美德乐吸奶器产品介绍&#xff0c;美德乐吸奶器售后服务。经过几十年躬耕和发展。美德乐已经赢得了中国妈妈的信赖&#xff0c;下面让我们来看一下妈妈们对美德乐产品的评价。 作为…

Mybatis怎么实现批量插入的

测试表USER_INFO结构&#xff1a; 目前表中是无数据的。 测试xml&#xff1a; <insert id"insertList">insert into user_info(id,user_name,age)values<foreach collection"list" item"item" separator",">(#{item.id},#…

MyBatis使用Foreach批量插入数据

## MySQL批量插入(效率较低) <insert id"saveBatch"><foreach collection"list" item"item" separator";">INSERT INTO student(name, age, id)VALUES(#{item.name},#{item.age},#{item.id})</foreach> </insert…

hibernate存取图片示例

一般网站在处理用户上传图片时通常采用两种策略&#xff1a;一是直接把图片存入数据库中的Blob字段&#xff1b;二是数据库中只存储图片的在服务器上的路径信息 &#xff0c;图片存放在分门别类的文件中&#xff0c;使用的时候从数据库读取路径信息到页面img元素即可&#xff0…