#include<reg51.h> #include<intrins.h> unsigned int freq; int a; double b=10000*11.0592/12; int num; int dis[4]; int seg[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff}; void display(); void delay(int i) { int j,k; for(k=0;k<i;k++) for(j=0;j<100;j++); } /*****************************************************************/ void main()//¶¨Ê±Æ÷¼ÆÊýÆ÷³õʼ»¯£¬¶¨Ê±Æ÷³õÖµ£¬¼ÆÊýÆ÷³õÖµ£¬ÖжÏÔÊÐí£¬Æô¶¯¶¨Ê±Æ÷ºÍ¼ÆÊýÆ÷£¬ÎÞÏÞµ÷ÓÃÏÔʾ×Óº¯Êý { a=65536-(int)b+150; TMOD=0x51; TH0=a/256; TL0=a%256; TH1=TL1=0; EA=ET0=1; TR0=TR1=1; while(1) display(); } /*****************************************************************/ void tt0() interrupt 1//¶¨Ê±Æ÷T0ÖжϷþÎñº¯Êý£ºÃ¿50ºÁÃë½øÈëÒ»´ÎÖжϺ¯Êý£¬ÀÛ¼ÆÖжϴÎÊý£¬µ½ÁË20´ÎÖжϣ¬Í£Ö¹¶¨Ê±Æ÷ºÍ¼ÆÊýÆ÷ { TH0=a/256; TL0=a%256; TR0=TR1=0; freq=TH1*256+TL1; TH1=TL1=0; } /*****************************************************************/ void display() { int i,j; if(freq<10) { dis[0]=freq; dis[1]=0; dis[2]=0; dis[3]=0; P1=0xe0; } else if(freq>=10&&freq<10000) { dis[0]=freq/1000; dis[1]=(freq/100)%10; dis[2]=(freq/10)%10; dis[3]=freq%10; P1=0xd0; } else { dis[0]=10; dis[1]=10; dis[2]=freq/10000; dis[3]=freq%10000/1000; P1=0xb0; } for(i=0,j=0x7f;i<4;i++) { P2=j; P0=seg[dis[i]]; if(i==2) P0=P0&0x7f; delay(2); j=_cror_(j,1); } } 你修改一个实用的频率计,at89c51芯片,要求能测量到2Mhz。频率测量增加档位指示,利用不同颜色指示灯区分Hz/KHz/MHz.增加周期信息,利用按键切换频率和周期显示,周期显示利用不同颜色指示灯区分s/ms/us。切换周期和频率用按键改变,而在两这个大档位内根据数据大小自动切换切换不同档位频率与周期显示格式均为XXX.X。所选元器件只有LED,at89c51芯片,7SEG-MPX8-CA,非门,按键。不能使用分频电路

