基于51单片机的T0计数器系统设计,使用定时器0作为计数器对外部脉冲进行计数,并通过数码管显示计数值。
一、系统设计 1.1 功能要求 T0作为计数器,对P3.4(T0)引脚的外部脉冲计数 4位共阳数码管显示计数值(0-9999) 支持加计数和减计数两种模式 按键控制:清零、模式切换、暂停/继续 计数溢出处理(0-9999循环) 计数频率可达50kHz 1.2 硬件资源 资源 型号/规格 数量 单片机 STC89C52/AT89C52 1 数码管 4位共阳数码管 1 按键 轻触开关 3 电阻 220Ω(限流) 8 电阻 10kΩ(上拉) 3 晶振 11.0592MHz 1 脉冲源 按键/信号发生器 1
二、硬件设计 2.1 电路原理图 +5V | +-+ 数码管显示 | 段选:P0.0-P0.7 (经220Ω电阻) | 位选:P2.0-P2.3 (经220Ω电阻) | +-+ 按键控制 | K1(清零) -> P3.0 -> GND | K2(模式) -> P3.1 -> GND | K3(暂停) -> P3.2 -> GND | (各接10kΩ上拉电阻到+5V) | +-+ 计数输入 | P3.4(T0) <- 外部脉冲输入 | +-+ 单片机最小系统 XTAL1/XTAL2 -> 11.0592MHz晶振 RST -> 10kΩ电阻+10μF电容 EA -> +5V VCC -> +5V GND -> GND2.2 引脚分配表 单片机引脚 连接设备 功能说明 P0.0-P0.7 数码管段选 a,b,c,d,e,f,g,dp P2.0-P2.3 数码管位选 千位、百位、十位、个位 P3.0 按键K1 清零 P3.1 按键K2 模式切换 P3.2 按键K3 暂停/继续 P3.4 外部脉冲 T0计数输入 P1.0 LED指示 计数状态指示
三、软件设计 3.1 主程序 (main.c) # include <reg52.h> # include "display.h" # include "keyboard.h" # include "timer0_counter.h" // 全局变量 unsigned int count_value= 0 ; // 当前计数值 unsigned int max_count= 9999 ; // 最大计数值 unsigned int min_count= 0 ; // 最小计数值 bit count_mode= 0 ; // 0:加计数 1:减计数 bit count_enable= 1 ; // 计数使能 bit overflow_flag= 0 ; // 溢出标志 void main ( void ) { System_Init ( ) ; while ( 1 ) { Key_Process ( ) ; // 按键处理 Display_Update ( ) ; // 显示更新 Count_Check ( ) ; // 计数范围检查 } } void System_Init ( void ) { // 初始化定时器0为计数器模式 Timer0_Counter_Init ( ) ; // 初始化数码管 Display_Init ( ) ; // 显示初始值 Display_Number ( count_value) ; // 开启总中断 EA= 1 ; // 启动计数 TR0= 1 ; } void Key_Process ( void ) { unsigned char key= Key_Scan ( ) ; switch ( key) { case KEY_CLEAR: // 清零 count_value= 0 ; Display_Number ( count_value) ; break ; case KEY_MODE: // 切换模式 count_mode= ! count_mode; if ( count_mode) Display_Show_Mode ( "DEC" ) ; // 显示减计数模式 else Display_Show_Mode ( "INC" ) ; // 显示加计数模式 break ; case KEY_PAUSE: // 暂停/继续 count_enable= ! count_enable; if ( count_enable) { TR0= 1 ; // 继续计数 P1_0= 0 ; // LED亮表示计数中 } else { TR0= 0 ; // 暂停计数 P1_0= 1 ; // LED灭表示暂停 } break ; default : break ; } } void Count_Check ( void ) { // 检查溢出 if ( overflow_flag) { overflow_flag= 0 ; if ( count_mode== 0 ) // 加计数溢出 { count_value= min_count; // 回到最小值 } else // 减计数溢出 { count_value= max_count; // 回到最大值 } Display_Number ( count_value) ; } // 检查是否达到极限 if ( count_value>= max_count&& count_mode== 0 ) { count_enable= 0 ; // 禁止计数 TR0= 0 ; P1_0= 1 ; // LED灭 } if ( count_value<= min_count&& count_mode== 1 ) { count_enable= 0 ; // 禁止计数 TR0= 0 ; P1_0= 1 ; // LED灭 } } // 定时器0中断服务程序(计数器溢出中断) void Timer0_ISR ( void ) interrupt1 { overflow_flag= 1 ; // 设置溢出标志 // 根据计数模式更新计数值 if ( count_mode== 0 ) // 加计数 { count_value++ ; if ( count_value> max_count) count_value= min_count; // 循环 } else // 减计数 { count_value-- ; if ( count_value< min_count) count_value= max_count; // 循环 } Display_Number ( count_value) ; } 3.2 定时器0计数器 (timer0_counter.h) # ifndef __TIMER0_COUNTER_H__ # define __TIMER0_COUNTER_H__ # include <reg52.h> // 函数声明 void Timer0_Counter_Init ( void ) ; void Timer0_Start ( void ) ; void Timer0_Stop ( void ) ; void Timer0_Set_Value ( unsigned int value) ; unsigned int Timer0_Get_Value ( void ) ; # endif 3.3 定时器0计数器 (timer0_counter.c) # include "timer0_counter.h" void Timer0_Counter_Init ( void ) { // 设置T0为计数器模式,工作方式1(16位计数) TMOD&= 0xF0 ; // 清除T0控制位 TMOD|= 0x05 ; // 0000 0101: T0为计数器,方式1(16位) // 设置计数初值(0) TH0= 0x00 ; TL0= 0x00 ; // 允许T0中断 ET0= 1 ; // 启动T0(由TR0控制) TR0= 0 ; // 先不启动 } void Timer0_Start ( void ) { TR0= 1 ; // 启动计数器 } void Timer0_Stop ( void ) { TR0= 0 ; // 停止计数器 } void Timer0_Set_Value ( unsigned int value) { TH0= value>> 8 ; // 高8位 TL0= value& 0xFF ; // 低8位 } unsigned int Timer0_Get_Value ( void ) { unsigned int value; value= ( TH0<< 8 ) | TL0; return value; } 3.4 数码管显示 (display.h) # ifndef __DISPLAY_H__ # define __DISPLAY_H__ # include <reg52.h> // 数码管段码定义(共阳) # 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_DP 0x80 // 数字0-9段码表 extern unsigned char code SegCode[ 10 ] ; // 位选定义 # define DIGIT_1 P2_0 // 千位 # define DIGIT_2 P2_1 // 百位 # define DIGIT_3 P2_2 // 十位 # define DIGIT_4 P2_3 // 个位 // 函数声明 void Display_Init ( void ) ; void Display_Number ( unsigned int num) ; void Display_Scan ( unsigned char pos) ; void Display_Clear ( void ) ; void Display_Show_Mode ( char * mode) ; // 显示缓冲区 extern unsigned char Display_Buffer[ 4 ] ; # endif 3.5 数码管显示 (display.c) # include "display.h" // 共阳数码管段码表(0-9) unsigned char code SegCode[ 10 ] = { 0xC0 , // 0: 1100 0000 0xF9 , // 1: 1111 1001 0xA4 , // 2: 1010 0100 0xB0 , // 3: 1011 0000 0x99 , // 4: 1001 1001 0x92 , // 5: 1001 0010 0x82 , // 6: 1000 0010 0xF8 , // 7: 1111 1000 0x80 , // 8: 1000 0000 0x90 // 9: 1001 0000 } ; // 字母段码表 unsigned char code LetterCode[ 26 ] = { 0x88 , // A 0x83 , // b 0xC6 , // C 0xA1 , // d 0x86 , // E 0x8E , // F 0xC2 , // G 0x89 , // H 0xF9 , // I 0xF1 , // J 0xC7 , // L 0xC8 , // M 0xC1 , // n 0xC0 , // O 0x8C , // P 0x98 , // q 0x92 , // r 0x87 , // S 0x83 , // t 0xC1 , // u 0x91 , // v 0xD1 , // w 0x89 , // X 0x49 , // y 0x90 // Z } ; unsigned char Display_Buffer[ 4 ] = { 0xC0 , 0xC0 , 0xC0 , 0xC0 } ; void Display_Init ( void ) { P0= 0xFF ; // 段选全灭 P2= 0x0F ; // 位选全灭 } void Display_Number ( unsigned int num) { // 分解各位数字 unsigned char thousand, hundred, ten, unit; thousand= num/ 1000 ; hundred= ( num% 1000 ) / 100 ; ten= ( num% 100 ) / 10 ; unit= num% 10 ; // 存储显示缓冲区 Display_Buffer[ 0 ] = SegCode[ thousand] ; Display_Buffer[ 1 ] = SegCode[ hundred] ; Display_Buffer[ 2 ] = SegCode[ ten] ; Display_Buffer[ 3 ] = SegCode[ unit] ; } void Display_Scan ( unsigned char pos) { // 关闭所有位选 P2= 0x0F ; // 送段码 switch ( pos) { case 0 : // 千位 DIGIT_1= 0 ; // 选中千位 P0= Display_Buffer[ 0 ] ; break ; case 1 : // 百位 DIGIT_2= 0 ; // 选中百位 P0= Display_Buffer[ 1 ] ; break ; case 2 : // 十位 DIGIT_3= 0 ; // 选中十位 P0= Display_Buffer[ 2 ] ; break ; case 3 : // 个位 DIGIT_4= 0 ; // 选中个位 P0= Display_Buffer[ 3 ] ; break ; } } void Display_Clear ( void ) { P0= 0xFF ; // 段选全灭 P2= 0x0F ; // 位选全灭 } void Display_Show_Mode ( char * mode) { // 显示模式标识 if ( mode[ 0 ] == 'I' ) // INC { Display_Buffer[ 0 ] = LetterCode[ 'I' - 'A' ] ; Display_Buffer[ 1 ] = LetterCode[ 'N' - 'A' ] ; Display_Buffer[ 2 ] = LetterCode[ 'C' - 'A' ] ; Display_Buffer[ 3 ] = 0xFF ; // 熄灭 } else if ( mode[ 0 ] == 'D' ) // DEC { Display_Buffer[ 0 ] = LetterCode[ 'D' - 'A' ] ; Display_Buffer[ 1 ] = LetterCode[ 'E' - 'A' ] ; Display_Buffer[ 2 ] = LetterCode[ 'C' - 'A' ] ; Display_Buffer[ 3 ] = 0xFF ; // 熄灭 } } 3.6 键盘扫描 (keyboard.h) # ifndef __KEYBOARD_H__ # define __KEYBOARD_H__ # include <reg52.h> // 按键定义 # define KEY_CLEAR 0x01 # define KEY_MODE 0x02 # define KEY_PAUSE 0x03 # define KEY_NONE 0xFF // 按键端口定义 sbit KEY_CLR_PIN= P3^ 0 ; sbit KEY_MOD_PIN= P3^ 1 ; sbit KEY_PAU_PIN= P3^ 2 ; // 函数声明 void Key_Init ( void ) ; unsigned char Key_Scan ( void ) ; # endif 3.7 键盘扫描 (keyboard.c) # include "keyboard.h" # include "delay.h" void Key_Init ( void ) { // 按键端口初始化 } unsigned char Key_Scan ( void ) { // 检测清零键 if ( KEY_CLR_PIN== 0 ) { Delay_Ms ( 10 ) ; // 消抖 if ( KEY_CLR_PIN== 0 ) { while ( KEY_CLR_PIN== 0 ) ; // 等待按键释放 return KEY_CLEAR; } } // 检测模式键 if ( KEY_MOD_PIN== 0 ) { Delay_Ms ( 10 ) ; if ( KEY_MOD_PIN== 0 ) { while ( KEY_MOD_PIN== 0 ) ; return KEY_MODE; } } // 检测暂停键 if ( KEY_PAU_PIN== 0 ) { Delay_Ms ( 10 ) ; if ( KEY_PAU_PIN== 0 ) { while ( KEY_PAU_PIN== 0 ) ; return KEY_PAUSE; } } return KEY_NONE; } 3.8 延时函数 (delay.h) # ifndef __DELAY_H__ # define __DELAY_H__ # include <reg52.h> // 函数声明 void Delay_Ms ( unsigned int ms) ; void Delay_Us ( unsigned int us) ; # endif 3.9 延时函数 (delay.c) # include "delay.h" // 毫秒延时 void Delay_Ms ( unsigned int ms) { unsigned int i, j; for ( i= ms; i> 0 ; i-- ) for ( j= 110 ; j> 0 ; j-- ) ; } // 微秒延时 void Delay_Us ( unsigned int us) { while ( us-- ) { _nop_ ( ) ; _nop_ ( ) ; _nop_ ( ) ; _nop_ ( ) ; } } 四、Proteus仿真电路 4.1 Proteus元件清单 1. AT89C52 (单片机) 2. 7SEG-MPX4-CA (4位共阳数码管) 3. BUTTON (按键×3) 4. RES (电阻220Ω×8) 5. RES (电阻10kΩ×3) 6. CRYSTAL (11.0592MHz) 7. CAP (电容30pF×2) 8. POWER (电源) 9. GROUND (地) 10. SIGNAL_GENERATOR (信号发生器,用于产生脉冲)4.2 仿真连接图 AT89C52: P0.0-P0.7 -> 数码管段选a-g,dp P2.0-P2.3 -> 数码管位选1-4 P3.0 -> 按键K1(清零) P3.1 -> 按键K2(模式) P3.2 -> 按键K3(暂停) P3.4 -> 信号发生器输出(T0计数输入) XTAL1/XTAL2 -> 11.0592MHz晶振 RST -> 10kΩ电阻+10μF电容 VCC -> +5V GND -> GND五、功能扩展 5.1 增加计数频率测量 // 测量计数频率 void Measure_Frequency ( void ) { static unsigned int last_count= 0 ; static unsigned long last_time= 0 ; unsigned int current_count; unsigned long current_time; unsigned int frequency; current_time= Get_System_Time ( ) ; // 获取系统时间(ms) current_count= Timer0_Get_Value ( ) ; if ( current_time- last_time>= 1000 ) // 每秒测量一次 { frequency= current_count- last_count; last_count= current_count; last_time= current_time; // 显示频率 Display_Frequency ( frequency) ; } } 5.2 增加计数预置值 // 设置预置值 void Set_Preset_Value ( unsigned int preset) { count_value= preset; Timer0_Set_Value ( preset) ; Display_Number ( count_value) ; } 5.3 增加串行通信 // 串口初始化 void UART_Init ( void ) { TMOD&= 0x0F ; // 清除T1控制位 TMOD|= 0x20 ; // 设置T1为模式2(8位自动重载) TH1= 0xFD ; // 波特率9600 TL1= 0xFD ; TR1= 1 ; // 启动T1 SM0= 0 ; // 设置串口为模式1 SM1= 1 ; REN= 1 ; // 允许接收 EA= 1 ; // 允许总中断 ES= 1 ; // 允许串口中断 } // 串口中断服务程序 void UART_ISR ( void ) interrupt4 { unsigned char received_data; if ( RI) // 接收中断 { RI= 0 ; // 清除接收中断标志 received_data= SBUF; // 根据接收到的数据设置预置值 if ( received_data>= '0' && received_data<= '9' ) { unsigned int digit= received_data- '0' ; count_value= count_value* 10 + digit; if ( count_value> 9999 ) count_value= 9999 ; Display_Number ( count_value) ; } else if ( received_data== 'C' || received_data== 'c' ) { count_value= 0 ; Display_Number ( count_value) ; } } } 参考代码 数码管T0计数器设计www.youwenfan.com/contentcsv/118036.html
六、使用说明 6.1 操作步骤 接通5V电源 数码管显示初始值"0000" 将脉冲源连接到P3.4(T0)引脚 按"模式"键切换加/减计数模式 按"暂停"键暂停/继续计数 按"清零"键将计数值清零 6.2 注意事项 外部脉冲幅度需在0-5V之间 脉冲宽度需大于2个机器周期 最大计数频率不超过50kHz 计数溢出时会自动循环 6.3 调试技巧 先用按键模拟脉冲测试基本功能 再用信号发生器测试高频计数 观察LED指示灯判断计数状态 使用示波器观察T0引脚波形 七、性能指标 参数 指标 计数范围 0-9999 计数模式 加计数/减计数 最大计数频率 50kHz 显示方式 4位静态显示 按键响应 <50ms 功耗 <50mA @5V