底层

定时器

系统时钟72MHz
image.png
时间管理,基于定时器的时间计数器

  1. typedef struct {
  2. uint8_t PWMZD_count; // 在PWM中计数 1/12.5K *25=2ms
  3. uint8_t IntClock_10ms; // 10ms时钟标志
  4. uint8_t Tim10ms_flag; // 10ms标志
  5. uint8_t Tim100ms_count; //100ms计数
  6. uint8_t Tim100ms_flag; //100ms事件标志位
  7. uint8_t Tim500ms_count; //500ms计数
  8. uint8_t Tim500ms_flag; //500ms事件标志位
  9. uint8_t Tim1s_count; //1s计数
  10. uint8_t Tim1s_flag ; //1s事件标志位
  11. uint8_t Tim10s_count; //10s计数
  12. uint8_t Tim10s_flag ; //10s事件标志位
  13. uint8_t Tim1min_count; //1计数
  14. uint8_t Tim1min_flag ; //1分钟事件标志位
  15. }TaskTime;
  1. void SysTickConfig(void)
  2. {
  3. /* Setup SysTick Timer for 1ms interrupts */
  4. if (SysTick_Config(SystemCoreClock /100)) // 1ms 设置SysTick 定时器以每 1ms 生成一次中断
  5. {
  6. /* Capture error */
  7. while (1);//SysTick_Config 函数返回一个值,指示配置是否成功。
  8. //如果配置失败(返回非零值),则代码进入错误状态,其中进入一个无限循环来卡住程序。
  9. }
  10. /* Configure the SysTick handler priority */
  11. NVIC_SetPriority(SysTick_IRQn, 0x0);//设置了 SysTick 中断的优先级为 0x0(最高优先级)
  12. }
  13. void RunSystimer(void)////根据不同的时间间隔设置各个标志位,用于在特定的时间点执行相应的操作,比如翻转 LED 状态
  14. {
  15. if(TaskTimePare.IntClock_10ms==1)
  16. {
  17. TaskTimePare.IntClock_10ms=0;
  18. TaskTimePare.Tim10ms_flag = 1;
  19. if(++TaskTimePare.Tim100ms_count >=10)
  20. {
  21. TaskTimePare.Tim100ms_count=0;
  22. TaskTimePare.Tim100ms_flag=1;
  23. }
  24. }
  25. ……………………………………………………
  26. }
  27. void CLEAR_flag(void) //清除事件标志位
  28. {
  29. TaskTimePare.Tim10ms_flag=0;
  30. TaskTimePare.Tim100ms_flag=0;
  31. TaskTimePare.Tim500ms_flag=0;
  32. TaskTimePare.Tim1s_flag=0;
  33. TaskTimePare.Tim1min_flag=0;
  34. }

PWM定时器

使用高级定时TIMER1

  1. TIM1_TimeBaseStructure.TIM_Prescaler=0;
  2. TIM1_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1 ;//中央对齐模式 1 计数模式
  3. TIM1_TimeBaseStructure.TIM_Period = PWM_PERIOD;//也就是ARR预装载寄存器里面的值=2250,
  4. TIM1_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//数字滤波器采样频率
  5. TIM1_TimeBaseStructure.TIM_RepetitionCounter = REP_RATE;//1个PWM周期更新一次

TIM1时基单元配置12500KHZ,一个PWM周期是80us
中央对齐
1个PWM周期更新一次

GPIO

引脚配置
LED引脚 推挽输出;
霍尔引脚 浮空输入;
CAN发送引脚 复用推挽输出GPIO_Mode_AF_PP
CAN接收引脚 输入上拉GPIO_Mode_IPU
串口发送引脚 复用推挽输出GPIO_Mode_AF_PP
串口接收引脚 浮空输入GPIO_Mode_IN_FLOATING
ADC引脚 模拟输入GPIO_Mode_AIN

ADC

使用ADC1

  1. ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  2. ADC_InitStructure.ADC_ScanConvMode = ENABLE;
  3. ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换开启
  4. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部触发
  5. ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
  6. ADC_InitStructure.ADC_NbrOfChannel = 5; //设置转换序列长度为5 5个通道
  7. ADC_Init(ADC1, &ADC_InitStructure);

采样转换时间

  1. //采样时间为1us左右,(7cycles) 保证在PWM中断后进来采样后为在PWM中间采集相电流
  2. //常规转换序列1:通道0
  3. ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_7Cycles5);
  4. //常规转换序列2:通道1
  5. ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_7Cycles5);
  6. //常规转换序列1:通道2
  7. ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_7Cycles5);
  8. //常规转换序列2:通道3
  9. ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_7Cycles5);
  10. //常规转换序列2:通道8
  11. ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 5, ADC_SampleTime_7Cycles5);

ADC自动校准

  1. // 下面是ADC自动校准,开机后需执行一次,保证精度
  2. // Enable ADC1 reset calibaration register
  3. ADC_ResetCalibration(ADC1);
  4. Delay(100);
  5. // Check the end of ADC1 reset calibration register
  6. while(ADC_GetResetCalibrationStatus(ADC1));
  7. // Start ADC1 calibaration
  8. ADC_StartCalibration(ADC1);
  9. Delay(100);
  10. // Check the end of ADC1 calibration
  11. while(ADC_GetCalibrationStatus(ADC1));
  12. // ADC自动校准结束---------------

采样值的计算