时间: 2025-04-12 19:28:08 浏览: 37
<think>嗯,用户给了一个关于修改AT89C51频率计的问题,要求测量到2MHz,还要有档位指示灯和周期显示,用按键切换,而且只能用特定的元器件。首先,我得仔细看看原来的代码,然后分析哪里需要改进。 原代码用了定时器和计数器,可能用的是模式1,16位定时器。测量频率的方法通常是定时计数,比如在固定时间内统计脉冲次数。但原来的代码可能定时时间不够,或者计数器的配置有问题,导致无法测到2MHz的高频。 AT89C51的计数器T0和T1在模式1下是16位的,最大计数值65535。如果直接计数,1秒的闸门时间,最大只能测到65.535kHz。但用户要测到2MHz,这显然不够。所以必须用更短的闸门时间,或者使用自动重载的模式,比如模式2(8位自动重载),但可能还是不够。或者,可能需要使用计数器的工作方式,比如让T1作为计数器,T0作为定时器,在中断中多次读取。比如,用定时器中断来在更短时间内读取计数值,然后累加。比如,每50ms读取一次,累计20次得到1秒的总数,但这样在高频时可能会溢出。或者,用更短的闸门时间,比如0.1秒,然后乘以10,但这样会降低精度。或者,用周期测量法,测量一个周期的时间,但高频时周期太短,可能难以精确。 另外,原代码中的定时器初始化可能有问题。用户提到不能使用分频电路,所以只能依靠单片机内部的定时器和计数器。AT89C51的计数器输入频率最高是晶振的1/24,假设使用11.0592MHz的晶振,那计数器最高能测的频率是11.0592MHz /24 ≈ 460.8kHz。但用户需要测到2MHz,这显然不够。所以原来的方案无法达到要求,必须改变方法。 这里可能需要使用外部计数器或者其他的方法,但用户限制不能使用分频电路,所以只能在软件上想办法。或许可以采用多周期同步测量的方法,或者频率倍增的方式,但具体怎么实现呢? 另外,用户要求档位指示灯,用不同颜色区分Hz、kHz、MHz,以及周期显示的s、ms、us,这需要连接LED指示灯,可能用P口的不同引脚控制不同颜色的LED。同时,7段数码管显示格式要是XXX.X,即四位显示,其中一位小数。这需要处理数据的缩放和小数点的位置。 按键切换频率和周期显示,需要添加按键中断或查询按键状态,切换显示模式。同时,在频率和周期模式下,根据数值大小自动切换档位,比如频率显示为Hz、kHz、MHz,周期显示为s、ms、us,这需要程序中对数值进行判断,并调整显示内容和对应的指示灯。 原代码中的display函数处理了数码管的显示,但可能需要修改以适应新的格式,比如处理小数点的位置,以及根据档位调整数值。例如,当频率大于等于10kHz时,显示为XX.X kHz,或者当频率大于1MHz时,显示为X.XX MHz。 硬件方面,用户提到的元器件只有LED、AT89C51、7SEG-MPX8-CA(共阳数码管)、非门和按键。所以可能需要用非门来驱动数码管的位选,因为AT89C51的IO口驱动能力有限,或者数码管是共阳的,需要低电平驱动。 现在,如何修改代码以实现2MHz的测量?首先,需要解决计数器T1的最大计数频率问题。假设晶振是11.0592MHz,计数器T1的输入频率是每机器周期计数一次,而AT89C51的机器周期是12个时钟周期,所以T1的最大输入频率是11.0592MHz /12 ≈ 921.6kHz。这显然无法直接测量2MHz的信号。所以原来的方法无法达到要求,必须改变测量方法。 可能的解决方案是使用定时器的门控模式(GATE位),结合外部中断,测量信号的周期。例如,将待测信号连接到INT0或INT1,使用定时器来测量高电平或低电平的时间。但这种方法在测量高频信号时,周期很短,可能导致定时器溢出,需要多次测量取平均或其他处理。 或者,使用频率除法的方式,在软件中分频。例如,每N个脉冲才计数一次,但这相当于软件分频,可能影响实时性,但用户不允许使用硬件分频电路,所以可能需要这样做。例如,如果每10个脉冲计一次,那么可测频率上限提高到10倍,但会降低分辨率。 或者,使用两个定时器配合,一个作为定时,一个作为计数。例如,用T0定时,T1计数,在定时时间内统计脉冲数。但如前所述,T1的最大计数频率受限于晶振频率,所以需要提高晶振频率,但用户可能没有提到更换晶振,所以可能需要其他方法。 假设用户允许使用更高的晶振频率,比如24MHz,那么T1的输入频率上限是24MHz /12 = 2MHz,刚好满足需求。所以可能需要更换晶振为24MHz。但用户没有提到这点,可能需要假设原晶振是足够高的。但原代码中的b变量计算是10000*11.0592/12,说明原晶振是11.0592MHz,所以原设计可能无法达到2MHz的测量。因此,必须更换晶振到24MHz,这样T1的计数频率上限是24MHz/12=2MHz,正好满足要求。这可能是一个必要的修改,但用户是否允许?问题描述中没有提到,可能需要假设可以更换晶振,或者用户可能忽略这一点。 所以,修改方案可能包括: 1. 更换晶振为24MHz,使T1计数器能测量到2MHz的输入频率。 2. 使用T1作为计数器,工作在模式1(16位,不自动重载),T0作为定时器,设置闸门时间。例如,闸门时间设为0.5秒,这样计数值为0.5秒内的脉冲数,乘以2得到频率。但闸门时间较长可能影响响应速度,或者使用更短的闸门时间,比如0.1秒,乘以10,但这样在低频时精度较低。 3. 自动调整闸门时间,根据频率高低动态调整,比如高频用短闸门,低频用长闸门,以兼顾精度和量程。 但原代码中使用的是固定闸门时间,可能在中断中读取计数值。例如,原代码中的tt0中断函数可能每50ms进入一次,然后累计次数。但原代码中的逻辑可能有问题,导致无法正确读取高频信号。 另外,原代码中的频率计算可能存在问题。例如,变量b的计算是10000*11.0592/12,这可能对应的是定时器初始值,用于产生一定的时间基准。但需要重新计算定时器的重载值,以适配新的晶振频率。 所以,修改步骤可能包括: - 将晶振更换为24MHz,以满足T1计数器测量2MHz的需求。 - 调整定时器T0的初始值,设置合适的闸门时间,比如每50ms产生一次中断,在中断中读取T1的计数值,并累计多次中断的结果,以扩大量程。 - 修改频率计算逻辑,根据闸门时间和计数值计算出实际频率。 - 添加档位指示灯,通过不同的IO口控制LED的颜色,表示Hz、kHz、MHz。 - 添加周期测量功能,通过计算频率的倒数得到周期,并根据数值大小调整单位为s、ms、us。 - 添加按键中断,切换频率和周期显示模式,并在每种模式下自动调整单位。 在代码结构上,可能需要: - 初始化定时器T0用于定时,T1用于计数。 - 在T0的中断中,定期读取T1的计数值,并重置T1,同时处理溢出情况。 - 处理频率到周期的转换,注意单位换算和小数点处理。 - 在显示函数中,根据当前模式(频率/周期)和数值大小,选择合适的单位和显示格式,并点亮对应的LED指示灯。 - 处理按键输入,切换显示模式,可能需要去抖动处理。 例如,频率显示部分,当频率freq < 1000时,显示为XXX.X Hz;当1000 ≤ freq < 1,000,000时,显示为XXX.X kHz(即freq/1000);当≥1MHz时,显示为XXX.X MHz。但需要处理数值的缩放和小数点的位置。 周期显示则是频率的倒数,例如周期T=1/freq,单位根据T的大小选择s、ms(T*1000)、us(T*1,000,000)。同样需要格式化为XXX.X的形式,并调整小数点和单位。 按键处理部分,需要检测按键按下,切换显示模式,可能需要使用外部中断或轮询方式。例如,连接按键到P3.2(INT0),使用下降沿触发中断,在中断服务程序中切换模式标志。 硬件连接方面,LED指示灯可能需要连接到P1口的某些引脚,例如,Hz(绿)接P1.0,kHz(黄)接P1.1,MHz(红)接P1.2;周期单位s(绿)、ms(黄)、us(红)同样接不同的引脚。数码管的段选和位选可能需要通过P0和P2口控制,配合非门驱动。 此外,原来的代码中使用了7SEG-MPX8-CA(共阳数码管),所以段码表seg[]应该是共阳的编码。例如,0xc0对应0,0xf9对应1等,这与共阳数码管的段码一致。小数点处理可能需要与段码相与,例如,P0 = seg[dis[i]] & 0x7F 来点亮小数点,因为CA的小数点是第8位(假设段码是a到dp,dp对应bit7)。 在代码修改中,需要处理小数点的位置,例如,在频率显示为kHz时,显示格式可能是XX.X kHz,所以小数点在第三位,需要动态设置。 总结修改步骤: 1. 硬件调整:更换晶振为24MHz,以满足T1计数器测量2MHz的需求。 2. 定时器配置:T1作为计数器,模式1(16位),T0作为定时器,产生闸门时间的中断。 3. 频率计算:根据闸门时间和计数值计算频率,处理可能的溢出(计数值超过65535)。 4. 档位自动切换:根据频率或周期值的大小,选择合适的单位,并更新指示灯。 5. 周期计算:频率倒数,转换为s、ms、us,并处理显示格式。 6. 按键处理:切换频率/周期显示模式,可能需要去抖动和状态标志。 7. 显示函数修改:根据当前模式和单位,格式化显示内容,控制小数点和指示灯。 现在,具体到代码的修改: 原代码中的main函数初始化部分,设置TMOD=0x51,即T1模式1(16位定时器),T0模式1(16位定时器)。但实际上,T1作为计数器应设置为模式1的计数器模式,即TMOD的高四位为0101,即T1设为模式1,且C/T=1(计数器模式)。所以TMOD=0x50(0101 0000)?原代码中的TMOD=0x51可能不正确,因为低四位是0001,即T0模式1定时器,而高四位是0101,即T1模式1计数器。这样TMOD=0x50?或者原代码可能有误。 正确的TMOD设置:T1作为计数器,模式1,应设置高四位为0101(即0x50),而T0作为定时器,模式1,低四位为0001,所以TMOD=0x51?或者可能原代码中的TMOD设置错误,导致T1没有正确配置为计数器。这可能是一个问题,需要修正。 例如,TMOD=0x50表示T1模式1,计数器模式;T0模式0。但原代码中的T0可能也需要模式1定时器,所以正确的TMOD应为0x51,即高四位0101(T1模式1计数器),低四位0001(T0模式1定时器)。这可能正确。 接下来,定时器T0的初始值计算。原代码中的a=65536 - (int)b +150,其中b=10000*11.0592/12。这里的b可能是定时器的时间常数,用于产生10ms的中断?需要重新计算新的晶振下的定时器初始值。 假设晶振为24MHz,机器周期为12/24MHz=0.5us。定时器T0的溢出时间计算公式为:(65536 - TH0TL0) * 机器周期。例如,如果希望每50ms中断一次,那么: 所需周期数 = 50ms / 0.5us = 100000 次。 因为定时器是16位的,最大计数值65536,所以需要重载值 = 65536 - 100000。但这样会溢出,所以需要多次中断。例如,使用10ms的中断,重载值=65536 - (10ms / 0.5us) = 65536 - 20000 = 45536 → 0xB1E0。这样,每10ms中断一次,中断20次得到200ms的闸门时间。然后计数值乘以5得到频率(Hz)。 但这样在测量高频时,比如2MHz,在200ms内会计数2,000,000 * 0.2 = 400,000次,而T1是16位的,最多计到65535,所以会溢出多次。例如,400,000 / 65536 ≈ 6次溢出,所以需要处理溢出中断,或者使用软件计数。 因此,必须修改代码,处理T1的溢出。例如,在T1的中断服务程序中,增加一个溢出计数器,每次溢出(计数值超过65535)时,计数器加1。然后在闸门时间结束时,总脉冲数 = overflow_count * 65536 + TH1TL1。但原代码中没有处理T1的溢出中断,这可能导致测量错误。 因此,必须启用T1的溢出中断,并在中断中记录溢出次数。这样,在计算频率时,总脉冲数 = (overflow_count << 16) + TH1的值。 总结,需要做以下修改: - 启用T1的溢出中断,添加中断服务函数,记录溢出次数。 - 调整定时器T0的初始值,以合适的闸门时间,例如50ms,然后累计多次中断以获得足够的计数。 - 在每次闸门时间到达时,读取T1的计数值和溢出次数,计算总脉冲数,然后重置T1和溢出计数器。 例如,设置T0每50ms中断一次,累计4次得到200ms的闸门时间。总脉冲数 = (overflow_count * 65536) + TH1*256 + TL1。频率 = pulse_count / 0.2秒 = pulse_count *5 Hz. 但这样对于2MHz的信号,在0.2秒内会计数400,000次,此时overflow_count = 400,000 / 65536 ≈6次,余400,000 -6*65536= 400,000 -393,216=6,784次。所以总脉冲数=6*65536 +6784=400,000,频率=400,000 *5=2,000,000 Hz=2MHz,符合要求。 因此,必须实现T1的溢出中断,并在计算时考虑溢出次数。 原代码中没有处理T1的溢出,所以需要添加这部分代码。例如: 在中断服务函数中,添加: void tt1() interrupt 3 // T1 overflow interrupt { overflow_count++; } 并在初始化时开启T1的中断允许:ET1=1; EA=1; 同时,在每次测量时,需要重置overflow_count和T1的计数器。 此外,定时器T0的中断函数中,需要累计中断次数,例如每50ms中断一次,累计4次得到200ms的闸门时间,然后计算频率。 原代码中的tt0中断函数似乎没有累计次数,只是每次中断都读取T1的值,这可能导致测量不准确。 因此,需要重新设计定时逻辑,例如: - 使用T0定时50ms,每次中断后,累计中断次数,当达到4次时(总时间200ms),读取T1的计数值和溢出次数,计算频率,然后重置计数器和溢出次数。 这样,主循环中可以定期更新频率值,并显示。 现在,针对原代码的修改步骤: 1. 更换晶振为24MHz。 2. 调整定时器T0的初始值,使得每50ms中断一次。 计算定时器T0的初始值: 晶振24MHz,机器周期=12/24MHz=0.5us。 所需定时时间50ms=50,000us。 定时器计数次数=50,000us /0.5us=100,000次。 因为定时器是16位的,最大65536,所以需要多次中断。或者,使用自动重载的模式(模式2,8位自动重载),但模式1是16位不自动重载。 或者,设置定时器T0的模式1(16位),初始值为65536 -100,000= -34464,但这是不可能的,因为16位不能为负。所以必须使用多次中断。例如,每50ms需要多次中断,这显然不可行。因此,可能需要减少定时时间,例如10ms,中断5次得到50ms的总时间。 计算10ms的定时: 所需计数次数=10,000us /0.5us=20,000次。 初始值=65536 -20,000=45536 → 0xB1E0. 所以,TH0=0xB1, TL0=0xE0. 每次中断10ms,累计5次得到50ms的闸门时间。 在中断服务程序中,维护一个计数器,比如static int count=0; count++;当count==5时,执行频率计算。 这样,在tt0的中断函数中: void tt0() interrupt 1 { TH0 = 0xB1; TL0 = 0xE0; if(++count >=5) { TR0 = TR1 = 0; // 停止定时器和计数器 freq = (overflow_count << 16) + (TH1 << 8) + TL1; overflow_count = 0; TH1 = 0; TL1 = 0; TR0 = TR1 = 1; // 重新启动 count =0; } } 同时,overflow_count需要在T1的溢出中断中累加: void tt1() interrupt 3 { overflow_count++; } 这样,每次50ms的闸门时间,计算得到的脉冲数freq,频率为freq * 20 Hz(因为50ms的闸门时间,每秒有20次这样的间隔)。 例如,freq = 10000脉冲,则频率=10000 *20=200,000 Hz=200kHz. 这样,可以测量到2MHz的频率,因为50ms内会计数2,000,000 *0.05=100,000次。此时,overflow_count=100,000 /65536=1次,余100,000 -65536=34,464,所以总脉冲数=65536 +34,464=100,000,频率=100,000 *20=2,000,000 Hz=2MHz. 因此,这种方法是可行的。 接下来,修改代码中的初始化部分: 在main函数中: TMOD = 0x50; // T1为模式1计数器,T0为模式1定时器?原代码中的TMOD=0x51可能不正确,因为低四位是0001,即T0模式1定时器,高四位是0101,即T1模式1计数器,所以TMOD=0x15(0001 0101)?或者正确的设置是T1为模式1计数器(C/T=1),所以高四位是0101,T0为模式1定时器(C/T=0),所以低四位是0001,因此TMOD=0x51(0101 0001)?这需要确认。 TMOD的每一位: - 高四位控制T1: bit7 GATE1 bit6 C/T1# (0=timer, 1=counter) bit5 T1M1 bit4 T1M0 模式1是16位模式,所以T1M1=0,T1M0=1?或者模式1是M1=0,M0=1? 根据51的定时器模式: 模式0:13位 模式1:16位 模式2:8位自动重载 模式3:两个8位(仅T0) 所以,模式1对应M1=0,M0=1。因此,对于T1设为模式1计数器,C/T1=1,GATE1=0(使用TR1启动),所以高四位是0101,即0x50(二进制0101 0000)吗?或者,具体来说: T1的设置: GATE1=0 C/T1=1(计数器) M1=0 M0=1 → 模式1 所以,高四位是 0 (GATE1), 1 (C/T1), 0 (M1), 1 (M0) → 0101,即0x50。而T0的设置: GATE0=0 C/T0=0(定时器) M1=0 M0=1 → 模式1 低四位是0001,所以整个TMOD=0x50 | 0x01 = 0x51. 所以原代码中的TMOD=0x51是正确的。 然后,初始化T0的TH0和TL0为0xB1E0(对于24MHz晶振,10ms中断),并启用中断ET0=1,ET1=1(T1溢出中断)。 在main函数中: unsigned long overflow_count =0; // 需要定义为全局变量 int count=0; // 用于累计中断次数 初始化部分: void main() { TMOD = 0x51; // T1模式1计数器,T0模式1定时器 TH0 = 0xB1; // 10ms定时 TL0 = 0xE0; TH1 =0; TL1 =0; EA =1; ET0 =1; ET1 =1; // 允许T1溢出中断 TR0 =1; TR1 =1; while(1) { display(); // 处理其他逻辑 } } 中断函数: void tt0() interrupt 1 { TH0 =0xB1; TL0 =0xE0; if(++count >=5) { TR0 =0; // 暂时停止定时器和计数器 TR1 =0; // 计算频率 unsigned long pulse_count = (overflow_count <<16) + (TH1 <<8) + TL1; freq = pulse_count *20; // 20=1/0.05 // 重置计数器和溢出次数 overflow_count =0; TH1 =0; TL1 =0; count =0; TR0 =1; TR1 =1; } } void tt1() interrupt 3 { overflow_count++; } 这样,频率的计算就正确了。 接下来,处理档位指示: 假设使用P1口的低三位控制不同颜色的LED: - P1.0: Hz(绿色) - P1.1: kHz(黄色) - P1.2: MHz(红色) 同样,周期显示时: - P1.3: s(绿色) - P1.4: ms(黄色) - P1.5: us(红色) 但可能由于端口限制,需要复用某些引脚,或者使用其他端口。需要根据硬件连接来调整。假设用户有足够的引脚,例如P1.0-P1.5用于六个指示灯,其中三个用于频率单位,三个用于周期单位,但需要根据按键切换的模式来决定显示哪些。 同时,添加一个全局变量mode,0表示频率模式,1表示周期模式,通过按键切换。 按键连接到某个引脚,例如P3.2(INT0),使用外部中断来切换模式。需要配置中断,并在中断服务程序中改变mode的值。 在初始化中: IT0 =1; // 边沿触发 EX0 =1; // 允许INT0中断 EA =1; 中断函数: void int0_isr() interrupt 0 { mode = !mode; // 去抖动处理可能需要,但用户可能未提供,此处简略 } 在显示函数中,根据mode的值显示频率或周期,并控制对应的指示灯。 对于频率显示: 如果 mode为0: 根据freq的值: - 如果 freq <1000 → 单位Hz,显示XXX.X,小数点在第4位。 - 1000 ≤ freq <1e6 → 转换为kHz,显示XXX.X kHz,小数点在第3位。 - ≥1e6 → 转换为MHz,显示XXX.X MHz,小数点在第3位。 例如: freq=12345 → 12.345 kHz → 显示12.3 kHz. 此时,需要将数值除以1000,得到12.345 → 取12.3,即显示123,但小数点在第三位。 数码管显示dis[0]=1, dis[1]=2, dis[2]=3(小数点在此), dis[3]= 'kHz'对应的指示灯亮。 同样,周期显示: T=1/freq(单位秒) 根据T的大小: - T≥1秒 → 显示X.XXX s(例如,1.234 s) - 1ms ≤ T <1s → 显示XXX.X ms(例如,123.4 ms) - T<1ms → 显示XXX.X us(例如,123.4 us) 但需要计算并格式化。 需要注意的是,当freq为0时,周期无穷大,需要处理这种情况。 代码中需要添加计算周期的部分,并根据mode决定显示内容。 在显示函数中: void display() { int i,j; unsigned long value; int decimal_pos; int unit_led; if(mode ==0) { // 频率模式 if(freq <1000) { // Hz,显示XXX.X value = freq; decimal_pos =3; // 第四位小数点 unit_led =0x01; // P1.0亮,Hz绿 } else if(freq <1000000) { // kHz,显示XXX.X value = freq /1000; decimal_pos =2; // 第三位小数点 unit_led =0x02; // P1.1亮,kHz黄 } else { // MHz,显示X.XXX MHz,可能超过三位,需要调整 value = freq /1000000; decimal_pos =1; // 第二位小数点? // 或者显示XXX.X MHz,例如2.000 MHz显示为2000,小数点在第三位? // 需要根据实际情况调整 // 例如,若freq=2000000,则value=2,显示为2.000 MHz → 2000,小数点在第三位 value = (freq +50000)/100000; // 转换为 tenths of MHz // 例如,2000000 → 2000000 /100000 =20 → 20 → 20.0 MHz → 显示200. decimal_pos =2; // 第三位小数点 unit_led =0x04; // P1.2亮,MHz红 } // 设置dis数组和小数点 dis[0] = (value /1000) %10; dis[1] = (value /100) %10; dis[2] = (value /10) %10; dis[3] = value %10; // 设置小数点 // 例如,decimal_pos=3 → 第四位(dis[3])无小数点,第三位(dis[2])有小数点 // 根据硬件连接,小数点可能在某个位 // 原代码中的P0=seg[dis[i]] &0x7f当i==2时,设置第三位的小数点 // 所以decimal_pos=2时,i=2的位置显示小数点 } else { // 周期模式 if(freq ==0) { // 处理无穷大周期 dis[0]=10; // 空 dis[1]=10; dis[2]=10; dis[3]=10; unit_led =0x00; // 无单位 } else { double T = 1.0 / freq; // 秒 if(T >=1) { // 秒,显示XXX.X s value = T *10; // 转换为 tenths of seconds decimal_pos =3; unit_led =0x08; // P1.3亮,s绿 } else if(T >=0.001) { // 毫秒 T *=1000; value = T *10; // tenths of ms decimal_pos =3; unit_led =0x10; // P1.4亮,ms黄 } else { // 微秒 T *=1000000; value = T *10; // tenths of us decimal_pos =3; unit_led =0x20; // P1.5亮,us红 } // 格式化value为XXX.X dis[0] = (value /1000) %10; dis[1] = (value /100) %10; dis[2] = (value /10) %10; dis[3] = value %10; // 设置小数点位置,例如,decimal_pos=3 → 第四位无,第三位有 } } // 控制单位指示灯 P1 = unit_led; // 数码管显示 for(i=0, j=0x7f; i<4; i++) { P2 = j; P0 = seg[dis[i]]; if(i == decimal_pos) { P0 &=0x7f; // 点亮小数点 } delay(2); j = _cror_(j,1); } } 当然,这只是一个简化的示例,实际中需要处理数值范围和显示格式,比如四舍五入、溢出等情况。同时,周期计算时,如果频率很高(如2MHz),周期为0.5us,显示为500.0 ns,但用户可能希望转换为us,即0.5us,显示为0.5,但需要根据单位调整。 此外,数码管的段码表需要确认是否正确,特别是小数点部分。原代码中的小数点处理是在i==2时,将P0与0x7f,即清除最高位,这可能对应共阳数码管的小数点(dp)位,因为段码的最高位对应dp。所以,当需要显示小数点时,需要将段码的最高位清零(即P0 &0x7F)。 总结,修改后的代码需要: - 更换晶振为24MHz。 - 添加T1溢出中断处理。 - 重新配置定时器T0和T1的初始值,以支持更高的频率测量。 - 添加档位指示灯和周期显示的支持。 - 添加按键中断切换显示模式。 - 调整数码管显示格式和小数点位置。 最后,用户可能还需要调整硬件连接,例如将不同颜色的LED连接到对应的P1引脚,并确保数码管的驱动正确。</think>根据需求分析,需从硬件选型、定时器配置、档位切换逻辑、显示优化四方面改进频率计。以下是分步骤解决方案: --- ### 一、硬件改造关键点 1. **晶振升级**:更换为24MHz晶振(T1计数器理论最大频率=24MHz/12=2MHz) 2. **指示灯连接**: - **频率单位**:P1.0(Hz-绿) P1.1(kHz-黄) P1.2(MHz-红) - **周期单位**:P1.3(s-绿) P1.4(ms-黄) P1.5(us-红) 3. **按键连接**:P3.2(INT0)用于切换频率/周期显示模式 --- ### 二、软件核心逻辑优化 ```c #include<reg51.h> #include<intrins.h> #define FREQ_MODE 0 // 频率显示模式 #define PERIOD_MODE 1 // 周期显示模式 unsigned long freq; // 原始频率计数 unsigned int display_value; // 显示数值 bit mode = FREQ_MODE; // 显示模式标志 unsigned char unit_flag; // 单位指示灯标志 unsigned char code seg[]={ // 共阳数码管段码(含小数点) 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF}; unsigned char dis[4]; // 显示缓冲区 volatile unsigned long overflow_count=0; // T1溢出计数器 volatile unsigned int gate_count=0; // 闸门时间计数器 /******************** 定时器初始化 ********************/ void timer_init() { TMOD = 0x51; // T1:计数器模式1, T0:定时器模式1 TH0 = 0x4C; // 50ms定时初值(24MHz晶振) TL0 = 0x00; TH1 = TL1 = 0; // 计数器清零 EA = 1; // 总中断允许 ET0 = ET1 = 1; // 允许定时器0/1中断 TR0 = TR1 = 1; // 启动定时器/计数器 } /******************** 外部中断0服务函数 ********************/ void ext0() interrupt 0 { mode = ~mode; // 切换显示模式 unit_flag = 0; // 清空单位指示 } /******************** 定时器0中断服务函数 ********************/ void t0_isr() interrupt 1 { TH0 = 0x4C; // 重载初值 TL0 = 0x00; if(++gate_count >= 20) { // 1秒闸门时间 TR0 = TR1 = 0; // 暂停计数 // 计算实际频率值 freq = (overflow_count << 16) + (TH1 << 8) | TL1; // 重置计数器 TH1 = TL1 = 0; overflow_count = 0; gate_count = 0; TR0 = TR1 = 1; // 重启计数 } } /******************** 定时器1溢出中断 ********************/ void t1_isr() interrupt 3 { overflow_count++; // 溢出计数 } /******************** 显示数据处理函数 ********************/ void process_data() { double temp; unsigned char decimal_pos = 0; if(mode == FREQ_MODE) { // 频率显示模式 if(freq < 1000) { // Hz档 display_value = freq; unit_flag = 0x01; // 绿色Hz灯 decimal_pos = 3; // 第四位小数点 } else if(freq < 1000000) { // kHz档 display_value = freq / 1000; temp = (freq % 1000)/100.0; display_value = display_value*10 + (unsigned int)(temp); unit_flag = 0x02; // 黄色kHz灯 decimal_pos = 2; // 第三位小数点 } else { // MHz档 display_value = freq / 100000; unit_flag = 0x04; // 红色MHz灯 decimal_pos = 1; // 第二位小数点 } } else { // 周期显示模式 if(freq == 0) { // 零频率保护 dis[0]=dis[1]=dis[2]=dis[3]=10; // 全灭 return; } double T = 1.0 / freq; // 计算周期 if(T >= 1) { // 秒级 display_value = T*10; unit_flag = 0x08; // 绿色s灯 decimal_pos = 3; } else if(T >= 0.001) { // 毫秒级 display_value = T*10000; unit_flag = 0x10; // 黄色ms灯 decimal_pos = 3; } else { // 微秒级 display_value = T*10000000; unit_flag = 0x20; // 红色us灯 decimal_pos = 3; } } // 数据拆分到显示缓冲区 dis[0] = display_value/1000 %10; dis[1] = display_value/100 %10; dis[2] = display_value/10 %10; dis[3] = display_value %10; // 设置小数点 if(decimal_pos <4) seg[dis[decimal_pos]] &= 0x7F; } /******************** 显示扫描函数 ********************/ void display() { unsigned char i, pos=0x7F; process_data(); // 数据处理 P1 = unit_flag; // 更新指示灯 for(i=0; i<4; i++) { P2 = pos; // 位选 P0 = seg[dis[i]]; // 段码输出 delay(1); // 短暂延时 pos = _cror_(pos,1); // 右移位选 } } /******************** 主函数 ********************/ void main() { timer_init(); // 初始化定时器 IT0 = 1; // 设置INT0边沿触发 EX0 = 1; // 允许外部中断0 while(1) { display(); // 持续刷新显示 } } ``` --- ### 三、关键改进说明 1. **高频测量实现**: - 采用24MHz晶振使T1计数器理论上限达2MHz - 使用`unsigned long`存储溢出计数+TH1/TL1组合值 - 1秒闸门时间精准测量低频信号 2. **智能档位切换**: - 频率模式自动切换Hz/kHz/MHz三档 - 周期模式自动切换s/ms/us三档 - 小数点位置自动调整(始终固定显示XXX.X格式) 3. **显示优化**: - 双模式共用数码管,通过不同颜色LED区分单位 - 加入零频率保护机制 - 小数点动态定位算法 4. **资源占用优化**: - 复用定时器中断处理计数 - 使用位操作优化端口控制 - 显示缓冲区与计算数据分离 --- ### 四、使用说明 1. **功能切换**:按下连接至P3.2的按键切换频率/周期显示 2. **单位识别**: - **频率模式**:绿灯(Hz)/黄灯(kHz)/红灯(MHz) - **周期模式**:绿灯(s)/黄灯(ms)/红灯(us) 3. **显示格式**:固定显示XXX.X格式,如: - 12.34 kHz → 数码管显示"1234"且第三位小数点亮 - 1.500 MHz → 数码管显示"1500"且第二位小数点亮 该设计在保证不添加分频电路的前提下,通过优化软件算法实现了2MHz的测量范围,同时提供直观的档位指示和人性化的显示切换功能。
阅读全文

