本文详细解释了SystemError(SError)在ARM架构中的概念,它是内存系统对意外事件的异步响应。Linux内核捕获SError,不做实际处理,而是报告并导致内核崩溃,展示了内核异常处理的流程,包括进入非可恢复错误状态和低功耗模式。 摘要生成于 ,由 DeepSeek-R1 满血版支持,

翻译如下:

System Error (SError) 是一种异常类型,旨在由内存系统响应意外事件而生成。我们不期望发生这些事件,但需要知道它们是否发生。这些异常是异步报告的,因为触发事件的指令可能已经执行完成。

SError 的典型示例是先前称为外部异步中止的情况。SError 中断的示例包括:

  • 内存访问通过所有MMU检查,但然后在内存总线上遇到错误
  • 对某些RAM进行奇偶校验或错误纠正码(ECC)检查,例如内置缓存中的RAM
  • 由缓存行中的脏数据写回到外部存储器触发的中止

SError 被视为一种单独的异步异常类别,因为通常针对这些情况会有单独的处理程序。SError 的生成是实现定义的。

2.SError的处理

Linux内核中,对SError进行了捕获,设置了相应的中断向量,当并未做实际的处理,只是上报异常,并终止内核,因为对于内核来说,SError是致命的,内核自身不知道具体原因,也不知道如何修复。

SYM_CODE_START_LOCAL(el1_error) kernel_entry 1 mrs x1, esr_el1 enable_dbg mov x0, sp bl do_serror kernel_exit 1 SYM_CODE_END(el1_error)

在do_serror跳转到arm64_serror_panic

asmlinkage void noinstr do_serror(struct pt_regs *regs, unsigned int esr) arm64_enter_nmi(regs); /* non-RAS errors are not containable */ if (!arm64_is_ras_serror(esr) || arm64_is_fatal_ras_serror(regs, esr)) arm64_serror_panic(regs, esr); arm64_exit_nmi(regs);

最终,dump出所有regs的值,然后产生panic,最后进入wfe或者wfi状态。

