大连创客空间

 找回密码
 立即注册吧!

QQ登录

只需一步,快速开始

一键登录:

搜索
查看: 8089|回复: 44
收起左侧

【大赛原创经验贴】高低频率测量分享和交流

  [复制链接]
发表于 2012-7-19 16:00:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多交流,硬件的世界向你敞开大门!

您需要 登录 才可以下载或查看,没有帐号?立即注册吧!

x
前几天利用定时器A做了频率计。分享交流下

程序一:
下面的是测量周期的程序,由于的利用在被测信号一个周期内A的计数,所以适合低频测量,在KHz以下精度高。
(程序显示的是周期,频率的话求倒就可以了,不过要注意小数位的处理)
#include<msp430x14x.h>
#define uint  unsigned int
#define uchar unsigned char
#define ulong unsigned long
#define RS_HIGH P4OUT|=BIT3 //指令数据选择信号
#define RS_LOW P4OUT&=~BIT3
#define RW_HIGH P4OUT|=BIT4 //读写信号
#define RW_LOW P4OUT&=~BIT4
#define E_HIGH P4OUT|=BIT5//使能信号
#define E_LOW P4OUT&=~BIT5
#define BUSY_OUT P2DIR|=BIT7//p2.7为输出口
#define BUSY_IN P2DIR&=~BIT7//p2.7为输入口
#define BUSY_DATA P2IN&BIT7
uchar Data1[16]={"   .     ms   MK"};
uchar Data2[16]={"                "};
ulong start,end;
ulong count=0;
uchar flag=0,temp=0,overflow=0,count1=0;
//时钟初始化函数
void clk(void){
  BCSCTL1=RSEL2+RSEL1+RSEL0;//XT2开启 LFXT1工作在低频模式 ACLK不分频 最高的标称频率
  DCOCTL=DCO2+DCO1+DCO0;//DCO为最高频率
  do{
    IFG1&=~OFIFG;//清除振荡器失效标志
    for(uint i=255;i>0;i--);
  }while(IFG1&OFIFG);//判断XT2是否起振
  BCSCTL2=SELM1+SELS;//MCLK SMCLK时钟源为TX2CLK不分频
}
void port(void)
{
  P6SEL=0X00;
  P6DIR=0XFF;
  P6OUT=0X00;
  
  P2SEL=0x00;//P2口所有引脚设置为一般的IO口
  P4SEL=0x00;//P4口所有引脚设置为一般的IO口
  
  P2DIR=0xFF;//P2口所有引脚设置为输出方向
  P4DIR=0xFF;//P4口所有引脚设置为输出方向
  
  P2OUT=0x00;//P2口先输出低电平
  P4OUT=0xFF;//P4口先输出低电平
}
delay()
{
  for(uint i=0;i<100;i++)
    for(uint j=0;j<100;j++);
}
// 测试LCD忙碌状态
void LcdBusy()
{
RS_LOW;
RW_HIGH;
E_HIGH;
_NOP();_NOP();
BUSY_IN;
        while(BUSY_DATA);
        BUSY_OUT;
E_LOW;
}
//写入指令到LCD
WriteCommand(uchar Command)
{
  LcdBusy();
  RS_LOW;
  RW_LOW;
  E_HIGH;
  _NOP();_NOP();
  P2OUT=Command;
  _NOP();_NOP();
  E_LOW;
}
//写入字符数据到LCD
WriteData(uchar Data)
{
  LcdBusy();
  RS_HIGH;
  RW_LOW;
  E_HIGH;
  _NOP();_NOP();
  P2OUT=Data;
  _NOP();_NOP();
  E_LOW;
}
//LCD初始化设定
LcdInit()
{
  WriteCommand(0x38);//8位数据端口,2行显示,5*7点阵
  delay();
  WriteCommand(0x0c);//开启显示, 无光标
  delay();
  WriteCommand(0x06);//AC递增, 画面不动
  delay();
  WriteCommand(0x01);//清屏
  delay();
}
//计数器A初始化
void int_cap()
{
  P1SEL=0X04;
  TACCTL1=CM0+SCS+CAP+CCIE;//输入上升沿捕获,CCI0A为捕获信号源
  TACTL=TASSEL1+TACLR+TAIE+MC1;//定时器A时钟信号选择SMCLK,同时设置定时器A计数模式为连续增模式
                  
}

