完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
电子发烧友论坛
|
本帖最后由 jianhong_wu 于 2013-11-8 11:48 编辑
第五十八节:按键的花样技巧----按住一个按键的连续触发 开场白: 从这一节开始,我准备花几个章节来讲讲按键的几种常用花样技巧。 (1) 功能需求: 两位数码管上显示一个数字,用一个按键作为“加”键,按一次此按键,数码管上面显示的数据字加一。如果按住此按键不放的时间超过2秒钟,这个数字会每个隔1秒有节奏地依次累加上去,到99时又从0开始累加。这个就是按键的连续触发功能。 (2)硬件原理: 独立按键和有源蜂鸣器的硬件原理请参考本连载文章的第二节。数码管的硬件原理请参考本连载文章的第二十一节。 (3)源码适合的单片机: PIC16F73,晶振为3.579545MHz。 (4)单片机的C语言源代码讲解: #include #define seg_0_dr RB6 //任意7个IO口接数码管的seg引脚 #define seg_1_dr RB5 #define seg_2_dr RB4 #define seg_3_dr RB3 #define seg_4_dr RC7 #define seg_5_dr RB0 #define seg_6_dr RB1 #define com_left_dr RB2 //任意2个IO口接数码管的com引脚 #define com_right_dr RB7 #define beep_dr RC1 //蜂鸣器输出 #define key_sr1 RC2 //按键 #define cnt_delay 100 //按键去抖动的时间 具体是多少我没验证过,请读者根据实际情况调整大小 #define cnt_delay_2s 4000 //按键按住2秒 具体是多少我没验证过,请读者根据实际情况调整大小 #define cnt_delay_1s 2000 //按键按住1秒 具体是多少我没验证过,请读者根据实际情况调整大小 #define cnt_voice 60 //蜂鸣器鸣叫的声音长短 具体是多少我没验证过,请读者根据实际情况调整大小 void initial();//初始化 void delay1(unsigned int de) ;//小延时程序,时间不宜太长,因为内部没有喂看门狗 void display_drive(); //数码管驱动程序,放在定时中断里 void display_seg(unsigned char seg); //编码转换程序,放在display_drive里 //补充说明:吴坚鸿程序风格是这样的,按键扫描函数通常放在定时中 //断里,按键服务程序通常放在main函数循环里。有人说不应该把子程序放在中断里,别听他们,信鸿哥无坎坷。 void key_scan(); //按键扫描函数,放在定时中断里 void key_service(); //按键服务函数,放在main函数循环里 unsigned char key_lock1=0; //按键自锁标志 unsigned int delay_cnt1=0; //按键去抖动的延时计数器的变量 unsigned int delay_continuity_cnt1=0; //按键连续触发的间隔时间计数器 unsigned int voice_time_cnt; //蜂鸣器响的声音长短的计数延时 unsigned char key_sec=0; //哪个按键被触发 unsigned char number_set=0; //设置显示的数字 unsigned char number_left=0; //左边数码管显示的内容 unsigned char number_right=0; //右边数码管显示的内容 unsigned char dis_step=1; //扫描的步骤 main() //主程序 { initial(); //初始化 while(1) { key_service(); //按键服务函数,放在main函数循环里 CLRWDT(); } } void key_scan() //按键扫描函数 { if(key_sr1==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位 { key_lock1=0; //按键自锁标志清零 delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙,仔细品味,回味无穷,在按键去抖动期间,如果低电平掺杂了干扰的高电平,则计数器重新清零计数,极大增强了抗干扰的能力。 delay_continuity_cnt1=0; //连续触发的间隔时间计数器清零 } else if(key_lock1==0) //有按键按下,且是第一次被按下 { ++delay_cnt1; //延时计数器 if(delay_cnt1>cnt_delay) // { delay_cnt1=0; key_lock1=1; //自锁按键置位,避免一直触发 key_sec=1; //触发按键 } } else if(delay_cnt1 ++delay_cnt1; } else { ++delay_continuity_cnt1; if(delay_continuity_cnt1>=cnt_delay_1s) //每1秒连续触发一次。 { delay_continuity_cnt1=0; key_sec=1; //触发按键 } } } void key_service() //按键服务函数 { switch(key_sec) //按键服务状态切换 { case 1:// 1号按键 number_set++; if(number_set>99) { number_set=0; } number_left=number_set/10; //左边数码管显示的内容 number_right=number_set%10; //右边数码管显示的内容 voice_time_cnt= cnt_voice; //蜂鸣器鸣叫 key_sec=0; //相应完按键处理程序之后,把按键选择变量清零,避免一直触发 break; } } void interrupt timer1rbint(void) //中断程序入口 { if(TMR1IE==1&&TMR1IF==1) //定时中断程序 { TMR1IF=0; TMR1ON=0; key_scan(); //按键扫描函数,放在定时中断里 display_drive(); //数码管驱动程序,放在定时中断里 if(voice_time_cnt) //控制蜂鸣器声音的长短 { beep_dr=1; //蜂鸣器响 --voice_time_cnt; //蜂鸣器响的声音长短的计数延时 } else { beep_dr=0; //蜂鸣器停止 } TMR1H=0xFF; TMR1L=0xC8; TMR1ON=1; } } void initial()//初始化 { ADCON0=0x41; //设置AD模式 ADCON1=0x04; //RA0作为AD输入通道 TRISB=0x00; //数码管IO口设置成输出 TRISC7=0; TRISC1=0; //蜂鸣器输出 TRISC2=1; //按键输入 T1CON=0x24; //定时中断配置 TMR1H=0xFE; TMR1L=0xEF; INTCON=0xC0; TMR1IF=0; TMR1IE=1; PEIE=1; //外围中断允许 GIE=1; TMR1ON=1; } void display_drive() //数码管驱动程序,放在定时中断里 { seg_0_dr=0; seg_1_dr=0; seg_2_dr=0; seg_3_dr=0; seg_4_dr=0; seg_5_dr=0; seg_6_dr=0; com_left_dr=1; com_right_dr=1; //在即将更换下一位数码管时,先把让它两个什么都不显示,让显示过度更加平稳 asm("nop"); //空指令延时 asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); switch(dis_step) { case 1: //扫描左边的数码管 display_seg(number_left); //如果不是任意IO口,可以直接用查表的方式取代此子程序 com_left_dr=0; //选中左数码管 com_right_dr=1; break; case 2: //扫描右边的数码管 display_seg(number_right); //如果不是任意IO口,可以直接用查表的方式取代此子程序 com_left_dr=1; com_right_dr=0; //选中右数码管 break; } delay1(50); //每一位数码管显示的停留延时时间,有疑问的朋友请自己尝试改成计数延时的方式, //鸿哥认为在此种环境下,在定时中断里用死延时delay1(50)是最佳的方式 ++dis_step; //下一次中断扫描另外一位的数码管,轮流扫描 if(dis_step>2) { dis_step=1; } } //不是鸿哥不懂爱,如果不是用任意IO口,而是直接用一个并口(比如51单片机中的P1口),那么就不用那么费力, //直接用查数组(俗称查表)的方式就可以替代display_seg这个编码转换程序, void display_seg(unsigned char seg) //编码转换程序,,放在display_drive里 { switch(seg) //switch指令,单片机中的战斗机,鸿哥的最爱! { case 0: //显示"0" seg_0_dr=1; seg_1_dr=1; seg_2_dr=1; seg_3_dr=1; seg_4_dr=0; seg_5_dr=1; seg_6_dr=1; break; case 1: //显示"1" seg_0_dr=1; seg_1_dr=1; seg_2_dr=0; seg_3_dr=0; seg_4_dr=0; seg_5_dr=0; seg_6_dr=0; break; case 2: //显示"2" seg_0_dr=1; seg_1_dr=0; seg_2_dr=1; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=0; break; case 3: //显示"3" seg_0_dr=1; seg_1_dr=1; seg_2_dr=0; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=0; break; case 4: //显示"4" seg_0_dr=1; seg_1_dr=1; seg_2_dr=0; seg_3_dr=0; seg_4_dr=1; seg_5_dr=0; seg_6_dr=1; break; case 5: //显示"5" seg_0_dr=0; seg_1_dr=1; seg_2_dr=0; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=1; break; case 6: //显示"6" seg_0_dr=0; seg_1_dr=1; seg_2_dr=1; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=1; break; case 7: //显示"7" seg_0_dr=1; seg_1_dr=1; seg_2_dr=0; seg_3_dr=0; seg_4_dr=0; seg_5_dr=1; seg_6_dr=0; break; case 8: //显示"8" seg_0_dr=1; seg_1_dr=1; seg_2_dr=1; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=1; break; case 9: //显示"9" seg_0_dr=1; seg_1_dr=1; seg_2_dr=0; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=1; break; case 10: //什么也不显示,空 seg_0_dr=0; seg_1_dr=0; seg_2_dr=0; seg_3_dr=0; seg_4_dr=0; seg_5_dr=0; seg_6_dr=0; break; } } void delay1(unsigned int de) { unsigned int t; for(t=0;t (5)下集预告: 按键的花样技巧----按住一个按键的连续加速触发 (未完待续,下节更精彩,不要走开哦!) |
|
|
|
|
|
|
|
|
我来拍砖了,发的这些程序连基本的模块化思想都没有,楼主吹大了!!
|
|
|
|
|
|
|
|
|
(1)买本学习和练习C++的书。 (2)买本VC的书自学。 (3)买本侯俊杰老师的 (4)买本VC串口通讯的书。 大概自学一年左右有点入门。我以后有时间,也会在电子发烧友论坛上发帖分享关于VC上位机编程方面的技术,目前暂时没有时间。 |
|
|
|
|
|
|
|
|
有些话极大地误导初学者。 在定时中断里面进行延时操作,是最糟糕的设计方法。 千万不要再中断做过多的事情,中断越简洁越好。 中断是用来处理紧急的事情的。一般来说,非常紧急的事情直接在中断里面处理。此外,在中断设定标志,主循环处理即可。 这样才能最大效率利用单片机资源。 希望初学者好好体会,不能一味盲目跟随。在网上搜从单片机初学者迈向单片机工程师,便可知道什么是真正的实战教程。 单片机程序要写好,有很多思想。只有掌握了这些思想,才能真正立于不败之地。不管PIC, 51, AVR 还是ARM 都只是一个芯片,一个工具而已。真正单片机程序的精髓在于程序思想。 |
|
|
|
|
|
|
|
|
请问鸿哥,第五十八节的例程你是否亲自上机验证过与描述的功能需求一致呢? |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
你的话从字面上理解没有错。 但凡事没有绝对,有些东西是毒药,但是医术高超的医生把握好剂量,却能用来治病救人。 对于实时性要求很高的系统,压根从开始就不应该选用动态扫描数码管的方案,而是选用 专用芯片来静态驱动数码管。在满足实时性要求的情况下,既然是选用动态扫描数码管的方案,我最关 注的是数码管的显示效果是否均匀,美观,不要有鬼影存在,在这种情况下,我义无返顾的选择了在定 时中断里加入少量的死延时来“调味”,这样编程非常简洁而且显示效果非常好,可见鸿哥艺高胆大! 至于按键扫描keyscan(),那更是鸿哥最引以为豪的地方,如果在一些实时性很高的系统,按键扫描 keyscan()可以直接放在main()函数的while(1)循环里,不影响使用。另外,不是所有的单片机系统都是 扫描越快越好,有一些系统还要人为编写额外的程序来减慢它扫描的速度。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
单片机程序的设计讲究的是思想,这是一个层次问题。一个泥瓦匠可以盖一栋两层小楼,但是绝不会设计出一栋高楼大厦。 写程序同样如此,思想层次的高低,最终会反映到程序的质量下。也许你说程序只要可以跑起来就可以了。管他什么结构思想。 这样的程序又有什么用呢?可读性,可移植性,模块化全都没有了。程序越大,越不好维护,越不好写。也许实现简单的功能没问题。在需求复杂时候还这样写程序,问题就会一大堆了。 实时性与你这里的数码管扫描没有一点关系,我前面已经提及了,实时性要求高,用中断处理。一个设计好的系统,不会说因为你用中断驱动了数码管,就会实时性有任何降低。另外数码管显示稳定,消鬼影,均不会需要用到延时。中断用延时,是极大地错误。你这样会严重误导初学者。怎么还能称艺高胆大???要知道,你的每段文字,都会影响到那些跟着你学习的初学者。他们未来会设计产品,也间接会受到你的影响。 至于按键扫描,一点都不高明。我先吃饭,完了再给你讲什么叫做按键程序设计。 |
|
|
|
|
|
|
|
|
谢谢你的热心,非常期待你跟大家分享你的按键扫描程序。 |
|
|
|
|
|
|
|
|
因为51单片机I/O口内部结构的限制,在读取外部引脚状态的时候,需要向端口写1.在51单片机复位后,不需要进行此操作也可以进行读取外部引脚的操作。因此,在按键的端口没有复用的情况下,可以省略此步骤。而对于其它一些真正双向I/O口的单片机来说,将引脚设置成输入状态,是必不可少的一个步骤。
下面的程序代码初始化引脚为输入。 void KeyInit(void) { io_key_1 = 1 ; io_key_2 = 1 ; io_key_3 = 1 ; io_key_4 = 1 ; } 根据按键硬件连接定义按键键值 #define KEY_VALUE_1 0x0e #define KEY_VALUE_2 0x0d #define KEY_VALUE_3 0x0b #define KEY_VALUE_4 0x07 #define KEY_NULL 0x0f 下面我们来编写按键的硬件驱动程序。 根据第一章所描述的按键检测原理,我们可以很容易的得出如下的代码: static uint8 KeyScan(void) { if(io_key_1 == 0)return KEY_VALUE_1 ; if(io_key_2 == 0)return KEY_VALUE_2 ; if(io_key_3 == 0)return KEY_VALUE_3 ; if(io_key_4 == 0)return KEY_VALUE_4 ; return KEY_NULL ; } 其中io_key_1等是我们按键端口的定义,如下所示: ***it io_key_1 = P3^0 ; ***it io_key_2 = P3^1 ; ***it io_key_3 = P3^2 ; ***it io_key_4 = P3^3 ; KeyScan()作为底层按键的驱动程序,为上层按键扫描提供一个接口,这样我们编写的上层按键扫描函数可以几乎不用修改就可以拿到我们的其它程序中去使用,使得程序复用性大大提高。同时,通过有意识的将与底层硬件连接紧密的程序和与硬件无关的代码分开写,使得程序结构层次清晰,可移植性也更好。对于单片机类的程序而言,能够做到函数级别的代码重用已经足够了。 在编写我们的上层按键扫描函数之前,需要先完成一些宏定义。 //定义长按键的TICK数,以及连_发间隔的TICK数 #define KEY_LONG_PERIOD 100 #define KEY_CONTINUE_PERIOD 25 //定义按键返回值状态(按下,长按,连_发,释放) #define KEY_DOWN 0x80 #define KEY_LONG 0x40 #define KEY_CONTINUE 0x20 #define KEY_UP 0x10 //定义按键状态 #define KEY_STATE_INIT 0 #define KEY_STATE_WOBBLE 1 #define KEY_STATE_PRESS 2 #define KEY_STATE_LONG 3 #define KEY_STATE_CONTINUE 4 #define KEY_STATE_RELEASE 5 接着我们开始编写完整的上层按键扫描函数,按键的短按,长按,连按,释放等等状态的判断均是在此函数中完成。对照状态流程转移图,然后再看下面的函数代码,可以更容易的去理解函数的执行流程。完整的函数代码如下: void GetKey(uint8 *pKeyValue) { static uint8 s_u8KeyState =KEY_STATE_INIT ; static uint8 s_u8KeyTimeCount = 0 ; static uint8 s_u8LastKey = KEY_NULL; //保存按键释放时候的键值 uint8 KeyTemp = KEY_NULL ; KeyTemp = KeyScan() ; //获取键值 switch(s_u8KeyState) { case KEY_STATE_INIT : { if(KEY_NULL !=(KeyTemp)) { s_u8KeyState =KEY_STATE_WOBBLE ; } } break ; case KEY_STATE_WOBBLE : //消抖 { s_u8KeyState =KEY_STATE_PRESS ; } break ; case KEY_STATE_PRESS : { if(KEY_NULL !=(KeyTemp)) { s_u8LastKey =KeyTemp ; //保存键值,以便在释放按键状态返回键值 KeyTemp |=KEY_DOWN ; //按键按下 s_u8KeyState =KEY_STATE_LONG ; } else { s_u8KeyState =KEY_STATE_INIT ; } } break ; case KEY_STATE_LONG : { if(KEY_NULL !=(KeyTemp)) { if(++s_u8KeyTimeCount > KEY_LONG_PERIOD) { s_u8KeyTimeCount = 0 ; KeyTemp |= KEY_LONG ; //长按键事件发生 s_u8KeyState= KEY_STATE_CONTINUE ; } } else { s_u8KeyState =KEY_STATE_RELEASE ; } } break ; case KEY_STATE_CONTINUE : { if(KEY_NULL !=(KeyTemp)) { if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD) { s_u8KeyTimeCount = 0 ; KeyTemp |=KEY_CONTINUE ; } } else { s_u8KeyState = KEY_STATE_RELEASE; } } break ; case KEY_STATE_RELEASE : { s_u8LastKey |= KEY_UP; KeyTemp = s_u8LastKey; s_u8KeyState = KEY_STATE_INIT; } break ; default : break ; } *pKeyValue = KeyTemp ; //返回键值 } 关于这个函数内部的细节我并不打算花过多笔墨去讲解。对照着按键状态流程转移图,然后去看程序代码,你会发现其实思路非常清晰。最能让人理解透彻的,莫非就是将整个程序自己看懂,然后想象为什么这个地方要这样写,抱着思考的态度去阅读程序,你会发现自己的程序水平会慢慢的提高。所以我更希望的是你能够认认真真的看完,然后思考。也许你会收获更多。 不管怎么样,这样的一个程序已经完成了本章开始时候要求的功能:按下,长按,连按,释放。事实上,如果掌握了这种基于状态转移的思想,你会发现要求实现其它按键功能,譬如,多键按下,功能键等等,亦相当简单,在下一章,我们就去实现它。 在主程序中我编写了这样的一段代码,来演示我实现的按键功能。 void main(void) { uint8 KeyValue = KEY_NULL; uint8 temp = 0 ; LED_CS11 = 1 ; //流水灯输出允许 LED_SEG = 0 ; LED_DIG = 0 ; Timer0Init() ; KeyInit() ; EA = 1 ; while(1) { Timer0MainLoop() ; KeyMainLoop(&KeyValue) ; if(KeyValue == (KEY_VALUE_1 |KEY_DOWN)) P0 = ~1 ; if(KeyValue == (KEY_VALUE_1 |KEY_LONG)) P0 = ~2 ; if(KeyValue == (KEY_VALUE_1 |KEY_CONTINUE)) { P0 ^= 0xf0;} if(KeyValue == (KEY_VALUE_1 |KEY_UP)) P0 = 0xa5 ; } } 按住第一个键,可以清晰的看到P0口所接的LED的状态的变化。当按键按下时候,第一个LED灯亮,等待2 S后第二个LED亮,第一个熄灭,表示长按事件发生。再过500 ms 第5~8个LED闪烁,表示连按事件发生。当释放按键时候,P0口所接的LED的状态为: 灭亮灭亮亮灭亮灭,这也正是P0 = 0xa5这条语句的功能。 评分
|
||
|
|
||
|
|
|
|
|
|
|
|
|
mculover 发表于 2013-11-9 14:17 打开你的链接后,我顿时感到非常惊喜和激动。 没想到《从单片机初学者迈向单片机工程师》的原创作者是你! 其实我以前就看过你这个帖子,当时我就感觉作者非常了不起,写得非常不错, 写得非常用心,命名也非常规范,而且图文并茂。我强烈建议你以后应该多在电子发烧友论坛发帖分享技 术,我也代表鸿哥欢迎你在电子发烧友论坛安家,我去过很多论坛,我认为电子发烧友论坛会给你很多惊 喜,这个论坛人气非常旺盛,管理员非常热心大度,技术牛人也非常多,卧虎藏龙。 关于技术方面的探讨,我相信我和你都是掌握了单片机编程精髓的人。我要跟你分享的经验是,电脑看 起来多任务并行处理,是多么令人向往的事情,但是你不要忘了,电脑的显卡,声卡,光驱每部分都是有 专用的单片机来驱动的,因为这些事情你必须一口气做完,不能有任何外界中断来干扰,不可能让你并行 处理,一台电脑本质上是“多核”的。单片机追求最大限度的分时处理,并行处理的结构是好事情,在这 一点上其实我本人的程序也是并行处理的风格,也涉及到状态机的思想。但是千万不要过于迷信并行处理 ,千万不要相信单片机能做出绝对精准的时间。我们做的东西最终只要满足用户需求就好。比如,你写的 LED闪烁程序,你在定时中断里用了一个标志位表示1ms定时标志,然后在main循环里累加500次时间就达 到了0.5秒,表面上看起来完美无缺,但是在有一些项目上你就不能这么做,因为这些任务是要你一口气 完成的,不允许分时并行处理,这时候你不能及时清零1ms的标志位,那么0.5秒就肯定不会准确的。至于按键扫描方面,在对比你的程 序之后,我仍然认为我的按键扫描程序会比你的略胜一筹。你批评说我模块化不强,其实我不这么认为, 我觉得我的程序结构思路是非常清晰的,模块化非常强,我的很多粉丝也这么认为。而且我用这种结构写的程序最大超过153K的容量,绝 对不像你所说的盖一两层房子那么简单,鸿哥多年来做的项目不计其数,功力深厚,超出你的想象,因为 鸿哥每天就是做新项目。我本人非常欣赏你,我觉得你是掌握编程精髓的人,而且命名比我的还要规范,比我 更加用心,很高兴认识你。 |
|
|
|
|
|
|
|
|
呵呵,关于编程方面,仁者见仁,智者见智。但是有些基本原则还是要坚守的。我们要知道这些原则适用的范围,然后才能在具体项目中灵活变通。不然直接告诉初学者这样用,而不讲清这样做的好处和缺点,这样导致的结果是程序出现问题时候,很难着手排除。我也很佩服你能够分享你的知识经验给初学者。 就像我当初一样,将很多经验分享出来。 另外我现在重心全部放在程序构架上面了。消息驱动,层次状态机方面,以及人机交互界面的可视化开发。编程方面的东西就不和你争论了,你提及的那些我以前全部经历过,明白你所想要表达的意思。 但是对于你的帖子中涉及到“原则性”的问题时候,我还是会给你鸡蛋挑骨头,希望你不要介意。这样是对看帖学习的人的负责与尊重。 我在龙华这边,有机会过来这边的时候告诉我一下,请你吃饭,认识下。方便的话,告诉一下你的联系方式给我。我的QQ:272402202 或者发邮件给我 power_mcu@163.com 告诉我你的联系方式,我会回邮件给你告诉我的联系方式。 |
|
|
|
|
|
|
|
|
我也来学习学习!!!
|
|
|
|
|
|
|
|
![]() ![]() {:1:}
|
|
|
|
|
|
|
|
只有小组成员才能发言,加入小组>>
求解外围电路实现的是4脚给持续低电平复位并正常工作,高电平不工作的原因
2381 浏览 1 评论
4238 浏览 3 评论
PIC1946程序有一个变量在运行过程中恢复初始值其他变量保持不变
2618 浏览 2 评论
3115 浏览 0 评论
PIC16F1825的RC5引脚,在主程序中操作无效,在中断中可以改变是为什么?
4572 浏览 5 评论
有套STM32与西门子200程序需要代写,有兴趣的工程师与有联系!
2485浏览 1评论
用XC8编译PIC18F25K80时提示下面Error,求怎么解决这个问题
6798浏览 0评论
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-13 06:28 , Processed in 1.250205 second(s), Total 105, Slave 87 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