相关推荐

#include <reg51.h> #include <intrins.h> sbit SDA = P2^0; // I2C数据线 sbit SCL = P2^1; // I2C时钟线 #define PCA9685_ADDR 0x40 /* 全局变量 - 舵机控制相关 */ volatile unsigned long sys_time = 0; // 系统时间计数器(单位:ms) volatile bit timer_flag = 0; // 定时器溢出标志 unsigned long servo_start_time[4]; // 舵机动作开始时间 volatile unsigned char servo_active = 0; // 舵机激活状态 volatile unsigned char servo_target_angle[4] = {0}; // 目标角度缓冲区 unsigned char UART_Data = 0; /************************ I2C通信协议 ************************/ void I2C_Delay() { _nop_(); _nop_(); _nop_(); _nop_(); } void I2C_Start() { SDA = 1; I2C_Delay(); SCL = 1; I2C_Delay(); SDA = 0; I2C_Delay(); SCL = 0; I2C_Delay(); } void I2C_Stop() { SDA = 0; I2C_Delay(); SCL = 1; I2C_Delay(); SDA = 1; I2C_Delay(); } void I2C_WriteByte(unsigned char dat) { unsigned char i; for(i = 0; i < 8; i++) { SDA = (dat & 0x80) ? 1 : 0; dat <<= 1; SCL = 1; I2C_Delay(); SCL = 0; I2C_Delay(); } SDA = 1; SCL = 1; I2C_Delay(); SCL = 0; I2C_Delay(); } /************************ PCA9685驱动 ************************/ void PCA9685_Write(unsigned char reg, unsigned char dat) { I2C_Start(); I2C_WriteByte(PCA9685_ADDR << 1); I2C_WriteByte(reg); I2C_WriteByte(dat); I2C_Stop(); } void PCA9685_Init() { unsigned char ch; PCA9685_Write(0x00, 0x20); // 进入睡眠模式 PCA9685_Write(0xFE, 121); // 预分频值(50Hz,25MHz时钟) PCA9685_Write(0x00, 0xA0); // 退出睡眠模式 for (ch = 0; ch < 4; ch++) { PCA9685_Write(0x06 + 4*ch, 0x00); PCA9685_Write(0x07 + 4*ch, 0x00); PCA9685_Write(0x08 + 4*ch, 0x00); PCA9685_Write(0x09 + 4*ch, 0x00); } } void Set_PWM(unsigned char ch, unsigned int on, unsigned int off) { I2C_Start(); I2C_WriteByte(PCA9685_ADDR << 1); I2C_WriteByte(0x06 + 4*ch); I2C_WriteByte(on & 0xFF); I2C_WriteByte(on >> 8); I2C_WriteByte(off & 0xFF); I2C_WriteByte(off >> 8); I2C_Stop(); } /************************ 舵机控制函数(修正后) ************************/ void Servo_SetAngle(unsigned char ch, unsigned char angle) { // 脉冲范围:0.5ms~2.5ms(适配常见180度舵机) float pulse_min = 0.5; // 0度对应0.5ms float pulse_max = 2.5; // 180度对应2.5ms float pulse = pulse_min + (angle / 180.0) * (pulse_max - pulse_min); // 计算OFF值(周期20ms,4096步长) unsigned int off = (unsigned int)(pulse * 4096 / 20); Set_PWM(ch, 0, off); } /************************ 定时器T0初始化 ************************/ void Timer0_Init() { TMOD |= 0x01; // 定时器0模式1 TH0 = 0xFC; // 1ms定时(11.0592MHz) TL0 = 0x67; ET0 = 1; TR0 = 1; } /************************ 定时器T0中断服务 ************************/ void Timer0_ISR() interrupt 1 { TH0 = 0xFC; TL0 = 0x67; sys_time++; timer_flag = 1; } /************************ 串口通信 ************************/ void UART_Init() { TMOD = 0x21; TH1 = 0xFD; TL1 = 0xFD; TR1 = 1; SCON = 0x50; ES = 1; EA = 1; } void UART_ISR() interrupt 4 { if (RI) { RI = 0; UART_Data = SBUF; if (UART_Data >= '1' && UART_Data <= '4') { unsigned char ch = UART_Data - '1'; servo_target_angle[ch] = 90; // 设置目标角度为90度 servo_start_time[ch] = sys_time; servo_active |= (1 << ch); } } } /************************ 主函数 ************************/ void main() { unsigned char i; // 初始化 servo_active = 0; for(i=0; i<4; i++) { servo_start_time[i] = 0; servo_target_angle[i] = 0; } UART_Init(); PCA9685_Init(); Timer0_Init(); while(1) { if(timer_flag) { timer_flag = 0; // 处理舵机角度设置 for(i=0; i<4; i++) { if(servo_target_angle[i] != 0) { Servo_SetAngle(i, servo_target_angle[i]); servo_target_angle[i] = 0; } } // 检查超时并回零 for(i=0; i<4; i++) { if(servo_active & (1 << i)) { if((sys_time - servo_start_time[i]) >= 3000) { Servo_SetAngle(i, 0); // 转回0度 servo_active &= ~(1 << i); } } } } } }根据这个代码修改

