求助!基于mega16的相位差计的程序问题~~~~~~~~~~~~~~~~~

ttdiablo   2010-5-22 16:19 楼主
mega16外围连接如下:相位差脉冲信号从PD6口输入,相位差关系判断信号从PD7口输入(前面整形电路已将两路同频输入信号处理成相位差脉冲信号和判断信号);如果判断信号是高电平,则显示“超前”,否则为“滞后”。
AVR与LCD1602连接信息
PA1 ->RS
PA3 ->EN
地  ->RW
PA4 ->D4
PA5 ->D5
PA6 ->D6
PA7 ->D7
与键盘连接:
PB4->key1
PB5->key2
PB6->key3
PB7->key4

我的思路是用PD6的输入捕获功能,分别设置上升沿和下降沿触发来计时,脉冲信号的上升沿和下降沿之间是脉冲宽度,上升沿和上升沿之间是周期,两者之比再乘以360就是相位差。

此原理是否可行,精度如何、

然后参考了网上的程序(网上的程序是测占空比和周期的,我觉得原理相同),将之修改了下,发现无法正确测周期和占空比,更不用说相位差了。因为本人单片机学得不怎么好,特来向各位求助!如得帮助,感激不尽~

这个相位计最终做成的效果是这样的:实时测量,每隔一秒刷新显示信息;在1602上第一行显示相位差,第二行显示相位关系,按key1能切换成显示输入信号的周期。请问各位如何实现程序?




看看程序的问题?是否有更好的方法?

-------------------------------------------------------------------------------------------------------------
主程序如下:main.c

#include
#include
#include "1602.h"
#include "delay.h"


#pragma data:data  //可以省略

unsigned char Data1[6]={0,0,0,'.',0,0};//周期数据
unsigned char Data2[6]={0,0,0,'.',0,0};//相位差数据

unsigned char num=0;//判断是前半周期还是后半周期
unsigned int hightime,    lowtime,      space,    totaltime;
//          前半周期时间,后半周期时间,占空比,周期

#define icp_low() TCCR1B&=~(1< #define icp_High() TCCR1B|=(1<
  
  //ICP捕获中断程序
#pragma interrupt_handler Icp_timer1:6
void Icp_timer1(void)
  {
  num++;
  if(num%2)//判断区分是前半周期还是后半周期
  {
   lowtime=ICR1;////记后前半周期时间
   ICR1=0;//ICR1清零
   TCNT1=0;//计数器清零
   icp_low();//改变触发方式为下降沿触发
  }
  else
  {
   hightime=ICR1;//记下前半周期时间
   ICR1=0;//ICR1清零
   TCNT1=0;//计数器清零
   icp_High();//改变触发方式为上升沿触发
  }
  
  }
  

//端口初始化
void port_int(void)
{
  DDRA=0XFF;
  PORTA=0XFF;
  DDRD=0X3F;//ICP脚为输入
  DDRB=0XFF;
  PORTD=0XFF;//初始化为高电平
  PORTB=0XFF;
}

void timer1_int(void)//定时器1初始化
{
  TIMSK=0X20;
  TCCR1A=0X00;
  TCCR1B=0XC2;//噪声抑制,捕获中断,8分频
  ICR1=0;//ICR1清零
  TCNT1=0;//计数器清零
  icp_High();//初始化上升沿触发
}

void init(void)
{
  port_int();
  timer1_int();
}


//主程序:测量ICP引脚上信号的周期
void main(void)
  {
  unsigned char num2;
  init();
  SREG|=0X80;
  LCD_init();//1602初始化
while(1)
      {
                totaltime=lowtime+hightime;//周期
                space=(hightime)*100/totaltime; //占空比
                                   SEI();//开全局中断
                                delay_nms(1);//这里延时限制了接收周期最大为1ms的方波       
                                CLI(); //禁止所有中断
                                //下面的是1602显示
                                chuli(totaltime,Data1);
                                chuli(space,Data2);
                                LCD_write_string(0,0,"Cycle:");
                                LCD_write_string(8,0,Data1);
                                LCD_write_string(14,0,"us");
                                LCD_write_string(0,1,"space:");
                                LCD_write_string(8,1,Data2);
                                LCD_write_string(14,1,"%");
      }
  }

void chuli(unsigned int i,unsigned char *p) //显示处理,+48是因为液晶显示的是ASCII码
{
p[0]=i/1000%10+48;//百
i = i % 1000;
p[1]=i/100%10+48;//十
i = i % 100;
p[2]=i/10%10+48;//个
p[4]=i%10+48;//小数点后一位
}


------------------------------------------------------------------------------------------------------
1602.h
/* 用法:
   LCD_init();
   LCD_write_string(列,行,"字符串");
   LCD_write_char(列,行,'字符');
---------------------------------------------------------------
下面是AVR与LCD连接信息
PA1 ->RS
PA3 ->EN
地  ->RW
PA4 ->D4
PA5 ->D5
PA6 ->D6
PA7 ->D7
要使用本驱动,改变下面配置信息即可
-----------------------------------------------------------------*/
#define LCD_EN_PORT    PORTA   //以下2个要设为同一个口
#define LCD_EN_DDR     DDRA
#define LCD_RS_PORT    PORTA   //以下2个要设为同一个口
#define LCD_RS_DDR     DDRA
#define LCD_DATA_PORT  PORTA   //以下3个要设为同一个口
#define LCD_DATA_DDR   DDRA    //默认情况下连线必须使用高四位端口,如果不是请注意修改
#define LCD_DATA_PIN   PINA
#define LCD_RS         (1< #define LCD_EN         (1< #define LCD_DATA       ((1< /*--------------------------------------------------------------------------------------------------
函数说明
--------------------------------------------------------------------------------------------------*/
void LCD_init(void);
void LCD_en_write(void);
void LCD_write_command(unsigned  char command) ;
void LCD_write_data(unsigned char data);
void LCD_set_xy (unsigned char x, unsigned char y);
void LCD_write_string(unsigned char X,unsigned char Y,unsigned char *s);
void LCD_write_char(unsigned char X,unsigned char Y,unsigned char data);

//-----------------------------------------------------------------------------------------

#include
#include "delay.h"

void LCD_init(void)         //液晶初始化
{
  LCD_DATA_DDR|=LCD_DATA;   //数据口方向为输出
  LCD_EN_DDR|=LCD_EN;       //设置EN方向为输出
  LCD_RS_DDR|=LCD_RS;       //设置RS方向为输出
  LCD_write_command(0x28);
  LCD_en_write();
  delay_nus(40);
  LCD_write_command(0x28);  //4位显示
  LCD_write_command(0x0c);  //显示开
  LCD_write_command(0x01);  //清屏
  delay_nms(2);
  

  
}

void LCD_en_write(void)  //液晶使能
{
  LCD_EN_PORT|=LCD_EN;
  delay_nus(1);
  LCD_EN_PORT&=~LCD_EN;
}

void LCD_write_command(unsigned char command) //写指令
{
  //连线为高4位的写法
  delay_nus(16);
  LCD_RS_PORT&=~LCD_RS;        //RS=0
  LCD_DATA_PORT&=0X0f;         //清高四位
  LCD_DATA_PORT|=command&0xf0; //写高四位
  LCD_en_write();
  command=command<<4;          //低四位移到高四位
  LCD_DATA_PORT&=0x0f;         //清高四位
  LCD_DATA_PORT|=command&0xf0; //写低四位
  LCD_en_write();

/*
  //连线为低四位的写法
  delay_nus(16);
  LCD_RS_PORT&=~LCD_RS;        //RS=0
  LCD_DATA_PORT&=0xf0;         //清高四位
  LCD_DATA_PORT|=(command>>4)&0x0f; //写高四位
  LCD_en_write();
  LCD_DATA_PORT&=0xf0;         //清高四位
  LCD_DATA_PORT|=command&0x0f; //写低四位
  LCD_en_write();
*/
  
}

