/*******************************************************************************
Platform: ATmega16+MAX7219+8位共阴数码管
Project : 实验44:基于TC1的ICP频率计(ATmega16)
Clock F : 外部RC:8M
Software: ICCAVR7.14C+Proteus7.4
Author : 林夕依然
AVRgroup: http://avrgroup.5d6d.com/
Version : 09.06.04
Updata :
comments:
1、外部8M晶体,定时器1取8分频,即每计数一次的时间为1us,便于计算;
2、M16的PC0/PC1/PC2控制MAX7219显示,使用第三种算法;
3、使用定时器1的ICP1输入捕捉功能;
4、最关键的地方,在于中断后取出ICR1的差值,这个值是测量的基础;
5、频率和周期的测量均取整数,带小数位的情况需要进行处理,显示函数也较为复杂,
此例中未做显示;
6、本程序的测量范围尚未计算确定;
7、程序显示1S实验日期后,进入频率/周期循环显示状态(间隔2S);
8、参考:www.avrvi.com 古欣的"利用ICP功能测周期频率的程序"(ATmega128);
*******************************************************************************/
#include <macros.h>
#include <iom16v.h>
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
//全局变量定义
uint TEN,QIAN,BAI,SHI,GE;
uchar flow_flag=0; //用于标记定时器溢出,如果溢出了,只记录到TCNT的值就不对了哦,需要进行处理,应该是TOP*flow_flag+TCNT
uint icpnum=0; //记录每次采集的TCNT差值,注意整个过程中不要操作TCNT,以免引入误差
uchar timerover_error=0;
uint last_icrvalue=0; //记录上次的ICR值
uint value;
//函数声明
void delay_ms(uint i);
void delay_us(uint n);
void init_max7219(void);
void send_max7219(uchar add,uchar data);
void timer1_init(void);
void init_devices(void);
void conver16_to10(uint num);
void display_date(void);
void display_HZ(uint hz);
void display_T(uint t);
/*-----------------------------------------------------------------
函数名称: void timer1_init(void)
函数功能: 定时器1初始化
参 数: 无
返 回 值: 无
说 明:使能噪声抑制器,上升沿触发,8分频
-----------------------------------------------------------------*/
void timer1_init(void)
{
TCCR1B = 0x00; //stop
TCNT1H = 0x00;
TCNT1L = 0x00;
ICR1H = 0x00;
ICR1L = 0x00;
TCCR1A = 0x00;
TCCR1B|=(1<<ICNC1)|(1<<ICES1)|(1<<CS11); //使能噪声抑制器,上升沿触发,8分频
}
/*-----------------------------------------------------------------
函数名称: void timer1_ovf_isr(void)
函数功能: 定时器1捕捉事件中断
参 数: 无
返 回 值: 无
说 明:ICP1引脚上升沿发生中断,如果需要测占空比,在此中断中按条件更改中断方向。
-----------------------------------------------------------------*/
#pragma interrupt_handler timer1_capt_isr:6
void timer1_capt_isr(void)
{
CLI(); //关全局中断
value=ICR1L; //先读取低字节 (important)
value|=(uint)ICR1H << 8; //读取高字节并同低字节合并
//根据 flow_flag 的不同,计算真实的ICR差值
if(flow_flag==0)
{
icpnum=value-last_icrvalue;
}
else
{
if(flow_flag==1)
{
//说明TCNT越过一次TOP
flow_flag=0;
icpnum=0xFFFF-last_icrvalue+value;
}
else
{
//超时(越界两次以上)未检查到信号,错误处理
timerover_error=1;
/****************************************
调试程序时注意,如果进入这里 结果已经不准确了
****************************************/
flow_flag=0;
}
}
last_icrvalue=value;
SEI(); //开全局中断
}
/*-----------------------------------------------------------------
函数名称: void timer1_ovf_isr(void)
函数功能: 定时器1溢出中断
参 数: 无
返 回 值: 无
-----------------------------------------------------------------*/
#pragma interrupt_handler timer1_ovf_isr:9
void timer1_ovf_isr(void)
{
flow_flag ++ ;
}
/*-----------------------------------------------------------------
函数名称: void init_devices(void)
函数功能: 器件初始化
参 数: 无
返 回 值: 无
-----------------------------------------------------------------*/
void init_devices(void)
{
CLI(); //关全局中断
PORTC=0x00;
DDRC=0xFF;
DDRD = 0x00;
PORTD= (1<<PD6); //ICP1引脚为输入
timer1_init(); //定时器1初始化
TIMSK|=(1<<TICIE1)|(1<<TOIE1); //TC1输入捕捉中断使能,溢出中断使能
SEI(); //开全局中断
}
/*-----------------------------------------------------------------
函数名称: void conver16_to10(uint num)
函数功能: 16进制转10进制函数
参 数: num
返 回 值: 无
-----------------------------------------------------------------*/
void conver16_to10(uint num)
{
TEN=num;
QIAN=TEN/0x03e8;
TEN%=0x03e8;
BAI=TEN/0x64;
TEN%=0x64;
SHI=TEN/0x0a;
TEN%=0x0a;
GE=TEN;
}
/*-----------------------------------------------------------------
函数名称: void display_date(void)
函数功能: 日期显示函数(2009.06.04)
参 数: 无
返 回 值: 无
-----------------------------------------------------------------*/
void display_date(void)
{
send_max7219(0x01,0); //1
send_max7219(0x02,9); //2
send_max7219(0x03,10); //3,显示'-'
send_max7219(0x04,0); //4
send_max7219(0x05,6); //5
send_max7219(0x06,10); //6,显示'-'
send_max7219(0x07,0); //7
send_max7219(0x08,4); //9
}
/*-----------------------------------------------------------------
函数名称: void display_HZ(uint hz)
函数功能: 频率显示函数,单位为HZ
参 数: hz,处理ICP1测量频率,显示在8位数码管上
返 回 值: 无
-----------------------------------------------------------------*/
void display_HZ(uint hz)
{
conver16_to10(hz);
send_max7219(0x01,10); //1显示'-'
send_max7219(0x02,12); //2显示'H',表示频率单位HZ
send_max7219(0x03,10); //3显示'-'
send_max7219(0x04,15); //4显示空
send_max7219(0x05,QIAN); //5显示千位
send_max7219(0x06,BAI); //6显示百位
send_max7219(0x07,SHI); //7显示十位
send_max7219(0x08,GE); //9显示个位
}
/*-----------------------------------------------------------------
函数名称: void display_T(uint t)
函数功能: 周期显示函数,单位为us
参 数: t,处理ICP1测量信号的周期,显示在8位数码管上
返 回 值: 无
-----------------------------------------------------------------*/
void display_T(uint t)
{
conver16_to10(t);
send_max7219(0x01,10); //1显示'-'
send_max7219(0x02,5); //2显示'5',等效S,其周期单位us
send_max7219(0x03,10); //3显示'-'
send_max7219(0x04,15); //4显示空
send_max7219(0x05,QIAN); //5显示千位
send_max7219(0x06,BAI); //6显示百位
send_max7219(0x07,SHI); //7显示十位
send_max7219(0x08,GE); //9显示个位
}
/*-----------------------------------------------------------------
函数名称: void main(void)
函数功能: 主函数
参 数: 无
返 回 值: 无
-----------------------------------------------------------------*/
void main(void)
{
//float Freque_value=0.00,T=0.00; //带小数位,精确度高,此程序中未使用
uint Freque=0,t=0; //只显示整数,精确度低
uint temp=0;
init_devices(); //器件初始化
init_max7219(); //max7219初始化
display_date(); //显示实验日期1S
delay_ms(1000);
while(1)
{
if(timerover_error==1)
{
//错误处理程序
}
temp = icpnum; //读出再处理,防止中断中在对icpnum改写,造成影响
//Freque_value=(float)((float)1000000/(float)temp); //频率HZ,有小数位,精确度高
//T = (float)((float)temp/(float)1000); //周期ms,有小数位,精确度高
Freque=(uint)((float)1000000/temp); //频率HZ,只取整数,精确度低
t=temp; //周期us,只取整数,精确度低
display_HZ(Freque); //显示ICP1信号频率,单位为HZ,显示2S
delay_ms(2000);
display_T(t); //显示ICP1信号周期,单位为us,显示2S
delay_ms(2000);
}
}
/******************************************************************************
★以下引