#include <reg51.h> #include "Delay.H" #include "Timer0.H" #include <string.h> #define uchar unsigned char #define uint unsigned int sbit Buzzer = P2^5; sbit m1=P3^1; sbit pause=P3^0; sbit m3=P3^2; sbit m4=P3^3; //·ÂÕæÏÔʾ sbit RS=P2^7; sbit RW=P2^6; sbit E=P2^5; //µ¥Æ¬»úÏÔʾ //sbit E=P2^7; //sbit RS=P2^6; //sbit RW=P2^5; uchar str0[]={" MUSIC "}; uchar str1[]={" QingTian "}; uchar str2[]={" Jay "}; uchar str3[]={" QiFengLe "}; uchar str4[]={" LaJiao "}; uchar str5[]={" Birthday "}; uchar str6[]={" QunXing "}; //µÍÒôÇø #define P 0 //ÐÝÖ¹·û #define L1 1 #define L1_ 2 #define L2 3 #define L2_ 4 #define L3 5 #define L4 6 #define L4_ 7 #define L5 8 #define L5_ 9 #define L6 10 #define L6_ 11 #define L7 12 //ÖÐÒôÇø #define M1 13 #define M1_ 14 #define M2 15 #define M2_ 16 #define M3 17 #define M4 18 #define M4_ 19 #define M5 20 #define M5_ 21 #define M6 22 #define M6_ 23 #define M7 24 //¸ßÒôÇø #define H1 25 #define H1_ 26 #define H2 27 #define H2_ 28 #define H3 29 #define H4 30 #define H4_ 31 #define H5 32 #define H5_ 33 #define H6 34 #define H6_ 35 #define H7 36 unsigned int code FreqTable[]={ 0, 63628, 63731, 63835, 63928, 64021, 64103, 64185, 64260, 64331, 64400, 64463, 64528, 64580, 64633, 64684, 64732, 64777, 64820, 64860, 64898, 64934, 64968, 65000, 65030, 65058, 65085, 65110, 65134, 65157, 65178, 65198, 65217, 65235, 65252, 65268, 65283, }; unsigned char code Music[]={ //ǰ×à L6,2,M1,2,M5,2,M1,2,L4,2,L5,1,L6,1,M5,2,M1,2, L1,2,L5,2,M5,2,M1,2,L1,2,M5,2,L7,2,M5,2, P,2,M5,2,M5,2,M1,2,M1,4,M2,2,M3,2, P,2,M5,2,M5,2,M1,2,M1,2,M2,1,M3,1,M2,1,M1,1,L5,2, P,2,M5,2,M5,2,M1,2,M1,4,M2,2,M3,2, P,2,M3,4,M2,1,M3,1,M4,1,M3,1,M2,1,M4,1,M3,1,M2,1,M1,2, P,2,L6,2,M1,2,M1,2,M3,2,M4,2,M3,2,M2,2,M1,1,M2,1, M3,2,M3,2,M3,2,M3,2,M2,1,M3,1,M2,1,M1,1,M1,4, //´µ×Åǰ×àÍû×ÅÌì¿Õ L5,2,M1,2,M1,2,M3,2,M4,2,M3,2,M2,2,M1,1,M2,1, M3,2,M3,2,M3,2,M3,2,M2,1,M3,1,M2,1,M1,1,M1,3,L7,1, //ΪÄãÇ̿εÄÄÇÒ»Ìì L7,1,M1,1,M1,1,M1,1,L7,1,M1,2,M1,1+1,M1,1,M1,1,M1,1,L7,1,M1,2,M1,1+1, //½ÌÊÒµÄÄÇÒ»¼ä M1,1,M1,1,M1,1,L7,1,M1,2,M1,1+1,M1,1,M1,1,M1,1,M5,1,M5,2,M5,1+1, M5,1,M5,1,M5,1,M5,1,M5,2,M5,1+1,M5,1,M5,1,M5,1,M5,1,M4,1,M4,1,M3,1+4+4, //ûÏ뵽ʧȥµÄÓÂÆøÎÒ»¹Áô×Å P,4,M1,1,M1,1,M1,1,M1,1,L6,2,L7,2,M1,2,M5,2,M4,2,M3,2,M1,1,M1,2+4, P,2,L7,2,M1,1,M1,1,M1,1,M1,1,M3,2,M1,2, //Äã»áµÈ´ý»¹ÊÇÀ뿪 L6,2,L7,2,M1,2,M5,2,M4,2,M3,1,M1,1,M1,2,M2,2+4+4,P,4+4, //¹Î·çÕâÌ죬ÎÒÊÔ¹ý M3,2,M2,2,M4,2,M3,4,M1,2,M5,2,M7,2, //ÎÕ×ÅÄãÊÖ H1,2,M7,2,M5,2,M1,4,M1,2,M6,2+2,M6,2,M5,2,M5,4,M5,2,M4,2,M3,2, M2,2,M3,2,M4,2,M3,2+4+4, //»¹Òª¶à¾Ã£¬ÎÒ²ÅÄÜ M3,2,M4_,2,M5_,2,M3,4,M4,2,M5,2,M7,2,H2,2,M7,2,H1,2,H1,2+4+2, //µÈµ½·ÅÇçµÄÄÇÌì H1,2,H1,2,M5,2,M5,2,M6,2,M5,1,M4,1,M4,2,M2,2,M3,2, M4,2,M5,2,M6,2,M1,2,M6,2+2,M7,1,M7,4, 0xFF }; unsigned char code Music1[]={ //ǰ×à M7,1,H1,1,H2,1,H3,1, M7,1,H5,1,H3,6, M7,1,H1,1,H2,1,H3,1,M3,1,H5,1,H3,1, H2,1,H3,1,H1,1,H2,1,M7,1,H1,1,M5,1,P,2, //Õâһ·ÉÏ×ß×ßͣͣ£¬Ë³×ÅÉÙÄêÆ¯Á÷µÄºÛ¼£ M2,6,M1,2,M2,6,M1,2,M2,2,M3,2,M5,2,M3,2,M2,6,M1,2,M2,6,M1,2,M2,1,M3,1,M2,1,M1,1,L5,4, //Âõ³ö³µÕ¾µÄǰһ¿Ì¾¹ÓÐЩÓÌÔ¥ M2,6,M1,2,M2,6,M1,2,M2,2,M3,2,M5,2,M3,2,M2,3,M3,1,M2,2,M1,2,M2,4,P,4, //²»½ûЦÕâ½üÏçÇéÇÓÈÔÎ޿ɱÜÃâ M2,6,M1,2,M2,6,M1,2,M2,2,M3,2,M5,2,M3,2,M2,2+1,M3,1,M2,2,M1,2,L6,4, //¶ø³¤Ò°µÄÌ죬ÒÀ¾ÉÄÇôů£¬·ï´µÆðÁË´Óǰ M3,1,M2,1,M1,1,M2,1,M1,4,M3,1,M2,1,M1,1,M2,1,M1,2+1, M5,1,M3,1,M2,1,M1,1,M2,1,M1,4,P,4, //´Óǰ³õʶÕâÊÀ½ç M1,2,M2,2,M3,2,M1,2,M6,2,M5,1,M6,1+1+1, P,2,M1,1,M7,2,M6,1,M7,1+4,P,2,M7,2,M6,1,M7,1+2,M3,2,H1,1,H2,1,H1,1,M7,1,M6,2, //Ò²¸ÊÔ¸¸°ÌÀµ¸»ðÈ¥×ßËüÒ»±é M5,2,M6,2,M5,1,M6,1+1,M5,1,M6,1,M5,1,M6,2,M5,1,M2,1+1,M5,3,M3,4,P,4, M1,2,M2,2,M3,2,M1,2,M6,2,M5,1,M6,1+1+1,M1,1,M7,2,M6,1,M7,1+4, M7,2,M6,1,M7,1+2,M3,2,H1,1,H2,1,H1,1,M7,1,M6,2,M5,2, //â§²»¼°·À´³ÈëÄãµÄÈÝÑÕ M6,2,H3,1,H3,1+2,M5,2,M6,2,H3,1,H3,1+1,M5,2,M6,1,M6,4,P,4+4, //ÎÒÔøÄÑ×Ô°ÎÓÚÊÀ½çÖ®´ó H1,2,H2,2,H3,2,H6,1,H5,1+2,H6,1,H5,1+2,H2,1, P,2,H3,1,H3,2,H6,1,H5,1+2,H6,1,H5,1+2,H6,1,H5,1+3,H3,1,P,2, //²»µÃÕæ¼Ù£¬²»×öÕõÔú£¬²»¾åЦ»° H2,2,H1,1,M6,1+1,H1,2,M6,1,H2,2,H1,1,M6,1+1, H1,2+1,H3,4+1,H4,1,H3,2,H3,1,H2,2+1,P,2, //ÎÒÔø½«Çà´º·­Ó¿³ÉËý H1,2,H2,4,H3,2,H6,1,H5,4,H6,1,H5,4,H2,1, H3,1,H3,2,H6,1,H5,4,H6,1,H5,4,H6,1,H5,1+3, //ÐÄÖ®Ëù¶¯ÇÒ¾ÍËæÔµÈ¥°É H3,1,H2,2,H1,1,M6,1+1,H3,2+1, H2,2,H1,1,M6,1+1,H1,3,H1,4,P,4+4, //Äæ×ŹâÐÐ×ßÈη紵Óê´ò H6,1,H3,2+1,H2,2,H1,1,M6,1+1,H3,2+1, H2,2,H1,1,M6,1+1,H1,3,H1,4+4+4+4, 0xFF }; unsigned char code Music2[]={ //ÉúÈÕ¸è M5,1,M5,1,M6,4,M5,4,H1,4,M7,4+4, M5,1,M5,1,M6,4,M5,4,H2,4,H1,4+4, M5,1,M1,2,H5,4,M3,4,H1,4,M7,2,M6,2, M4,1,M4,1,M3,4,M1,4,M2,4,M1,4+4, 0xFF }; unsigned long FreqSelect,MusicSelect,MusicSelect1,MusicSelect2; void init(); void wc51r(uchar i); void wc51ddr(uchar i); void lcd1602wstr(uchar hang,uchar lie,uchar length,uchar *str); void fbusy(); void main() { MusicSelect=0x00; MusicSelect1=0x00; MusicSelect2=0x00; SP=0x50; init(); Timer0Init(); lcd1602wstr(0,1,strlen(str0),str0); while(1) { if(m1==0&&pause==1) { lcd1602wstr(0,1,strlen(str1),str1); lcd1602wstr(1,1,strlen(str2),str2); if(pause==0)//ÔÝÍ£²¥·Å { TR0=0; //¹Ø¶¨Ê±Æ÷ Buzzer=1; //¹Ø·äÃùÆ÷ } if(Music[MusicSelect]!=0xFF) { FreqSelect = Music[MusicSelect]; MusicSelect++; Delay(125*Music[MusicSelect]); MusicSelect++; //̧ÊÖ£¬Òô·ûÍ£¶Ù TR0=0; Delay(50); TR0=1; } else { TR0=0; Delay(500); MusicSelect=0x00; //ÇåÁ㣬ÔÙ´ÎÑ­»· } continue; } else if(m3==0&&pause==1){ lcd1602wstr(0,1,strlen(str3),str3); lcd1602wstr(1,1,strlen(str4),str4); if(pause==0)//ÔÝÍ£²¥·Å { TR0=0; //¹Ø¶¨Ê±Æ÷ Buzzer=1; //¹Ø·äÃùÆ÷ } if(Music1[MusicSelect1]!=0xFF) { FreqSelect = Music1[MusicSelect1]; MusicSelect1++; Delay(125*Music1[MusicSelect1]); MusicSelect1++; //̧ÊÖ£¬Òô·ûÍ£¶Ù TR0=0; Delay(50); TR0=1; } else { TR0=0; Delay(500); MusicSelect1=0x00; //ÇåÁ㣬ÔÙ´ÎÑ­»· } continue; } else if(m4==0&&pause==1){ lcd1602wstr(0,1,strlen(str5),str5); lcd1602wstr(1,1,strlen(str6),str6); if(pause==0)//ÔÝÍ£²¥·Å { TR0=0; //¹Ø¶¨Ê±Æ÷ Buzzer=1; //¹Ø·äÃùÆ÷ } if(Music2[MusicSelect2]!=0xFF) { FreqSelect = Music2[MusicSelect2]; MusicSelect2++; Delay(125*Music2[MusicSelect2]); MusicSelect2++; //̧ÊÖ£¬Òô·ûÍ£¶Ù TR0=0; Delay(50); TR0=1; } else { TR0=0; Delay(500); MusicSelect2=0x00; //ÇåÁ㣬ÔÙ´ÎÑ­»· } continue; } else{ SP=0x07; TR0=0; Buzzer=1; } } } void Timer0_Routine() interrupt 1 { if(FreqTable[FreqSelect]) { TL0 = FreqTable[FreqSelect]%256;//ÉèÖö¨Ê±³õÖµ TH0 = FreqTable[FreqSelect]/256;//ÉèÖö¨Ê±³õÖµ Buzzer=!Buzzer; } } //³õʼ»¯ void init(){ wc51r(0x38); //ʹÓÃ8λÊý¾Ý,ÏÔʾÁ½ÐÐ,ʹÓÃ5¡Á7µÄ×ÖÐÍ wc51r(0x0c); //ÏÔʾÆ÷¿ª,¹â±ê¹Ø,×Ö·û²»ÉÁ˸ wc51r(0x06); //×Ö·û²»¶¯,¹â±ê×Ô¶¯ÓÒÒÆÒ»¸ñ wc51r(0x01); //ÇåÆÁ } //¼ì²é溯Êý void fbusy(){ P0=0Xff;RS=0;RW=1; E=0;E=1; while(P0&0x80){ E=0;E=1; } } //дÃüÁÊý void wc51r(uchar j){ fbusy(); E=0;RS=0;RW=0; E=1; P0=j; E=0; } //дÊý¾Ýº¯Êý void wc51ddr(uchar j){ fbusy(); E=0;RS=1;RW=0; E=1; P0=j; E=0; } //×Ö·û´®ÏÔʾº¯Êý void lcd1602wstr(uchar hang,uchar lie,uchar length,uchar *str){ uchar i; wc51r(0x80+0x40*hang+lie); for(i=0;i<length;i++){ wc51ddr(*str); str++; } }修改为stc15w4k56s4可以使用的

