当前位置: 首页 > news >正文

电机应用-直流有刷电机

目录

直流有刷电机

工作原理

直流有刷减速电机的重要参数

电路原理与分析

驱动芯片分析

L298N驱动芯片

直流有刷减速电机控制实现

控制速度原理

硬件设计

L298N

野火直流有刷电机驱动板-MOS管搭建板

软件设计1:两个直流有刷减速电机按键控制

开发设计

定时器配置

测试环节

直流有刷减速电机驱动板电流电压采集

ADC配置

测试环节

直流有刷减速电机驱动板限电流、过电压、欠电压保护

ADC配置

测试环节


直流有刷电机

直流有刷电机具有结构简单、易于控制、成本低等特点。

基本的直流有刷电机在电源和电机间只需要两根电缆,可以节省配线和连接器所需的空间,并降低电缆和连接器的成本。

还可以使用MOSFET/IGBT开关对直流有刷电机进行控制,给电机提供足够好的性能,并且整个电机控制系统也会比较便宜。

直流有刷电机转速快、扭矩小,在某些应用中可能无法满足要求。直流有刷减速电机可以降低转速并提高力矩

工作原理

直流有刷电机在其电枢(转子)上绕有大量的线圈,所产生强大的磁场与外部磁场相互作用产生旋转运动。

电源通过电刷向线框供电,线框就有电流通过,在线框两侧放一对磁极NS形成磁场,磁力线由N到S。

线框有电流时,线框就会收到磁场的作用力,按照左手定则,红色线框受到力F1,蓝色线框受到力F2,F1和F2力的方向相反,使得线框会转动。

当线框转到90°时,换向器改变了线框电流的方向,产生的安培力方向不变,于是线框会连续旋转。

直流有刷减速电机的重要参数

空载转速:正常工作电压下电机不带任何负载的转速(单位为r/min,即转/分)。空载转速由于没有反向力矩,所以输出功率和堵转情况不一样,该参数只是提供一个电机在规定电压下最大转速的参考。

空载电流:正常工作电压下电机不带任何负载的工作电流(单位为mA)。电机越好,该值越小。

负载转速:正常工作电压下电机带负载的转速。

负载力矩:正常工作电压下电机带负载的力矩(单位为N·m,即牛·米)。

负载电流:电机拖动负载时实际检测到的定子电流数值。

堵转力矩:在电机受反向外力使其停止转动时的力矩。如果电机堵转现象经常出现,则会损坏电机或烧坏电机驱动芯片。所以选电机时,除了考虑转速还要考虑堵转。堵转时间一长,电机温度上升的很快,这个值也会下降的很厉害。

堵转电流:在电机受反向外力使其停止转动时的电流。此时电流非常大,稍微就可能会烧毁电机,在实际使用时应尽量避免。

减速比:没有减速齿轮组时转速和有减速齿轮组时转速之比。

功率:一般为额定功率(单位W,即瓦),即在额定电压下能够长期正常运转的最大功率,也是指电动机在制造厂所规定的额定情况下运行时其输出端的机械功率。

电路原理与分析

当拥有一个直流电机和一节电池,只要把电机的两根电源线和电池的电源线连接在一起时,电机就可以正常旋转。反接时电机就反向旋转。但实际单片机一般是用H桥电路来驱动电机。

如下图:H桥电机驱动电路包括4个三极管和一个电机。要想电机运转就必须导通对角线上的一对三极管,所以电流可能会从左到右或从右到左流过电机,从而控制电机转向。

当同一侧的Q1和Q2导通时,电流将从电源经过Q1和Q2,然后直接流到电源负极,这个回路中除了三极管外没有经过负载(电机),这时电流可能会达到最大值而造成烧毁三极管。当同一侧的Q3和Q4导通时也一样。于是可以改进电路原理图。

改进后的电路增加了4个与门和2个非门,以及Q1和Q3换成了NPN三极管。这样的组合可以实现一个信号控制两个同一侧的三极管,并且可以保证在同一侧的两个三极管不会同时导通,即在同一时刻只有一个三极管是导通的。

ENABLE脚接入高电平,IN1脚接入高电平,AND1_2脚是低电平,所以AND1_3脚是低电平,所以Q1截止。而AND2_1脚和AND2_2脚都是高电平,所以ADN2_3脚是高电平,所以Q2导通。然后IN2脚接入低电平,AND4_2脚是高电平,且AND4_1是高电平,所以AND4_3脚是高电平,所以Q3导通。而AND3_1脚是高电平,AND3_2脚是低电平,所以AND3_3脚是低电平,所以Q4截止。故电机逆时针转动。

ENABLE脚接入高电平,IN1脚接入低电平,AND1_2脚是高电平,所以AND1_3脚是高电平,所以Q1导通。而AND2_1脚是高电平,AND2_2脚是低电平,所以ADN2_3脚是低电平,所以Q2截止。然后IN2脚接入高电平,AND4_2脚是低电平,AND4_1是高电平,所以AND4_3脚是低电平,所以Q3截止。而AND3_1脚是高电平,AND3_2脚是高电平,所以AND3_3脚是高电平,所以Q4导通。故电机顺时针转动。

当ENABLE脚接入高电平,IN1和IN2都接入同一电平(高电平或低电平)都只会同时导通上面或下面的两个三极管,不会出现同一侧的三极管同时导通的情况。此时电机停止,当然,ENABLE脚接入低电平时不管IN1、IN2怎样电平,都停止电机。

驱动芯片分析

通常在驱动电机时会选择集成H桥的IC,因为H桥使用分立元件搭建比较麻烦,增加了硬件设计难度。

当我们选择集成IC时,需要考虑集成IC是否能满足电机的驱动电压要求,是否能承受电机工作时的电流等情况。

如果集成IC无法满足功率要求时还是需要自己使用MOS管、三极管等元件搭建H桥电路,这样的分立元件搭建的H桥一般驱动能力也会比集成IC要高。

L298N驱动芯片

