This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] CCS/MSP430G2553:PID 控制算法

Guru**** 2582405 points
Other Parts Discussed in Thread: MSP-IQMATHLIB

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/775080/ccs-msp430g2553-pid-control-algorithm

器件型号:MSP430G2553
主题中讨论的其他器件:MSP-IQMATHLIB

工具/软件:Code Composer Studio

我已经编写了一个用于在电机上实现 PID 控制的代码。 代码如下:

#include 
/**
* main.c
*/
int main (void){

WDTCTL = WDTPW + WDTHOLD;//禁用看门狗计时器

P2DIR = BIT2;//选择和设置计时器模块的输出位
P2SEL = BIT2;
BCSCTL1 = CALBC1_16MHz;//将时钟频率设置为16MHz


;/CACT_TAC1MHz 计时器= CC1000;/CACT_TACT_TALP0;//将时钟频率设置为16MHz;
//为要生成的 PWM 总时间提供计数
TA1CCTL1 = OUTMOD_7;//将计时器模式设置为在设置复位模式下计数
TA1CTL = MC_1 + ID_0 + TASSEL_2 + TACLR; //MC_1是模式控制1它意味着定时器在增模式中计数
//ID_0是时钟分频器1、TASSEL_2选择 SMCLK 和 TACLR 清除定时器(这使定时器从0开始计数)

//ADC 模块
ADC10CTL0 = ADC10ON + ADC10IE + SREF_1 + REFON + REMSC 5V + REF2 + REF2 + MCU_5V; //启用中断,将基准设置为2.5V
ADC10CTL1 = ADC10DIV_0 + CONSEQ_2;//时钟分频和 CONSEQ_2用于单通道重复转换
ADC10AE0 = INCH_0;//通道0上可用的输入(即引脚 A0

ADC10CTL0 |= ENC + ADC10SC;//开始转换

(BIS_1

),同时启用中断




#pragma vector = ADC10_Vector
__interrupt void ADC10_interrupt (void){

P2DIR |= 0x01;//set P2.0
P2OUT |= 0x01;//将 P2.0设置为高电平(用于检查循环运行所需的时间)
float SET_point = 2.24、t_on;//设置点为2.24V
float Kp = 1、IMKp = 1、Imax = 2.24、IMKp = 1、Imax = 2.24、IMKp = 1、IMKp = 2.24、IM pi_term、pi_min = 0、pi_max = 2;
静态浮点 i_term = 0;

int adc_val = ADC10MEM;//将 ADC 值分配给变量
float adc_out =(float)(adc_val * 2.5/1024);//根据基准将十六进制值缩放到电压、给定
float 错误= set_point - adc_out; //从 ADC 获取错误和设定点

//比例项
p_term = Kp *错误;

//积分项
静态浮点 i_temp = 0;
i_temp = I_temp +错误;//计算积分项

if (i_temp > Imax)//积分抗饱和
i_temp = Imax;

否则 if (i_temp < in)/i_imp
= iimp-in;

I_term = ki * I_temp;//final I_term

//spi term
/* float d_temp = error;//检查这是否存在静态
d_term = Kd *(d_temp-error);//检查计算*//PI

term
pi_term = p_term + i_term;

//剪切输出 pi_term = p_if (pi_term
= p_max)


= p_term = p_term = p_max;// p_term = p_term = p_term = p_max (pi_term = p_term = p_min)


T_ON =(pi_term * 1000)/pi_max;

TA1CCR1 = t_on;//根据 pi_term
P2OUT ^= 0x01设置导通时间;//将 P2.0设置为低电平(用于检查循环运行所需的时间)
_BIS_SR (GIE);//全局中断使能

}

我所面临的问题是:

1) 1)我必须计算 PI 环路运行所需的时间。 为此、我在控制环路的开头将一个引脚设为高电平、并在末尾将其切换 我已经检查了 DSO 上该引脚的输出。 令人惊讶的是、中断只被调用一次。 因此、我在运行代码时只会在示波器上看到一个尖峰。

