电机应用-无刷直流电机

2024-01-14 13:50

本文主要是介绍电机应用-无刷直流电机,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

无刷直流电机

KV值

工作原理

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

霍尔传感器模式

野火无刷电机驱动板

直流无刷电机控制实现

速度控制原理

定时器8配置

定时器TIM5配置

定时器相关函数

电机初始化及调用函数

测试环节

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

硬件接线

ADC配置

ADC采集函数

测试环节

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

ADC配置

ADC采集函数

测试环节


无刷直流电机

本篇总结:

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

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

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

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

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

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

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

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

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

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

KV值

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

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

工作原理

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

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

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°电度角。

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

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

野火无刷电机驱动板

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

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

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

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

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

从真值表可以看出,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(改变难度大)、调整电枢绕组的供电电压、调整励磁磁通(改变难度大)。

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

无刷直流电机也可以使用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配置

高级定时器,属于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-PH13GPIO_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-PH14GPIO_InitStructure.Pin = GPIO_PIN_14;HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);// OCNPWM3-PH15GPIO_InitStructure.Pin = GPIO_PIN_15;HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);// OCPWM1-PI5GPIO_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-PI6GPIO_InitStructure.Pin 			= GPIO_PIN_6;GPIO_InitStructure.Alternate 	= MOTOR_OCPWM2_AF;HAL_GPIO_Init(GPIOI, &GPIO_InitStructure);// OCPWM3-PI7GPIO_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;                         // 默认必须要初始为0TIM_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 输出 PWMHAL_TIM_PWM_ConfigChannel(&htimx_bldcm, &TIM_OCInitStructure, TIM_CHANNEL_2);  // 初始化通道 2 输出 PWMHAL_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-UGPIO_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-VGPIO_InitStruct.Pin = GPIO_PIN_11;HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);// PH12-WGPIO_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;}#elsestate = (GPIOH->IDR >> 10) & 7;    // 读 3 个霍尔传感器的状态
#endifreturn 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 0if (get_bldcm_direction() == MOTOR_FWD){step = 7 - step;        // 根据换向表的规律可知: REV = 7 - FWD;}#endifif (get_bldcm_direction() == MOTOR_FWD){switch (step){case 1:    /* U+ W- */__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_2, 0);                            // 通道 2 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_1, 0);                            // 通道 1 配置为 0HAL_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 配置为 0HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);    // 关闭下桥臂__HAL_TIM_SET_COMPARE(&htimx_bldcm, TIM_CHANNEL_3, 0);                            // 通道 3 配置为 0HAL_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)
{// 有一次在产生更新中断前霍尔传感器没有捕获到值,说明电机堵转了并至少堵转了100msif (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采样电阻,并将另一头贴于电机表面。

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);// UADC_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);// VADC_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);// WADC_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是采样电阻,单位mAreturn ((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是采样电阻,单位mAreturn ((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是采样电阻,单位mAreturn ((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;}}
}

这篇关于电机应用-无刷直流电机的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/605391

相关文章

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

Go信号处理如何优雅地关闭你的应用

《Go信号处理如何优雅地关闭你的应用》Go中的优雅关闭机制使得在应用程序接收到终止信号时,能够进行平滑的资源清理,通过使用context来管理goroutine的生命周期,结合signal... 目录1. 什么是信号处理?2. 如何优雅地关闭 Go 应用?3. 代码实现3.1 基本的信号捕获和优雅关闭3.2

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6

python中的与时间相关的模块应用场景分析

《python中的与时间相关的模块应用场景分析》本文介绍了Python中与时间相关的几个重要模块:`time`、`datetime`、`calendar`、`timeit`、`pytz`和`dateu... 目录1. time 模块2. datetime 模块3. calendar 模块4. timeit

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个