目录
无刷直流电机
KV值
工作原理
直流无刷电机驱动设计与分析
霍尔传感器模式
野火无刷电机驱动板
直流无刷电机控制实现
速度控制原理
定时器8配置
定时器TIM5配置
定时器相关函数
电机初始化及调用函数
测试环节
直流无刷驱动板温度电压采集
硬件接线
ADC配置
ADC采集函数
测试环节
直流无刷驱动板温度、电源电压、三相电流读取
ADC配置
ADC采集函数
测试环节
本篇总结:
无刷直流电机(BLDC)由电动机和驱动器组成,无碳刷和无换向器(电子换向器去替代,如霍尔传感器)。和有刷直流电机相反,定子为线圈,转子为永磁铁。
实际转速 = KV值 * 工作电压。无刷直流电机的转速和电压成正比。
获取转子位置信息的方法:使用传感器(霍尔传感器成本低、光栅编码器精度高、旋转变压器可靠性高)、不使用传感器(直接使用反电动势来估计转子位置)。
霍尔效应传感器是测量转子上永磁体产生的磁场,通常摆放在离转子很近的地方。一般来说,我们需要三个霍尔传感器。
无刷直流电机(Brushless Dirent Current Motor,简称BLDC)由电动机主体和驱动器组成,无电刷和无换向器,是除了有刷电机外用得最多的一种电机。
无刷直流电机不使用机械的电刷装置,采用方波自控式永磁同步电机。与有刷电机相比,无刷直流电机将转子和定子交换(即使用电枢绕组作为定子,使用钕铁硼的永磁材料作为转子),以霍尔传感器取代碳刷换向器,性能上比一般的直流电机有很大优势。广泛应用于航模、高速车模和船模。
优点:高效率、低功耗、低噪音、超长寿命、高可靠性、可伺服控制、无极变频调速等。
缺点:贵、比有刷直流电机开发复杂。转矩小,功率不能做太大。
普通的有刷电机旋转的是绕组,而无刷电机无论是外转子结构还是内转子结构,旋转的都是磁铁。
无刷直流电机(BLDC)和永磁同步电机(PMSM)区别在于:反电动势为梯形波的是BLDC,反电动势为正弦波的是PMSM。
有刷直流电机是根据额定工作电压来标注额定转速的,无刷电机引入了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°运行方式下进行。
高级定时器,属于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();
}
通用定时器,属于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采样电阻,并将另一头贴于电机表面。
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();
}
/**
* @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;
}
}
}
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();
}
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;
}
}
}