pyxyn
发表于 2025-5-27 13:27:12
王昱顺 发表于 2025-5-27 13:13
开环控制的话直接给Ud就行,范围就是0~90(你设置的幅值)
#include "STC32G.h"
#include <math.h>
// 系统配置
#define SYS_CLK 32768000UL // 使用STC出厂预校准的22.1184MHz时钟
#define PWM_PERIOD 1420 // 中心对齐模式计数器值
#define DEAD_TIME 70 // 死区时间(约1uS@22.1184MHz)
#define PI 3.141592653589793
#define PWM1_1 0x00 //P:P1.0N:P1.1
#define PWM1_2 0x01 //P:P2.0N:P2.1
#define PWM1_3 0x02 //P:P6.0N:P6.1
#define PWM2_1 0x00 //P:P1.2/P5.4N:P1.3
#define PWM2_2 0x04 //P:P2.2N:P2.3
#define PWM2_3 0x08 //P:P6.2N:P6.3
#define PWM3_1 0x00 //P:P1.4N:P1.5
#define PWM3_2 0x10 //P:P2.4N:P2.5
#define PWM3_3 0x20 //P:P6.4N:P6.5
#define PWM4_1 0x00 //P:P1.6N:P1.7
#define PWM4_2 0x40 //P:P2.6N:P2.7
#define PWM4_3 0x80 //P:P6.6N:P6.7
#define PWM4_4 0xC0 //P:P3.4N:P3.3
#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long
// 全局变量
volatile float freq = 50.0; // 输出频率(5~100Hz可调)
volatile float theta = 0.0; // 当前角度
volatile float delta_theta; // 角度增量
unsigned charr_f;
unsigned charr_c;
unsigned charr_t;
#define voltage_power_supply 90.0 // 最高电压限制
#define _constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
float Ualpha, Ubeta = 0, Ua = 0, Ub = 0, Uc = 0;
void send(unsigned char d[],unsigned int m);
// PWM初始化
void PWMA_Init(void)
{
PWMA_ENO = 0x00;
PWMA_CCER1 = 0x00; //写 CCMRx 前必须先清零 CCxE 关闭通道
PWMA_CCER2 = 0x00;
PWMA_CCMR1 = 0x68; //通道模式配置
PWMA_CCMR2 = 0x68;
PWMA_CCMR3 = 0x68;
PWMA_ARRH =1410/256;
PWMA_ARRL =1410%256;
PWMA_DTR=70; // 设置死区时间
PWMA_CCER1 = 0x55; //配置通道输出使能和极性
PWMA_CCER2 = 0x05;
PWMA_CCMR1 = 0x60; // 通道模式配置, PWM模式1, 预装载允许
PWMA_CCMR2 = 0x60;
PWMA_CCMR3 = 0x60;
PWMA_ENO= 0x3F;//IO输出PWM允许
//高级PWM输出脚会自动设置为推挽输出模式
PWMA_PS = 0x00;//高级 PWM 通道输出脚选择位
PWMA_PS |= PWM1_2; //选择 PWM1_3 通道
PWMA_PS |= PWM2_2; //选择 PWM2_3 通道
PWMA_PS |= PWM3_2; //选择 PWM3_3 通道
PWMA_BKR = 0x80; //使能主输出
PWMA_CR1 = 0x81; //使能ARR预装载,开始计时
}
// 定时器0初始化(1ms中断)
void Timer0_Init(void)
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初始值
TH0 = 0x80; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void ledcWrite(u8 in, u16 Dat)
{
switch (in)
{
case 0:
{
PWMA_CCR1H = (u8)(Dat >> 8); // 设置占空比
PWMA_CCR1L = (u8)(Dat);
}
break;
case 1:
{
PWMA_CCR2H = (u8)(Dat >> 8); // 设置占空比
PWMA_CCR2L = (u8)(Dat);
}
break;
case 2:
{
PWMA_CCR3H = (u8)(Dat >> 8); // 设置占空比
PWMA_CCR3L = (u8)(Dat);
}
break;
default:
break;
}
}
// 设置PWM到控制器输出
void setPwm(float Ua, float Ub, float Uc)
{
// 变量声明
float dc_a, dc_b, dc_c;
u16 dutyA,dutyB,dutyC,ty;
// 限制上限
Ua = _constrain(Ua, 0.0f, voltage_power_supply);
Ub = _constrain(Ub, 0.0f, voltage_power_supply);
Uc = _constrain(Uc, 0.0f, voltage_power_supply);
// 计算占空比
// 限制占空比从0到1
dc_a = _constrain(Ua / voltage_power_supply, 0.0f, 1.0f);
dc_b = _constrain(Ub / voltage_power_supply, 0.0f, 1.0f);
dc_c = _constrain(Uc / voltage_power_supply, 0.0f, 1.0f);
dutyA=(u16)(dc_a * 1420);dutyB=(u16)(dc_b *1420);dutyC=(u16)(dc_c * 1420);
// 写入PWM到PWM 0 1 2 通道
ledcWrite(0, dutyA);
ledcWrite(1, dutyB);
ledcWrite(2, dutyC);
ty=(u16)(theta*180/PI);
r_t='a';r_t=dutyA/1000+0x30;r_t=(dutyA%1000)/100+0x30;r_t=(dutyA%100)/10+0x30;r_t=dutyA%10+0x30;
r_t='b';r_t=dutyB/1000+0x30;r_t=(dutyB%1000)/100+0x30;r_t=(dutyB%100)/10+0x30;r_t=dutyB%10+0x30;
r_t='c';r_t=dutyC/1000+0x30;r_t=(dutyC%1000)/100+0x30;r_t=(dutyC%100)/10+0x30;r_t=dutyC%10+0x30;
r_t='t';r_t=ty/100+0x30;r_t=(ty%100)/10+0x30;r_t=ty%10+0x30;
r_t=0x0d;r_t=0x0a;
send(r_t,21);
}
// 归一化角度到
float _normalizeAngle(float angle)
{
float a = fmod(angle, 2 * PI); // 取余运算可以用于归一化,列出特殊值例子算便知
return a >= 0 ? a : (a + 2 * PI);
// 三目运算符。格式:condition ? expr1 : expr2
// 其中,condition 是要求值的条件表达式,如果条件成立,则返回 expr1 的值,否则返回 expr2 的值。可以将三目运算符视为 if-else 语句的简化形式。
// fmod 函数的余数的符号与除数相同。因此,当 angle 的值为负数时,余数的符号将与 _2PI 的符号相反。也就是说,如果 angle 的值小于 0 且 _2PI 的值为正数,则 fmod(angle, _2PI) 的余数将为负数。
// 例如,当 angle 的值为 -PI/2,_2PI 的值为 2PI 时,fmod(angle, _2PI) 将返回一个负数。在这种情况下,可以通过将负数的余数加上 _2PI 来将角度归一化到 的范围内,以确保角度的值始终为正数。
}
#define _Conv 1.15470053838f
// 2023年9月22日添加SVPWM
void setTorque(float Uq, float Ud, float angle_el)
{
float Max = 0, Min = 0, Adder = 0;
// 幅值限位
Uq = _constrain(Uq, -(voltage_power_supply) / 2, (voltage_power_supply) / 2);
Ud = _constrain(Ud, -(voltage_power_supply) / 2, (voltage_power_supply) / 2);
angle_el = _normalizeAngle(angle_el);
//angle_el=theta;
// 帕克逆变换
Ualpha = (Ud * cos(angle_el)) - (Uq * sin(angle_el));
Ubeta = (Uq * cos(angle_el)) + (Ud * sin(angle_el));
// 克拉克逆变换
Ua = Ualpha + voltage_power_supply / 2;
Ub = (sqrt(3) * Ubeta - Ualpha) / 2 + voltage_power_supply / 2;
Uc = (-Ualpha - sqrt(3) * Ubeta) / 2 + voltage_power_supply / 2;
// Fast_Svpwm实现
Max = Ua > Ub ? (Ua > Uc ? Ua : Uc) : (Ub > Uc ? Ub : Uc);
Min = Ua < Ub ? (Ua < Uc ? Ua : Uc) : (Ub < Uc ? Ub : Uc);
Adder = -(Max + Min) / 2;
Ua = (Ua + Adder) * _Conv, Ub = (Ub + Adder) * _Conv, Uc = (Uc + Adder) * _Conv;
setPwm(Ua, Ub, Uc);
}
//////////串口1通信初始化
void serial_init()
{
unsigned int i;
for(i=0;i<100;i++) r_f=0;
r_c=0;
}
void send(unsigned char d[],unsigned int m)
{
unsigned int i;
for(i=0;i<m;i++)
{
SBUF = d; //发一个字节
while(!TI);
TI=0;
}
}
/********************* UART1中断函数************************/
void UART1_int ( ) interrupt 4
{
if(RI)
{
r_f = SBUF;//保存一个字节
if(r_c>=100) r_c=0;
else r_c++;
RI = 0;
}
}
void UartInit(void) //115200bps@32.768MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR |= 0x04; //定时器时钟1T模式
T2L = 0xB9; //设置定时初始值
T2H = 0xFF; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
}
// 主函数
void main()
{
WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P2M0=0x00;P2M1=0x00;
PWMA_Init();
Timer0_Init();
UartInit();
ET0=1;
EA = 1;
serial_init();
while(1)
{
delta_theta = 2 * PI * freq * 0.002; // 1ms中断对应角度增量
}
}
// 定时器0中断服务函数
void Timer0_ISR() interrupt 1
{
theta += delta_theta; // 更新角度
if(theta >= 2*PI) theta = 0;
setTorque(0,70,theta);
}
pyxyn
发表于 2025-5-27 13:29:03
王昱顺 发表于 2025-5-27 13:13
开环控制的话直接给Ud就行,范围就是0~90(你设置的幅值)
#include "STC32G.h"
#include <math.h>
// 系统配置
#define SYS_CLK 32768000UL // 使用STC出厂预校准的22.1184MHz时钟
#define PWM_PERIOD 1420 // 中心对齐模式计数器值
#define DEAD_TIME 70 // 死区时间(约1uS@22.1184MHz)
#define PI 3.141592653589793
#define PWM1_1 0x00 //P:P1.0N:P1.1
#define PWM1_2 0x01 //P:P2.0N:P2.1
#define PWM1_3 0x02 //P:P6.0N:P6.1
#define PWM2_1 0x00 //P:P1.2/P5.4N:P1.3
#define PWM2_2 0x04 //P:P2.2N:P2.3
#define PWM2_3 0x08 //P:P6.2N:P6.3
#define PWM3_1 0x00 //P:P1.4N:P1.5
#define PWM3_2 0x10 //P:P2.4N:P2.5
#define PWM3_3 0x20 //P:P6.4N:P6.5
#define PWM4_1 0x00 //P:P1.6N:P1.7
#define PWM4_2 0x40 //P:P2.6N:P2.7
#define PWM4_3 0x80 //P:P6.6N:P6.7
#define PWM4_4 0xC0 //P:P3.4N:P3.3
#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long
// 全局变量
volatile float freq = 50.0; // 输出频率(5~100Hz可调)
volatile float theta = 0.0; // 当前角度
volatile float delta_theta; // 角度增量
unsigned charr_f;
unsigned charr_c;
unsigned charr_t;
#define voltage_power_supply 90.0 // 最高电压限制
#define _constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
float Ualpha, Ubeta = 0, Ua = 0, Ub = 0, Uc = 0;
void send(unsigned char d[],unsigned int m);
// PWM初始化
void PWMA_Init(void)
{
PWMA_ENO = 0x00;
PWMA_CCER1 = 0x00; //写 CCMRx 前必须先清零 CCxE 关闭通道
PWMA_CCER2 = 0x00;
PWMA_CCMR1 = 0x68; //通道模式配置
PWMA_CCMR2 = 0x68;
PWMA_CCMR3 = 0x68;
PWMA_ARRH =1410/256;
PWMA_ARRL =1410%256;
PWMA_DTR=70; // 设置死区时间
PWMA_CCER1 = 0x55; //配置通道输出使能和极性
PWMA_CCER2 = 0x05;
PWMA_CCMR1 = 0x60; // 通道模式配置, PWM模式1, 预装载允许
PWMA_CCMR2 = 0x60;
PWMA_CCMR3 = 0x60;
PWMA_ENO= 0x3F;//IO输出PWM允许
//高级PWM输出脚会自动设置为推挽输出模式
PWMA_PS = 0x00;//高级 PWM 通道输出脚选择位
PWMA_PS |= PWM1_2; //选择 PWM1_3 通道
PWMA_PS |= PWM2_2; //选择 PWM2_3 通道
PWMA_PS |= PWM3_2; //选择 PWM3_3 通道
PWMA_BKR = 0x80; //使能主输出
PWMA_CR1 = 0x81; //使能ARR预装载,开始计时
}
// 定时器0初始化(1ms中断)
void Timer0_Init(void)
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初始值
TH0 = 0x80; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void ledcWrite(u8 in, u16 Dat)
{
switch (in)
{
case 0:
{
PWMA_CCR1H = (u8)(Dat >> 8); // 设置占空比
PWMA_CCR1L = (u8)(Dat);
}
break;
case 1:
{
PWMA_CCR2H = (u8)(Dat >> 8); // 设置占空比
PWMA_CCR2L = (u8)(Dat);
}
break;
case 2:
{
PWMA_CCR3H = (u8)(Dat >> 8); // 设置占空比
PWMA_CCR3L = (u8)(Dat);
}
break;
default:
break;
}
}
// 设置PWM到控制器输出
void setPwm(float Ua, float Ub, float Uc)
{
// 变量声明
float dc_a, dc_b, dc_c;
u16 dutyA,dutyB,dutyC,ty;
// 限制上限
Ua = _constrain(Ua, 0.0f, voltage_power_supply);
Ub = _constrain(Ub, 0.0f, voltage_power_supply);
Uc = _constrain(Uc, 0.0f, voltage_power_supply);
// 计算占空比
// 限制占空比从0到1
dc_a = _constrain(Ua / voltage_power_supply, 0.0f, 1.0f);
dc_b = _constrain(Ub / voltage_power_supply, 0.0f, 1.0f);
dc_c = _constrain(Uc / voltage_power_supply, 0.0f, 1.0f);
dutyA=(u16)(dc_a * 1420);dutyB=(u16)(dc_b *1420);dutyC=(u16)(dc_c * 1420);
// 写入PWM到PWM 0 1 2 通道
ledcWrite(0, dutyA);
ledcWrite(1, dutyB);
ledcWrite(2, dutyC);
ty=(u16)(theta*180/PI);
r_t='a';r_t=dutyA/1000+0x30;r_t=(dutyA%1000)/100+0x30;r_t=(dutyA%100)/10+0x30;r_t=dutyA%10+0x30;
r_t='b';r_t=dutyB/1000+0x30;r_t=(dutyB%1000)/100+0x30;r_t=(dutyB%100)/10+0x30;r_t=dutyB%10+0x30;
r_t='c';r_t=dutyC/1000+0x30;r_t=(dutyC%1000)/100+0x30;r_t=(dutyC%100)/10+0x30;r_t=dutyC%10+0x30;
r_t='t';r_t=ty/100+0x30;r_t=(ty%100)/10+0x30;r_t=ty%10+0x30;
r_t=0x0d;r_t=0x0a;
send(r_t,21);
}
// 归一化角度到
float _normalizeAngle(float angle)
{
float a = fmod(angle, 2 * PI); // 取余运算可以用于归一化,列出特殊值例子算便知
return a >= 0 ? a : (a + 2 * PI);
// 三目运算符。格式:condition ? expr1 : expr2
// 其中,condition 是要求值的条件表达式,如果条件成立,则返回 expr1 的值,否则返回 expr2 的值。可以将三目运算符视为 if-else 语句的简化形式。
// fmod 函数的余数的符号与除数相同。因此,当 angle 的值为负数时,余数的符号将与 _2PI 的符号相反。也就是说,如果 angle 的值小于 0 且 _2PI 的值为正数,则 fmod(angle, _2PI) 的余数将为负数。
// 例如,当 angle 的值为 -PI/2,_2PI 的值为 2PI 时,fmod(angle, _2PI) 将返回一个负数。在这种情况下,可以通过将负数的余数加上 _2PI 来将角度归一化到 的范围内,以确保角度的值始终为正数。
}
#define _Conv 1.15470053838f
// 2023年9月22日添加SVPWM
void setTorque(float Uq, float Ud, float angle_el)
{
float Max = 0, Min = 0, Adder = 0;
// 幅值限位
Uq = _constrain(Uq, -(voltage_power_supply) / 2, (voltage_power_supply) / 2);
Ud = _constrain(Ud, -(voltage_power_supply) / 2, (voltage_power_supply) / 2);
angle_el = _normalizeAngle(angle_el);
//angle_el=theta;
// 帕克逆变换
Ualpha = (Ud * cos(angle_el)) - (Uq * sin(angle_el));
Ubeta = (Uq * cos(angle_el)) + (Ud * sin(angle_el));
// 克拉克逆变换
Ua = Ualpha + voltage_power_supply / 2;
Ub = (sqrt(3) * Ubeta - Ualpha) / 2 + voltage_power_supply / 2;
Uc = (-Ualpha - sqrt(3) * Ubeta) / 2 + voltage_power_supply / 2;
// Fast_Svpwm实现
Max = Ua > Ub ? (Ua > Uc ? Ua : Uc) : (Ub > Uc ? Ub : Uc);
Min = Ua < Ub ? (Ua < Uc ? Ua : Uc) : (Ub < Uc ? Ub : Uc);
Adder = -(Max + Min) / 2;
Ua = (Ua + Adder) * _Conv, Ub = (Ub + Adder) * _Conv, Uc = (Uc + Adder) * _Conv;
setPwm(Ua, Ub, Uc);
}
//////////串口1通信初始化
void serial_init()
{
unsigned int i;
for(i=0;i<100;i++) r_f=0;
r_c=0;
}
void send(unsigned char d[],unsigned int m)
{
unsigned int i;
for(i=0;i<m;i++)
{
SBUF = d; //发一个字节
while(!TI);
TI=0;
}
}
/********************* UART1中断函数************************/
void UART1_int ( ) interrupt 4
{
if(RI)
{
r_f = SBUF;//保存一个字节
if(r_c>=100) r_c=0;
else r_c++;
RI = 0;
}
}
void UartInit(void) //115200bps@32.768MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR |= 0x04; //定时器时钟1T模式
T2L = 0xB9; //设置定时初始值
T2H = 0xFF; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
}
// 主函数
void main()
{
WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P2M0=0x00;P2M1=0x00;
PWMA_Init();
Timer0_Init();
UartInit();
ET0=1;
EA = 1;
serial_init();
while(1)
{
delta_theta = 2 * PI * freq * 0.002; // 1ms中断对应角度增量
}
}
// 定时器0中断服务函数
void Timer0_ISR() interrupt 1
{
theta += delta_theta; // 更新角度
if(theta >= 2*PI) theta = 0;
setTorque(0,70,theta);
}
辛苦王工帮我看看,这个程序有无问题?更改70的数值,改变幅度(还没有去写这部分程序)。谢谢。
王昱顺
发表于 2025-5-27 16:20:19
pyxyn 发表于 2025-5-27 13:29
#include "STC32G.h"
#include
目前来看是没什么大问题的,实际效果需要自己实物上示波器测测才知道
一些比较隐蔽的问题单纯看代码也不是很好发现
pyxyn
发表于 2025-5-28 10:55:49
王昱顺 发表于 2025-5-27 16:20
目前来看是没什么大问题的,实际效果需要自己实物上示波器测测才知道
一些比较隐蔽的问题单纯看代码也不 ...
感谢,我理解是不是Uq是控制输出电压幅度的,Ud是控制转矩的?
王昱顺
发表于 2025-5-28 11:01:17
pyxyn 发表于 2025-5-28 10:55
感谢,我理解是不是Uq是控制输出电压幅度的,Ud是控制转矩的?
并不是,Uq是控制电机切线方向的力,Ud是控制电机法向方向上的力
这两个力方向上成正交关系
pyxyn
发表于 2025-5-28 12:18:58
王昱顺 发表于 2025-5-28 11:01
并不是,Uq是控制电机切线方向的力,Ud是控制电机法向方向上的力
这两个力方向上成正交关系 ...
我感觉调Uq和Ud都可以调节输出的幅度,我需要开环对电压幅度进行控制,是不是Uq和Ud同进去调节?还有一个问题,我现在看到的波形上面是马鞍波形,但是下面是一条直的,和您发的那个图中下面也是马鞍波不一样,这样是正常的吗?
王昱顺
发表于 2025-5-28 14:11:25
pyxyn 发表于 2025-5-28 12:18
我感觉调Uq和Ud都可以调节输出的幅度,我需要开环对电压幅度进行控制,是不是Uq和Ud同进去调节?还有一个 ...
我发的那个测量不出来,需要使用rc滤波电路才行
如果是滤波后的波形下边是平的,说明幅值给的有点大了,要再小一点
开环状态想要控制幅度,可以随便挑Ud或者Uq来控制,效果都一样的
pyxyn
发表于 2025-5-28 14:50:49
王昱顺 发表于 2025-5-28 14:11
我发的那个测量不出来,需要使用rc滤波电路才行
如果是滤波后的波形下边是平的,说明幅值给的有点大了, ...
Ud从0-90我都试过了,下边也不算是平的,有一点点向上的角度,然后就突然变大。我用的是两级RC,22K、103和2K、104来做的滤波,之前生成的SPWM也是用这个滤波的,效果非常好。
pyxyn
发表于 7 天前
王昱顺 发表于 2025-5-28 14:11
我发的那个测量不出来,需要使用rc滤波电路才行
如果是滤波后的波形下边是平的,说明幅值给的有点大了, ...
王总好,我现在是90V母线输入,但是输出电压最大值只能调到50V左右,这个需要改下程序的哪些地方?谢谢。输出功率我设置是5-100Hz。谢谢
王昱顺
发表于 7 天前
pyxyn 发表于 2025-6-10 09:21
王总好,我现在是90V母线输入,但是输出电压最大值只能调到50V左右,这个需要改下程序的哪些地方?谢谢。 ...
因为输出的时候会抬高基准到电源电压的1/2,所以输出的这个电压应该是差不多满了的
输出的电压是正负变化的
如果还想增加可以试试再拉高Uq或者Ud,不过可能会进入输出的过调制状态
页:
2
3
4
5
6
7
8
9
10
11
[12]
13