栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

STM32

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

STM32

1. 整体流程

1、令iq = 0, id = 合适值,假设电角度θ = 0

2、park逆变换求得iα和iβ

3、确定扇区

4、计算中间变量 X Y Z

5、根据扇区和中间变量得到 t_1st 和 t_2nd

6、当t_1st + t_2nd > Ts 进行 过调制处理。

7、根据扇区和t_1st 和 t_2st,得到Tcm1 Tcm2 Tcm3

8、根据Tcm1 Tcm2 Tcm3设置PWM对应三个通道的比较值

说白了,就是要实现电流环的固定输入的开环控制。

2. STM32CubeMx对PWM需要的配置

参考链接:STM32用cube配置PWM波输出,HAL库PWM_Drive World的博客-CSDN博客_hal库pwm

复习一下定时器挂再哪个APB上:STM32使用定时器实现<获取代码块运算时间>的功能heqiunong的博客-CSDN博客

我们用 TIM1和TIM8做电机的控制, 他们都挂在APB2上,也就是主频168MHz

另外,看下电路原理图

电机1 连的是 PC6 PC7 PC8、电机2 连的是 PA8 PA9 PA10, 因此先用钉子定一手

后续就用TIM1为例,继续讲解。也就是说TIM1的通道1 通道2 通道3 分别对应 电机的三相输入电压。继续看其他的配置,首先看下TIM1总体Mode的配置

 

 然后看看具体参数的配置

参考链接:STM32定时器三种中心对齐计数模式简介_茶话MCU的博客-CSDN博客

关于Counter Mode 有 Up / Down / Center Aligned mode 1、2、3

Center Aligned mode和Up还有Down的主要区别是:STM32的通用定时器和高级定时器除了支持单向的向上或向下计数模式外,还支持中心对齐计数模式,即一个计数周期内分别由向上计数和向下计数两个过程组成。讲更细一点,假设你的ARR寄存器设置为500, 你的计数器寄存器CNT会从0 -> 500,然后再从500 ->0。 假设比较寄存器CCR=400,当计数器寄存器向上计数到400时, PWM输出通道会发生一次电平翻转,当计数器寄存器从500向下计数时,再一次计数到400时,PWM输出通道又会发生一次电平翻转。

在我的理解中,如果默认时低电平, 那么输出波形就是长这样。

 如果默认时是高电平,那么输出波形应该就是长这样

那默认的电平到底是高电平还得低电平呢? 一探究竟。

手册上讲了,计数器和捕获/比较寄存器比较之后,会输出一个OC1REF

可以看到, 输出的时候有两个互补的输出 TIMx_CH1和TIMx_CH1N,继续看这里可能看不出来啥了,看一下Cube上的配置,发现通道可以配置 极性(CH Polarity)和 空闲状态(CH Idle State)

CSDN有好兄弟做了充分的实验专门讲这个:STM32Cube的PWM控制基础篇(三)定时器的PWM设置详解ASWaterbenben的博客-CSDN博客pwm空闲状态,让我们忘掉手册上讲的,就看这个就可以了,非常感谢作者的分享。

说白了, 主要看→ “CH Polarity”,如果 CH Polarity = High, 我们PWM的输出就是,平时是低电平,发生第一次翻转的后变成高电平。(如果是中心对称模式)发生第二次翻转后变成低电平。CH Idle State是说PWM不输出的时候默认的电平,这个其实不太重要。

下面说一下Center Aligned mode 1、2、3是个mode之间的区别

 

箭头表示事件标志

这里CCR=4,ARR=8。当选择中心对齐计数模式1时,只在向下计数过程中发生匹配动作时才置位比较事件标志CC4IF; 当选择中心对齐计数模式2时,只在向上计数过程中发生匹配动作时才置位比较事件标志CC4IF; 当选择中心对齐计数模式3时,在向上/向下计数过程中发生匹配动作时都会置位比较事件标志CC4IF。当然,比较事件标志被置位时可以触发中断或DMA请求。

再看看PWM的其他配置

 这些都没管,那配置就剩下

这个配置的理解前面也提到了,网上的兄弟讲得很到位了:STM32Cube的PWM控制基础篇(三)定时器的PWM设置详解ASWaterbenben的博客-CSDN博客pwm空闲状态

PWM mode1 和 PWM mode2 是互补波形。

Pulse是用于配置初始的脉冲宽度。 占空比=(Pulse/自动重载值)*100%

Fast Mode网上的兄弟也没搞清楚。

If CH Polarity = High, 我们PWM的输出就是,平时是低电平,发生第一次翻转的后变成高电平。(如果是中心对称模式)发生第二次翻转后变成低电平。

CH Idle State是说PWM不输出的时候默认的电平,这个其实不太重要

那么PWM的配置就这样了。

3. STM32工程里面需要做哪些工作

首先工程里面需要打开一下 PWM,代码如下

    HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3);
	HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_2);
	HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_3);