大家在看

recommend-type

AAA2.5及汉化补丁

Advanced Aircraft Analysis V2.5.1.53 (3A) 在win7 64位上安装测试。有注册机和安装视频。支持winxp和win732位和64位系统。 Darcorp Advanced Aircraft Analysis V2.5.1.53 (AAA) 软件是一款面向于高级用户的飞机设计和仿真分析软件,目前广泛应用于数十个国家的各种机构,已然成为飞机设计、开发、稳定性分析以及飞行控制的工业标准软件。适用于 FAR23、FAR25、UAV无人驾驶飞机与 Military 规范,为全球飞机公司(如波音公司)、政府部门(如 FAA)与学校采用于飞机初步设计、分析、与 3-D 绘图的一套完整软件工具。 Advanced Aircraft Analysis (AAA) 是行业标准的飞机设计,稳定性和控制分析软件。 安装在超过45个国家,AAA所使用的主要航空工程大学,飞机制造商和世界各地的军事组织。 Advanced Aircraft Analysis(AAA)是行业标准的飞机设计 AAA提供了一个功能强大的框架,以支持飞机初步设计迭代和非独特的过程。 AAA计划允许学生和初步设计工程师从早期的大小通过开环和闭环动态稳定性和灵敏度分析的重量,而该机的配置工作在监管和成本的限制。
recommend-type