image.png

  1. //校准作用,电流传感器的偏移值为1.65V
  2. void Offset_CurrentReading(void) // 没有PWM输出是调用
  3. {
  4. static uint16_t ADC_PhaseU_Curr[64];
  5. static uint16_t ADC_PhaseV_Curr[64];
  6. static uint16_t ADC_BUS_Curr[64];
  7. static uint8_t i=0;
  8. /* ADC Channel used for current reading are read in order to get zero currents ADC values*/
  9. //64次采样求平均值,电流传感器初始校准
  10. ADC_PhaseU_Curr[i] = ADC_ConvertedValue[2];
  11. ADC_PhaseV_Curr[i] = ADC_ConvertedValue[1];
  12. ADC_BUS_Curr[i] = ADC_ConvertedValue[0];;
  13. i++;
  14. if(i>=64)
  15. i=0;
  16. //对于相电流和母线电流的电阻法测量电流,需要上电读取初始偏执电压
  17. if(logicContr.drive_car==0)//
  18. {
  19. uint32_t sum_U=0;
  20. uint32_t sum_V=0;
  21. uint32_t sum_BUS=0;
  22. uint8_t i;
  23. for(i=0; i < 64; i++)
  24. {
  25. sum_U += ADC_PhaseU_Curr[i];
  26. sum_V += ADC_PhaseV_Curr[i];
  27. sum_BUS += ADC_BUS_Curr[i];
  28. }
  29. ADCSampPare.OffsetPhaseU_Curr = sum_U /64;
  30. ADCSampPare.OffsetPhaseV_Curr = sum_V /64;
  31. ADCSampPare.OffsetBUS_Curr = sum_BUS /64;
  32. }
  33. }
  1. void ADC_Sample(void ) // 放在PWM中断进来后,采样时间为1us左右,(7cycles) 保证在PWM中断后进来采样后为在PWM中间采集相电流
  2. {
  3. // 把电流采集运算偏执后,左移1为,乘2倍后将电流传感器AD采集值转换IQ12格式 -2048到2048
  4. // 此电流乘2倍与硬件差分放大电流2K/1K电阻放大2倍没有关系
  5. // 此传感器量程,100毫欧 运算放大器2倍,硬件0到3.3等效-1.6到1.6,100mr电阻传感器量程大约±8A
  6. // 若硬件修改,按照此算法比例计算
  7. ADCSampPare.BUS_Curr = ((ADC_ConvertedValue[0]-ADCSampPare.OffsetBUS_Curr)<<1)+25; // 补偿一阶低通滤波后的小的插值25经验值
  8. ADCSampPare.PhaseV_Curr = -(ADC_ConvertedValue[1]-ADCSampPare.OffsetPhaseV_Curr)<<1;
  9. ADCSampPare.PhaseU_Curr = -(ADC_ConvertedValue[2]-ADCSampPare.OffsetPhaseU_Curr)<<1;
  10. ADCSampPare.RP_speed_Voltage = ADC_ConvertedValue[3];
  11. ADCSampPare.BUS_Voltage = ADC_ConvertedValue[4];
  12. // 一阶低通数字滤波器 公式查文献百度 历史量0.96 采集新值0.04
  13. ADCSampPare.BUS_CurrF= _IQ10mpy(ADCSampPare.Coeff_filterK1,ADCSampPare.BUS_CurrF)+_IQ10mpy(ADCSampPare.Coeff_filterK2,ADCSampPare.BUS_Curr);
  14. }

DMA

  1. void DMA_Configuration(void)
  2. {
  3. DMA_InitTypeDef DMA_InitStructure;
  4. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);
  5. DMA_DeInit(DMA1_Channel1);
  6. DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address;//设置DMA传输的外设基地址
  7. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;// 设置DMA传输的内存基地址
  8. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//设置DMA的传输方向
  9. //BufferSize=5,因为ADC转换序列有5个通道
  10. //如此设置,使序列1结果放在RegularConvData_Tab[0],序列2结果放在RegularConvData_Tab[1]
  11. DMA_InitStructure.DMA_BufferSize =5;//设置DMA的缓冲区大小为5
  12. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//设置DMA的外设地址增量模式为禁止增量
  13. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//设置DMA的存储器地址增量模式为使能增量
  14. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//设置DMA的外设数据大小为半字(HalfWord)
  15. //表示每次传输的外设数据大小为半字节(16位)
  16. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//设置DMA的存储器数据大小为半字(HalfWord)
  17. //表示每次传输的存储器数据大小为半字节(16位)
  18. //循环模式开启,Buffer写满后,自动回到初始地址开始传输
  19. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  20. DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  21. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  22. DMA_Init(DMA1_Channel1, &DMA_InitStructure);
  23. //配置完成后,启动DMA通道
  24. DMA_Cmd(DMA1_Channel1, ENABLE);
  25. }

电机算法

IF/VF

PMSM的恒压频比V/F控制是保持电机的电压和频率之比固定,即磁通为常数,既不需要转速闭环控制,也不需要进行电流采样,是一种完全的开环控制方式。VF控制有两个明显的不足:不具备负载转矩匹配能力,转速容易产生振荡;最佳V/F曲线的整定比较困难,容易引起电机过电流
相比于恒压频比V/F控制,流频比I/F控制是一种转速开环,电流闭环的控制方式,可以直接控制定子绕组电流幅值,因此这种控制方式不会出现电机过电流现象;通过控制定子绕组电流,使电机具有较好的负载转矩匹配能力,依靠“转矩-功角自平衡”特性,使电机具备较强的抗负载扰动能力。I/F控制框图如下所示
image.png

为什么要进行IF开环控制?

在低速启动阶段,基于反电势无位置算法的位置观测器不能获得精确稳定的位置和速度信号只能对系统进行电流闭环控制,而速度环可由开环位置角发生器替代。

开环之前的预定位?

预定位是给电机定子绕组通静止的电流矢量,产生的电磁转矩会使电机转子旋转到预定的位置,然后从该位置加速启动,通常将转子强拉到0位置,然后从0开始加速。
image.png