另外,电机芯片(L6230Q),有几个引脚需要使能, STM32_MOTOR_EN1和STM32_MOTOR_DIAGEN1

电机2:STM32_MOTOR_EN2和STM32_MOTOR_DIAGEN2

 

 PA4 PA5 PA11 PA12配置成输出模式, 这些引脚拉高就是Enable电机, 拉低就是Disable电机。

配置完啦,就可以上代码啦

// motor/motor.c
#include "motor.h"

void motor_stop(void){
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_RESET);
}

void motor_start(void){
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
}

// motor/motor.h
#ifndef __USERPROGRAM_MOTOR_H
#define __USERPROGRAM_MOTOR_H

#include "gpio.h"
#include "stm32f405xx.h"

void motor_stop(void);
void motor_start(void);

#endif

// control/current.c
#include "current.h"

#include "arm_math.h"

float iq = 0.0f;
float id = 0.0f;

float theta			= 0.0f;
float sin_theta = 0.0f;
float cos_theta = 0.0f;

float V_Alpha		= 0.0f;
float V_Beta		= 0.0f;	

float SectorJudgmentFactor1 = 0.0;
float SectorJudgmentFactor2 = 0.0;

int	  SectorNum = 0;

float MiddleTerm_X = 0.0f;
float MiddleTerm_Y = 0.0f;
float MiddleTerm_Z = 0.0f;

float t_first = 0.0f;
float t_second = 0.0f;
float t_firstaddsecond = 0.0f;
float t_a = 0.0f;
float t_b = 0.0f;
float t_c = 0.0f;

float t_cm1 = 0.0f;
float t_cm2 = 0.0f;
float t_cm3 = 0.0f;

uint16_t	TIM1_CH1_CMP_VAL = 0,
			TIM1_CH2_CMP_VAL = 0,
			TIM1_CH3_CMP_VAL = 0; // 重新装载到比较寄存器的值
				 