void LCD_write_data(unsigned char data) //写数据
{
  //连线为高4位的写法
  delay_nus(16);
  LCD_RS_PORT|=LCD_RS;       //RS=1
  LCD_DATA_PORT&=0X0f;       //清高四位
  LCD_DATA_PORT|=data&0xf0;  //写高四位
  LCD_en_write();
  data=data<<4;               //低四位移到高四位
  LCD_DATA_PORT&=0X0f;        //清高四位
  LCD_DATA_PORT|=data&0xf0;   //写低四位
  LCD_en_write();
  
/*
  //连线为低四位的写法
  delay_nus(16);
  LCD_RS_PORT|=LCD_RS;       //RS=1
  LCD_DATA_PORT&=0Xf0;       //清高四位
  LCD_DATA_PORT|=(data>>4)&0x0f;  //写高四位
  LCD_en_write();

  LCD_DATA_PORT&=0Xf0;        //清高四位
  LCD_DATA_PORT|=data&0x0f;   //写低四位
  LCD_en_write();
*/
  
}


void LCD_set_xy( unsigned char x, unsigned char y )  //写地址函数
{
    unsigned char address;
    if (y == 0) address = 0x80 + x;
    else   address = 0xc0 + x;
    LCD_write_command( address);
}
  
void LCD_write_string(unsigned char X,unsigned char Y,unsigned char *s) //列x=0~15,行y=0,1
{
    LCD_set_xy( X, Y ); //写地址   
    while (*s)  // 写显示字符
    {
      LCD_write_data( *s );
      s ++;
    }
      
}

void LCD_write_char(unsigned char X,unsigned char Y,unsigned char data) //列x=0~15,行y=0,1
{
  LCD_set_xy( X, Y ); //写地址
  LCD_write_data( data);


-------------------------------------------------------------------------------------------------------------

/*-----------------------------------------------------------------------
延时函数
编译器:ICC-AVR v6.31A
目标芯片 : M16
时钟: 8.0000Mhz
-----------------------------------------------------------------------*/
#ifndef __delay_h
#define __delay_h
void delay_nus(unsigned int n);
void delay_nms(unsigned int n);
void delay_1us(void);
void delay_1ms(void) ;

void delay_1us(void)                 //1us延时函数
  {
   asm("nop");
  }

void delay_nus(unsigned int n)       //N us延时函数
  {
   unsigned int i=0;
   for (i=0;i    delay_1us();
  }
  
void delay_1ms(void)                 //1ms延时函数
  {
   unsigned int i;
   for (i=0;i<1140;i++);
  }
  
void delay_nms(unsigned int n)       //N ms延时函数
  {
   unsigned int i=0;
   for (i=0;i    delay_1ms();
  }
  
  #endif







回复评论 (3)

你的被测脉冲,也就是相位差脉冲一般是多大?先确定你的mcu的定时器捕捉功能能不能捕捉到那么高的频率。
点赞  2010-5-22 16:26
大概1k到50kHz之间吧~这个mega16应该时能测的。
有没有人做过类似的,传授一下啊。
点赞  2010-5-22 21:04
这么低的频率肯定可以测的,假如你的脉宽不经常变动,那么你现在的方案可行,假如经常变动,那么最好你开多一个定时器,在定时时间内测试检测到上升沿或者下降沿的次数。

像你现在所的问题,不是说,单纯看这个代码怎样怎样,毕竟你是网上找的,不成功也不怪,一来人家硬件和你不一样,就算硬件一样,人家的也不一定是对的。我建议,你一步步来,既然说捕捉错误,如何错误?哪里出问题了?是没进入中断?还是丢失了很多捕捉中断?还是计算错误?假如有仿真环境,用断点好好调试一下,看看问题所在吧。
点赞  2010-5-23 00:59
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复