L298N是ST公司的产品,内部包含4通道逻辑驱动电路,是一种二相和四相电机的专门驱动芯片,即内含两个H桥的高电压大电流双桥式驱动器,接收标准的TTL逻辑电平信号,可驱动4.5V~46V、2A以下的电机,电流峰值输出可达3A。

原理和刚刚的电路原理分析一致。

直流有刷减速电机控制实现

控制速度原理

PWM通过一定的频率来改变通电和断电的时间,从而控制电路输出功率,在电机的控制周期中,通电时间决定了它的转速。

占空比 = 通电时间 / (通电时间+断电时间),即高电平占整个周期的百分比。

假如T1为高电平时间,T2为低电平时间,T为周期,D为占空比,则D = T1 / T。设电机速度为V,最大速度为Vmax,则V = Vmax * D。当D的大小改变时,速度V也会改变,所以只要改变占空比就能达到控制电机的速度。

硬件设计

主控有刷电机接口如上图,有刷电机接口和无刷电机接口都使用同一个接口。本实验只用到了TIM1_CH1和TIM1_CH2,即PA8和PA9来输出PWM信号控制电机,需注意主控板需要和电机驱动板共地。

L298N

驱动板可以支持12~46V的宽电压供电,并且带输入电压转5V的电压芯片,所以驱动板只需一个电源输入,具体需要多大电压需要根据电机来选择。

ENABLEA和ENABLEB都是使能输入脚,ENABLEA用于控制A桥,ENABLEB用于控制B桥,可以接到单片机的引脚进行电平控制,也可以直接使用跳帽接入5V。

INPUT1和INPUT2是A桥的控制信号,INPUT3和INPUT4是B桥的控制信号,可以接PWM控制电机。

OUTPUT1和OUTPUT2是A桥的输出信号,OUTPUT3和OUTPUT4是B桥的输出信号。

两路电机接口的8个二极管用于防止电机的反电动势损坏L298N。

当E点反电动势为正,超过电源+0.7V时,上端二极管导通,因此输出线就被限位在电源电压+0.7V上,不会超过这个数值。

当E点反电动势为负,低于电源-0.7V时,下端二极管导通,因此输出线就被限位在电源电压-0.7V上,不会低于这个数值。

野火直流有刷电机驱动板-MOS管搭建板

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

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

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

下图是使用MOS管搭建的H桥电路,使用两个EG2104驱动四个MOS管。

EG2104S主要功能有逻辑信号输入处理、死区时间控制、电平转换功能、悬浮自举电源结构和上下桥图腾柱式输出。逻辑信号输入端高电平阈值为2.5V以上,低电平阈值为1.0V以下,要求逻辑信号的输出电流小,可以使MCU输出逻辑信号直接连接到EG2104S的输入通道上。EG2104S芯片有一个SHUTDOWN引脚,逻辑输入控制信号低电平有效,强行控制下功率管LO、上功率管HO输出低电平,这样可以直接使用这个引脚做软件控制电机的旋转和停止,还可以实现硬件的限流和保护。

EG2104S内部集成了死区时间控制电路,死区时间波形如下图,其中死区时间DT的典型值为640ns。EG2104S采用自举悬浮驱动电源结构大大简化了驱动电源设计,只用一路电源电压VCC即可完成高端N沟道MOS管和低端N沟道MOS管两个功率开关器件的驱动,给实际应用带来了极大的方便。EG2104S自举电路结构也如下图。RG2104S可以使用外接一个自举二极管和一个自举电容自动完成自举升压功能,假如在下管开通、上管关断期间VC自举电容已充到足够的电压(Vc=VCC),当HO输出高电平时上管开通、下管关断时,VC自举电容上的电压将等效一个电压源作为内部驱动器VB和VS的电源,完成高端N沟道MOS管的驱动。

软件设计1:两个直流有刷减速电机按键控制

以STM32F4为例。

开发设计

按键1:电机1速度加(总10个级别)。

按键2:电机1速度减(总10个级别)。

按键3:电机2速度加(总10个级别)。

按键4:电机2速度减(总10个级别)。

按键5:电机1、电机2同向且都转化方向。

两电机开始状态为停止。

定时器配置

TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim8;void MX_TIM1_Init(void)
{TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};TIM_OC_InitTypeDef sConfigOC = {0};TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};htim1.Instance 					= TIM1;htim1.Init.Prescaler 			= 1;htim1.Init.CounterMode 			= TIM_COUNTERMODE_UP;htim1.Init.Period 				= 5599;htim1.Init.ClockDivision 		= TIM_CLOCKDIVISION_DIV1;htim1.Init.RepetitionCounter 	= 0;htim1.Init.AutoReloadPreload 	= TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim1) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK){Error_Handler();}if (HAL_TIM_PWM_Init(&htim1) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger 	= TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode 		= TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK){Error_Handler();}sConfigOC.OCMode 		= TIM_OCMODE_PWM1;sConfigOC.Pulse 		= 0;sConfigOC.OCPolarity 	= TIM_OCPOLARITY_HIGH;sConfigOC.OCNPolarity 	= TIM_OCNPOLARITY_HIGH;sConfigOC.OCFastMode 	= TIM_OCFAST_DISABLE;sConfigOC.OCIdleState 	= TIM_OCIDLESTATE_SET;sConfigOC.OCNIdleState 	= TIM_OCNIDLESTATE_RESET;if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)	// 配置PWM通道{Error_Handler();}if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)	// 配置PWM通道{Error_Handler();}sBreakDeadTimeConfig.OffStateRunMode 	= TIM_OSSR_DISABLE;sBreakDeadTimeConfig.OffStateIDLEMode 	= TIM_OSSI_DISABLE;sBreakDeadTimeConfig.LockLevel 			= TIM_LOCKLEVEL_OFF;sBreakDeadTimeConfig.DeadTime 			= 0;sBreakDeadTimeConfig.BreakState 		= TIM_BREAK_DISABLE;sBreakDeadTimeConfig.BreakPolarity 		= TIM_BREAKPOLARITY_HIGH;sBreakDeadTimeConfig.AutomaticOutput 	= TIM_AUTOMATICOUTPUT_DISABLE;if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK){Error_Handler();}HAL_TIM_MspPostInit(&htim1);/*开始输出PWM*/HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
}void MX_TIM8_Init(void)
{TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};TIM_OC_InitTypeDef sConfigOC = {0};TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};htim8.Instance 					= TIM8;htim8.Init.Prescaler 			= 1;htim8.Init.CounterMode 			= TIM_COUNTERMODE_UP;htim8.Init.Period 				= 5599;htim8.Init.ClockDivision 		= TIM_CLOCKDIVISION_DIV1;htim8.Init.RepetitionCounter 	= 0;htim8.Init.AutoReloadPreload 	= TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim8) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK){Error_Handler();}if (HAL_TIM_PWM_Init(&htim8) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger 	= TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode 		= TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK){Error_Handler();}sConfigOC.OCMode 		= TIM_OCMODE_PWM1;sConfigOC.Pulse 		= 0;sConfigOC.OCPolarity 	= TIM_OCPOLARITY_HIGH;sConfigOC.OCNPolarity 	= TIM_OCNPOLARITY_HIGH;sConfigOC.OCFastMode 	= TIM_OCFAST_DISABLE;sConfigOC.OCIdleState 	= TIM_OCIDLESTATE_SET;sConfigOC.OCNIdleState 	= TIM_OCNIDLESTATE_RESET;if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)	// 配置PWM通道{Error_Handler();}if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)	// 配置PWM通道{Error_Handler();}sBreakDeadTimeConfig.OffStateRunMode 	= TIM_OSSR_DISABLE;sBreakDeadTimeConfig.OffStateIDLEMode 	= TIM_OSSI_DISABLE;sBreakDeadTimeConfig.LockLevel 			= TIM_LOCKLEVEL_OFF;sBreakDeadTimeConfig.DeadTime 			= 0;sBreakDeadTimeConfig.BreakState 		= TIM_BREAK_DISABLE;sBreakDeadTimeConfig.BreakPolarity 		= TIM_BREAKPOLARITY_HIGH;sBreakDeadTimeConfig.AutomaticOutput 	= TIM_AUTOMATICOUTPUT_DISABLE;if (HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK){Error_Handler();}HAL_TIM_MspPostInit(&htim8);/*开始输出PWM*/HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);
}void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *tim_baseHandle)
{if (tim_baseHandle->Instance == TIM1){__HAL_RCC_TIM1_CLK_ENABLE();}else if (tim_baseHandle->Instance == TIM8){__HAL_RCC_TIM8_CLK_ENABLE();}
}void HAL_TIM_MspPostInit(TIM_HandleTypeDef *timHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if (timHandle->Instance == TIM1){__HAL_RCC_GPIOE_CLK_ENABLE();/**TIM1 GPIO ConfigurationPE9     ------> TIM1_CH1PE11     ------> TIM1_CH2*/GPIO_InitStruct.Pin 		= GPIO_PIN_9 | GPIO_PIN_11;GPIO_InitStruct.Mode 		= GPIO_MODE_AF_PP;GPIO_InitStruct.Pull 		= GPIO_NOPULL;GPIO_InitStruct.Speed 		= GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate 	= GPIO_AF1_TIM1;HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);}else if (timHandle->Instance == TIM8){__HAL_RCC_GPIOC_CLK_ENABLE();/**TIM8 GPIO ConfigurationPC6     ------> TIM8_CH1PC7     ------> TIM8_CH2*/GPIO_InitStruct.Pin 		= GPIO_PIN_6 | GPIO_PIN_7;GPIO_InitStruct.Mode 		= GPIO_MODE_AF_PP;GPIO_InitStruct.Pull 		= GPIO_NOPULL;GPIO_InitStruct.Speed 		= GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate 	= GPIO_AF3_TIM8;HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);}
}void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *tim_baseHandle)
{if (tim_baseHandle->Instance == TIM1){__HAL_RCC_TIM1_CLK_DISABLE();}else if (tim_baseHandle->Instance == TIM8){__HAL_RCC_TIM8_CLK_DISABLE();}
}