人脸检测 人脸关键点检测 口罩检测.zip

RetinaFace 实现的同时人脸检测 关键点 口罩检测 1. linux用户打开rcnn/cython/setup.py 121行注释(windows跳过) 2. 进入cython目录 执行python setup.py build_ext --inplace 3. 运行python test.py 注意如果缺少mxnet等类库 自行使用pip安装
recommend-type

commons-collections4-4.1-bin.zip

commonS操作源码及jar包,没有外部依赖,jar包比较全
recommend-type

CENTUM TP 安装授权及windows设置.rar

CENTUM VP系统软件的安装,以及安装前的必要工作。 CENTUM VP 系统软件的构成: CENTUM VP software(系统组态、操作监视功能、其他可选软件包)控制总线驱 动电子文档。 CENTUM VP 系统软件的安装步骤 安装Windows。(如果PC已经安装可以不做) 启动 Windows 对Windows 进行必要设置以便CENTUM VP的运行: 添加网络适配器 安装Ethernet 适配器(已经存在不必安装)。 安装控制总线驱动(已经存在不必安装)。 添加打印机 安装Service Pack。 安装USB操作键盘驱动 使用USB接口操作键盘时安装 安装CENTUM VP 软件。 Windows构成指定。(包括运行CENTUM VP必须的网络、用户名、其他必要信息 等的设置)。 在CENTUM VP的功能已经具备,如果仅仅是用于工程组态,不需要制定“CENTUM” 用户“自动登录HIS”。 以管理员身份对以下内容进行必要设置。 l 计算机名(站名) 计算机名是Windows 网络用于识别每一台计算机的标志。 一个站名是CENTUM VP 系统中,根据控制总线地址确定的唯一名称。 应确保计算机名和站名的一致性。 计算机名(站名)的设定例: HIS0164 (HISddss:“dd”域号;“ss”站号。) l IP 地址 IP 地址是Vnet 或Vnet Open。用于识别每台PC的确定地址。在各网络中每台PC 的地址是唯一的。 例:172.16.1.64(Vnet);192.168.129.193(Vnet Open) Vnet 地址:172.16.dd.ss “dd”域号:01~16 “ss”站号:01~64 Vnet Open 地址:192.168.128+ dd.129+ ss 子网掩码 255.255.0.0 设置Administrator 密码 为PC机管理者设定密码。 l CENTUM VP 帐户和密码 操作监视功能帐户:CENTUM(系统固定)。 该帐户是在系统安装时自动生成的,账户名不能更改。
recommend-type

