突发奇想,于是便写了一个小程序用于控制台灯,这几天功能也在不断的完善中,目前基本已经完成.下面进行功能的简述的代码的分析.
整体设计包含下位机程序和上位机程序.下位机用的c语言,上位机用的c# .功能显示见视频
整个系统功能包括:定时采集室温在电脑右下角显示,可联网校准电子时钟,可以电脑端快捷键控制台灯.视频中展示的顺序为
1,自动获取温度,图标动态显示室温 2,手动获取温度 3,按钮控制台灯 4 ,快捷键控制台灯 5,联网校准电子时钟 6最后展示
在任何界面只要按下快捷键便可以打开台灯(windows hook).
下面进行整个系统代码和原理的介绍.
下位机,
硬件上 ,包括 51单片机,ds1302,18b20,uln2003,pl2303 .硬件连接图如下:
下位机程序分析:
1 /************************************************************************/ 2 // 本程序作者 HennSun ,转载请表明出处. 3 // 4 /************************************************************************/ 5 #include <reg52.h> 6 #include "uart.h" 7 #include "18b20.h" 8 #include "ds1302.h" 9 #define buff_size 5 10 sbit jdq=P1^0; 11 sbit key_led=P3^3; 12 unsigned int num; 13 unsigned char control=0,set_time=0; 14 unsigned char time[buff_size]; //用于设定时间 15 extern unsigned int temp; 16 extern unsigned char flag_get; 17 18 /*----------初始化定时器---------*/ 19 20 void init_timer() 21 { 22 TMOD =0x01;//定时器设置 T0工作于方式1 16位 23 TH0=0xef; 24 TL0=0xf0; 25 ET0=1; //定时器 0 中断允许 . 26 TR0=1; // run the timer0 这个中断会让单片机查询中断向量表. 27 EA = 1; //打开总中断 28 } 29 30 //显示 open 字符 31 void disp_open() 32 { 33 unsigned int i=400; 34 while(i--) 35 { 36 P2=2; 37 P0=0x3f; //'O' 38 delay1(1); 39 P0=0X00; 40 41 P2=3; 42 P0=0x73; //'P' 43 delay1(1); 44 P0=0X00; 45 46 P2=4; 47 P0=0x79; //'E' 48 delay1(1); 49 P0=0X00; 50 51 P2=5; 52 P0=0x37; //'N' 53 delay1(1); 54 P0=0X00; 55 } 56 57 } 58 // 显示close 字符 59 void disp_close() 60 { 61 unsigned int i=400; 62 while(i--) 63 { 64 P2=2; 65 P0=0x39; //'C' 66 delay1(1); 67 P0=0X00; 68 69 P2=3; 70 P0=0x38; //'L' 71 delay1(1); 72 P0=0X00; 73 74 P2=4; 75 P0=0x3f; //'O' 76 delay1(1); 77 P0=0X00; 78 79 P2=5; 80 P0=0x6d; //'S' 81 delay1(1); 82 P0=0X00; 83 84 P2=6; 85 P0=0x79; //'E' 86 delay1(1); 87 P0=0X00; 88 } 89 90 } 91 92 93 /*---------------------------------------------------------------------------------------- 94 这是主函数部分 95 --------------------------------------------------------------------------------------*/ 96 void main() 97 { 98 unsigned int TempH,TempL; 99 unsigned char H, L ; 100 jdq=0; 101 init_timer(); 102 init_ds1302(); 103 UARTinit(); 104 105 while(1) 106 { 107 if(flag_get) //定时读取当前温度 108 { 109 110 temp=ReadTemperature(); //这个函数8ms 111 112 if(temp&0x8000) 113 { 114 temp=~temp; // 取反加1 115 temp +=1; 116 } 117 TempH=temp>>4; 118 TempL=temp&0x0F; 119 TempL=TempL*6/10;//小数近似处理 120 121 H=(unsigned char)TempH; 122 L=(unsigned char)TempL; 123 /**/ 124 send_char_com('a'); 125 send_char_com(H); 126 send_char_com(L); 127 send_char_com('e'); 128 flag_get=0; 129 130 } //这个循环用11ms 131 if(control) 132 { 133 jdq=~jdq; 134 if(jdq) 135 { 136 send_char_com('o'); 137 send_char_com('e'); 138 disp_open(); 139 } 140 else 141 { 142 send_char_com('c'); 143 send_char_com('e'); 144 disp_close(); 145 } 146 control=0; 147 } 148 if(set_time) 149 { 150 if((time[1]<0x60)||(time[2]<0x60)&&(time[3]<0x24)) 151 { 152 set_ds1302(time[1],time[2],time[3]); // s , m ,h 153 //不知为何时间定时器自动停止. ,数组越界 154 set_time=0; 155 } 156 } 157 if(!key_led) 158 { 159 control=1; 160 } 161 //添加时钟显示代码 162 get_ds1302(); //2.23ms 163 } //不进第一个if 时间为 2ms 进入 13ms 这里指周期 164 } 165 166 /******************************************************************/ 167 /* 定时器中断 */ 168 /******************************************************************/ 169 void tim(void) interrupt 1 using 1//中断用于温度检测 170 { 171 TR0=0; //关闭定时器 172 TH0=0x0f;//定时器重装值 定时器有没有中断标志位??,未清零? 173 TL0=0x00; 174 flag_get=1;//标志位有效 175 } 176 177 178 179 180 void UART_SER() interrupt 4 181 { 182 unsigned char Temp; 183 static unsigned char flag=0; 184 if(RI) 185 { 186 RI=0; 187 Temp=SBUF; 188 switch(Temp) 189 { 190 case 's': 191 control=1; 192 break; 193 case 't': 194 //set_time=1; 195 flag=buff_size; 196 break; 197 case 'w': 198 TR0=1; 199 break; 200 default: 201 break; 202 } 203 if(flag) 204 { 205 flag--; 206 time[flag]=Temp; //全局变量 207 if(time[0]=='e') 208 { 209 time[0]=0x11; 210 set_time=1; 211 } 212 } 213 } 214 215 }
18b20.c
1 #include "reg52.h" 2 #include "18b20.h" 3 /******************************************************************/ 4 /* 定义端口 */ 5 /******************************************************************/ 6 sbit DQ=P1^3;//ds18b20 端口 7 unsigned int temp; 8 unsigned char flag_get=0 ; 9 10 11 /******************************************************************/ 12 /* 延时函数 */ 13 /******************************************************************/ 14 void delay(unsigned int i)//延时函数 15 { 16 while(i--); 17 } 18 19 /******************************************************************/ 20 /* 初始化 */ 21 /******************************************************************/ 22 void Init_DS18B20(void) 23 { 24 unsigned char x=0; 25 DQ = 1; //DQ复位 26 delay(8); //稍做延时 27 DQ = 0; //单片机将DQ拉低 28 delay(80); //精确延时 大于 480us 29 DQ = 1; //拉高总线 30 delay(10); 31 x=DQ; //稍做延时后 如果x=0则初始化成功 x=1则初始化失败 32 delay(5); 33 } 34 35 /******************************************************************/ 36 /* 读一个字节 */ 37 /******************************************************************/ 38 unsigned char ReadOneChar(void) 39 { 40 unsigned char i=0; 41 unsigned char dat = 0; 42 for (i=8;i>0;i--) 43 { 44 DQ = 0; // 给脉冲信号 45 dat>>=1; 46 DQ = 1; // 给脉冲信号 47 if(DQ) 48 dat|=0x80; 49 delay(5); 50 } 51 return(dat); 52 } 53 54 55 /******************************************************************/ 56 /* 写一个字节 */ 57 /******************************************************************/ 58 void WriteOneChar(unsigned char dat) 59 { 60 unsigned char i=0; 61 for (i=8; i>0; i--) 62 { 63 DQ = 0; 64 DQ = dat&0x01; 65 delay(5); 66 DQ = 1; 67 dat>>=1; 68 } 69 delay(5); 70 } 71 72 73 /******************************************************************/ 74 /* 读取温度 */ 75 /******************************************************************/ 76 unsigned int ReadTemperature(void) 77 { 78 unsigned char a=0; 79 unsigned int b=0; 80 unsigned int t=0; 81 Init_DS18B20(); 82 WriteOneChar(0xCC); // 跳过读序号列号的操作 83 WriteOneChar(0x44); // 启动温度转换 84 delay(200); 85 Init_DS18B20(); 86 WriteOneChar(0xCC); //跳过读序号列号的操作 87 WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度 88 a=ReadOneChar(); //低位 89 b=ReadOneChar(); //高位 90 91 b<<=8; 92 t=a+b; 93 94 return(t); 95 }
ds1302.c
1 #include <reg52.h> 2 #include "ds1302.h" 3 4 //===以下IO定义请根据您硬件的连接修改=== 5 sbit T_RST=P3^5;//ds1302-5 6 sbit T_IO=P3^4;//ds1302-6 7 sbit T_CLK=P3^6;//ds1302-7 8 sbit ACC0=ACC^0; 9 sbit ACC7=ACC^7;//累加器A 51单片机原理中有介绍 10 11 unsigned char a,b,clock_ss,clock_sg,clock_fs,clock_fg,clock_ms,clock_mg; 12 13 int hour,mie,sei; 14 15 unsigned char clk_time[3]; //秒,分,时寄存器初始值 16 code unsigned char ledmap[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40}; 17 //数码管段码 18 19 /******************DS1302:写入操作(上升沿)*********************/ 20 void write_byte(unsigned char da) 21 { 22 unsigned char i; 23 ACC=da;//10000001 24 for(i=8;i>0;i--) 25 { 26 T_IO=ACC0; 27 T_CLK=0; 28 T_CLK=1; 29 ACC=ACC>>1;//01000000 30 } 31 } 32 /******************DS1302:读取操作(下降沿)*****************/ 33 unsigned char read_byte(void) 34 { 35 unsigned char i; 36 for(i=0;i<8;i++)//00000001 假设ACC=00000000 37 { 38 ACC=ACC>>1;//01000000 39 T_CLK = 1; 40 T_CLK = 0; 41 ACC7 = T_IO;//10000000 42 } 43 return(ACC); 44 45 } 46 47 /******************DS1302:写入数据(先送地址,再写数据)***************************/ 48 void write_1302(unsigned char addr,unsigned char da) 49 { 50 T_RST=0; //停止工作 51 T_CLK=0; 52 T_RST=1; //重新工作 53 write_byte(addr); //写入地址 54 55 write_byte(da); 56 T_RST=0; 57 T_CLK=1; 58 } 59 60 /******************DS1302:读取数据(先送地址,再读数据)**************************/ 61 unsigned char read_1302(unsigned char addr) 62 { 63 unsigned char temp; 64 T_RST=0; //停止工作 65 T_CLK=0; 66 T_RST=1; //重新工作 67 write_byte(addr); //写入地址 68 temp=read_byte(); 69 T_RST=0; 70 T_CLK=1; //停止工作 71 return(temp); 72 } 73 74 75 /***********************延时程序=a*1ms**************************************/ 76 void delay1(unsigned char a) 77 { 78 unsigned int i; 79 while(a-- !=0) 80 { // led_disp(); 81 for(i=0;i<12;i++); //12 82 } 83 } 84 85 86 /***********************显示程序**********************************************/ 87 88 /* DS1302秒,分,时寄存器是BCD码形式: 用16求商和余进行"高4位"和"低4位"分离 */ 89 /****************************************************************************/ 90 void led_disp() 91 { 92 clock_ms=clk_time[0]/ 16; 93 clock_mg=clk_time[0]%16; 94 95 clock_fs=clk_time[1]/ 16; 96 clock_fg=clk_time[1]%16; 97 98 mie=clock_fs*10+ clock_fg; //这个有什么用? 99 100 clock_ss=clk_time[2]/ 16; 101 clock_sg=clk_time[2]%16;//BCD*to*10 102 hour=clock_ss*10+ clock_sg; //用16求商和余进行"高4位"和"低4位"分离 103 104 P2=0; 105 P0=ledmap[clock_ss]; 106 delay1(1); 107 P0=0X00; 108 P2=1; 109 P0=ledmap[clock_sg];//时个位 110 delay1(1); 111 P0=0X00; 112 P2=2; 113 P0=ledmap[10];//显示"-"数组里的 0x40 114 delay1(1); 115 P0=0X00; 116 P2=3; 117 P0=ledmap[clock_fs];//分十位 118 delay1(1); 119 P0=0X00; 120 P2=4; 121 P0=ledmap[clock_fg];//时个位 122 delay1(1);P0=0X00; 123 P2=5; 124 P0=ledmap[10];//显示"-"数组里的 0x40 125 delay1(1); 126 P0=0X00; 127 P2=6; 128 P0=ledmap[clock_ms];//秒十位 129 delay1(1); 130 P0=0X00; 131 P2=7; 132 P0=ledmap[clock_mg]; 133 delay1(1); 134 P0=0X00; 135 } 136 void init_ds1302() 137 { 138 write_1302(0x8e,0x80); //WP=1 写保护,加上这个操作之后断电重新上电是正确的时间. 139 } 140 void get_ds1302() 141 { 142 unsigned char temp=0x81; 143 unsigned char i; 144 for(i=0;i<3;i++)//分别把秒分时数据读出分3次读好一次地址加2" temp+=2;" 145 { 146 clk_time[i]=read_1302(temp); 147 temp+=2; 148 } 149 led_disp(); 150 } 151 //添加设置时间的代码 ,结合key 152 void set_ds1302(unsigned m,unsigned char f,unsigned char s) 153 { 154 write_1302(0x8e,0x00); //WP=0 写操作 155 /**/ 156 write_1302(0x80,m);//0x80是写秒数据此处写进"01"秒 157 write_1302(0x82,f);//0x82是写分数据 158 write_1302(0x84,s);//0x84是写时数据 159 160 write_1302(0x8e,0x80); //WP=1 写保护 161 delay1(255); 162 }
串口部分程序没有比较好写,我便不贴上来了.程序往上位机传输数据时,我这里没有考虑负数.所以如果需要使用本程序需要注意这一点.
这个是下位机程序开发日志:
已经实现串口控制台灯的开关. 下面添加串口测温功能 2013.11.22 目前系统有个奇怪的问题, 不能打开串口接收中断但是不发送 ,且发送不能少于2个byte . 当关闭ES 时不会出现这个问题 . 在初始化时 TI 置位会引用跳到串口接收中断部分 ,稍过后没有此问题. 每次发生 接收中断时系统运算减慢,过片刻正常. 2013.11.22 设置日期的格式 7423570965 't''h''m''s''e' 发送的数据 均为 0x00格式 比如说 设置为 20:00:01 则发送 't''0x20''0x00''0x01''e' 关于首次初始化问题 思路是将1302的某个寄存器定义为是否首次开机检测标志,比如存入0xaa数值。 上电时读取1302的这个寄存器,如果是0xaa,说明不是首次,便不再初始化,否则初始化,并向开机定义的寄存器中写入0xaa。 我这里直接对写保护位写 1. 2013.11.23 添加发送'w'获取温度 返回的数据 'a''H''L''e' 2013.11.24 添加返回台灯状态 'c''e' 'o''e' 添加握手信息 开机是请求获取一次温度 , 2013.11.24 添加 修改 second 2013.11.26 上述提到问题已经更正
上位机程序分析,程序界面如图:
上位机程序有几个我自认为的亮点. 1,可以联网校准电子时钟. 2,可以在右下角显示温度 . 3,可以在任何状态下按下快捷键控制台灯.这里主要讲解这三部分功能.我这里把打包好的程序共享给大家,有需要源码的可以在下面留言.
亮点功能1,联系校准电子时钟:
private void set_auto_Click(object sender, EventArgs e) { set_auto.Enabled = false; //关闭按键 set_time.Enabled = false; WebRequest wrt = null; WebResponse wrp = null; try { wrt = WebRequest.Create("http://www.time.ac.cn/timeflash.asp?user=flash"); wrt.Credentials = CredentialCache.DefaultCredentials; wrp = wrt.GetResponse(); StreamReader sr = new StreamReader(wrp.GetResponseStream(), Encoding.UTF8); string html = sr.ReadToEnd(); sr.Close(); wrp.Close(); int yearIndex = html.IndexOf("<year>") + 6; int monthIndex = html.IndexOf("<month>") + 7; int dayIndex = html.IndexOf("<day>") + 5; int hourIndex = html.IndexOf("<hour>") + 6; int miniteIndex = html.IndexOf("<minite>") + 8; int secondIndex = html.IndexOf("<second>") + 8; string year = html.Substring(yearIndex, html.IndexOf("</year>") - yearIndex); string month = html.Substring(monthIndex, html.IndexOf("</month>") - monthIndex); ; string day = html.Substring(dayIndex, html.IndexOf("</day>") - dayIndex); string hour = html.Substring(hourIndex, html.IndexOf("</hour>") - hourIndex); string minite = html.Substring(miniteIndex, html.IndexOf("</minite>") - miniteIndex); string second = html.Substring(secondIndex, html.IndexOf("</second>") - secondIndex); set_time_function(hour,minite,second); textBox1.Text = hour + ":" + minite + ":" + second; } catch (WebException web) { MessageBox.Show(web.Message); } catch (Exception xx) { MessageBox.Show(xx.Message); } finally { if (wrp != null) wrp.Close(); if (wrt != null) wrt.Abort(); } set_auto.Enabled = true; //打开按键 set_time.Enabled = true; }
这个功能代码我是参考下面[参考博客]部分的程序 ,实现了网络时间的获取,获取的格式为string 类型.这里如果想进行ds1302时间的设置,需要进行一下转码,方法如下:
1 /// <summary> 2 /// 这里进行编码转化 ,ds1302 每一位是16进制数据.如果你想设置成12点, 需要发送 0x12 . 3 /// </summary> 4 /// <param name="code"></param> 5 /// <returns></returns> 6 private byte Transcoding(string code ) 7 { 8 byte[] bit = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 9 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19, 10 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29, 11 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39, 12 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49, 13 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59}; 14 return bit[int.Parse(code)<60 ? int.Parse(code):59] ; 15 }
亮点二,右下角动态显示温度
这个代码直接用的这个作者的http://www.codeproject.com/Articles/9361/WeatherNotify ,有兴趣的可以直接到这里查看.
亮点三,c# hook
代码参考第五条引用.原程序是有一点小错误,这里的更正方法如下:
在globalKeyboardHook.cs 文件中定义的委托下面添加这一句 private keyboardHookProc _keyboardHookProc; //新添加的
在 hook() 方法体中的代码更改为
1 /* 2 IntPtr hInstance = LoadLibrary("User32"); 3 hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0); 4 */ 5 //把引掉的部分更改为下面的 6 IntPtr hInstance = LoadLibrary("User32"); 7 _keyboardHookProc = new keyboardHookProc(hookProc); 8 hhook = SetWindowsHookEx(WH_KEYBOARD_LL, _keyboardHookProc, hInstance, 0);
如果想按下快捷键不对其它的程序产生影响,可以去掉这个方法体 public int hookProc(int code, int wParam, ref keyboardHookStruct lParam){}中的这一句
/*
if (kea.Handled)
return 1;
*/
好了,整个系统差不多介绍完了.虽然程序不太,但涉及到许多的知识 .需要上位机的源代码的可以留言.
博文为本人所写,转载请表明出处 电脑控制台灯
参考博客:
http://www.cnblogs.com/slyzly/articles/2108877.html
http://www.codeproject.com/Articles/9361/WeatherNotify
http://www.cnblogs.com/hocylan/archive/2008/01/14/1038390.html
http://bbs.csdn.net/topics/100169052
http://www.codeproject.com/Articles/19004/A-Simple-C-Global-Low-Level-Keyboard-Hook