测试环节
/*** @brief  设置TIM通道的占空比* @param  channel		通道	(1,2)* @param  compare		占空比*	@note 	无* @retval 无*/
void TIM_SetPWM_pulse(TIM_HandleTypeDef *TIM_TimeStructure, uint32_t channel, int compare)
{switch (channel){case TIM_CHANNEL_1:__HAL_TIM_SET_COMPARE(TIM_TimeStructure, TIM_CHANNEL_1, compare);break;case TIM_CHANNEL_2:__HAL_TIM_SET_COMPARE(TIM_TimeStructure, TIM_CHANNEL_2, compare);break;}
}void motor_init(void)
{GPIO_InitTypeDef GPIO_InitStruct;__GPIOG_CLK_ENABLE();__GPIOE_CLK_ENABLE();// ENA--PG12GPIO_InitStruct.Pin 	= GPIO_PIN_12;GPIO_InitStruct.Mode 	= GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Speed 	= GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);// ENB--PE6GPIO_InitStruct.Pin = GPIO_PIN_6;HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
}/*** @brief  设置电机速度* @param  motor: 电机选择(1、2)* @param  speed: 速度(占空比)* @retval 无*/
void set_motor_speed(uint8_t motor, uint16_t speed)
{if(motor == 1){dutyfactor = speed;if (direction == MOTOR_FWD){TIM_SetPWM_pulse(&htim1, TIM_CHANNEL_1, dutyfactor);	// 设置比较寄存器的值}	else	{	TIM_SetPWM_pulse(&htim1, TIM_CHANNEL_2, dutyfactor);	// 设置比较寄存器的值}}else{dutyfactor2 = speed;if (direction == MOTOR_FWD){TIM_SetPWM_pulse(&htim8, TIM_CHANNEL_1, dutyfactor);	// 设置比较寄存器的值}	else	{	TIM_SetPWM_pulse(&htim8, TIM_CHANNEL_2, dutyfactor);	// 设置比较寄存器的值}}
}/*** @brief  设置电机方向* @param  motor: 电机选择(1、2)* @param  motor: 方向选择(MOTOR_FWD、MOTOR_REV)* @retval 无*/
void set_motor_direction(uint8_t motor, motor_dir_t dir)
{if(motor == 1){direction = dir;if (direction == MOTOR_FWD){TIM_SetPWM_pulse(&htim1, TIM_CHANNEL_1, dutyfactor);	// 设置比较寄存器的值TIM_SetPWM_pulse(&htim1, TIM_CHANNEL_2, 0);				// 设置比较寄存器的值}else{TIM_SetPWM_pulse(&htim1, TIM_CHANNEL_1, 0);				// 设置比较寄存器的值TIM_SetPWM_pulse(&htim1, TIM_CHANNEL_2, dutyfactor);	// 设置比较寄存器的值}}else{direction2 = dir;if (direction2 == MOTOR_FWD){TIM_SetPWM_pulse(&htim8, TIM_CHANNEL_1, dutyfactor);	// 设置比较寄存器的值TIM_SetPWM_pulse(&htim8, TIM_CHANNEL_2, 0);			// 设置比较寄存器的值}else{TIM_SetPWM_pulse(&htim8, TIM_CHANNEL_1, 0);			// 设置比较寄存器的值TIM_SetPWM_pulse(&htim8, TIM_CHANNEL_2, dutyfactor);	// 设置比较寄存器的值}}
}/*** @brief  使能电机* @param  motor: 电机选择(1、2)* @retval 无*/
void set_motor_enable(uint8_t motor)
{if(motor == 1){HAL_GPIO_WritePin(ENA_GPIO_PORT, ENA_PIN, GPIO_PIN_SET);HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);    // 使能 PWM 通道 1HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);    // 使能 PWM 通道 2}else{HAL_GPIO_WritePin(ENB_GPIO_PORT, ENB_PIN, GPIO_PIN_SET);HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);   // 使能 PWM 通道 1HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);   // 使能 PWM 通道 2}
}/*** @brief  禁用电机* @param  motor: 电机选择(1、2)* @retval 无*/
void set_motor_disable(uint8_t motor)
{if(motor == 1){HAL_GPIO_WritePin(ENA_GPIO_PORT, ENA_PIN, GPIO_PIN_RESET);HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);    // 禁用 PWM 通道 1HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2);    // 禁用 PWM 通道 2}else{HAL_GPIO_WritePin(ENB_GPIO_PORT, ENB_PIN, GPIO_PIN_RESET);HAL_TIM_PWM_Stop(&htim8, TIM_CHANNEL_1);   // 禁用 PWM 通道 1HAL_TIM_PWM_Stop(&htim8, TIM_CHANNEL_2);   // 禁用 PWM 通道 2}
}void test(void)
{__IO uint16_t ChannelPulse = 0;__IO uint16_t ChannelPulse2 = 0;uint8_t i = 0;初始化motor_init();set_motor_enable(1);				// 使能电机1set_motor_enable(2);				// 使能电机2set_motor_speed(1, ChannelPulse);	// 电机1开始状态为停止set_motor_speed(2, ChannelPulse2);	// 电机2开始状态为停止while (1){/* 扫描KEY1 */if (Key_Scan(KEY1_GPIO_PORT, KEY1_PIN) == KEY_ON){/* 加大占空比,即加快电机1的速度 */ChannelPulse += 5600 / 10;if (ChannelPulse > 5600){ChannelPulse = 5600;}set_motor_speed(1, ChannelPulse);}/* 扫描KEY2 */if (Key_Scan(KEY2_GPIO_PORT, KEY2_PIN) == KEY_ON){if (ChannelPulse < 5600 / 10){ChannelPulse = 0;}else{/* 减小占空比,即减满电机1的速度 */ChannelPulse -= 5600 / 10;}set_motor_speed(1, ChannelPulse);}/* 扫描KEY3 */if (Key_Scan(KEY3_GPIO_PORT, KEY3_PIN) == KEY_ON){/* 加大占空比,即加快电机2的速度 */ChannelPulse2 += 5600 / 10;if (ChannelPulse2 > 5600){ChannelPulse2 = 5600;}set_motor_speed(2, ChannelPulse2);}/* 扫描KEY4 */if (Key_Scan(KEY4_GPIO_PORT, KEY4_PIN) == KEY_ON){if (ChannelPulse2 < 5600 / 10){ChannelPulse2 = 0;}else{/* 减小占空比,即减满电机1的速度 */ChannelPulse2 -= 5600 / 10;}set_motor_speed(2, ChannelPulse2);}/* 扫描KEY5 */if (Key_Scan(KEY5_GPIO_PORT, KEY5_PIN) == KEY_ON){/* 转换方向(两电机同向) */set_motor_direction(1, (++i % 2) ? MOTOR_FWD : MOTOR_REV);set_motor_direction(2, (i % 2) ? MOTOR_FWD : MOTOR_REV);}}
}

直流有刷减速电机驱动板电流电压采集

ADC配置

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;void MX_ADC1_Init(void)
{ADC_ChannelConfTypeDef sConfig = {0};/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)*/hadc1.Instance 						= ADC1;hadc1.Init.ClockPrescaler 			= ADC_CLOCK_SYNC_PCLK_DIV4;hadc1.Init.Resolution 				= ADC_RESOLUTION_12B;hadc1.Init.ScanConvMode 			= ENABLE;hadc1.Init.ContinuousConvMode 		= ENABLE;hadc1.Init.DiscontinuousConvMode 	= DISABLE;hadc1.Init.ExternalTrigConvEdge 	= ADC_EXTERNALTRIGCONVEDGE_NONE;hadc1.Init.ExternalTrigConv 		= ADC_SOFTWARE_START;hadc1.Init.DataAlign 				= ADC_DATAALIGN_RIGHT;hadc1.Init.NbrOfConversion 			= 2;hadc1.Init.DMAContinuousRequests 	= ENABLE;hadc1.Init.EOCSelection 			= ADC_EOC_SINGLE_CONV;if (HAL_ADC_Init(&hadc1) != HAL_OK){Error_Handler();}/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.*/sConfig.Channel 		= ADC_CHANNEL_9;sConfig.Rank 			= 1;sConfig.SamplingTime 	= ADC_SAMPLETIME_3CYCLES;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.*/sConfig.Channel 		= ADC_CHANNEL_8;sConfig.Rank 			= 2;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}
}void HAL_ADC_MspInit(ADC_HandleTypeDef *adcHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if (adcHandle->Instance == ADC1){__HAL_RCC_ADC1_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/**ADC1 GPIO ConfigurationPB0     ------> ADC1_IN8PB1     ------> ADC1_IN9*/GPIO_InitStruct.Pin 	= GPIO_PIN_0 | GPIO_PIN_1;GPIO_InitStruct.Mode 	= GPIO_MODE_ANALOG;GPIO_InitStruct.Pull 	= GPIO_NOPULL;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* ADC1 DMA Init *//* ADC1 Init */hdma_adc1.Instance 					= DMA2_Stream0;hdma_adc1.Init.Channel 				= DMA_CHANNEL_0;hdma_adc1.Init.Direction 			= DMA_PERIPH_TO_MEMORY;hdma_adc1.Init.PeriphInc 			= DMA_PINC_DISABLE;hdma_adc1.Init.MemInc 				= DMA_MINC_ENABLE;hdma_adc1.Init.PeriphDataAlignment 	= DMA_PDATAALIGN_HALFWORD;hdma_adc1.Init.MemDataAlignment 	= DMA_MDATAALIGN_HALFWORD;hdma_adc1.Init.Mode 				= DMA_CIRCULAR;hdma_adc1.Init.Priority 			= DMA_PRIORITY_HIGH;hdma_adc1.Init.FIFOMode 			= DMA_FIFOMODE_DISABLE;if (HAL_DMA_Init(&hdma_adc1) != HAL_OK){Error_Handler();}__HAL_LINKDMA(adcHandle, DMA_Handle, hdma_adc1);}
}void HAL_ADC_MspDeInit(ADC_HandleTypeDef *adcHandle)
{if (adcHandle->Instance == ADC1){__HAL_RCC_ADC1_CLK_DISABLE();/**ADC1 GPIO ConfigurationPB0     ------> ADC1_IN8PB1     ------> ADC1_IN9*/HAL_GPIO_DeInit(GPIOB, GPIO_PIN_0 | GPIO_PIN_1);/* ADC1 DMA DeInit */HAL_DMA_DeInit(adcHandle->DMA_Handle);}
}void MX_DMA_Init(void)
{/* DMA controller clock enable */__HAL_RCC_DMA2_CLK_ENABLE();/* DMA interrupt init *//* DMA2_Stream0_IRQn interrupt configuration */HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}

测试环节

#define ADC_NUM_MAX		2048     		// ADC 转换结果缓冲区最大值
#define VREF            3.3f     		// 参考电压,理论上是3.3,可通过实际测量得3.258static uint16_t adc_buff[ADC_NUM_MAX];	// 电压采集缓冲区
static uint16_t vbus_adc_mean = 0;    	// 电源电压 ACD 采样结果平均值
static uint32_t adc_mean_sum = 0;     	// 平均值累加
static uint32_t adc_mean_count = 0;   	// 累加计数/*** @brief  常规转换在非阻塞模式下完成回调* @param  hadc: ADC  句柄.* @retval 无*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{uint32_t adc_mean = 0;// 停止 ADC 采样,处理完一次数据在继续采样HAL_ADC_Stop_DMA(hadc);       /* 累加ADC通道9的采样值 */for (uint32_t count = 0; count < ADC_NUM_MAX; count += 2){adc_mean += (uint32_t)adc_buff[count];}adc_mean_sum += adc_mean / (ADC_NUM_MAX / 2);   // 累加电流平均后累加adc_mean_count++;								// 累加计数adc_mean = 0;/* 累加ADC通道8的采样值 */for (uint32_t count = 1; count < ADC_NUM_MAX; count += 2){adc_mean += (uint32_t)adc_buff[count];}vbus_adc_mean = adc_mean / (ADC_NUM_MAX / 2);   				// 保存平均值HAL_ADC_Start_DMA(hadc, (uint32_t *)&adc_buff, ADC_NUM_MAX);   	// 开始 ADC 采样
}	/*** @brief  获取电流值* @param  无* @retval 转换得到的电流值*/
int32_t get_curr_val(void)
{static uint8_t flag = 0;static uint32_t adc_offset = 0;    	// 偏置电压int16_t curr_adc_mean = 0;         	// 电流 ADC 采样结果平均值curr_adc_mean = adc_mean_sum / adc_mean_count;    // 保存平均值adc_mean_count = 0;adc_mean_sum = 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)vbus_adc_mean / 4096.0f * VREF);// 得到电流值,电压放大8倍,0.02是采样电阻,单位mA。电流采样电路得知的return ( (float)vdc / 8.0f / 0.02f * 1000.0f )          
}/*** @brief  获取电源电压值* @param  无* @retval 转换得到的电流值*/
float get_vbus_val(void)
{// 获取电压值float vdc = ((float)vbus_adc_mean / 4096.0f * VREF);		// 电压最大值(测量电压是电源电压的1/37),电流、电压采样电路得知的return ( ((float)vdc - 1.24f) * 37.0f );      				
}void test(void)
{uint8_t flag = 0;初始化HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&adc_buff, ADC_NUM_MAX);while(1){if (HAL_GetTick() % 50 == 0 && flag == 0)  // 每50毫秒读取一次电流、电压{flag = 1;int32_t current = get_curr_val();printf("电源电压:%.2fV,电流:%dmA\r\n", get_vbus_val(), current);}else if (HAL_GetTick() % 50 != 0 && flag == 1){flag = 0;}}	
}

直流有刷减速电机驱动板限电流、过电压、欠电压保护

ADC配置

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;/* ADC1 init function */
void MX_ADC1_Init(void)
{ADC_AnalogWDGConfTypeDef AnalogWDGConfig = {0};ADC_ChannelConfTypeDef sConfig = {0};/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)*/hadc1.Instance 						= ADC1;hadc1.Init.ClockPrescaler 			= ADC_CLOCK_SYNC_PCLK_DIV4;hadc1.Init.Resolution 				= ADC_RESOLUTION_12B;hadc1.Init.ScanConvMode 			= ENABLE;hadc1.Init.ContinuousConvMode 		= ENABLE;hadc1.Init.DiscontinuousConvMode 	= DISABLE;hadc1.Init.ExternalTrigConvEdge 	= ADC_EXTERNALTRIGCONVEDGE_NONE;hadc1.Init.ExternalTrigConv 		= ADC_SOFTWARE_START;hadc1.Init.DataAlign 				= ADC_DATAALIGN_RIGHT;hadc1.Init.NbrOfConversion 			= 2;hadc1.Init.DMAContinuousRequests 	= ENABLE;hadc1.Init.EOCSelection 			= ADC_EOC_SINGLE_CONV;if (HAL_ADC_Init(&hadc1) != HAL_OK){Error_Handler();}/** Configure the analog watchdog*/AnalogWDGConfig.WatchdogMode 	= ADC_ANALOGWATCHDOG_SINGLE_REG;AnalogWDGConfig.HighThreshold 	= (15 / 37 + 1.24) / 3.3 * 4096;AnalogWDGConfig.LowThreshold 	= (10 / 37 + 1.24) / 3.3 * 4096;AnalogWDGConfig.Channel 		= ADC_CHANNEL_8;AnalogWDGConfig.ITMode 			= ENABLE;if (HAL_ADC_AnalogWDGConfig(&hadc1, &AnalogWDGConfig) != HAL_OK){Error_Handler();}/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.*/sConfig.Channel 		= ADC_CHANNEL_9;sConfig.Rank 			= 1;sConfig.SamplingTime 	= ADC_SAMPLETIME_3CYCLES;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.*/sConfig.Channel 		= ADC_CHANNEL_8;sConfig.Rank 			= 2;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}
}void MX_DMA_Init(void)
{__HAL_RCC_DMA2_CLK_ENABLE();HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}void HAL_ADC_MspInit(ADC_HandleTypeDef *adcHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if (adcHandle->Instance == ADC1){/* ADC1 clock enable */__HAL_RCC_ADC1_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/**ADC1 GPIO ConfigurationPB0     ------> ADC1_IN8PB1     ------> ADC1_IN9*/GPIO_InitStruct.Pin 	= GPIO_PIN_0 | GPIO_PIN_1;GPIO_InitStruct.Mode 	= GPIO_MODE_ANALOG;GPIO_InitStruct.Pull 	= GPIO_NOPULL;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* ADC1 DMA Init *//* ADC1 Init */hdma_adc1.Instance 					= DMA2_Stream0;hdma_adc1.Init.Channel 				= DMA_CHANNEL_0;hdma_adc1.Init.Direction 			= DMA_PERIPH_TO_MEMORY;hdma_adc1.Init.PeriphInc 			= DMA_PINC_DISABLE;hdma_adc1.Init.MemInc 				= DMA_MINC_ENABLE;hdma_adc1.Init.PeriphDataAlignment 	= DMA_PDATAALIGN_HALFWORD;hdma_adc1.Init.MemDataAlignment 	= DMA_MDATAALIGN_HALFWORD;hdma_adc1.Init.Mode 				= DMA_CIRCULAR;hdma_adc1.Init.Priority 			= DMA_PRIORITY_HIGH;hdma_adc1.Init.FIFOMode 			= DMA_FIFOMODE_DISABLE;if (HAL_DMA_Init(&hdma_adc1) != HAL_OK){Error_Handler();}__HAL_LINKDMA(adcHandle, DMA_Handle, hdma_adc1);/* ADC1 interrupt Init */HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);HAL_NVIC_EnableIRQ(ADC_IRQn);}
}void HAL_ADC_MspDeInit(ADC_HandleTypeDef *adcHandle)
{if (adcHandle->Instance == ADC1){__HAL_RCC_ADC1_CLK_DISABLE();/**ADC1 GPIO ConfigurationPB0     ------> ADC1_IN8PB1     ------> ADC1_IN9*/HAL_GPIO_DeInit(GPIOB, GPIO_PIN_0 | GPIO_PIN_1);/* ADC1 DMA DeInit */HAL_DMA_DeInit(adcHandle->DMA_Handle);/* ADC1 interrupt Deinit */HAL_NVIC_DisableIRQ(ADC_IRQn);}
}

测试环节

#define VREF         	3.3f    // 参考电压,理论上是3.3,可通过实际测量得3.258
#define ADC_NUM_MAX  	2048    // ADC 转换结果缓冲区最大值
#define VBUS_MAX        15     	// 电压最大值
#define VBUS_MIN        10     	// 电压最小值uint16_t adc_buff[ADC_NUM_MAX];
uint16_t vbus_adc_mean = 0;    	// 电源电压 ACD 采样结果平均值
uint32_t adc_mean_sum = 0;      // 平均值累加
uint32_t adc_mean_count = 0;    // 累加计数
uint16_t flag_num = 0;/*** @brief  常规转换在非阻塞模式下完成回调* @param  hadc: ADC  句柄.* @retval 无*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{uint32_t adc_mean = 0;// 停止 ADC 采样,处理完一次数据在继续采样HAL_ADC_Stop_DMA(hadc);       // 电流数据第一次滤波for (uint32_t count = 0; count < ADC_NUM_MAX; count += 2){adc_mean += (uint32_t)adc_buff[count];}adc_mean_sum += adc_mean / (ADC_NUM_MAX / 2);    // 累加电压adc_mean_count++;adc_mean = 0;// 电压数据第一次滤波for (uint32_t count = 1; count < ADC_NUM_MAX; count += 2){adc_mean += (uint32_t)adc_buff[count];}vbus_adc_mean = adc_mean / (ADC_NUM_MAX / 2);    // 保存平均值HAL_ADC_Start_DMA(hadc, (uint32_t *)&adc_buff, ADC_NUM_MAX); // 开始 ADC 采样
}/*** @brief  在非阻塞模式模拟看门狗回调* @param  hadc: ADC  句柄.* @retval 无*/
void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef *hadc)
{float temp_adc;flag_num++;     // 电源电压超过阈值电压temp_adc = get_vbus_val();if (temp_adc > VBUS_MIN && temp_adc < VBUS_MAX){flag_num = 0;}if (flag_num > 2048){set_motor_disable();	// 停止电机flag_num = 0;printf("电源电压超过限制!请检查原因,复位开发板在试!\r\n");while (1);}
}/*** @brief  获取电流值(应定时调用)* @param  无* @retval 转换得到的电流值*/
int32_t get_curr_val(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 / adc_mean_count;    // 保存平均值adc_mean_count = 0;adc_mean_sum = 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)vbus_adc_mean / 4096.0f * VREF);// 得到电流值,电压放大8倍,0.02是采样电阻,单位mA。电流采样电路得知的return ( (float)vdc / 8.0f / 0.02f * 1000.0f )     
}/*** @brief  获取电源电压值* @param  无* @retval 转换得到的电流值*/
float get_vbus_val(void)
{// 获取电压值float vdc = ((float)vbus_adc_mean / 4096.0f * VREF);		// 电压最大值(测量电压是电源电压的1/37),电流、电压采样电路得知的return ( ((float)vdc - 1.24f) * 37.0f ); 
}#define CURR_MAX    500    // 最大电流(单位mA)void test(void)
{uint8_t curr_max_count = 0;uint8_t flag = 0;uint8_t dir = 0;初始化HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&adc_buff, ADC_NUM_MAX);while(1){if (HAL_GetTick() % 50 == 0 && flag == 0)  // 每50毫秒读取一次电流、电压{flag = 1;int32_t current = get_curr_val();printf("电源电压:%.2fV,电流:%dmA\r\n", get_vbus_val(), current);if (current > CURR_MAX)    		// 判断是不是超过限定的值{if (curr_max_count++ > 5)   // 连续5次超过{set_motor_disable();	// 电机停止curr_max_count = 0;printf("电流超过限制!请检查原因,复位开发板在试!\r\n");while (1);}}}else if (HAL_GetTick() % 50 != 0 && flag == 1){flag = 0;}	}	
}

相关文章:

电机应用-直流有刷电机

目录 直流有刷电机 工作原理 直流有刷减速电机的重要参数 电路原理与分析 驱动芯片分析 L298N驱动芯片 直流有刷减速电机控制实现 控制速度原理 硬件设计 L298N 野火直流有刷电机驱动板-MOS管搭建板 软件设计1&#xff1a;两个直流有刷减速电机按键控制 开发设计 …...

BIM、建筑机器人、隧道工程施工关键技术

一、BIM简介 &#xff08;一&#xff09;BIM概念 BIM&#xff08;Building Information Modeling&#xff09;&#xff0c;建筑信息模型。该技术通过数字化手段&#xff0c;在计算机中建立虚拟建筑&#xff0c;该虚拟建筑提供从单一到完整、包含逻辑关系的建筑信息库。信息库…...

快速了解什么是跳跃表(skip list)

什么是跳跃表&#xff08;skip list&#xff09; 跳跃表&#xff08;Skip List&#xff09;是一种概率性的数据结构&#xff0c;它通过在多层链表的基础上添加“快速通道”来提高搜索效率。跳跃表的效率可以与平衡树相媲美&#xff0c;即在平均和最坏的情况下&#xff0c;查找…...

【Node.js入门】1.1Node.js 简介

Node.js入门之—1.1Node.js 简介 文章目录 Node.js入门之—1.1Node.js 简介什么是 Node.js错误说法 Node.js 的特点跨平台三方类库自带http服务器非阻塞I/O事件驱动单线程 Node.js 的应用场合适合用Node.js的场合不适合用Node.js的场合弥补Node.js不足的解决方案 什么是 Node.j…...

