完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本帖最后由 friend0720 于 2016-2-25 17:23 编辑
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|
|
|
|
|
本帖最后由 friend0720 于 2016-2-25 17:23 编辑
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|
|
|
|
|
我的成长跟武林小说里的男主角很像,一开始功夫很平庸,偶然一次不小心掉入悬崖大难不死,在悬崖里闭关修炼两三年,打通了任督二脉,从此逍遥江湖。当初我一开始就是想发明一两个产品,然后赚点快钱,没想到产品开发出来后却赚不了大钱,然后我就被迫在外面做自由职业者,每天就是接项目开发养家糊口,不知不觉两三年过去了,这期间我接了很多大小项目,也遇到很多技术问题,但是都被我一一解决了,我的技术就自然而然就有所提升。就像你看到现在的一些一线唱歌明星,其实很多人当年出道的时候就是跑夜场,或者在露天的舞台卖唱,但是这些都练就了他们的功力。欲知详情,请看我另外一个分享贴《血与泪的发明创造之路》。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
本帖最后由 jianhong_wu 于 2013-4-3 18:53 编辑
第二十四节:两片联级的74HC595动态扫描8位数码管 (1)开场白: 我写的所有章节都是我亲身经历过的,但是这一节的灵感完全来源于电子发烧友Stone_up。他告诉我他曾经用两片联级74HC595动态扫描8位数码管,我仔细一想,用1片74HC595驱动数码管的8个COM,用另外1片74HC595驱动数码管的8个SEG,这种方法实在是巧妙,妙在特别省硬件资源,外围的电路也非常简单,也非常节省单片机的IO口,仅用3根单片机IO口就可以驱动8位数码管,这么巧妙的方法我之前 居然没有想到,实在是惭愧。在此特别点名表扬”Stone_up”的贡献。 (2)功能需求: 在8个数码管中,从左到右,依次显示“12345678”。 (3)硬件原理: 用单片机的3根IO口驱动2片联级的74HC595,其中1片74HC595的8个输出口分别串接470欧电阻之后连接到数码管的SEG引脚,另外1片74HC595的8个输出口分别经过NPN三极管(也可以用ULN2003A替代)之后连接到数码管的COM引脚。本程序是驱动8位的共阴数码管。 (4)源码适合的单片机: PIC18f4520,晶振为11.0592MHz。 (5)源代码讲解如下: #include //74HC595的OE引脚直接硬件接地 # define hc595_sh_dr LATA3 //74hc595的3根驱动IO之一 # define hc595_st_dr LATA4 //74hc595的3根驱动IO之一 # define hc595_ds_dr LATA5 //74hc595的3根驱动IO之一 #define const_left_com1 0x01 //左边第1个数码管的com显示 #define const_left_com2 0x02 //左边第2个数码管的com显示 #define const_left_com3 0x04 //左边第3个数码管的com显示 #define const_left_com4 0x08 //左边第4个数码管的com显示 #define const_left_com5 0x10 //左边第5个数码管的com显示 #define const_left_com6 0x20 //左边第6个数码管的com显示 #define const_left_com7 0x40 //左边第7个数码管的com显示 #define const_left_com8 0x80 //左边第8个数码管的com显示 #define const_left_com_off 0x00 //所有的数码管都不显示 void display_drive(); //数码管驱动程序,放在定时中断里 void hc595_drive(); //驱动2片联级的74hc595的子程序,放在display_drive()里 void _nop_(); //驱动时序延时函数 void delay1(unsigned int de); unsigned char number_left1=0; //左边第1位数码管显示的内容 unsigned char number_left2=0; //左边第2位数码管显示的内容 unsigned char number_left3=0; //左边第3位数码管显示的内容 unsigned char number_left4=0; //左边第4位数码管显示的内容 unsigned char number_left5=0; //左边第5位数码管显示的内容 unsigned char number_left6=0; //左边第6位数码管显示的内容 unsigned char number_left7=0; //左边第7位数码管显示的内容 unsigned char number_left8=0; //左边第8位数码管显示的内容 unsigned char number_temp[2]; //即将更新显示内容的中间变量 unsigned char dis_step=1; //扫描的步骤 const unsigned char number_table[]= //数码管的字模转换表 { 0xbe, //显示"0" 0x06, //显示"1" 0x7c, //显示"2" 0x5e, //显示"3" 0xc6, //显示"4" 0xda, //显示"5" 0xfa, //显示"6" 0x0e, //显示"7" 0xfe, //显示"8" 0xde, //显示"9" }; //主程序 main() { ADCON0=0x00; ADCON1=0x0f; //全部为数字信号 ADCON2=0xa1; //右对齐 RBPU=0; //上拉电阻 SSPEN=0; TRISA3=0; //74hc595的3根驱动IO之一 TRISA4=0; //74hc595的3根驱动IO之一 TRISA5=0; //74hc595的3根驱动IO之一 T1CON=0x24; //定时器中断配置 TMR1H=0xFE; TMR1L=0xEF; TMR1IF=0; TMR1IE=1; TMR1ON=1; TMR1IE=1; while(1) { CLRWDT(); //喂看门狗,大家不用过度关注此行 number_left1=1; //左边第1位数码管显示"1" number_left2=2; //左边第2位数码管显示"2" number_left3=3; //左边第3位数码管显示"3" number_left4=4; //左边第4位数码管显示"4" number_left5=5; //左边第5位数码管显示"5" number_left6=6; //左边第6位数码管显示"6" number_left7=7; //左边第7位数码管显示"7" number_left8=8; //左边第8位数码管显示"8" } } void interrupt timer1rbint(void) //中断 { if(TMR1IE==1&&TMR1IF==1) //定时中断 { TMR1IF=0; //定时中断标志位关闭 TMR1ON=0; //定时中断开关关闭 display_drive(); //数码管驱动程序 TMR1H=0xFe; //重新设置定时时间间隔 TMR1L=0x00; TMR1ON=1; //定时中断开关打开 } } void display_drive() //数码管驱动程序,放在定时中断里 { switch(dis_step) { case 1: number_temp[0]=const_left_com1; //载入即将显示的那位数码管的COM number_temp[1]=number_table[number_left1]; //载入即将显示的内容 break; case 2: number_temp[0]=const_left_com2; //载入即将显示的那位数码管的COM number_temp[1]=number_table[number_left2]; //载入即将显示的内容 break; case 3: number_temp[0]=const_left_com3; //载入即将显示的那位数码管的COM number_temp[1]=number_table[number_left3]; //载入即将显示的内容 break; case 4: number_temp[0]=const_left_com4; //载入即将显示的那位数码管的COM number_temp[1]=number_table[number_left4]; //载入即将显示的内容 break; case 5: number_temp[0]=const_left_com5; //载入即将显示的那位数码管的COM number_temp[1]=number_table[number_left5]; //载入即将显示的内容 break; case 6: number_temp[0]=const_left_com6; //载入即将显示的那位数码管的COM number_temp[1]=number_table[number_left6]; //载入即将显示的内容 break; case 7: number_temp[0]=const_left_com7; //载入即将显示的那位数码管的COM number_temp[1]=number_table[number_left7]; //载入即将显示的内容 break; case 8: number_temp[0]=const_left_com8; //载入即将显示的那位数码管的COM number_temp[1]=number_table[number_left8]; //载入即将显示的内容 break; } hc595_drive(); //驱动2片联级的74hc595的子程序 delay1(15); //每一位数码管显示的停留时间 number_temp[0]=number_table[const_left_com_off]; //让所有的数码管都不显示,让显示效果更好 number_temp[1]=0x00; //载入即将显示的空内容 hc595_drive(); //驱动2片联级的74hc595的子程序 ++dis_step; //进入下一个数码管的扫描 if(dis_step>8) { dis_step=1; } } void hc595_drive() //驱动2片联级的74hc595的子程序 { unsigned char tempdata; //每个字节的每一位,共8位 unsigned char com_select; //中间变量 unsigned char tube_cnt; //数码管个数,共2个 hc595_sh_dr=0; hc595_st_dr=0; for(tube_cnt=2;tube_cnt!=0;tube_cnt--) //2个74hc595 { com_select=number_temp[tube_cnt-1]; for(tempdata=0;tempdata<8;tempdata++) //每个8位 { CLRWDT(); if(com_select>=0x80)hc595_ds_dr=1; else hc595_ds_dr=0; hc595_sh_dr=0; _nop_(); _nop_(); hc595_sh_dr=1; _nop_(); _nop_(); com_select<<=1; } } hc595_st_dr=0; _nop_(); _nop_(); hc595_st_dr=1; _nop_(); _nop_(); hc595_sh_dr=0; //拉低,抗干扰就增强 hc595_st_dr=0; hc595_ds_dr=0; } void _nop_() //驱动时序延时函数 { unsigned char n; for(n=0;n<0x0f;n++); } void delay1(unsigned int de) { unsigned int t; for(t=0;t (6)小结: 本程序我没有经过实际电路验证,纯粹凭我个人的内功直接编写出来,如果读者在实际应用中发现我的程序走不通,请不要死磕,应该自己想办法改进和解决。 (7)下集预告: 专用集成芯片TM1639驱动8位数码管。 (未完待续,下节更精彩,不要走开哦) |
|
|
|
|
|
第二十五节:专用集成芯片TM1639驱动8位数码管
(1)开场白: 在那么多种数码管的驱动方案中,我最喜欢使用专用集成芯片TM1639来驱动数码管。我认为这种方案是性价比最高的。既然是专用的集成芯片,那么我们可以把TM1639看成是一个独立的单片机,这个单片机也是利用动态扫描数码管的办法,只是厂家出厂时就已经把扫描驱动数码管的程序都写好了,并且烧录进了TM1639,他们预留3根IO口出来让外部的单片机跟TM1639通讯,我们编写程序时,只要按照厂家提供的通讯协议,往里面写入对应的指令和数据就可以间接静态驱动数码管。注意,我这里提到了“动态驱动”和“静态驱动”的概念,TM1639直接驱动数码管是属于动态的,但是我们编写程序跟TM1639通讯,这种间接驱动数码管是属于静态驱动的,因为我们的单片机不用定时扫描数码管。总之,我认为TM1639有4个好处: (a)节省单片机IO口,3根IO口就可以控制8位数码管. (b)CPU开销小。对于单片机来说,是属于静态驱动控制。 (c)外围电路简单,连串接的限流电阻和三极管也不用了,因为此芯片内部集成了8级灰度调节电路,我们 只要发送相应的指令,就可以调节数码管的亮度。 (d)这个芯片本身价格不算高。 (2)功能需求: 在8个数码管中,从左到右,依次显示“12345678”。 (3)硬件原理: 用单片机的3根IO口跟TM1639通讯。TM1639跟数码管的电路特别简单,不用串电阻,也不用加三极管,具体的电路与通讯协议请读者自己在网上下载,资料很容易获得,而且还是中文的。本程序是驱动8位的共阴数码管。 (4)源码适合的单片机: PIC18f4520,晶振为11.0592MHz。 (5)源代码讲解如下: #include //tm1639的IO宏定义 #define tm1639_stb_dr LATA3 #define tm1639_clk_dr LATA4 #define tm1639_dio_dr LATA5 //位地址,共8位地址,每位地址里面的1个字节的数据代表8个LED灯的亮或灭 #define const_dig_addr0 0xc0 #define const_dig_addr1 0xc2 #define const_dig_addr2 0xc4 #define const_dig_addr3 0xc6 #define const_dig_addr4 0xc8 #define const_dig_addr5 0xca #define const_dig_addr6 0xcc #define const_dig_addr7 0xce //数码管或者LED灯的显示亮度级别 #define const_level_off 0x80 //级别最低,最暗 #define const_level_1 0x88 #define const_level_2 0x89 #define const_level_4 0x8a //本程序采取这个级别的亮度 #define const_level_10 0x8b #define const_level_11 0x8c #define const_level_12 0x8d #define const_level_13 0x8e #define const_level_14 0x8f //级别最高,最亮 #define const_auto_add_addr 0x40 //采用地址自动加一方式写显存 void write_byte_tm1639(unsigned char tm1639_byte); //写入一个字节数据进tm1639 void display_tm1639(unsigned char dig_addr,unsigned char dig_data,unsigned char dig_level); //一次显示8个LED灯或者一位数码管 //其中dig_addr是地址,dig_data是地址的数据,dig_level是亮度 void display_drive(); //数码管驱动程序,放在main函数的while(1)循环里 unsigned char number_left1=0; //左边第1位数码管显示的内容 unsigned char number_left2=0; //左边第2位数码管显示的内容 unsigned char number_left3=0; //左边第3位数码管显示的内容 unsigned char number_left4=0; //左边第4位数码管显示的内容 unsigned char number_left5=0; //左边第5位数码管显示的内容 unsigned char number_left6=0; //左边第6位数码管显示的内容 unsigned char number_left7=0; //左边第7位数码管显示的内容 unsigned char number_left8=0; //左边第8位数码管显示的内容 unsigned char led_update=0; //更新显示变量,等于1时将执行一次更新显示数码管的程序 unsigned char number_temp[8]; //即将更新显示内容的中间变量 const unsigned char number_table[]= //数码管的字模转换表 { 0x3F, //0 0x06, //1 0x5B, //2 0x4F, //3 0x66, //4 0x6D, //5 0x7D, //6 0x07, //7 0x7F, //8 0x6F, //9 }; //主程序 main() { ADCON0=0x00; ADCON1=0x0f; //全部为数字信号 ADCON2=0xa1; //右对齐 RBPU=0; //上拉电阻 SSPEN=0; TRISA3=0; //TM1639的3根驱动IO之一 TRISA4=0; //TM1639的3根驱动IO之一 TRISA5=0; //TM1639的3根驱动IO之一 number_left1=1; //左边第1位数码管显示"1" number_left2=2; //左边第2位数码管显示"2" number_left3=3; //左边第3位数码管显示"3" number_left4=4; //左边第4位数码管显示"4" number_left5=5; //左边第5位数码管显示"5" number_left6=6; //左边第6位数码管显示"6" number_left7=7; //左边第7位数码管显示"7" number_left8=8; //左边第8位数码管显示"8" led_update=1; //更新显示 while(1) { CLRWDT(); //喂看门狗,大家不用过度关注此行 display_drive(); //数码管驱动程序,放在main函数的while(1)循环里 } } void display_drive() //数码管驱动程序,放在main函数的while(1)循环里 { if(led_update==1) //有数据更新 { led_update=0; //标志及时清零,避免一直扫描 number_temp[0]=number_table[number_left1]; //载入即将显示的内容 number_temp[1]=number_table[number_left2]; //载入即将显示的内容 number_temp[2]=number_table[number_left3]; //载入即将显示的内容 number_temp[3]=number_table[number_left4]; //载入即将显示的内容 number_temp[4]=number_table[number_left5]; //载入即将显示的内容 number_temp[5]=number_table[number_left6]; //载入即将显示的内容 number_temp[6]=number_table[number_left7]; //载入即将显示的内容 number_temp[7]=number_table[number_left8]; //载入即将显示的内容 display_tm1639(const_dig_addr0,number_temp[0],const_level_4); //显示左边第1位 display_tm1639(const_dig_addr1,number_temp[1],const_level_4); //显示左边第2位 display_tm1639(const_dig_addr2,number_temp[2],const_level_4); //显示左边第3位 display_tm1639(const_dig_addr3,number_temp[3],const_level_4); //显示左边第4位 display_tm1639(const_dig_addr4,number_temp[4],const_level_4); //显示左边第5位 display_tm1639(const_dig_addr5,number_temp[5],const_level_4); //显示左边第6位 display_tm1639(const_dig_addr6,number_temp[6],const_level_4); //显示左边第7位 display_tm1639(const_dig_addr7,number_temp[7],const_level_4); //显示左边第8位 } } //写入一个字节数据进tm1639 void write_byte_tm1639(unsigned char tm1639_byte) { unsigned char tm1639_i; tm1639_stb_dr=0; //stb为低电平,程序不依赖于之前端口的状态,避免出现“端口迷失” for(tm1639_i=0;tm1639_i<8;tm1639_i++) { tm1639_clk_dr=0; if((tm1639_byte & 0x01)!=0) tm1639_dio_dr=1; else tm1639_dio_dr=0; tm1639_clk_dr=1; tm1639_byte=tm1639_byte>>1; } } //一次显示8个LED灯或者一位数码管 void display_tm1639(unsigned char dig_addr,unsigned char dig_data,unsigned char dig_level) { tm1639_dio_dr=1; tm1639_clk_dr=1; tm1639_stb_dr=1; write_byte_tm1639(const_auto_add_addr); //设置成自动加一方式 tm1639_stb_dr=1; write_byte_tm1639(dig_addr); //设置显示的位地址 write_byte_tm1639(dig_data & 0x0f); //显示的低4位数据,共代表4个led灯 write_byte_tm1639(dig_data >> 4 & 0x0f); //显示的高4位数据,共代表4个led灯 tm1639_stb_dr=1; write_byte_tm1639(dig_level); //设置显示的亮度 tm1639_stb_dr=1; } (6)小结: 前面花了几个章节来讲常用的数码管驱动方式,下一节将要讲一下操作数码管显示的基本程序框架。 (7)下集预告: 按键操作数码管菜单的基本程序框架。 (未完待续,下节更精彩,不要走开哦) |
|
|
|
|
|
只有小组成员才能发言,加入小组>>
求解外围电路实现的是4脚给持续低电平复位并正常工作,高电平不工作的原因
2077 浏览 1 评论
3505 浏览 3 评论
PIC1946程序有一个变量在运行过程中恢复初始值其他变量保持不变
2333 浏览 2 评论
2760 浏览 0 评论
PIC16F1825的RC5引脚,在主程序中操作无效,在中断中可以改变是为什么?
4015 浏览 5 评论
967浏览 0评论
用XC8编译PIC18F25K80时提示下面Error,求怎么解决这个问题
6350浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-28 06:15 , Processed in 0.944095 second(s), Total 89, Slave 80 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号