#pragma vector=TIMERA1_VECTOR              //定时器A中断处理
__interrupt void timer_a(void)
{
switch(TAIV)                              //向量查询
  { case 2:                                //捕获中断
        if(CCTL1&CM0)                     //上升沿
         {
           CCTL1=(CCTL1&(~CM0))|CM1;     //更变设置为下降沿触发
           TAR=0;                      //记录初始时间
           overflow=0;
           flag=1;
         }
       else if (CCTL1&CM1)                //下降沿
        {  count1=overflow;
           CCTL1=(CCTL1&(~CM1))|CM0;     //更变设置为上升沿触发
           end=TAR;                        //记录终止时间
           flag=1;
        } break;  

    case 10: overflow++;break;      //溢出次数
    default:break;
  }
  
}
void display()
{
  count=65536*count1+end;
  count=count*100;
  count=count/8;
  Data1[0]=count/10000000+0x30;
  Data1[1]=count%10000000/1000000+0x30;
  Data1[2]=count%1000000/100000+0x30;
  Data1[4]=count%100000/10000+0x30;
  Data1[5]=count%10000/1000+0x30;
  Data1[6]=count%1000/100+0x30;
  Data1[7]=count%100/10+0x30;
  Data1[8]=count%10+0x30;

         
  WriteCommand(0x80);//定位在第一行第一个位置
   for(uchar i=0;i<16;i++) WriteData(Data1[i]);
  WriteCommand(0x80|0x40);//定位在第二行第一个位置
  for(uchar i=0;i<16;i++) WriteData(Data2[i]);
}


void main (void)
{
  WDTCTL=WDTPW+WDTHOLD;
  clk();
  port();
  int_cap();
  LcdInit();
  _EINT();

while(1)                               //LOOP
{
   display();
}
}


程序二
类似等精度频率计的原理,测量范围为0~260KHz。
/***************************************
    利用定时器A的定时和捕获功能测频率
      被测信号从P1.2脚输入
        测量范围0~260Khz
      (10HZ起就测量值精确)
***************************************/

#include<msp430x14x.h>
#define uint  unsigned int
#define uchar unsigned char
#define ulong unsigned long
#define RS_HIGH P4OUT|=BIT3 //指令数据选择信号
#define RS_LOW P4OUT&=~BIT3
#define RW_HIGH P4OUT|=BIT4 //读写信号
#define RW_LOW P4OUT&=~BIT4
#define E_HIGH P4OUT|=BIT5//使能信号
#define E_LOW P4OUT&=~BIT5
#define BUSY_OUT P2DIR|=BIT7//p2.7为输出口
#define BUSY_IN P2DIR&=~BIT7//p2.7为输入口
#define BUSY_DATA P2IN&BIT7
uchar Data1[16]={"    .     K   PL"};
uchar Data2[16]={"                "};
uchar Cou=0;
ulong count=0,N1=0,count1=0;
//时钟初始化函数
void clk()
{
  BCSCTL1=RSEL2+RSEL1+RSEL0;//XT2开启 LFXT1工作在低频模式 ACLK不分频 最高的标称频率
  DCOCTL=DCO2+DCO1+DCO0;//DCO为最高频率
  do{
    IFG1&=~OFIFG;//清除振荡器失效标志
    for(uint i=255;i>0;i--);
  }while(IFG1&OFIFG);//判断XT2是否起振
  BCSCTL2=SELM1+SELS;//MCLK SMCLK时钟源为TX2CLK不分频
}
void port(void)
{
  P2SEL=0x00;//P2口所有引脚设置为一般的IO口
  P4SEL=0x00;//P4口所有引脚设置为一般的IO口
  
  P2DIR=0xFF;//P2口所有引脚设置为输出方向
  P4DIR=0xFF;//P4口所有引脚设置为输出方向
  
  P2OUT=0x00;//P2口先输出低电平
  P4OUT=0xFF;//P4口先输出低电平
  
}
delay()
{
  for(uint i=0;i<100;i++)
    for(uint j=0;j<100;j++);
}
// 测试LCD忙碌状态
void LcdBusy()
{
RS_LOW;
RW_HIGH;
E_HIGH;
_NOP();_NOP();
BUSY_IN;
        while(BUSY_DATA);
        BUSY_OUT;
E_LOW;
}
//写入指令到LCD
WriteCommand(uchar Command)
{
  LcdBusy();
  RS_LOW;
  RW_LOW;
  E_HIGH;
  _NOP();_NOP();
  P2OUT=Command;
  _NOP();_NOP();
  E_LOW;
}
//写入字符数据到LCD
WriteData(uchar Data)
{
  LcdBusy();
  RS_HIGH;
  RW_LOW;
  E_HIGH;
  _NOP();_NOP();
  P2OUT=Data;
  _NOP();_NOP();
  E_LOW;
}
//LCD初始化设定
LcdInit()
{
  WriteCommand(0x38);//8位数据端口,2行显示,5*7点阵
  delay();
  WriteCommand(0x0c);//开启显示, 无光标
  delay();
  WriteCommand(0x06);//AC递增, 画面不动
  delay();
  WriteCommand(0x01);//清屏
  delay();
}