数据库 高阶语句

目录 数据库 高阶语句 使用select 语句&#xff0c;用order by来对进行排序 区间判断查询和去重查询 如何对结果进行分组查询group by语句 limit 限制输出的结果记录&#xff0c;查看表中的指定行 通配符 设置别名&#xff1a;alias 简写就是 as 使用select 语句&#x…...

jenkins Java heap space

jenkins Java heap space&#xff0c;是内存不够。 两个解决方案&#xff1a; 一&#xff0c;修改配置文件 windows系统中&#xff0c;找到Jenkins的安装路径&#xff0c; 修改jenkins.xml 将 -Xmx256m 改为 -Xmx1024m 或者更大 重启jenkins服务。 二&#xff0c;jenkins增…...

OpenCV校准棋盘集合

棋盘格可以与相机校准工具一起使用&#xff0c;例如ROS的camera_calibration包。您可以通过单击下面的任何链接免费下载 PDF 格式的各种棋盘&#xff0c;没有水印或广告。此外&#xff0c;还添加了基于 JavaScript 的棋盘生成器&#xff0c;允许您生成自定义尺寸。 提示&#…...

使用git将本地项目推送到远程仓库github

总结&#xff1a;本地项目通过git上传到github 1)、在本地创建一个版本库&#xff08;即文件夹&#xff09;&#xff0c;通过 git init 把它变成Git仓库&#xff1b; 2)、把项目复制到这个文件夹里面&#xff0c;再通过 git add . 把项目添加到仓库&#xff1b; 3)、再通过 gi…...

Mybatis-Plus使用Wrapper自定义SQL

文章目录 准备工作Mybatis-Plus使用Wrapper自定义SQL注意事项目录结构如下所示domain层Controller层Service层ServiceImplMapper层UserMapper.xml 结果如下所示&#xff1a;单表查询条件构造器单表查询&#xff0c;Mybatis-Plus使用Wrapper自定义SQL联表查询不用&#xff0c;My…...

仿mudou库one thread one loop式并发服务器

目录 1.实现目标 2.HTTP服务器 实现高性能服务器-Reactor模型 模块划分 SERVER模块&#xff1a; HTTP协议模块&#xff1a; 3.项目中的子功能 秒级定时任务实现 时间轮实现 正则库的简单使用 通⽤类型any类型的实现 4.SERVER服务器实现 日志宏的封装 缓冲区Buffer…...

二十三种设计模式全面解析-组合模式与装饰器模式的结合:实现动态功能扩展

在前文中&#xff0c;我们介绍了组合模式的基本原理和应用&#xff0c;以及它在构建对象结构中的价值和潜力。然而&#xff0c;组合模式的魅力远不止于此。在本文中&#xff0c;我们将继续探索组合模式的进阶应用&#xff0c;并展示它与其他设计模式的结合使用&#xff0c;以构…...