Cluster Load Balance Algorithm Simulation Based on Repast

Cluster Load Balance Algorithm Simulation Based on Repast

最新推荐

recommend-type

mavlink协议,c++语言版本,用于px4飞控通信

mavlink协议,c++语言版本,用于px4飞控通信
recommend-type

(完整word版)网上订餐系统软件测试总结报告.doc

(完整word版)网上订餐系统软件测试总结报告.doc
recommend-type

chromedriver-mac-x64-140.0.7295.0(Canary).zip

chromedriver-mac-x64-140.0.7295.0(Canary).zip
recommend-type

(完整版)基因工程药物干扰素的制备.ppt

(完整版)基因工程药物干扰素的制备.ppt
recommend-type

用户完成登录后自动跳转至主界面

资源下载链接为: https://pan.quark.cn/s/9e7ef05254f8 Delphi 7 中做一个「登录→主界面」的完整流程: 建立登录窗体 File → New → VCL Forms Application,拖两个 TEdit(用户名、密码)、一个 TButton(登录)、一个 TLabel(提示)。把 Edit2 的 PasswordChar 设为 *,Button.Caption 设为“登录”。 写验证代码 Button 的 OnClick 里: 多窗体管理 Project → Options → Forms,把 Form1 设为主窗体,Form2 设为可用窗体。登录成功后创建并显示 Form2,隐藏 Form1;主界面退出时调用 Application.Terminate 结束程序。 错误提示 验证失败直接用 ShowMessage,也可把 Label 换成红色文字提示。
recommend-type

Web2.0新特征图解解析