2) 2)因此、我尝试将控制环路置于 while (1){}循环中。 现在、当我重复这里的相同过程时、要切换引脚、我观察到完全不同的意外行为。 当我在开始时将引脚保持为高电平、并在结束时切换时、DSO 会显示一个时序图、其中高电平脉冲持续280微秒的较长时间、低电平脉冲持续0.5微秒。 这似乎是正确的。 但是、当我以另一种方式执行它时、即在环路的开头、将引脚设置为低电平。 DSO 向我显示占空比为50%的脉冲、时间周期为(2*280)微秒。

#include 
/**
* main.c
*/
int main (void){

WDTCTL = WDTPW + WDTHOLD;//禁用看门狗计时器

P2DIR = BIT2;//选择和设置计时器模块的输出位
P2SEL = BIT2;
BCSCTL1 = CALBC1_16MHz;//将时钟频率设置为16MHz


;/CACT_TAC1MHz 计时器= CC1000;/CACT_TACT_TALP0;//将时钟频率设置为16MHz;
//为要生成的 PWM 总时间提供计数
TA1CCTL1 = OUTMOD_7;//将计时器模式设置为在设置复位模式下计数
TA1CTL = MC_1 + ID_0 + TASSEL_2 + TACLR; //MC_1是模式控制1它意味着定时器在增模式中计数
//ID_0是时钟分频器1、TASSEL_2选择 SMCLK 和 TACLR 清除定时器(这使定时器从0开始计数)

//ADC 模块
ADC10CTL0 = ADC10ON + ADC10IE + SREF_1 + REFON + REMSC 5V + REF2 + REF2 + MCU_5V; //启用中断、将基准设置为2.5V
ADC10CTL1 = ADC10DIV_0 + CONSEQ_2;//时钟分频和 CONSEQ_2用于单通道重复转换
ADC10AE0 = INCH_0;//通道0上可用的输入如 A0引脚 A0

ADC10CTL0 |= ENC + ADC10SC;//开始转换

while


= 0xP201;//设置 P2DIR = 0x00;//
//将 P2.0设置为低电平(用于检查循环运行所需的时间)
float set_point = 2.24、t_on;//设置点为2.24V
float Kp = 1、ki = 1、p_term、Imax = 2.24、Imin = 0、 pi_term、pi_min = 0、pi_max = 2;
静态浮点 i_term = 0;

int adc_val = ADC10MEM;//将 ADC 值分配给变量
float adc_out =(float)(adc_val * 2.5/1024);//根据基准将十六进制值缩放到电压、给定
float 错误= set_point - adc_out; //从 ADC 获取错误和设定点

//比例项
p_term = Kp *错误;

//积分项
静态浮点 i_temp = 0;
i_temp = I_temp +错误;//计算积分项

if (i_temp > Imax)//积分抗饱和
i_temp = Imax;

否则 if (i_temp < in)/i_imp
= iimp-in;

I_term = ki * I_temp;//final I_term

//spi term
/* float d_temp = error;//检查这是否存在静态
d_term = Kd *(d_temp-error);//检查计算*//PI

term
pi_term = p_term + i_term;

//剪切输出 pi_term = p_if (pi_term
= p_max)


= p_term = p_term = p_max;// p_term = p_term = p_term = p_max (pi_term = p_term = p_min)


T_on =(pi_term * 1000)/pi_max;