void __noreturn arm64_serror_panic(struct pt_regs *regs, u32 esr) console_verbose(); pr_crit("SError Interrupt on CPU%d, code 0x%08x -- %s\n", smp_processor_id(), esr, esr_get_class_string(esr)); if (regs) __show_regs(regs);//dump下来所有寄存器的值 nmi_panic(regs, "Asynchronous SError Interrupt"); cpu_park_loop();//通过wfe和wfi指令,让当前cpu进入low-power standby模式 unreachable();//无法到达这里
Serror (也称System Error),是异步异常的一种,一般是来自External aborts,当memory system访问时bus上产生的External aborts。例如 data abort prefetch abort 访问内存被TZC挡住时,bus会返回的一个异常 注意,像MMU产生的异常,它属于internal abort,它是同步异常。像未定义指令异常,它也是同步异常 .........
内核稳定性问题复杂多样,最常见的莫过于“kernel panic”,意为“内核恐慌,不知所措”。这种情况下系统自然无法正常运转,只能自我结束生命,留下死亡信息。诸如: “Unable to handle kernel XXX at virtual address XXX” “undefined instruction XXX” “Bad mode in Error handler detected on CPUX, code 0xbe000011 -- SError ” ...... 这些死亡信息是
最近在 linux 4.9 arm 64遇到了bad mode的kernel oops oops的内容大概如下,而且oops过后看起来系统并没有异常,oops出现的概率比较小,而且每次oops打印的信息中task 都是不同的,oops之后,看起来大部分时候系统还是可以正常运行的 [ 1259.654597] Bad mode in Error handler detected, code 0xb...
异常(exception)和特权(privilege)是在 ARMv 8 -A中定义的两个概念。 现代软件期望被分成不同的模块,每个模块对系统和处理器资源具有不同的访问级别。这方面的一个例子是操作系统内核和用户应用程序之间的分离,前者具有对系统资源的高级别访问权限,后者配置系统的能力更为有限。 Armv 8 -A通过实现不同级别的特权来实现这种分离。当前特权级别只能在处理器接受异常或从异常返回时更改。因此,在 Armv 8 -A体系结构中,这些特权级别被称为异常级别(exception level,以下简称EL)。每个异常
ARM armv 8 armv 9、 arm 架构、 arm 视频、添加vx : arm _2023 获取更多资料, arm 资料、 arm 下载、 arm 学习、 arm 免费、 arm 课程, PE - processing element ARM 体系结构定义了抽象机器的行为,称为处理元素(processing element),通常缩写为PE。与 ARM 体系结构兼容的实现必须符合处理元素所描述的行为。 RISC架构 ARM 架构是一个精简指令集计算机(RISC)架构,具有以下RISC架构特征: - 一个大的统一寄存器文件。 float Encoder_Theta_coefficient; //编码器转换成电角度的系数值 float Encoder_Theta_m_coefficient; //编码器转换成机械角度的系数值 float We_coefficient; //编码器转电角速度的系数值 float Wm_coefficient; //编码器转机械角速度的系数值 float t; float Current_BandWidth; //电流环带宽 float CurrentLoop_Kpd; //电流环D轴比例增益 float CurrentLoop_Kid ; //电流环D轴积分增益 float CurrentLoop_Kpq ; //电流环Q轴比例增益 float CurrentLoop_Kiq; //电流环Q轴积分增益 int Speed_BandWidth ; //速度环带宽 float Ba ; //有功阻尼系数 float SpeedLoop_Kp ; //速度环比例增益 float SpeedLoop_Ki ; //速度环积分增益 void Foc_Init() float temp1,temp2; Motor1.Wm_Set=0; Motor1.Id_Set=0; //设定为20k(电流采样由定时器1的PWM比较触发,频率也为20k) Motor1_Time.PWM_Fre = 16 8 000000.0f / (TIM1_ARR * TIM1_PSC) / 2; Motor1_Time.PWM_Ts = 1 / Motor1_Time.PWM_Fre; //电流环:速度环:位置环的速度比例有多种,有16:4:1, 2:1:1, 1:1:1,都可以 Motor1_Time.Current_Loop_TimeGain = 1.0f; Motor1_Time.Speed_Loop_TimeGain = 1.0f; Motor1_Time.Locate_Loop_TimeGain = 1.0f; //计算频率设定为40k Motor1_Time.Calculate_Fre = 8 4000000.0f / (TIM2_ARR * TIM2_PSC ); Motor1_Time.Calculate_Ts = 1 / Motor1_Time.Calculate_Fre; //电流环设定为20k Motor1_Time.Current_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Current_Loop_TimeGain; Motor1_Time.Current_Loop_Ts = 1 / Motor1_Time.Current_Loop_Fre; //速度环设定为10k Motor1_Time.Speed_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Speed_Loop_TimeGain; Motor1_Time.Speed_Loop_Ts = 1 / Motor1_Time.Speed_Loop_Fre; //位置设定为10k Motor1_Time.Locate_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Locate_Loop_TimeGain; Motor1_Time.Locate_Loop_Ts = 1 / Motor1_Time.Locate_Loop_Fre; //编码器计数值转换为角度的系数值 Encoder_Theta_coefficient = 360 * MOTOR1_Pn /(ENCODER_NUMBER * 4); Encoder_Theta_m_coefficient = 360 /(ENCODER_NUMBER * 4); //编码器计数值转换为角速度的系数值 We_coefficient = 2* PI /(ENCODER_NUMBER * 4) / Motor1_Time.Speed_Loop_Ts; Wm_coefficient = 60.0f /(ENCODER_NUMBER * 4) / Motor1_Time.Speed_Loop_Ts; //计算电流环的PI系数 temp1=MOTOR1_Ld/MOTOR1_R; temp2=MOTOR1_Lq/MOTOR1_R; t=(temp1<temp2)?temp1:temp2; Current_BandWidth = 2*PI/t; //算出来的带宽太大了,有九千多,感觉是电机的参数给的可能有问题?不知道,反正不用这个带宽 //小了响应速度不够快,大了电流波动大 Current_BandWidth =1500; CurrentLoop_Kpd = Current_BandWidth * MOTOR1_Ld; CurrentLoop_Kid = Current_BandWidth * MOTOR1_R; CurrentLoop_Kpq = Current_BandWidth * MOTOR1_Ld; CurrentLoop_Kiq = Current_BandWidth * MOTOR1_R; //计算速度环的带宽 Speed_BandWidth = 150; Ba = (Speed_BandWidth *MOTOR1_J - MOTOR1_B ) / (1.5f * MOTOR1_Pn * MOTOR1_flux); //这个值在速度环我看别人的仿真不知道为啥给扔了,在速度环的时候不要这个 SpeedLoop_Kp = ( Speed_BandWidth * MOTOR1_J ) / (1.5f * MOTOR1_Pn * MOTOR1_flux) ; SpeedLoop_Ki = Speed_BandWidth * SpeedLoop_Kp; //整个闭环控制计算可以满足40k的处理频率 void Foc_Control(void) static int Number1=0,Number2=0,Number3=0; Motor1.Encoder_Value=Get_Encoder_Count(); //根据编码器计算角度 Motor1.Theta=Value_Limit(( Motor1.Encoder_Value + ENCODER_OFFSET ) * Encoder_Theta_coefficient,0.0f,360.0f); Motor1.Theta_m = Value_Limit(( Motor1.Encoder_Value + ENCODER_OFFSET ) * Encoder_Theta_m_coefficient,0.0f,360.0f); if(++Number1 >= Motor1_Time.Locate_Loop_TimeGain) { //执行位置环 Locate_Loop(); Number1= 0; } if(++Number2 >= Motor1_Time.Speed_Loop_TimeGain) { //执行速度环 Speed_Loop(); Number2= 0; } if(++Number3 >= Motor1_Time.Current_Loop_TimeGain) { //执行电流环 Current_Loop(); Number3= 0; } //对电压进行反Park变换 Park_Inverse_Transform(); //执行SVPWM并设定电机占空比 FOC_SVPWM(); //执行高频方波注入算法 // HFI_Sensorless(); //SVPWM void FOC_SVPWM(void) uint 8 _t N,A,B,C; float Vref1,Vref2,Vref3,X,Y,Z,temp1,Tfirst,Tsecond,T0,Ta,Tb,Tc,Tcm1,Tcm2,Tcm3; //计算转子所在的山区 Vref1=Motor1.Ubeta; Vref2=(SQRT_3* Motor1.Ualpha- Motor1.Ubeta)/2; Vref3=(-SQRT_3* Motor1.Ualpha- Motor1.Ubeta)/2; A=Vref1>0 ? 1 :0 ; B=Vref2>0 ? 1 :0 ; C=Vref3>0 ? 1 :0 ; N=4*C+2*B+A; temp1=SQRT_3* SVPWM_TS/ UDC; X=temp1*Vref1; Y=-temp1*Vref3; Z=-temp1*Vref2; //矢量作用时间计算 switch(N) { case 1: Tfirst= Z; Tsecond= Y; Motor1.Sector= 2; break; case 2: Tfirst= Y; Tsecond= -X; Motor1.Sector= 6; break; case 3: Tfirst= -Z; Tsecond= X; Motor1.Sector= 1; break; case 4: Tfirst= -X; Tsecond= Z; Motor1.Sector= 4; break; case 5: Tfirst= X; Tsecond= -Y; Motor1.Sector= 3; break; case 6: Tfirst= -Y; Tsecond= -Z; Motor1.Sector= 5; break; default: Tfirst= 0; Tsecond= 0; Motor1.Sector= 0; break; } //超限判断 if(( Tfirst + Tsecond )> SVPWM_TS) { Tfirst=(Tfirst/(Tfirst+Tsecond))/ SVPWM_TS; Tsecond=(Tsecond/(Tfirst+Tsecond))/ SVPWM_TS; } T0= (SVPWM_TS- Tfirst- Tsecond)/2; Ta=T0/2; Tb=Ta+Tfirst/2; Tc=Tb+Tsecond/2; //每相桥臂切换时间计算 switch(N) { case 1: Tcm1=Tb; Tcm2=Ta; Tcm3=Tc; break; case 2: Tcm1=Ta; Tcm2=Tc; Tcm3=Tb; break; case 3: Tcm1=Ta; Tcm2=Tb; Tcm3=Tc; break; case 4: Tcm1=Tc; Tcm2=Tb; Tcm3=Ta; break; case 5: Tcm1=Tc; Tcm2=Ta; Tcm3=Tb; break; case 6: Tcm1=Tb; Tcm2=Tc; Tcm3=Ta; break; default: break; } Motor1.Tcm1=Tcm1; Motor1.Tcm2=Tcm2; Motor1.Tcm3=Tcm3; //设置定时器1的PWM占空比 __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,TIM1_ARR*(1-2*Tcm1/ SVPWM_TS)); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,TIM1_ARR*(1-2*Tcm2/ SVPWM_TS)); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,TIM1_ARR*(1-2*Tcm3/ SVPWM_TS)); void ADC_Transform(void) //顺便软件滤波一下 //按照无刷驱动板的原理图上的公式算的,0.02是电阻值,电流转成的电压 除以电阻就是电流 // Motor1.Ibeta=((HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1) * 3.3f /4095.0f) -1.24f)/ 8 .0f/0.02f; 单位A Motor1.Ia =IRFilter1(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1)) * 0.005f -7.75f; Motor1.Ib =IRFilter2(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_2)) * 0.005f -7.75f; Motor1.Ic =IRFilter3(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_3)) * 0.005f -7.75f; //位置环 void Locate_Loop(void) //用不上暂时还没学,就没写 //速度环 void Speed_Loop(void) static float temp; static int Encoder_Last; float Encoder_W_temp; if(Encoder_Z_Flag==1) { Encoder_W_temp = KalmanFilter(Motor1.Encoder_Value - Encoder_Last + Encoder_temp); Encoder_Z_Flag=0; } else Encoder_W_temp = KalmanFilter(Motor1.Encoder_Value - Encoder_Last); //根据编码器计算角速度 速度范围波动会比较大,因为采样频率太高了,编码器的分辨率又不够高,所以这是很正常的。降低采样频率(即减小TIM2的时钟频率)可以减小波动 Motor1.We = Encoder_W_temp * We_coefficient; //单位 rad/s(电角速度) Motor1.Wm = Encoder_W_temp * Wm_coefficient; //单位 RPM(角速度) Encoder_Last=Motor1.Encoder_Value; Motor1.Iq_Set = SpeedLoop_Kp * (Motor1.Wm_Set - Motor1.Wm) + temp; temp = Value_SetMaxMin(temp +SpeedLoop_Ki * (Motor1.Wm_Set - Motor1.Wm) * Motor1_Time.Speed_Loop_Ts,-10.0f,10.0f); //给个限值防止他一直往上加,一启动电机速度飞飚,这个值是慢慢试的,大概速度到3000RPM时,这个值为10.多 //电流环 void Current_Loop(void) static float temp,temp2; //对三相电流进行采样,三相电流相加不为0,是因为硬件上有误差(野火客服说的,可恶!) 电流采样频率和电流频率一样都为20k ADC_Transform(); //对电流进行Clark变换,再Park变换 Motor1.Ialpha = Motor1.Ia; Motor1.Ibeta = (Motor1.Ia + 2*Motor1.Ib)/SQRT_3; Motor1.Id = Motor1.Ialpha* qfcosd(Motor1.Theta)+ Motor1.Ibeta* qfsind(Motor1.Theta); Motor1.Iq = -Motor1.Ialpha* qfsind(Motor1.Theta)+ Motor1.Ibeta* qfcosd(Motor1.Theta); //PI环节 //这个电流环的积分项我没整明白,加上积分的话,积分项会直接冲到最大,且一直为最大,然后就电机转动也没有更好,感觉就是反作用,先不整了,把它干掉,后面再研究 Motor1.Ud = CurrentLoop_Kpd *(Motor1.Id_Set - Motor1.Id) + temp - Motor1.We * MOTOR1_Lq * Motor1.Iq; // temp = Value_SetMaxMin( temp+ CurrentLoop_Kid * (Motor1.Id_Set - Motor1.Id) * Motor1.Calculate_Ts, -0.1f, 0.1f); Motor1.Uq = CurrentLoop_Kpq *(Motor1.Iq_Set - Motor1.Iq) + temp2 + Motor1.We * (MOTOR1_Ld * Motor1.Id + MOTOR1_flux); // temp2 = Value_SetMaxMin( temp2+ CurrentLoop_Kiq * (Motor1.Iq_Set - Motor1.Iq) * Motor1.Calculate_Ts, -0.1f, 0.1f); //合成电压最大值为SQRT_3 * UDC / 3 = 13. 8 5 , Ud 最大值设为3 是为了高频方波注入算法, 这些限值都是试出来的,我也不会算理论值;这样限值转速最大能到2600RPM左右,够了 Motor1.Ud = Value_SetMaxMin(Motor1.Ud,-3.0f,3.0f); Motor1.Uq = Value_SetMaxMin(Motor1.Uq,-12.0f,12.0f); // Motor1.Ud = 0; // Motor1.Uq = 2; 这是电机控制FOC的速度和电流环驱动代码 #include "main.h" #include "adc.h" #include "dma.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stdio.h" #include "bsp_key.h" #include "hall.h" #include " arm _math.h" #include " arm _const_structs.h" #include "math.h" #include "encoder.h" #include "mymath.h" #include "foc.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ MOTOR Motor1; TIME Motor1_Time; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ * @brief The application entry point. * @retval int int main(void) /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_TIM5_Init(); MX_USART1_UART_Init(); MX_ADC1_Init(); MX_DMA_Init(); /* USER CODE BEGIN 2 */ My_Timer_Init(); Encoder_Init(); Foc_Init(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //按键1按下,电机启动 if(Key_Scan(KEY1_GPIO_Port,KEY1_Pin)==KEY_ON) //点亮LED_D11,串口输出电机启动,打开控制端口,设置初始速度(占空比)和初始方向,判断扇区并启动一次 LED_D11_ON; printf("Motor Running\r\n"); HAL_GPIO_WritePin(MOTOR_SD_GPIO_Port,MOTOR_SD_Pin,GPIO_PIN_SET); // Motor1.Direction=Motor_Clockwise; // Motor1.Speed=Motor_Speed_20; //初始执行一次霍尔换相 // HAL_TIM_PeriodElapsedCallback(&htim2); Motor1.Wm_Set= 8 00; }else if(Key_Scan(KEY2_GPIO_Port,KEY2_Pin)==KEY_ON) //按键2按下,电机停止 //关闭LED_D11,串口输出电机停止,关闭控制端口,设置初始速度(占空比)和初始方向 LED_D11_OFF; printf("Motor Stop\r\n"); HAL_GPIO_WritePin(MOTOR_SD_GPIO_Port,MOTOR_SD_Pin,GPIO_PIN_RESET); // Motor1.Direction=Motor_Stop; // Motor1.Speed=Motor_Speed_0; Motor1.Wm_Set=0; }else if(Key_Scan(KEY3_GPIO_Port,KEY3_Pin)==KEY_ON) //按键3按下,电机加速 // if(Motor1.Speed<Motor_Speed_100) // Motor1.Speed++; Motor1.Wm_Set +=100; }else if(Key_Scan(KEY4_GPIO_Port,KEY4_Pin)==KEY_ON) //按键4按下,电机减速 // if(Motor1.Speed>Motor_Speed_20) // Motor1.Speed--; Motor1.Wm_Set -=100; }else if(Key_Scan(KEY5_GPIO_Port,KEY5_Pin)==KEY_ON) //按键5按下,电机反向 // if(Motor1.Direction==Motor_Clockwise) // Motor1.Direction=Motor_AntiClockwise; // else if(Motor1.Direction==Motor_AntiClockwise) // Motor1.Direction=Motor_Clockwise; printf("%.2f,%.2f\n",Motor1.Wm,Motor1.Wm_Set); /* USER CODE END 3 */ * @brief System Clock Configuration * @retval None void SystemClock_Config(void) RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = 8 ; RCC_OscInitStruct.PLL.PLLN = 16 8 ; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 4; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) Error_Handler(); /** Initializes the CPU, AHB and APB buses clocks RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) Error_Handler(); /* USER CODE BEGIN 4 */ //定时器2更新中断(20k) void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) if(htim == &htim2) //测试中断所需时间 HAL_GPIO_WritePin(GPIO_TEST1_GPIO_Port,GPIO_TEST1_Pin,GPIO_PIN_SET); Foc_Control(); HAL_GPIO_WritePin(GPIO_TEST1_GPIO_Port,GPIO_TEST1_Pin,GPIO_PIN_RESET); /* USER CODE END 4 */ * @brief This function is executed in case of error occurrence. * @retval None void Error_Handler(void) /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) /* USER CODE END Error_Handler_Debug */ #ifdef USE_FULL_ASSERT * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None void assert_failed(uint 8 _t *file, uint32_t line) /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 这是主函数,我现在需要你为代码添加位置环的控制部分,要求可以实现位置控制的功能
<think>我们将在现有代码的基础上添加位置环控制。根据之前的讨论,我们需要在FOC控制循环中集成位置环,并且位置环的执行频率由Motor1_Time.Locate_Loop_TimeGain控制(在Foc_Init中初始化为1,即与计算频率相同,40kHz)。但实际位置环不需要这么高的频率,我们可以调整Motor1_Time.Locate_Loop_TimeGain来降低位置环的执行频率。 位置环控制思路: 1. 添加目标位置变量(target_angle)和位置控制使能标志(position_enabled)到Motor1结构体中,或者作为全局变量。 2. 在位置环函数Locate_Loop()中,如果位置控制使能,则计算位置误差(注意角度是0-360°的循环,需要处理边界),然后通过位置控制器(例如比例控制器)计算出速度指令(Motor1.Wm_Set)。 3. 在速度环中,速度环的设定值Motor1.Wm_Set将由位置环的输出提供(当位置控制使能时)。 修改步骤: 步骤1:在foc.h中定义位置环相关的变量和函数 步骤2:在foc.c中实现位置环函数Locate_Loop() 步骤3:修改main.c,添加按键控制目标位置和位置控制使能 考虑到现有代码结构,我们将在Motor1结构体中添加位置环相关的变量。但是,由于Motor1结构体在foc.h中定义,我们可以在foc.h中修改Motor1结构体。 但是,由于现有代码中Motor1结构体没有位置环相关变量,我们需要扩展它。另外,为了避免对原有代码造成太大影响,我们也可以使用全局变量。这里为了保持代码的模块化,我们扩展Motor1结构体。 然而,查看现有代码,Motor1结构体在foc.h中并没有定义,而是在main.c中定义为全局变量Motor1。因此,我们需要在foc.h中声明Motor1为外部变量,并在foc.h中定义位置环控制所需的新变量(如目标位置、位置使能标志)作为Motor1结构体的成员。 但是,由于原代码中Motor1结构体类型定义在foc.c中(未在头文件中定义),这样其他文件无法访问其成员。因此,我们需要将Motor1结构体类型定义移到foc.h中,并使其成为公共结构体。 由于修改较大,我们采用另一种方法:在foc.c中添加全局变量,并在foc.h中通过extern声明,然后在main.c中通过函数接口设置这些变量。 为了最小化修改,我们选择在foc.c中添加两个全局变量: float target_angle = 0.0f; // 目标位置(度) uint 8 _t position_enabled = 0; // 位置控制使能标志 并在foc.h中声明: extern float target_angle; extern uint 8 _t position_enabled; 然后,在位置环函数Locate_Loop()中,使用这些变量。 步骤1:修改foc.h 添加以下声明: extern float target_angle; extern uint 8 _t position_enabled; 步骤2:在foc.c中定义这些全局变量,并实现Locate_Loop()函数 步骤3:修改main.c中的按键控制,通过按键设置目标位置和使能位置控制。 具体实现如下:</think>### 位置环控制集成方案 我将为您的FOC电机控制系统添加位置环控制功能,保持原有结构不变,仅添加必要的位置控制逻辑。以下是完整的修改方案: #### 1. 在`foc.h`中添加位置控制声明 /* 添加位置控制声明 */ #ifdef __cplusplus extern "C" { #endif // 位置环控制函数声明 void Locate_Loop(void); // 位置控制变量声明 extern float target_angle; // 目标位置(度) extern uint 8 _t position_enabled; // 位置控制使能标志 #ifdef __cplusplus #endif #### 2. 在`foc.c`中添加位置控制实现 /* 添加位置控制全局变量 */ float target_angle = 0.0f; // 目标位置(度) uint 8 _t position_enabled = 0; // 位置控制使能标志 /* 位置环实现 */ void Locate_Loop(void) // 位置控制未使能时直接返回 if (!position_enabled) return; // 计算位置误差(处理360°边界) float error = target_angle - Motor1.Theta_m; // 处理360°圆周上的最短路径 if (error > 1 8 0.0f) { error -= 360.0f; } else if (error < -1 8 0.0f) { error += 360.0f; // 比例控制输出作为速度指令 const float Kp_position = 0.5f; // 位置环比例增益(可调整) Motor1.Wm_Set = Kp_position * error; // 速度限幅(根据电机性能调整) const float max_speed = 1000.0f; // 最大速度限制 Motor1.Wm_Set = Value_SetMaxMin(Motor1.Wm_Set, -max_speed, max_speed); // 位置到达检测(误差小于1度视为到达) if (fabsf(error) < 1.0f) { Motor1.Wm_Set = 0; // 停止电机 // 可选:到达后自动禁用位置控制 // position_enabled = 0; #### 3. 修改`main.c`添加位置控制按键功能 /* USER CODE BEGIN PV */ // 位置控制临时变量 static float position_increment = 30.0f; // 位置调整步长 /* USER CODE END PV */ int main(void) // ... 初始化代码保持不变 ... while (1) // 原有按键功能保持不变... /* 新增位置控制按键功能 */ if(Key_Scan(KEY1_GPIO_Port, KEY1_Pin) == KEY_ON) // 启动位置控制 position_enabled = 1; LED_D11_ON; printf("Position control ON\n"); HAL_Delay(200); else if(Key_Scan(KEY2_GPIO_Port, KEY2_Pin) == KEY_ON) // 停止位置控制 position_enabled = 0; Motor1.Wm_Set = 0; // 停止电机 LED_D11_OFF; printf("Position control OFF\n"); HAL_Delay(200); else if(Key_Scan(KEY3_GPIO_Port, KEY3_Pin) == KEY_ON) // 位置增加 target_angle += position_increment; if(target_angle >= 360.0f) target_angle -= 360.0f; printf("Target: %.1f°\n", target_angle); HAL_Delay(200); else if(Key_Scan(KEY4_GPIO_Port, KEY4_Pin) == KEY_ON) // 位置减少 target_angle -= position_increment; if(target_angle < 0.0f) target_angle += 360.0f; printf("Target: %.1f°\n", target_angle); HAL_Delay(200); else if(Key_Scan(KEY5_GPIO_Port, KEY5_Pin) == KEY_ON) // 位置归零 target_angle = 0.0f; printf("Target set to 0°\n"); HAL_Delay(200); // 位置监控(每200ms输出一次) static uint32_t last_print = 0; if(HAL_GetTick() - last_print > 200) { printf("Target: %.1f° | Actual: %.1f° | Error: %.1f°\n", target_angle, Motor1.Theta_m, target_angle - Motor1.Theta_m); last_print = HAL_GetTick(); HAL_Delay(10); ### 位置控制原理说明 #### 1. 控制结构 位置环 → 速度环 → 电流环 → PWM输出 ↑ ↑ ↑ 目标位置 位置环输出 速度环输出 #### 2. 关键参数说明 1. **位置环比例增益 (Kp_position)** const float Kp_position = 0.5f; // 位置环比例增益 - 控制位置响应速度 - 值越大响应越快,但可能导致超调 - 推荐范围:0.1~2.0(根据电机性能调整) 2. **位置调整步长** static float position_increment = 30.0f; // 位置调整步长 - 每次按键调整的角度值 - 可根据需要调整为5°、15°、45°等 3. **最大速度限制** const float max_speed = 1000.0f; // 最大速度限制 - 防止位置环输出过大速度指令 - 应根据电机最大转速设置 ### 位置环工作流程 1. **位置环调用** - 在`Foc_Control()`函数中周期性调用 - 调用频率由`Motor1_Time.Locate_Loop_TimeGain`控制 2. **位置误差计算** float error = target_angle - Motor1.Theta_m; - 计算目标位置与实际位置的差值 - 处理360°边界问题 3. **比例控制** Motor1.Wm_Set = Kp_position * error; - 输出作为速度环的设定值 - 简单高效的比例控制 4. **速度限幅** Motor1.Wm_Set = Value_SetMaxMin(Motor1.Wm_Set, -max_speed, max_speed); - 防止速度指令超出电机能力范围 5. **位置到达检测** if (fabsf(error) < 1.0f) { Motor1.Wm_Set = 0; - 当误差小于1°时停止电机 - 可添加到达提示或自动禁用位置控制 ### 优化建议 #### 1. 添加S曲线加减速 // 在位置环中添加S曲线速度规划 void Position_Planner(void) static float current_position = 0.0f; const float max_accel = 2000.0f; // 最大加速度 (°/s²) // 计算S曲线速度规划 float position_error = target_angle - current_position; float direction = (position_error > 0) ? 1.0f : -1.0f; // 计算加速距离 float accelerate_distance = 0.5f * max_speed * max_speed / max_accel; // 速度规划 if (fabsf(position_error) > 2 * accelerate_distance) { // 梯形速度规划 if (fabsf(Motor1.Wm_Set) < max_speed) { Motor1.Wm_Set += direction * max_accel * Motor1_Time.Locate_Loop_Ts; } else { // 减速阶段 float decelerate_speed = sqrtf(2 * max_accel * fabsf(position_error)); Motor1.Wm_Set = direction * fminf(fabsf(Motor1.Wm_Set), decelerate_speed); // 更新当前位置 current_position += Motor1.Wm_Set * Motor1_Time.Locate_Loop_Ts; #### 2. 抗积分饱和处理 // 在位置环中添加积分项(可选) static float position_integral = 0.0f; const float Ki_position = 0.01f; // 积分增益 // 积分项计算 if (fabsf(error) > 5.0f) { // 仅在大误差时积分 position_integral += Ki_position * error * Motor1_Time.Locate_Loop_Ts; position_integral = Value_SetMaxMin(position_integral, -max_speed/2, max_speed/2); // 输出速度指令 Motor1.Wm_Set = Kp_position * error + position_integral; #### 3. 位置环参数初始化 // 在Foc_Init()中添加位置环参数初始化 void Foc_Init() // ...原有代码... // 位置环参数初始化 const float position_BandWidth = 10.0f; // 位置环带宽(Hz) Motor1_Time.Locate_Loop_Kp = position_BandWidth * 2 * PI * MOTOR1_J / (1.5f * MOTOR1_Pn * MOTOR1_flux); Motor1_Time.Locate_Loop_Ki = position_BandWidth * Motor1_Time.Locate_Loop_Kp; ### 完整的位置控制流程图 设置目标位置 ────┐ │ │ ▼ ▼ 位置环控制 ──▶ 速度环控制 ──▶ 电流环控制 ──▶ PWM输出 ▲ ▲ ▲ │ │ │ 位置反馈 ──────┘ │ │ │ 编码器读数 ───────────────┘ 此实现方案保持了原有的FOC控制结构,仅添加了必要的位置控制逻辑,通过按键可以方便地控制目标位置和启停位置控制。位置环采用简单的比例控制,输出作为速度环的设定值,实现电机的位置控制功能。

最全最详细的讲解ARMv8/ARMv9架构的核心知识点,由易到难/循序渐进/章节有序/通俗易懂。 要学就学最新架构,十几年前/二十年前的技术何必再学习呢。本系列内容包括不限于:ARM基础、SOC芯片基础、Trustzone、gic、异常和中断、AMBA、Cache、MMU... 且会持续更新。