Web2.0是互联网发展的一个阶段,相对于早期的Web1.0时代,Web2.0具有以下显著特征和知识点: ### Web2.0的定义与特点 1. **用户参与内容生产**: - Web2.0的一个核心特征是用户不再是被动接收信息的消费者,而是成为了内容的生产者。这标志着“读写网络”的开始,用户可以在网络上发布信息、评论、博客、视频等内容。 2. **信息个性化定制**: - Web2.0时代,用户可以根据自己的喜好对信息进行个性化定制,例如通过RSS阅读器订阅感兴趣的新闻源,或者通过社交网络筛选自己感兴趣的话题和内容。 3. **网页技术的革新**: - 随着技术的发展,如Ajax、XML、JSON等技术的出现和应用,使得网页可以更加动态地与用户交互,无需重新加载整个页面即可更新数据,提高了用户体验。 4. **长尾效应**: - 在Web2.0时代,即使是小型或专业化的内容提供者也有机会通过互联网获得关注,这体现了长尾理论,即在网络环境下,非主流的小众产品也有机会与主流产品并存。 5. **社交网络的兴起**: - Web2.0推动了社交网络的发展,如Facebook、Twitter、微博等平台兴起,促进了信息的快速传播和人际交流方式的变革。 6. **开放性和互操作性**: - Web2.0时代倡导开放API(应用程序编程接口),允许不同的网络服务和应用间能够相互通信和共享数据,提高了网络的互操作性。 ### Web2.0的关键技术和应用 1. **博客(Blog)**: - 博客是Web2.0的代表之一,它支持用户以日记形式定期更新内容,并允许其他用户进行评论。 2. **维基(Wiki)**: - 维基是另一种形式的集体协作项目,如维基百科,任何用户都可以编辑网页内容,共同构建一个百科全书。 3. **社交网络服务(Social Networking Services)**: - 社交网络服务如Facebook、Twitter、LinkedIn等,促进了个人和组织之间的社交关系构建和信息分享。 4. **内容聚合器(RSS feeds)**: - RSS技术让用户可以通过阅读器软件快速浏览多个网站更新的内容摘要。 5. **标签(Tags)**: - 用户可以为自己的内容添加标签,便于其他用户搜索和组织信息。 6. **视频分享(Video Sharing)**: - 视频分享网站如YouTube,用户可以上传、分享和评论视频内容。 ### Web2.0与网络营销 1. **内容营销**: - Web2.0为内容营销提供了良好的平台,企业可以通过撰写博客文章、发布视频等内容吸引和维护用户。 2. **社交媒体营销**: - 社交网络的广泛使用,使得企业可以通过社交媒体进行品牌传播、产品推广和客户服务。 3. **口碑营销**: - 用户生成内容、评论和分享在Web2.0时代更易扩散,为口碑营销提供了土壤。 4. **搜索引擎优化(SEO)**: - 随着内容的多样化和个性化,SEO策略也必须适应Web2.0特点,注重社交信号和用户体验。 ### 总结 Web2.0是对互联网发展的一次深刻变革,它不仅仅是一个技术变革,更是人们使用互联网的习惯和方式的变革。Web2.0的时代特征与Web1.0相比,更加注重用户体验、社交互动和信息的个性化定制。这些变化为网络营销提供了新的思路和平台,也对企业的市场策略提出了新的要求。通过理解Web2.0的特点和应用,企业可以更好地适应互联网的发展趋势,实现与用户的深度互动和品牌的有效传播。
recommend-type

【C++编程新手必看】:一步步带你制作出风靡全球的“别踩白块儿”游戏

# 摘要 本文全面介绍C++编程在游戏开发中的应用,涵盖了从基础概念到具体实现的多个方面。首先,文章提供了游戏开发环境的搭建指南,包括编译器配置和开发工具的选择。随后,重点介绍了游戏主循环和基本框架的构建,强调了事件处理和渲染技术。在游戏逻辑和交互设计方面,本文阐述了界面布局、事件响应和游戏状态管理的核心实现。为了提升游戏体验,本文还探讨了添加音效和背景音乐以及开发高级游戏特性的方法。最后,文章介绍了性能优化和跨平台发布的过程,包括游戏的打包和针对不同平台的发布策略。本文旨在为C++游戏开发者提供一个实用的开发指南,帮助他们从零开始构建出性能优化、跨平台兼容的游戏。 # 关键字 C++编程;
recommend-type

使用scikit-learn训练模型来预测鸢尾花种类

<think>我们使用scikit-learn库来训练一个分类模型预测鸢尾花种类。鸢尾花数据集是scikit-learn内置的经典数据集,包含三种鸢尾花的四个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)和对应的类别标签。步骤:1.导入必要的库和数据集2.数据探索(可选,但建议了解数据)3.数据预处理(如划分训练集和测试集、标准化等)4.选择模型(例如KNN、随机森林等)5.训练模型6.模型评估7.使用模型进行预测下面以KNN(K近邻)算法为例,展示完整流程。</think>### 使用 scikit-learn 训练鸢尾花分类模型 以下是基于 scikit-learn 训练鸢尾花分类模型的
recommend-type

WWF工作流设计器C#源码解析及演示

### WWF工作流设计器控件C#源码知识点 #### 1. WWF(Windows Workflow Foundation)概述 WWF是微软公司推出的一个工作流框架,作为.NET Framework的一部分。它提供了一套丰富的API,用于设计、执行和管理工作流。工作流可以用于各种应用程序,包括Web应用、服务和桌面应用,使得开发者能够将复杂的业务逻辑以工作流的形式表现出来,简化业务流程自动化和管理。 #### 2. 工作流设计器控件(Workflow Designer Control) 工作流设计器控件是WWF中的一个组件,主要用于提供可视化设计工作流的能力。它允许用户通过拖放的方式在界面上添加、配置和连接工作流活动,从而构建出复杂的工作流应用。控件的使用大大降低了工作流设计的难度,并使得设计工作流变得直观和用户友好。 #### 3. C#源码分析 在提供的文件描述中提到了两个工程项目,它们均使用C#编写。下面分别对这两个工程进行介绍: - **WorkflowDesignerControl** - 该工程是工作流设计器控件的核心实现。它封装了设计工作流所需的用户界面和逻辑代码。开发者可以在自己的应用程序中嵌入这个控件,为最终用户提供一个设计工作流的界面。 - 重点分析:控件如何加载和显示不同的工作流活动、控件如何响应用户的交互、控件状态的保存和加载机制等。 - **WorkflowDesignerExample** - 这个工程是演示如何使用WorkflowDesignerControl的示例项目。它不仅展示了如何在用户界面中嵌入工作流设计器控件,还展示了如何处理用户的交互事件,比如如何在设计完工作流后进行保存、加载或执行等。 - 重点分析:实例程序如何响应工作流设计师的用户操作、示例程序中可能包含的事件处理逻辑、以及工作流的实例化和运行等。 #### 4. 使用Visual Studio 2008编译 文件描述中提到使用Visual Studio 2008进行编译通过。Visual Studio 2008是微软在2008年发布的集成开发环境,它支持.NET Framework 3.5,而WWF正是作为.NET 3.5的一部分。开发者需要使用Visual Studio 2008(或更新版本)来加载和编译这些代码,确保所有必要的项目引用、依赖和.NET 3.5的特性均得到支持。 #### 5. 关键技术点 - **工作流活动(Workflow Activities)**:WWF中的工作流由一系列的活动组成,每个活动代表了一个可以执行的工作单元。在工作流设计器控件中,需要能够显示和操作这些活动。 - **活动编辑(Activity Editing)**:能够编辑活动的属性是工作流设计器控件的重要功能,这对于构建复杂的工作流逻辑至关重要。 - **状态管理(State Management)**:工作流设计过程中可能涉及保存和加载状态,例如保存当前的工作流设计、加载已保存的工作流设计等。 - **事件处理(Event Handling)**:处理用户交互事件,例如拖放活动到设计面板、双击活动编辑属性等。 #### 6. 文件名称列表解释 - **WorkflowDesignerControl.sln**:解决方案文件,包含了WorkflowDesignerControl和WorkflowDesignerExample两个项目。 - **WorkflowDesignerControl.suo**:Visual Studio解决方案用户选项文件,该文件包含了开发者特有的个性化设置,比如窗口布局、断点位置等。 - **Thumbs.db**:缩略图缓存文件,由Windows自动生成,用于存储文件夹中的图片缩略图,与WWF工作流设计器控件功能无关。 - **WorkflowDesignerExample**:可能是一个文件夹,包含了示例工程相关的所有文件,或者是示例工程的可执行文件。 - **EULA.txt**:最终用户许可协议文本文件,通常说明了软件的版权和使用许可条件。 综上所述,该文件集包含了WWF工作流设计器控件的完整C#源码以及相应的Visual Studio项目文件,开发者可以利用这些资源深入理解WWF工作流设计器控件的工作机制,并将其应用于实际的项目开发中,实现工作流的设计和管理功能。
recommend-type

CAD数据在ANSA中:完美修复几何数据的策略与方法

# 摘要 CAD数据的准确性和几何完整性对于产品设计和工程分析至关重要。本文首先强调了CAD数据和几何修复的重要性,随后介绍了ANSA软件在处理CAD数据中的角色。通过详细的分析,本文探讨了几何数据的常见问题,以及有效的手动和自动修复技术。文中还提供了几何修复在实际应用中的案例分析,并讨论了行业未来对几何修复技术的需求和新兴技术趋势。文章旨在为CAD数据几何修复提供全面的理论知识、诊断方法和修复策略,并