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.
工具/软件:Code Composer Studio
大家好
我需要捕获 msp432p401r 中引脚的频率。 我在完成代码时需要帮助。
我已将输入引脚定义为 CCIxA:P10.4。
您能帮我了解如何读取频率吗? 应该使用中断吗? 我为您带来了一小段代码供您查看。
谢谢
#include "msp.h" /** * main.c * / void main (void) { WDT_A->CTL = WDT_A_CTL_PW | WDT_A_CTL_HOLD;//停止看门狗计时器 /********* 定时器_A0 ******** / NVIC_ClearPendingIRQ (TA0_0_IRQn);//清除任何过时状态 NVIC_EnableIRQ (TA0_0_IRQn);//在中断控制器中启用 TA0_0 Timer_A0->CTL = TIMER_A_CTL_tassel_2 |//选择 SMCLK 作为计时器的源 Timer_A_CTL_ID_3 |//将时钟除以8 (这将为计时器时钟生成6MHz) Timer_A_CTL_MC_2;//连续模式 Timer_A_CTL_CLR;//清除计时器计数 TA0CTL = TAIE;//Timer_A 中断使能。 TA0CCTL0 = CM_1 + CCIS_0 + CAP;//捕捉模式:上升沿|捕捉/比较输入选择(CCIxA:P10.4)|捕捉模式|
您期望的频率范围约为多少? 它带来了不同:
1) 1)为了测量低于5kHz 的频率、捕获通常工作得更好、因为每个捕获对都可以提供非常好的瞬时测量。 两次连续捕获的差值是周期(以定时器节拍为单位)、频率为1/周期。
2) 2)要测量大于50kHz 的频率、应通过引入 TA0CLK 等信号并对固定周期内的脉冲数进行计数来使用频率计数。 例如、如果您在1ms 内看到5个周期、即5个周期/.001s = 5000Hz。
3) 3)对于介于之间的频率、两者中的任何一个都可能是可以的。
示例套件中有一个捕获示例(msp430p401x_ta0_capture)、如下所示:
http://dev.ti.com/tirex/explore/node?node=AOqyDJonBtS4QACP3tJVEg__z-lQYNj__LATEST
我看不到频率计数示例。
感谢您的回复。
实际上、信号来自用于测量/控制电机轴角速度的编码器。 因此、信号频率范围介于大约13kHz 至大约160kHz 之间。 在这种情况下、您认为哪些建议会更好?
谢谢
Saber
查看您提到的示例 msp430p401x_ta0_capture。 我更改了代码、以研究它是否进入计时器中断处理程序。 为此、我定义了一个变量标志 并将其放置在中断处理程序中。 当中断发生且 TIMER_A0->CCR[0]的值存储在变量 a 中时、该标志被置位。在 while 循环中、首先 检查该标志、如果该标志被置位、代码将打印出来。与 TA0CLK 关联的 PINT 为 P10.4。 我测试了程序。 它不会进入中断
#include "msp.h" /** * main.c */ long long int a=0; int flag=0; void main (void) { WDT_A->CTL = WDT_A_CTL_PW | WDT_A_CTL_HOLD;//停止看门狗计时器 /********* 定时器_A0 ******** / /***** 中断激活器******* / NVIC_ClearPendingIRQ (TA0_0_IRQn);//清除任何过时状态 NVIC_EnableIRQ (TA0_0_IRQn);//在中断控制器中启用 TA0_0 /***** Tegister 配置**** / //配置 GPIO P1->DIR |= BIT0; //将 P1.0设置为输出 P1->OUT |= BIT0; // P1.0高位 P2->DIR |= BIT1; //将 P2.1设置为输出 P2->OUT &=~BIT1; // P2.1高电平 P10->SEL0 |= BIT4; // TA0.CCI0A 输入捕捉引脚、第二个功能 P10->DIR &=~BIT4; // Timer0_A0设置 Timer_A0->CCTL[0]= TIMER_A_CCTLN_CM_1 | //捕获上升沿, Timer_A_CCTLN_CCIS_0 |//使用 CCI2A=ACLK、 Timer_A_CCTLN_CCIE | //启用捕捉中断 Timer_A_CCTLN_CAP | //启用捕获模式, Timer_A_CCTLN_SCS; //同步捕捉 // Timer0_A0设置 Timer_A0->CTL = TIMER_A_CTL_tassel_2 |//选择 SMCLK 作为计时器的时钟源 Timer_A_CTL_MC__Continuous|//在连续模式中启动计时器 Timer_A_CTL_CLR; //清除 TA0R SCB->SCR |= SCB_SCR_SLEEPONEXIT_MSK;//从 ISR 退出时启用睡眠 //启用全局中断 __ENABLE_IRQ(); while (1) { if (flag==1) { flag=0; P2->OUT ^= BIT1; } } // 计时器 A0中断服务例程 void TA0_N_IRQHandler (void) { //将 CCR 寄存器值捕获到数组中 a =定时器_A0->CCR[0]; flag=1; P2->OUT ^= BIT1; //清除中断标志 Timer_A0->CCTL[0]&&~(timer_A_CCTLN_CCIFG); }
。
您是否对该代码的问题来自哪里有任何疑问? 以及如何使用 CCR0中的数字 和 SMCLK 的时钟源(3MHz)/计算 TA0CLK 上的信号频率
谢谢
根据数据表(SLAS826G)表6-79、P10.4为 TA3.CCI0A、即连接到 TIMER_A3、而不是 TIMER_A0。
TA0.CCI0A 在 P7.3上[参考表6-68]。 因此、您可以(a)将输入线移至 P7.3、或(b)将 TIMER_A0更改为 TIMER_A3 (不要忘记 TA3_0_IRQn)、以较容易的为准。
------------
>
SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk;
// Enable sleep on exit from ISR
删除此行。 一旦你获得一个中断、主程序将永远不会再次运行。
------------
> int
flag=0;
这用于主函数和 ISR 之间的通信、因此请使用:
> volatile int
flag=0;
----------
[编辑:
> void
TA0_N_IRQHandler(
void
)
This should be
> void
TA0_0_IRQHandler(
void
)
to match with TA0_0_IRQn. If you switch to TA3, it would be TA3_0_IRQHandler. [Ref TRM (SLAU356I) Sec 19.2.6]
感谢 Bruce 的回复。 现在、计时器中断通过向 P7.3提供外部信号来工作。
现在、我有一个问题、即如何使用这次捕获计算信号的频率。 时钟源为 SMCLK、3MHz。 具体而言、TA0CCR0中的值是否表示两次捕获之间的时间间隔?
例如、我有两个中断、第一个中断的 CCR0为 25806、第二个中断的 CCR0值为 27301、因此我只需按计算两者之间的时间即可
(27301-25806)/(3MHz)=0.4ms。
如果您能帮我解决这个问题、我将不胜感激。 我已将代码提交您审核。
谢谢
#include "msp.h" /** * main.c * / unsigned int a=0; unsigned int b=0; volatile int flag=0; void main (void) { WDT_A->CTL = WDT_A_CTL_PW | WDT_A_CTL_HOLD;//停止看门狗计时器 /********* 定时器_A0 ******** / /***** 中断激活器******* / NVIC_ClearPendingIRQ (TA0_0_IRQn);//清除任何过时状态 NVIC_EnableIRQ (TA0_0_IRQn);//在中断控制器中启用 TA0_0 /***** Tegister 配置**** / //配置 GPIO P1->DIR |= BIT0; //将 P1.0设置为输出 P1->OUT |= BIT0; // P1.0高位 P2->DIR |= BIT1; //将 P2.1设置为输出 P2->OUT &=~BIT1; // P2.1高电平 P7->SEL0 |= BIT3; // TA0.CCI0A 输入捕捉引脚、第二个功能 P3->DIR &=~BIT3; P2->SEL0 |= BIT5; // TA0.CCI2A 输入捕捉引脚、第二个功能 P2->DIR &=~BIT5; P4->SEL0 |= BIT2; //设置为 ACLK 引脚,第二个功能 P4->DIR |= BIT2; //******** Timer0_A0设置******** // Timer_A0->CCTL[0]= TIMER_A_CCTLN_CM_1 | //捕获上升沿, Timer_A_CCTLN_CCIS_0 |//使用 CCI2A=ACLK、 Timer_A_CCTLN_CCIE | //启用捕捉中断 Timer_A_CCTLN_CAP | //启用捕获模式, Timer_A_CCTLN_SCS; //同步捕捉 Timer_A0->CTL = TIMER_A_CTL_tassel_1 |//选择 SMCLK 作为计时器的时钟源 Timer_A_CTL_MC__Continuous|//在连续模式中启动计时器 Timer_A_CTL_CLR; //清除 TA0R //******** // CS->KEY = CS_KEY_VAL; //解锁 CS 模块以进行寄存器访问 //选择 ACLK = REFO、SMCLK = MCLK = DCO CS->CTL1 = CS_CTL1_SERA_2 | CS_CTL1_SELS _3 | CS_CTL1_SELM_3; CS->KEY = 0; //锁定 CS 模块,防止意外访问 //确保 SLEEPONEXIT 立即生效 _DSB(); NVIC->ISER[0]= 1 <<(TA0_N_IRQn)& 31); __ENABLE_IRQ(); __ENABLE_INTERRUPT (); //********* // while (1) { if (flag==1) { flag=0; P2->OUT ^= BIT1; printf ("b:%d "、b);//timer_A0->CCR[0]/); printf ("\n"); } } // 计时器 A0中断服务例程 void TA0_0_IRQHandler (void) { //将 CCR 寄存器值捕获到 A 中 b = TA0CCR0; A++; flag=1; P2->OUT ^= BIT1; //清除中断标志 TA0CCTL0 &=~(CCIFG); }
>(27301-25806)/(3MHz)=0.4ms
是的。 我的计算器显示(1/0.000498秒/周期)=2006Hz、这就是您的瞬时频率。
最终计时器(TA0R)将回绕到0。 如果将计数保持为"uint16_t"(我认为这是"unsigned short")、则由于无符号下溢、减法仍然有效。
16位计数器的周期有限:6556/3MHz=~21ms、因此频率下限为1/21ms=~45Hz。 下面、TA0R 在捕获之间完全换行、并且您的算术运算会发生分离。 您可以通过应用分频器(TACTL:ID、例如)来减小此下限、以牺牲一定精度为代价增大范围。
>
NVIC->ISER[0] = 1 << ((TA0_N_IRQn) & 31);
This looks like a leftover from something else; I think you don't want it. It's effectively NVIC_EnableIRQ(TA0_N_IRQn).
谢谢 Bruce!
到目前为止、为了测量信号的瞬时频率、我需要连续采集两个信号、并测量这两个采集之间的时间间隔。 然后计算频率。 我是否在正确的道路上?
我已经定义了两个变量 、Capture1、Capture2 来分配连续的捕获。 每当捕获发生时、在 ISR 中、第一次捕获被分配给 Capture1、 第二次捕获分配给 Capture2。 要定义捕获顺序、将检查变量即时信息、并根据其值定义顺序。 分配两个连续捕获后、将分配下一个捕获以计算频率(瞬时= 2)。 此外、如果两次捕获之间发生溢出、变量会递增。 我已经完成了这一操作、但在 while 循环中、不会在 ISR 中打印计算出的频率。 代码似乎永远不会退出 ISR。
您是否知道如何使用 main 在最小中断条件下计算频率(因为我需要对三个编码器进行九次捕捉!:)))
谢谢
NVIC_ClearPendingIRQ (TA0_0_IRQn);//清除任何过时状态 NVIC_EnableIRQ (TA0_0_IRQn);//在中断控制器中启用 TA0_0 /***** Tegister 配置**** / //配置 GPIO P1->DIR |= BIT0; //将 P1.0设置为输出 P1->OUT |= BIT0; // P1.0高位 P2->DIR |= BIT1; //将 P2.1设置为输出 P2->OUT &=~BIT1; // P2.1高电平 P7->SEL0 |= BIT3; // TA0.CCI0A 输入捕捉引脚、第二个功能 P3->DIR &=~BIT3; P2->SEL0 |= BIT5; // TA0.CCI2A 输入捕捉引脚、第二个功能 P2->DIR &=~BIT5; P4->SEL0 |= BIT2; //设置为 ACLK 引脚,第二个功能 P4->DIR |= BIT2; //******** Timer0_A0设置******** // Timer_A0->CCTL[0]= TIMER_A_CCTLN_CM_1 | //捕获上升沿, Timer_A_CCTLN_CCIS_0 |//使用 CCI2A=ACLK、 Timer_A_CCTLN_CCIE | //启用捕捉中断 Timer_A_CCTLN_CAP | //启用捕获模式, Timer_A_CCTLN_SCS; //同步捕捉 Timer_A0->CTL = TIMER_A_CTL_tassel_2 |//选择 SMCLK 作为计时器的时钟源 Timer_A_CTL_ID_3 |//ID=8;SMCLK/8=3MHz/8=375kHz Timer_A_CTL_MC__Continuous|//在连续模式中启动计时器 Timer_A_CTL_CLR; //清除 TA0R //******** 周期计算******** // PULSE_FREQUENCY = 375000;//SMCLK/ID_3;//一个脉冲频率。 CYCLE = 1/Pulse 频率;//一个脉冲的持续时间。 //********* // CS->KEY = CS_KEY_VAL; //解锁 CS 模块以进行寄存器访问 //选择 ACLK = REFO、SMCLK = MCLK = DCO CS->CTL1 = CS_CTL1_SERA_2 | CS_CTL1_SELS _3 | CS_CTL1_SELM_3; CS->KEY = 0; //锁定 CS 模块,防止意外访问 //确保 SLEEPONEXIT 立即生效 _DSB(); __ENABLE_IRQ(); __ENABLE_INTERRUPT (); //********* // while (1) { IF (定时器_A0->CCTL[0]&COV) {overflow++; Timer_A0->CCTL[0]&=~COV;} printf ("频率:%d "、频率);//打印频率 } } //计时器 A0中断服务例程 void TA0_0_IRQHandler (void) { 如果(瞬时= 0) {inste++; Capture1=TA0CCR0; } 如果(瞬时= 1)则为其他值 { //将 CCR 寄存器值捕获到 A 中 Capture2 =溢出*2^16 + TA0CCR0; 溢出=0; Instant ++; flag=1; } 其他 { 瞬时= 0; duration =(Capture2-Capture1)* cycle; 频率= 1/持续时间 ;} P2->OUT ^= BIT1; //清除中断标志 TA0CCTL0 &=~(CCIFG); }
您会得到"frequency:0"、还是根本没有输出?
频率和周期是如何声明的? 如果它们是整数、请记住(int)(1/integer_greater = 1)=0。
您提供的频率是多少? 如果它真的是375kHz、请减慢它的速度。 此代码将无法继续。
----------
>
capture2 = overflow*2^16 + TA0CCR0;
"^" is not the exponentiation operator, it's XOR.
Also COV indicates an overrun (new capture before you've retrieved the old one), not a timer overflow (0xFFFF->0).
As long as you stay within the prescribed range (the >45Hz I mentioned before) there's no need to count overflows at all, since uint16_t subtraction does the right thing, even if the counter overflows in between.
我得到的是"频率:0"。 我将变量类型更改为 float、现在它似乎正在工作。
该周期为1/clock_source。 例如、如果时钟源为 ACLK (32kHz)、则周期(浮点)将为 0.00003125秒。
duration =(Capture2-Capture1)* cycle;
IN_FREQUENCY (float)= 1/周期(float);
具有32kHz 时钟源的经修改的代码如下所示。 现在的问题是电机轴以内容速度旋转、因此编码器应提供固定频率。 有时 IN_FREQUENCY 会提供一个合理的数字、有时它会提供不同于第一次的频率、这毫无意义。 是否有任何问题在哪里以及如何解决问题的想法?
谢谢
#include "msp.h" /** * main.c * / unsigned int a=0; unsigned int b=0; volatile int flag=0; unsigned long long volatile int overflow=0; unsigned long long volature1=0; unsigned long volatile int Capture2=0; float cycle=0; int Pulse_frequency; int inste=0;int futh=0;int frequency;int inste=0; int define SMCLK 3000000;//SMCLK #define ACLK 32000;//ACLK #define ID_0 1; #define ID_1 2; #define ID_2 4; #define ID_3 8; 浮点持续时间; 浮点 IN_FREQUENCY; //float 频率; void main (void) { WDT_A->CTL = WDT_A_CTL_PW | WDT_A_CTL_HOLD;//停止看门狗计时器 /********* 定时器_A0 ******** / /***** 中断激活器******* / NVIC_ClearPendingIRQ (TA0_0_IRQn);//清除任何过时状态 NVIC_EnableIRQ (TA0_0_IRQn);//在中断控制器中启用 TA0_0 /***** Tegister 配置**** / //配置 GPIO P1->DIR |= BIT0; //将 P1.0设置为输出 P1->OUT |= BIT0; // P1.0高位 P2->DIR |= BIT1; //将 P2.1设置为输出 P2->OUT &=~BIT1; // P2.1高电平 P7->SEL0 |= BIT3; // TA0.CCI0A 输入捕捉引脚、第二个功能 P3->DIR &=~BIT3; P2->SEL0 |= BIT5; // TA0.CCI2A 输入捕捉引脚、第二个功能 P2->DIR &=~BIT5; P4->SEL0 |= BIT2; //设置为 ACLK 引脚,第二个功能 P4->DIR |= BIT2; //******** Timer0_A0设置******** // Timer_A0->CCTL[0]= TIMER_A_CCTLN_CM_1 | //捕获上升沿, Timer_A_CCTLN_CCIS_0 |//使用 CCI2A=ACLK、 Timer_A_CCTLN_CCIE | //启用捕捉中断 Timer_A_CCTLN_CAP | //启用捕获模式, Timer_A_CCTLN_SCS; //同步捕捉 Timer_A0->CTL = TIMER_A_CTL_tassel_1 |//选择 SMCLK 作为计时器的时钟源 Timer_A_CTL_ID_0 |//ID=8;SMCLK/8=3MHz/8=375kHz Timer_A_CTL_MC__Continuous|//在连续模式中启动计时器 Timer_A_CTL_CLR; //清除 TA0R //******** 周期计算******** // // Pulse 频率= 32000;//SMCLK/ID_3;//一个脉冲频率。 周期= 0.00003125;//一个脉冲的持续时间。 //********* // CS->KEY = CS_KEY_VAL; //解锁 CS 模块以进行寄存器访问 //选择 ACLK = REFO、SMCLK = MCLK = DCO CS->CTL1 = CS_CTL1_SERA_2 | CS_CTL1_SELS _3 | CS_CTL1_SELM_3; CS->KEY = 0; //锁定 CS 模块,防止意外访问 //确保 SLEEPONEXIT 立即生效 _DSB(); __ENABLE_IRQ(); __ENABLE_INTERRUPT (); //********* // while (1) { // if (timer_A0->CCTL[0]和 COV) //{overflow++; // timer_A0->CCTL[0]&=~COV;} 如果(flag==1) { 瞬时= 0; duration =(Capture2-Capture1)* cycle; IN_FREQUENCY = 1/持续时间; flag=0; //frequency = 1/时长; // printf ("频率:%f"、in_frequency);//打印频率 // printf ("\n"); } } // 计时器 A0中断服务例程 void TA0_0_IRQHandler (void) { 如果(瞬时= 0) {inste++; Capture1=TA0CCR0; } 如果(瞬时= 1)则为其他值 { //将 CCR 寄存器值捕获到 A 中 Capture2 = TA0CCR0; //overflow=0; 瞬时= 0; flag=1; } P2->OUT ^= BIT1; //清除中断标志 TA0CCTL0 &=~(CCIFG); }
> unsigned
long
long
volatile
int
capture1=0;
> unsigned
long
long
volatile
int
capture2=0;
谢谢 Bruce!
你猜对了。 我更改了数据类型、现在是预期的。 我还有另外一个问题:
我想在我的代码中、只要需要、就会启用捕获、并计算出下一个信号的瞬时频率。 然后禁用它。 我该怎么做? 我不需要连续计算信号的频率。 只要调用函数、我就需要拥有它。
谢谢
没有任何原因可以让计时器停止(MC=0)、直到您需要它、然后设置 MC=2并等待"标志"变为1、然后再次停止计时器(MC=0)。
您可以通过读取几次频率并对其求平均值来获得一些东西。
我的问题已完全解决!
谢谢 Bruce!
由于这个问题已经解决、我将关闭这个线程。 谢谢 Bruce。
伊斯天