智慧城市建设解决方案分享【完整】

文章目录 第1章 前言第2章 智慧城市建设的背景2.1 智慧城市的发展现状2.2 智慧城市的发展趋势 第3章 智慧城市“十二五”规划要点3.1 国民经济和社会发展“十二五”规划要点3.2 “十二五”信息化发展规划要点 第4章 大数据&#xff1a;智慧城市的智慧引擎4.1 大数据技术—智慧城…...

unity - Blend Shape - 变形器 - 实践

文章目录 目的Blend Shape 逐顶点 多个混合思路Blender3Ds maxUnity 中使用Project 目的 拾遗&#xff0c;备份 Blend Shape 逐顶点 多个混合思路 blend shape 基于&#xff1a; vertex number, vertex sn 相同&#xff0c;才能正常混合、播放 也就是 vertex buffer 的顶点数…...

asp.net core mvc之路由

一、默认路由 &#xff08;Startup.cs文件&#xff09; routes.MapRoute(name: "default",template: "{controllerHome}/{actionIndex}/{id?}" ); 默认访问可以匹配到 https://localhost:44302/home/index/1 https://localhost:44302/home/index https:…...

前端设计模式之【访问者模式】

文章目录 前言介绍实现优缺点应用场景后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;前端设计模式 &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现错误&#…...

通过docker-compose部署elk日志系统,并使用springboot整合

ELK是一种强大的分布式日志管理解决方案&#xff0c;它由三个核心组件组成&#xff1a; Elasticsearch&#xff1a;作为分布式搜索和分析引擎&#xff0c;Elasticsearch能够快速地存储、搜索和分析大量的日志数据&#xff0c;帮助用户轻松地找到所需的信息。 Logstash&#xf…...

【NLP】特征提取: 广泛指南和 3 个操作教程 [Python、CNN、BERT]

什么是机器学习中的特征提取&#xff1f; 特征提取是数据分析和机器学习中的基本概念&#xff0c;是将原始数据转换为更适合分析或建模的格式过程中的关键步骤。特征&#xff0c;也称为变量或属性&#xff0c;是我们用来进行预测、对对象进行分类或从数据中获取见解的数据点的…...

数据结构-单链表

1 链表的概念及结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。 从以上图片可以看出&#xff1a; 1.链式结构在逻辑上是连续的&#xff0c;但在物理上不一定是连续的。 2.现实中的节…...

红队系列-IOT安全深入浅出

红队专题 设备安全概述物联网设备层次模型设备通信模型 渗透测试信息收集工具 实战分析漏洞切入点D-link 850L 未授权访问 2017 认证绕过认证绕过 D-link DCS-2530Ltenda 系列 路由器 前台未授权RTSP 服务未授权 访问 弱口令命令注入思科 路由器 固件二进制 漏洞 IoT漏洞-D-Lin…...

亚数受邀参加“长三角G60科创走廊量子密码应用创新联盟(中心)”启动仪式

11月8日&#xff0c;在第六届中国国际进口博览会2023长三角G60科创走廊高质量发展要素对接大会上&#xff0c;亚数信息科技&#xff08;上海&#xff09;有限公司CEO翟新元作为密码企业代表之一受邀参加“长三角G60科创走廊量子密码应用创新联盟&#xff08;中心&#xff09;”…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

基于鸿蒙(HarmonyOS5)的打车小程序

1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...

FOPLP vs CoWoS

以下是 FOPLP&#xff08;Fan-out panel-level packaging 扇出型面板级封装&#xff09;与 CoWoS&#xff08;Chip on Wafer on Substrate&#xff09;两种先进封装技术的详细对比分析&#xff0c;涵盖技术原理、性能、成本、应用场景及市场趋势等维度&#xff1a; 一、技术原…...

深度解析云存储:概念、架构与应用实践

在数据爆炸式增长的时代&#xff0c;传统本地存储因容量限制、管理复杂等问题&#xff0c;已难以满足企业和个人的需求。云存储凭借灵活扩展、便捷访问等特性&#xff0c;成为数据存储领域的主流解决方案。从个人照片备份到企业核心数据管理&#xff0c;云存储正重塑数据存储与…...

OpenGL-什么是软OpenGL/软渲染/软光栅?

‌软OpenGL&#xff08;Software OpenGL&#xff09;‌或者软渲染指完全通过CPU模拟实现的OpenGL渲染方式&#xff08;包括几何处理、光栅化、着色等&#xff09;&#xff0c;不依赖GPU硬件加速。这种模式通常性能较低&#xff0c;但兼容性极强&#xff0c;常用于不支持硬件加速…...