void Get_EncoderOffset_Of_Zero_Electric_Angle_Method_1(void){
	
    float TempVar1 = 0.0f;
	float TempVar2 = 0.0f;

	id = 1;
	
	theta			= 0.0f; 
	sin_theta = arm_sin_f32(theta);
	cos_theta = arm_cos_f32(theta);
	
	
	// Anti-Park	
	V_Alpha	= (id*cos_theta - iq*sin_theta);
  	V_Beta	= (id*sin_theta + iq*cos_theta);	
	
	// 确定扇区
	TempVar1 = V_Alpha * 0.86602540378444f;
	TempVar2 = V_Beta * 0.5f;
	
	SectorJudgmentFactor1 =   TempVar1 - TempVar2;
	SectorJudgmentFactor2 = - TempVar1 - TempVar2;
	
	SectorNum = 0;
	if( V_Beta >= 0)
		SectorNum = SectorNum + 1;
	if( SectorJudgmentFactor1 >= 0 )
		SectorNum = SectorNum + 2;
	if( SectorJudgmentFactor2 >= 0 )
		SectorNum = SectorNum + 4;
	
	// 计算中间变量X Y Z
	MiddleTerm_X = V_Beta;
	MiddleTerm_Y = TempVar1 + TempVar2;
	MiddleTerm_Z = -TempVar1 + TempVar2;
	
	// ? where is √3*Ts/Vdc ?
	
	// 作用时间计算
	switch(SectorNum){
		case 0:
			t_cm1	= 0;
			t_cm2 = 0;
			t_cm3 = 0;
			break;
		case 1:   
			t_first		= MiddleTerm_Z;
			t_second	= MiddleTerm_Y;
			
			//	过调制处理
			if(t_first + t_second > 1){
				t_firstaddsecond = t_first + t_second;
				t_first		= t_first		/ t_firstaddsecond;
				t_second	= t_second	/ t_firstaddsecond;  
			}
			
			t_a = (1 - t_first - t_second)*0.5f;		// ? why not (Ts - t_first - t_second) / 4 
			t_b = t_a + t_first;
			t_c = t_b + t_second;
			
			t_cm1	= t_b;
			t_cm2 = t_a;
			t_cm3 = t_c;
			break;
		case 2:   
			t_first		= MiddleTerm_Y;
			t_second	= -MiddleTerm_X;
			//	过调制处理
			if(t_first + t_second > 1){
				t_firstaddsecond = t_first + t_second;
				t_first		= t_first		/ t_firstaddsecond;
				t_second	= t_second	/ t_firstaddsecond;  
			}		
			t_a = (1 - t_first - t_second)*0.5f;		// ? why not (Ts - t_first - t_second) / 4 
			t_b = t_a + t_first;
			t_c = t_b + t_second;
			t_cm1	= t_a;
			t_cm2 = t_c;
			t_cm3 = t_b;		
			break;
		case 3:   
			t_first		= -MiddleTerm_Z;
			t_second	= MiddleTerm_X;
		
			//	过调制处理
			if(t_first + t_second > 1){
				t_firstaddsecond = t_first + t_second;
				t_first		= t_first		/ t_firstaddsecond;
				t_second	= t_second	/ t_firstaddsecond;  
			}
			
			t_a = (1 - t_first - t_second)*0.5f;		// ? why not (Ts - t_first - t_second) / 4 
			t_b = t_a + t_first;
			t_c = t_b + t_second;
			t_cm1	= t_a;
			t_cm2 = t_b;
			t_cm3 = t_c;		
			break;
		case 4:   
			t_first		= -MiddleTerm_X;
			t_second	= MiddleTerm_Z;
			//	过调制处理
			if(t_first + t_second > 1){
				t_firstaddsecond = t_first + t_second;
				t_first		= t_first		/ t_firstaddsecond;
				t_second	= t_second	/ t_firstaddsecond;  
			}		
			t_a = (1 - t_first - t_second)*0.5f;		// ? why not (Ts - t_first - t_second) / 4 
			t_b = t_a + t_first;
			t_c = t_b + t_second;
			t_cm1	= t_c;
			t_cm2 = t_b;
			t_cm3 = t_a;		
			break;
    case 5:   
			t_first		= MiddleTerm_X;
			t_second	= -MiddleTerm_Y;
			//	过调制处理
			if(t_first + t_second > 1){
				t_firstaddsecond = t_first + t_second;
				t_first		= t_first		/ t_firstaddsecond;
				t_second	= t_second	/ t_firstaddsecond;  
			}		
			t_a = (1 - t_first - t_second)*0.5f;		// ? why not (Ts - t_first - t_second) / 4 
			t_b = t_a + t_first;
			t_c = t_b + t_second;
			t_cm1	= t_b;
			t_cm2 = t_c;
			t_cm3 = t_a;				
			break;
    case 6:   
			t_first		= -MiddleTerm_Y;
			t_second	= -MiddleTerm_Z;
			//	过调制处理
			if(t_first + t_second > 1){
				t_firstaddsecond = t_first + t_second;
				t_first		= t_first		/ t_firstaddsecond;
				t_second	= t_second	/ t_firstaddsecond;  
			}		
			t_a = (1 - t_first - t_second)*0.5f;		// ? why not (Ts - t_first - t_second) / 4 
			t_b = t_a + t_first;
			t_c = t_b + t_second;
			t_cm1	= t_c;
			t_cm2 = t_a;
			t_cm3 = t_b;				
			break;
		
		default:
			break;
	}
	
	TIM1_CH1_CMP_VAL = (uint16_t)((t_cm1)*500.0f);
	TIM1_CH2_CMP_VAL = (uint16_t)((t_cm2)*500.0f);
	TIM1_CH3_CMP_VAL = (uint16_t)((t_cm3)*500.0f);		

	TIM1->CCR1 = TIM1_CH1_CMP_VAL;
	TIM1->CCR2 = TIM1_CH2_CMP_VAL;
	TIM1->CCR3 = TIM1_CH3_CMP_VAL;		 	
	
	
}



// control/current.h
#ifndef __USERPROGRAM_CURRENT_H
#define __USERPROGRAM_CURRENT_H

#include "stm32f405xx.h"
#include "tim.h"

void Get_EncoderOffset_Of_Zero_Electric_Angle_Method_1(void);

#endif




// main.c
// 部分代码
 
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_TIM3_Init();
  MX_TIM2_Init();
  MX_SPI1_Init();
  MX_SPI2_Init();
  MX_SPI3_Init();
  MX_ADC1_Init();
  MX_ADC2_Init();
  MX_TIM1_Init();
  MX_TIM8_Init();
  
	HAL_TIM_Base_Start_IT(&htim3);
	userinit();
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); 	// 使能串口空闲中断
    HAL_UART_Receive_DMA(&huart1, AngelaRxBuffer,16);			// 启串口DMA接收
	
	// 两轴电机对应的PWM使能
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);		// ★★★ ← 重点看这里
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3);
	HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_2);
	HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_3);
	
	// HAL_UART_Receive_IT(&huart1,value,16);
  
  

一些理解性的东西:

1、比较/捕获 CCR寄存器的值,并不是越大占空比就越大,而是越小占空比越到

2、 令iq=1,id=1,θ=0的方法。与直接A相电压输入高电平,B相和C相电压输入低电平的方法,是等价的。

3、关于频率的问题, 168MHz主频的TIM1和TIM8,经过2分频后,变成了84MHz,如果ARR计数周期设置为500的话,计数是中心对齐模式1,0 → 500 → 0。也就是完成一次PWM操作的频率为 84MHz/1000 = 84kHz。但电流环控制频率不必达到 84kHz, 比如你10k的执行频率, 那么就是说,可能会有8个周期,你的PWM输出是一样的,这很容易理解。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/934396.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号