//定时器A初始化
void InitTimerA()
{
  P1SEL=0X04;//P1.2作为被测脉冲输入
  TACCTL1=CM0+SCS+CAP+CCIE;
  TACTL=TASSEL1+ID1+ID0+MC0+TACLR;//选择1/8SMCLK 增计数 清除TAR
  CCTL0=CCIE;//CCR0中断允许 比较模式
  CCR0=50000;//时间间隔50ms
}
//定时器A中断
#pragma vector=TIMERA0_VECTOR
__interrupt void TimerAINT(void)
{
  Cou++;
  if(Cou==20)
  {
    N1=count1;
    count1=0;
    Cou=0;
  }
}
#pragma vector=TIMERA1_VECTOR
__interrupt void timea(void)
{
  
  switch(TAIV)
  {
  case 2 :count1++;break;
  case 4 :break;
  case 10:break;
  }
  
}


void display()
{
WriteCommand(0x80);//定位在第一行第一个位置
      for(uchar i=0;i<16;i++) WriteData(Data1[i]);
WriteCommand(0x80|0x40);//定位在第二行第一个位置
      for(uchar i=0;i<16;i++) WriteData(Data2[i]);
}


void main()
{
  WDTCTL=WDTPW+WDTHOLD;
  clk();
  port();
  LcdInit();
  InitTimerA();
  _EINT();
  while(1)
  {
  Data1[0]=N1/1000000+0x30;      
  Data1[1]=N1%1000000/100000+0x30;
  Data1[2]=N1%100000/10000+0x30;
  Data1[3]=N1%10000/1000+0x30;
  
  Data1[5]=N1%1000/100+0x30;
  Data1[6]=N1%100/10+0x30;
  Data1[7]=N1%10+0x30;   
      
  display();
  }
}

