测频指标:测频刷新速度为2s,精度为 1Hz ,输入信号峰值>3V;
原理上采用32.768K外部晶振产生异步时钟信号 ,作为M8定时器2的时钟源,设定1024的预分频,可以得到TCNT2溢出的精确时间为1s,在溢出中断时控制74ls00与非门进而控制被测信号的通断,累计1s 内计数器获得的值,经过简单的运算则可获得被测信号的频率
M8 采用内部 8M 内部RC震荡 工作模式 , 电路采用74ls393 对被测信号进行预分频,相当于扩张T1计数器的位数,T1 为16位,74ls393为8位,扩展后为24位,T1不溢出的话 最高可测 16.777216M ,溢出则累计中断次数然后进行累加即可。(另外添加74ls393进行预分频的目的是为了解决T1引脚时钟信号不宜大于单片机工作频率的二分之一的问题)
目前测频 4M 已经成功通过,由于没有函数信号发生器,所以其他高频还没有做测试
PCB原理图
/////////////////////////////以下为程序, 只有一个文件 main.c /////////////////////////////////
#include <avr/io.h> #include <avr/iom8.h> #include <avr/signal.h> #include <avr/interrupt.h> #include <stdint.h> #include <avr/wdt.h> #include <util/delay.h> #include <stdio.h>
/*----------------------常用参数定义-------------------*/
#define P0 0 #define P1 1 #define P2 2 #define P3 3 #define P4 4 #define P5 5 #define P6 6 #define P7 7
#define FREQ 8 #define uint unsigned int #define uchar unsigned char
/*----------------------某些端口操作-------------------*/
#define SET_DOOR PORTB|=_BV(P1) #define CLR_DOOR PORTB&=~_BV(P1)
#define SET_CLEAR PORTB|=_BV(P2) #define CLR_CLEAR PORTB&=~_BV(P2)
/*----------------------1602定义-------------------*/
#define SET_LCD_RS PORTD|=_BV(P2) #define CLR_LCD_RS PORTD&=~_BV(P2)
#define SET_LCD_RW PORTD|=_BV(P3) #define CLR_LCD_RW PORTD&=~_BV(P3)
#define SET_LCD_E PORTD|=_BV(P4) #define CLR_LCD_E PORTD&=~_BV(P4)
#define SET_74LS595_SHIFT PORTB|=_BV(P0) #define CLR_74LS595_SHIFT PORTB&=~_BV(P0)
#define SET_74LS595_DI PORTD|=_BV(P6) #define CLR_74LS595_DI PORTD&=~_BV(P6)
#define SET_74LS595_CLK PORTD|=_BV(P7) #define CLR_74LS595_CLK PORTD&=~_BV(P7)
void LCD_ON(void); //启动LCD void LongConvertToChar(unsigned long WD); void LCDInit(void); void WritEDAtaLCD(unsigned char WDLCD); void WriteCommandLCD(unsigned char WCLCD); void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData); void DisplayListChar(unsigned char X, unsigned char Y,unsigned char *DData); void WritEDAtaTo595(unsigned char WDLCD);
const unsigned char Owner[] = {"Hello World !!!"}; const unsigned char uctech[] = {"INPUT FREQUENCE:"}; const unsigned char Init[] = {"Initialization."}; unsigned char net[16] = {"Axin & Cornsoup"}; unsigned char Net_Pointer=0;//net[] 的指针
unsigned char Timer1_Counter_H=0; unsigned char Timer1_Counter_L=0; unsigned long Frequence="0"; unsigned char T2_OV_Time=1; //T2溢出对应的时间 unsigned char T1_OV_Times=0; //T1溢出次数
/*----------------------串口定义-------------------*/
unsigned char SetPrintfConvertMode="0"; //使用printf作其他转换,并非输出到UART
void Uart_Init(void);
int System_putchar(char c, FILE *stream); int System_getchar(FILE *stream);
FILE mystd = FDEV_SETUP_STREAM(System_putchar, System_getchar,_FDEV_SETUP_RW);
/*----------------------常用函数定义------------------*/
void delay_nms(unsigned int ms) //N ms延时函数 { uint i; for(i=0;i<ms;i++) _delay_loop_2(FREQ*250); }
/*----------------------系统初始化函数定义------------------*/
void IO_INIT(void);
/////////////////////////////////////////////////////////////////
int main(void) { wdt_disable(); IO_INIT(); Uart_Init(); LCD_ON(); //初始化 LCD1602 并显示制作者信息 delay_nms(1500); DisplayListChar(0, 0, uctech); //输出英文 "Input Frequence:" 到LCD1602 第一行
DisplayListChar(0, 5, Init); //显示稍等
CLR_DOOR; //关闭阀门 _delay_loop_2(5); SET_CLEAR; _delay_loop_2(5); //清空74ls393数据 CLR_CLEAR;
TCNT1H=0; TCNT1L=0; //清空T1计数器
ASSR=_BV(AS2); //允许异步时钟 TCCR2=_BV(CS22)|_BV(CS20); TCCR1B=_BV(CS12)|_BV(CS11); //外部T1引脚输入 下降沿有效 (一定要下降沿) TIMSK=_BV(TOIE1)|_BV(TOIE2); //允许溢出中断
sei();
while(1); }
/*----------------------系统初始化函数实体------------------*/
void IO_INIT(void) { DDRB|=0x0f; PORTB&=0x0f; DDRC|=0x00; PORTC&=0x00; DDRD|=0xdc; PORTD&=0xdc; }
/*----------------------系统中断函数实体-----------------*/
ISR(TIMER1_OVF_vect) //定时器1溢出中断 { T1_OV_Times++; }
ISR(TIMER2_OVF_vect) //定时器2溢出中断 { if(T2_OV_Time==2) { CLR_DOOR;
Timer1_Counter_L=TCNT1L; //读取TCNT1数据要按照顺序,先低8位后高8位 Timer1_Counter_H=TCNT1H; Frequence=((unsigned long)Timer1_Counter_H<<16)|((unsigned long)Timer1_Counter_L<<8)|((PINC&0x3c)>>2)|((PINC&0x03)<<6)|(PINB&0x30); if(T1_OV_Times!=0) //其实这个是多余的,这里目的是测量16.7M 以上的频率 不过我们测量的频率不可能达到这个 { Frequence+=(unsigned long)0xffff*0xff*T1_OV_Times; T1_OV_Times=0; } //printf("\n\nTCNT1H: 0X%X TCNT1L: 0X%x",Timer1_Counter_H,Timer1_Counter_L); //printf("\nOverFlowTimes %d",T1_OV_Times); LongConvertToChar(Frequence); //把Frequence 转换后的数据 放到 net[]数组里面 DisplayListChar(0, 5, net); //把net[]的数据输出到LCD SET_CLEAR; //一定要先把 74ls393 清零 再对TCNT1 清零 _delay_loop_2(2); //适当延时,其实可不要 CLR_CLEAR; _delay_loop_2(2); TCNT1H=0x00; TCNT1L=0x00;
T2_OV_Time=1; //启动下一次测量 } else { SET_DOOR; T2_OV_Time=2; } }
/*----------------------LCD_1602函数实体------------------*/
void LCD_ON(void) { delay_nms(400); //启动等待,等LCD讲入工作状态 LCDInit(); //LCD初始化 delay_nms(100); //延时片刻(可不要) DisplayListChar(0, 0, Owner); DisplayListChar(0, 5, net); }
void UsePrintfToConvert(unsigned long WD) //利用 printf 的转换功能 爽! ^.^ { SetPrintfConvertMode=1; //设置 printf 为自定义转换模式 printf("%13ld Hz",WD); //net[]数组总共有 16 个成员 与LCD1602的一行16个位对应 SetPrintfConvertMode=0; //还原 printf 为 Uart 输出模式
/*---------为输出数据添加逗号 999,999,999 ------------*/
net[2]=net[4]; net[3]=net[5]; net[4]=net[6]; if(net[2]!=' '||net[3]!=' '||net[4]!=' ') { if(net[4]!='-') { net[5]=','; } } net[6]=net[7]; net[7]=net[8]; net[8]=net[9]; if(net[6]!=' '||net[7]!=' '||net[8]!=' ') { if(net[8]!='-') { net[9]=','; } } }
void LongConvertToChar(unsigned long WD) { UsePrintfToConvert(WD); }
void WritEDAtaTo595(unsigned char WDLCD) // 74hc595 串行转并行输出 { unsigned char i; CLR_74LS595_CLK; for(i=0;i<8;i++) { CLR_74LS595_SHIFT; if(WDLCD&0x01) { SET_74LS595_DI; } else { CLR_74LS595_DI; } WDLCD>>=1; SET_74LS595_SHIFT; } _delay_loop_2(1); SET_74LS595_CLK; }
//写数据 void WritEDAtaLCD(unsigned char WDLCD) { delay_nms(1);//适当加延时,避免 LCD1602 繁忙 WritEDAtaTo595(WDLCD); SET_LCD_RS; CLR_LCD_RW; CLR_LCD_E; //若晶振速度太高可以在这后加小的延时 _delay_loop_2(1); SET_LCD_E; }
//写指令 void WriteCommandLCD(unsigned char WCLCD) { delay_nms(1);//适当加延时,避免 LCD1602 繁忙 WritEDAtaTo595(WCLCD); CLR_LCD_RS; CLR_LCD_RW; CLR_LCD_E; _delay_loop_2(1); SET_LCD_E; }
void LCDInit(void) //LCM初始化 { WritEDAtaTo595(0); WriteCommandLCD(0x38); //三次显示模式设置,不检测忙信号 delay_nms(15); WriteCommandLCD(0x38); delay_nms(5); WriteCommandLCD(0x38); delay_nms(5);
WriteCommandLCD(0x38); //显示模式设置,开始要求每次检测忙信号 WriteCommandLCD(0x08); //关闭显示 WriteCommandLCD(0x01); //显示清屏 WriteCommandLCD(0x06); // 显示光标移动设置 WriteCommandLCD(0x0C); // 显示开及光标设置 }
//按指定位置显示一个字符 void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData) { Y &= 0x1; X &= 0xF; //限制X不能大于15,Y不能大于1 if (Y) X |= 0x40; //当要显示第二行时地址码+0x40; X |= 0x80; // 算出指令码 WriteCommandLCD(X); //这里不检测忙信号,发送地址码 WritEDAtaLCD(DData); }
//按指定位置显示一串字符 void DisplayListChar(unsigned char X, unsigned char Y, unsigned char *DData) { unsigned char ListLength;
ListLength = 0; Y &= 0x1; X &= 0xF; //限制X不能大于15,Y不能大于1 while (DData[ListLength]>=0x20) //若到达字串尾则退出 { if (X <= 0xF) //X坐标应小于0xF { DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符 ListLength++; X++; } } }
/*----------------------串口函数实体------------------*/
void Uart_Init(void) { UCSRB=_BV(RXEN)|_BV(TXEN); UBRRL=51; stdout=&mystd; stdin=&mystd; }
int System_putchar(char c, FILE *stream) { if(SetPrintfConvertMode==1) { net[Net_Pointer]=c; Net_Pointer++; if(Net_Pointer>=16) { Net_Pointer=0; } } else { if (c == '\n') System_putchar('\r', stream); loop_until_bit_is_set(UCSRA, UDRE); UDR = c; }
return 0; }
int System_getchar( FILE *stream) { loop_until_bit_is_set(UCSRA,RXC); return UDR; }
///////////////////程序结束////////////////////////
|
文章评论(0条评论)
登录后参与讨论