TA1CCR1 = t_on;//根据 pi_term
P2OUT ^= 0x01设置导通时间;//设置 P2.0翻转(用于检查循环运行所需的时间)
//_BIS_SR (GIE);//全局中断启用
}

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    嗨、Shariva、

    我相信您会获得50%的占空比、因为您正在尝试使用或清除该位。  基本上、您只需启用 GPIO、最后只需切换 GPIO   

    P2OUT |= 0x00;//将 P2.0设置为低电平(用于检查循环运行所需的时间)
    
    //需要 P2OUT
    
    &=~0x01;//将 P2.0设置为低电平(用于检查循环运行所需的时间) 

    谢谢、

    JD

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    谢谢 JD

    如前所述、当引脚在开始时设置为低电平时、会出现50%占空比。 那么、当它在开始时设置为高电平时、如何获得不同的结果呢? 尽管我同意您提供的解决方案。 我一定会尝试一下。

    另一个问题是、XOR、NOT 和是所有逻辑运算符。 那么、是否使用其中任何一个来切换引脚是否重要?


    此外、控制环路是否看起来正确? 我之所以提出这个问题、是因为当我在 CCS 上构建它时、没有错误或警告、但它在我必须在其上实现它的系统上不起作用。 积分项应使最终 PWM 上升至其最大值、但它在中途达到饱和。


    每行都会弹出另一个消息:乘法是在没有硬件乘法器的器件上完成的。 是否有解决办法?

    谢谢、此致、

    Shariva

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    嗨、Shariva、

    [引述 USER="Shariva Dhekane">正如我之前提到的、这个50%的占空比出现在引脚开始被设定为低电平时。 那么、当它在开始时设置为高电平时、如何获得不同的结果呢? 尽管我同意您提供的解决方案。 我一定会尝试这一点。[/引述]

    您是否尝试过我的解决方案?  我想问题是、您实际上不是使用上面的代码将引脚设置为低电平。   

    [引用 user="Shariva Dhekane"]另一个问题是 XOR、NOT 和、并且都是逻辑运算符。 因此、是否使用其中任何一个来切换引脚是否重要?

    通常使用"切换" XOR、因为它可以轻松地翻转当前状态。  其他引脚将用于在特定条件下设置引脚。  您能否举例说明您的想法?  

    您也不必切换引脚。  您可以将其设置为开始时的高电平、然后在结束时清除它  然后、只要引脚处于高电平、您就会进入您的环路。  

    [引用 user="Shariva Dhekane">每行都会弹出另一条消息:乘法是在没有硬件乘法器的器件上完成的。 是否有解决此问题的方法?

    不是、我相信没有任何解决方案、编译器只是警告您、乘法函数对 CPU 的要求会很高(并且还会使用大量时钟周期)。  如果您需要提高性能、我们提供了定点数学库 MSP-IQMATHLIB、它具有优化的数学函数。   

    谢谢、

    JD

      

      

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    嗨、Shariva、

    我还简要介绍了您的代码、但我不熟悉 PID 控制环路、因此我无法在那里为您提供任何建议。

    谢谢、
    JD
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    通常、您希望中断例程不会超过所需的时间。 在本例中、您将执行一组 缓慢的浮点计算。 因此 、在 ISR 完成之前、您将面临另一个中断的风险。

    虽然主代码没有执行任何操作、最好只让 ISR 获取 ADC 数据并设置一个标志来告知主例程数据可用。 您还可以在等待数据以节省功耗时将 CPU 置于睡眠状态。 MSP430的一个要点。

    您似乎已经将 ADC10模块配置为使用其内部振荡器(高达6.3MHz)而不使用分频器。 这可以实现非常快的转换。 (希望信号源能够支持这些短采样和保持时间)这是一个问题、因为每个信号源都会生成一个中断。 您的代码无法跟上这种情况。

    PID 代码的运行频率不需要比 PWM 占空比高。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我需要帮助的最后一点是、 当我使用中断时、循环仅运行一次。 但是、当我将其放入 while (1)循环时、它会执行该操作。 因此、我无法确定中断有什么问题。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    通常情况下、这是因为您让 MCU 在从中断返回时返回低功耗模式、这会阻止主循环运行。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    是的、正如 Keith 提到的、器件将进入低功耗模式、永远不会唤醒、或者您的中断只会触发一次。 您可能需要清除 ISR 标志或重新启用它以再次触发。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您对代码进行了哪些更改? 当中断被处理时、数据可用中断被复位、这样它将在下次有数据时触发。

    我喜欢使用 gdb 来调试这种事情。 我可以停止执行并检查器件寄存器以查看正在发生的情况。