超过260K就不行了,希望可以指点下怎样做到MHz以上(最好能到10M,程序的方法,外部对被测信号分频不算哦


回复

使用道具 举报

发表于 2012-7-19 16:03:45 | 显示全部楼层
收藏了!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-19 16:06:08 | 显示全部楼层
回复 支持 反对

使用道具 举报

发表于 2012-7-19 16:07:46 | 显示全部楼层
呵呵,不错。有时间作为参考!!!
回复 支持 反对

使用道具 举报

发表于 2012-7-19 16:09:22 | 显示全部楼层
cloudviolet 发表于 2012-7-19 16:06
谢谢支持!对于问题希望一起探讨下

  OK,有机会就一起讨论。
回复 支持 反对

使用道具 举报

发表于 2012-7-20 00:07:55 | 显示全部楼层
楼主太厉害了,我的只能测到65536HZ,有时间探讨一下,问一下,不知道楼主测量的误差大概在什么范围??

点评

由于计数是整数,我测量时误差在1Hz一下。你可以实际测一下  详情 回复 发表于 2012-7-20 08:13
回复 支持 反对

使用道具 举报

发表于 2012-7-20 00:45:07 | 显示全部楼层
不错
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-20 08:13:46 | 显示全部楼层
huangweiwei_48 发表于 2012-7-20 00:07
楼主太厉害了,我的只能测到65536HZ,有时间探讨一下,问一下,不知道楼主测量的误差大概在什么范围?? ...

由于计数是整数,我测量时误差在1Hz一下。你可以实际测一下

点评

1Hz以下(不好意思,打错字了)  详情 回复 发表于 2012-7-20 08:15
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-20 08:15:20 | 显示全部楼层
cloudviolet 发表于 2012-7-20 08:13
由于计数是整数,我测量时误差在1Hz一下。你可以实际测一下

1Hz以下(不好意思,打错字了)
回复 支持 反对

使用道具 举报

发表于 2012-7-20 19:24:26 | 显示全部楼层
楼主显示的是频率啊……不是周期的说,还有能问下为什么要加0x30吗?

点评

第一个是测周期的,第二个是测频的。两个可以在数据处理的时候自己转换下 LCD1602的ASC码字符库里0对应的是0x30,显示其他数字就可以在后面直接加了  详情 回复 发表于 2012-7-20 20:21
回复 支持 反对

使用道具 举报

发表于 2012-7-20 19:52:38 | 显示全部楼层
楼主很强大,受教了~
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-20 20:21:49 | 显示全部楼层
DnStart 发表于 2012-7-20 19:24
楼主显示的是频率啊……不是周期的说,还有能问下为什么要加0x30吗?

第一个是测周期的,第二个是测频的。两个可以在数据处理的时候自己转换下
LCD1602的ASC码字符库里0对应的是0x30,显示其他数字就可以在后面直接加了
回复 支持 反对

使用道具 举报

发表于 2012-7-20 20:26:09 | 显示全部楼层
cloudviolet 发表于 2012-7-20 20:21
第一个是测周期的,第二个是测频的。两个可以在数据处理的时候自己转换下
LCD1602的ASC码字符库里0对应的 ...

明白了,谢啦,我 用的字符的方式输入的,所以没有涉及转换ASC码的方式
回复 支持 反对

使用道具 举报

发表于 2012-7-21 20:21:28 | 显示全部楼层
Cou++;
  if(Cou==20)
  {
    N1=count1;
    count1=0;
    Cou=0;
  }
楼主:这两句什么意思呢

点评

定时器定时50ms,进入定时中断20次读取脉冲计数 即定时1s,对脉冲计数进行读取清零 可以根据需要修改,在频率计算的时候注意下就可以了  详情 回复 发表于 2012-7-22 08:17
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-22 08:17:54 | 显示全部楼层
莹恒羁绊 发表于 2012-7-21 20:21
Cou++;
  if(Cou==20)
  {

定时器定时50ms,进入定时中断20次读取脉冲计数
即定时1s,对脉冲计数进行读取清零
可以根据需要修改,在频率计算的时候注意下就可以了
回复 支持 反对

使用道具 举报

发表于 2012-7-22 10:23:20 | 显示全部楼层
#pragma vector=TIMERA1_VECTOR
__interrupt void timea(void)
{
  
  switch(TAIV)
  {
  case 2 :count1++;break;
  case 4 :break;
  case 10:break;
  }
  
}
请问楼主这里的count1是怎么计数的呢,这里这个中断向量是怎么进入主程序工作的

点评

被测信号 (打错字了)  详情 回复 发表于 2012-7-22 10:41
Timer_A的捕捉模块1设置为上升沿捕捉,在不测信号上升沿来临时,CCR1的中断CCIFG置1,进入中断。 判断中断向量寄存器TAIV的值来进行计数  详情 回复 发表于 2012-7-22 10:40
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-22 10:40:36 | 显示全部楼层
莹恒羁绊 发表于 2012-7-22 10:23
#pragma vector=TIMERA1_VECTOR
__interrupt void timea(void)
{

Timer_A的捕捉模块1设置为上升沿捕捉,在不测信号上升沿来临时,CCR1的中断CCIFG置1,进入中断。
判断中断向量寄存器TAIV的值来进行计数
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-22 10:41:19 | 显示全部楼层
莹恒羁绊 发表于 2012-7-22 10:23
#pragma vector=TIMERA1_VECTOR
__interrupt void timea(void)
{

被测信号
(打错字了
回复 支持 反对

使用道具 举报

发表于 2012-7-22 15:25:18 | 显示全部楼层
if(new_cap>old_cap)
        cap_diff=new_cap-old_cap-30;
    else
        cap_diff=65536-old_cap+new_cap-30;

请问楼主上面的算法中为什么要减30呢?


点评

这个好像不是我的程序吧  详情 回复 发表于 2012-7-22 16:47
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-22 16:47:43 | 显示全部楼层
莹恒羁绊 发表于 2012-7-22 15:25
if(new_cap>old_cap)
        cap_diff=new_cap-old_cap-30;
    else

这个好像不是我的程序吧
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册吧!

本版积分规则

单片机开发者1群:235808606;会员QQ 6群:254085994; 1群:12835691(满); 2群:121061287(满); 3群:237320668(满);4群:249652919(满);5群:253578883(满);大赛1群:187926790(满),2群:231577758(满),3群:249652919(满)。 TI杯4群:58129202(满)。
载入中

QQ|手机版|小黑屋|Archiver|大连创客空间 ( 辽ICP备15004617号-2  

GMT+8, 2017-3-1 00:50 , Processed in 0.246423 second(s), 34 queries .

地址:辽宁省大连市高新园区火炬路7号17层 QQ:2401085253

Copyright © 2010-2015 创客空间(大连)科技有限公司 All Rights Reserved.

快速回复 返回顶部 返回列表