📄 countermeter.c
字号:
/*************************************************
**
** 本程序根据Jesper的应用例子改编
** 源例子网页:http://www.myplace.nu/avr/countermeasures/index.htm
** CPU ATmega16
** 晶振频率 4MHz
** 编译环境 AVR Studio 4.12.460 + GCC 3.4.3
** 程序修改 machao
** 时间 2005-12-07
**
*************************************************/
#define COUNTERMETER_C
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdlib.h>
//定义计数器控制引脚
#define CLEAR PA6
#define OE_H PA5
#define OE_L PA4
//#define F_CPU 4000000 // 晶振频率 4MHz
#define CYCLES_PER_US ((F_CPU+500000)/1000000) // cpu cycles per microsecond
// 数码管显示数据
#define SEG_a 0x01
#define SEG_b 0x02
#define SEG_c 0x04
#define SEG_d 0x08
#define SEG_e 0x10
#define SEG_f 0x20
#define SEG_g 0x40
#define SEG_dot 0x80
unsigned char digits[] = {
(SEG_a|SEG_b|SEG_c|SEG_d|SEG_e|SEG_f), // 0
(SEG_b|SEG_c), // 1
(SEG_a|SEG_b|SEG_d|SEG_e|SEG_g), // 2
(SEG_a|SEG_b|SEG_c|SEG_d|SEG_g), // 3
(SEG_b|SEG_c|SEG_c|SEG_f|SEG_g), // 4
(SEG_a|SEG_c|SEG_d|SEG_f|SEG_g), // 5
(SEG_a|SEG_c|SEG_d|SEG_e|SEG_f|SEG_g), // 6
(SEG_a|SEG_b|SEG_c), // 7
(SEG_a|SEG_b|SEG_c|SEG_d|SEG_e|SEG_f|SEG_g), // 8
(SEG_a|SEG_b|SEG_c|SEG_d|SEG_f|SEG_g), // 9
(SEG_a), // mode 0 indicator (Hz)
(SEG_g), // mode 1 indicator (kHz)
(SEG_d), // mode 2 indicator (MHz)
};
/****************************************************************************/
// timer 0 interrupt handles multiplex and refresh of the displays
//定时器T0的溢出中断用于刷新数码管
// timer is clocked at 62500 Hz
#define TI0_L (256-125) // 500 Hz -> 2 mS
volatile unsigned char active_led = 0; //当前点亮的数码管
volatile unsigned long led_value = 0; // four BCD nibbles
volatile unsigned char decimal_point = 0;
volatile unsigned char mode_setting = 0;
SIGNAL(SIG_OVERFLOW0) //定时器T0溢出中断程序(16 uS中断一次)
{
unsigned char a,b;
//重新装载初值
TCNT0 = TI0_L;
//关闭所有数码管
PORTA |= 0X07;
if (active_led == 5)
{
b = digits[10 + mode_setting]; //第6为数码管用于模式指示
}
else
{
a = led_value >> (( 4 - active_led ) * 4);
b = digits[a & 0x0f];
if (decimal_point == (4 - active_led) )
b |= SEG_dot;
}
PORTB = b;
//设置显示位置
PORTA = ((PORTA & 0Xf8)|active_led);
active_led = (active_led+1) % 6;
}
/****************************************************************************/
/* helpers ****************************************************************/
/****************************************************************************/
void delay(unsigned short us)
{
unsigned short delay_loops;
register unsigned short i;
delay_loops = (us+3)/5*CYCLES_PER_US; // +3 for rounding up (dirty)
// one loop takes 5 cpu cycles
for (i=0; i < delay_loops; i++);
}
//读16位计数值
unsigned int read_counters(void)
{
unsigned int counter_value;
// 读数据时关闭中断
cli();
//关闭数码管显示
PORTA |= 0X07;
//设置PORTB位输入
DDRB = 0X00;
//置OE_H为低读取高8位
PORTA &= ~(1<<OE_H);
asm volatile("nop");
//1个脉冲锁存计数值
PORTA |= (1<<OE_H);
asm volatile("nop");
PORTA &= ~(1<<OE_H);
asm volatile("nop");
//读高8位数据
counter_value = PINB;
//置OE_H为高
PORTA |= (1<<OE_H);
counter_value <<= 8;
//置OE_L为低读取低8位
PORTA &= ~(1<<OE_L);
asm volatile("nop");
//1个脉冲锁存计数值
PORTA |= (1<<OE_L);
asm volatile("nop");
PORTA &= ~(1<<OE_L);
asm volatile("nop");
//读低8位数据
counter_value |= PINB ;
//置OE_L为高
PORTA |= (1<<OE_L);
//设置PORTB位输出
DDRB = 0XFF;
//使能数码管刷新
sei();
return counter_value;
}
void capture(unsigned int compare)
{
//清零外部计数器
PORTA &= ~(1<<CLEAR);
asm volatile("nop");
//置清零为高
PORTA |= (1<<CLEAR);
//清计数值
TCNT1H = 0;
TCNT1L = 0;
//设置输出比较寄存器为所需值
OCR1AH = compare >> 8;
OCR1AL = compare;
//比较匹配时OC1A/OC1B 电平取反
TCCR1A = (1<<COM1A0); //0x40;//
//清比较匹配标志
TIFR |= (1<<OCF1A);
if (compare == 15625)
{
// start with fClk/256 (15625 Hz) and compare clear
TCCR1B = (1<<WGM12)|(1<< CS12); //0x0C;//
}
else
{
// start with fClk/8 (500 kHz) and compare clear
TCCR1B = (1<<WGM12)|(1<<CS11); //0x0A;//
}
// wait for bit
while ( ! (unsigned char) ( TIFR & (1<< OCF1A)) );
// clear flags
TIFR |= (1<<OCF1A);
// counter input now enabled
// for the specified time
// wait again for bit
while ( ! (unsigned char) ( TIFR & (1<< OCF1A)) );
// stop timer
TCCR1B = 0;
// counter input disabled
}
/****************************************************************************/
/* main *******************************************************************/
/****************************************************************************/
int main(void)
{
int i,j;
unsigned char dp,ms;
unsigned long lv;
unsigned int count;
//设置PORTA为输出高电平
DDRA = 0XFF;
PORTA = 0XFF;
//设置PORTB为输出高电平
DDRB = 0XFF;
PORTB = 0XFF;
//设置PORTD为输出高电平
DDRD = 0XFF;
PORTD = 0XFF;
//设置定时器T0
// 计数时钟为晶振频率64分频 周期为 = 1/(4000000/64 )-> 16 uS
TCCR0 = (1<<CS01)|(1<<CS00); //0x03;//
//使能定时器T0溢出中断
TIMSK |= (1<<TOIE0);
//使能全局中断
sei();
/* 输出比较寄存器初值在 fclk/8 (500 kHz, 2 uS) 下的门控值:
500 = 1 mS
5000 = 10 mS
50000 = 100 mS
在 fclk/256 (15.625 kHz, 64 uS) :下的门控值:
15625 = 1 S
*/
// 清零16位计数器T1
TCNT1H = 0;
TCNT1L = 0;
// 设置输出比较寄存器初值为 200
OCR1AH = 0;
OCR1AL = 200;
//比较匹配时置位 OC1A/OC1B ( 输出高电平)
TCCR1A = (1<<COM1A1)|(1<<COM1A0); //0xC0;//
//启动定时器T1
TCCR1B = (1<<CS10); //0x01;//
//等待比较匹配标志位置位
while ( ! (unsigned char) ( TIFR & (1<<OCF1A)) );
//清楚标志
TIFR |= (1<< OCF1A);
//定时器T1停止计数
TCCR1B = 0;
// compare bit no HI, start
// doing some useful work
while (1)
{
//用最小的门控尝试
capture(500); // 1 mS
//读取计数值
count = read_counters();
dp = 3; // 小数点位置
ms = 2; // 频率单位 MHz
if (count < 4096) // 小于 4.096 MHz
{
//尝试下一个门控值
capture(5000); // 10 mS
//读取计数值
count = read_counters();
dp = 4; // 小数点位置
ms = 2; // 频率单位 MHz
if (count < 4096) // 小于 409.6 kHz
{
// 尝试下一个门控值
capture(50000); // 100 mS
//读取计数值
count = read_counters();
dp = 2; // 小数点位置
ms = 1; // 频率单位 kHz
if (count < 4096) // 小于 40.96 kHz
{
// 尝试下一个门控值
capture(15625); // 1 S
// 读取计数值
count = read_counters();
dp = 0; // 小数点位置
ms = 0; // 频率单位 Hz
}
}
}
//将二进制16位计数值转换位BCD码
lv = 0;
for (j=0;j<8;j++)
{
i = count % 10;
lv >>= 4;
lv |= ((unsigned long)i << 28);
count /= 10;
}
//设置显示参数
decimal_point = dp;
mode_setting = ms;
led_value = lv;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -