您的位置:首页 > 编程语言 > C#

电脑控制台灯(c# hook,显示室温,联网校正时间)

2013-11-24 19:36 477 查看
  突发奇想,于是便写了一个小程序用于控制台灯,这几天功能也在不断的完善中,目前基本已经完成.下面进行功能的简述的代码的分析.

整体设计包含下位机程序和上位机程序.下位机用的c语言,上位机用的c# .功能显示见视频

整个系统功能包括:定时采集室温在电脑右下角显示,可联网校准电子时钟,可以电脑端快捷键控制台灯.视频中展示的顺序为

1,自动获取温度,图标动态显示室温 2,手动获取温度 3,按钮控制台灯 4 ,快捷键控制台灯 5,联网校准电子时钟 6最后展示

在任何界面只要按下快捷键便可以打开台灯(windows hook).

下面进行整个系统代码和原理的介绍.

下位机,

硬件上 ,包括 51单片机,ds1302,18b20,uln2003,pl2303 .硬件连接图如下:







下位机程序分析:

/************************************************************************/
//     本程序作者 HennSun ,转载请表明出处.
//
/************************************************************************/
#include <reg52.h>
#include "uart.h"
#include "18b20.h"
#include "ds1302.h"
#define buff_size 5
sbit jdq=P1^0;
sbit key_led=P3^3;
unsigned int num;
unsigned char control=0,set_time=0;
unsigned char time[buff_size];           //用于设定时间
extern unsigned int temp;
extern unsigned char flag_get;

/*----------初始化定时器---------*/

void init_timer()
{
TMOD =0x01;//定时器设置     T0工作于方式1 16位
TH0=0xef;
TL0=0xf0;
ET0=1;       //定时器 0 中断允许 .
TR0=1;        // run the timer0     这个中断会让单片机查询中断向量表.
EA    = 1;                  //打开总中断
}

//显示 open 字符
void disp_open()
{
unsigned int i=400;
while(i--)
{
P2=2;
P0=0x3f;  //'O'
delay1(1);
P0=0X00;

P2=3;
P0=0x73;  //'P'
delay1(1);
P0=0X00;

P2=4;
P0=0x79;   //'E'
delay1(1);
P0=0X00;

P2=5;
P0=0x37;   //'N'
delay1(1);
P0=0X00;
}

}
// 显示close 字符
void disp_close()
{
unsigned int i=400;
while(i--)
{
P2=2;
P0=0x39;  //'C'
delay1(1);
P0=0X00;

P2=3;
P0=0x38;  //'L'
delay1(1);
P0=0X00;

P2=4;
P0=0x3f;   //'O'
delay1(1);
P0=0X00;

P2=5;
P0=0x6d;   //'S'
delay1(1);
P0=0X00;

P2=6;
P0=0x79;   //'E'
delay1(1);
P0=0X00;
}

}

/*----------------------------------------------------------------------------------------
这是主函数部分
--------------------------------------------------------------------------------------*/
void main()
{
unsigned int TempH,TempL;
unsigned char H, L ;
jdq=0;
init_timer();
init_ds1302();
UARTinit();

while(1)
{
if(flag_get)       //定时读取当前温度
{

temp=ReadTemperature();   //这个函数8ms

if(temp&0x8000)
{
temp=~temp;  // 取反加1
temp +=1;
}
TempH=temp>>4;
TempL=temp&0x0F;
TempL=TempL*6/10;//小数近似处理

H=(unsigned char)TempH;
L=(unsigned char)TempL;
/**/
send_char_com('a');
send_char_com(H);
send_char_com(L);
send_char_com('e');
flag_get=0;

}     //这个循环用11ms
if(control)
{
jdq=~jdq;
if(jdq)
{
send_char_com('o');
send_char_com('e');
disp_open();
}
else
{
send_char_com('c');
send_char_com('e');
disp_close();
}
control=0;
}
if(set_time)
{
if((time[1]<0x60)||(time[2]<0x60)&&(time[3]<0x24))
{
set_ds1302(time[1],time[2],time[3]);   // s , m ,h
//不知为何时间定时器自动停止.  ,数组越界
set_time=0;
}
}
if(!key_led)
{
control=1;
}
//添加时钟显示代码
get_ds1302();  //2.23ms
}  //不进第一个if  时间为 2ms   进入 13ms  这里指周期
}

/******************************************************************/
/*                  定时器中断                                    */
/******************************************************************/
void tim(void) interrupt 1 using 1//中断用于温度检测
{
TR0=0;       //关闭定时器
TH0=0x0f;//定时器重装值    定时器有没有中断标志位??,未清零?
TL0=0x00;
flag_get=1;//标志位有效
}

void UART_SER() interrupt 4
{
unsigned char Temp;
static unsigned char flag=0;
if(RI)
{
RI=0;
Temp=SBUF;
switch(Temp)
{
case 's':
control=1;
break;
case 't':
//set_time=1;
flag=buff_size;
break;
case 'w':
TR0=1;
break;
default:
break;
}
if(flag)
{
flag--;
time[flag]=Temp;   //全局变量
if(time[0]=='e')
{
time[0]=0x11;
set_time=1;
}
}
}

}


18b20.c

#include "reg52.h"
#include "18b20.h"
/******************************************************************/
/*                    定义端口                                    */
/******************************************************************/
sbit DQ=P1^3;//ds18b20 端口
unsigned int temp;
unsigned char flag_get=0 ;

/******************************************************************/
/*                    延时函数                                    */
/******************************************************************/
void delay(unsigned int i)//延时函数
{
while(i--);
}

/******************************************************************/
/*                    初始化                                      */
/******************************************************************/
void Init_DS18B20(void)
{
unsigned char x=0;
DQ = 1;    //DQ复位
delay(8);  //稍做延时
DQ = 0;    //单片机将DQ拉低
delay(80); //精确延时 大于 480us
DQ = 1;    //拉高总线
delay(10);
x=DQ;      //稍做延时后 如果x=0则初始化成功 x=1则初始化失败
delay(5);
}

/******************************************************************/
/*                    读一个字节                                  */
/******************************************************************/
unsigned char ReadOneChar(void)
{
unsigned char i=0;
unsigned char dat = 0;
for (i=8;i>0;i--)
{
DQ = 0; // 给脉冲信号
dat>>=1;
DQ = 1; // 给脉冲信号
if(DQ)
dat|=0x80;
delay(5);
}
return(dat);
}

/******************************************************************/
/*                 写一个字节                                     */
/******************************************************************/
void WriteOneChar(unsigned char dat)
{
unsigned char i=0;
for (i=8; i>0; i--)
{
DQ = 0;
DQ = dat&0x01;
delay(5);
DQ = 1;
dat>>=1;
}
delay(5);
}

/******************************************************************/
/*                   读取温度                                     */
/******************************************************************/
unsigned int ReadTemperature(void)
{
unsigned char a=0;
unsigned int b=0;
unsigned int t=0;
Init_DS18B20();
WriteOneChar(0xCC); // 跳过读序号列号的操作
WriteOneChar(0x44); // 启动温度转换
delay(200);
Init_DS18B20();
WriteOneChar(0xCC); //跳过读序号列号的操作
WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度
a=ReadOneChar();   //低位
b=ReadOneChar();   //高位

b<<=8;
t=a+b;

return(t);
}


ds1302.c

#include <reg52.h>
#include "ds1302.h"

//===以下IO定义请根据您硬件的连接修改===
sbit T_RST=P3^5;//ds1302-5
sbit T_IO=P3^4;//ds1302-6
sbit T_CLK=P3^6;//ds1302-7
sbit ACC0=ACC^0;
sbit ACC7=ACC^7;//累加器A 51单片机原理中有介绍

unsigned char a,b,clock_ss,clock_sg,clock_fs,clock_fg,clock_ms,clock_mg;

int hour,mie,sei;

unsigned char clk_time[3];  //秒,分,时寄存器初始值
code unsigned char ledmap[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};
//数码管段码

/******************DS1302:写入操作(上升沿)*********************/
void write_byte(unsigned char da)
{
unsigned char i;
ACC=da;//10000001
for(i=8;i>0;i--)
{
T_IO=ACC0;
T_CLK=0;
T_CLK=1;
ACC=ACC>>1;//01000000
}
}
/******************DS1302:读取操作(下降沿)*****************/
unsigned char read_byte(void)
{
unsigned char i;
for(i=0;i<8;i++)//00000001   假设ACC=00000000
{
ACC=ACC>>1;//01000000
T_CLK = 1;
T_CLK = 0;
ACC7 = T_IO;//10000000
}
return(ACC);

}

/******************DS1302:写入数据(先送地址,再写数据)***************************/
void write_1302(unsigned char addr,unsigned char da)
{
T_RST=0;    //停止工作
T_CLK=0;
T_RST=1;   //重新工作
write_byte(addr);    //写入地址

write_byte(da);
T_RST=0;
T_CLK=1;
}

/******************DS1302:读取数据(先送地址,再读数据)**************************/
unsigned char read_1302(unsigned char addr)
{
unsigned char temp;
T_RST=0;                                   //停止工作
T_CLK=0;
T_RST=1;                         //重新工作
write_byte(addr);   //写入地址
temp=read_byte();
T_RST=0;
T_CLK=1;     //停止工作
return(temp);
}

/***********************延时程序=a*1ms**************************************/
void delay1(unsigned char a)
{
unsigned int i;
while(a-- !=0)
{    // led_disp();
for(i=0;i<12;i++);  //12
}
}

/***********************显示程序**********************************************/

/* DS1302秒,分,时寄存器是BCD码形式:  用16求商和余进行"高4位"和"低4位"分离 */
/****************************************************************************/
void led_disp()
{
clock_ms=clk_time[0]/ 16;
clock_mg=clk_time[0]%16;

clock_fs=clk_time[1]/ 16;
clock_fg=clk_time[1]%16;

mie=clock_fs*10+ clock_fg;   //这个有什么用?

clock_ss=clk_time[2]/ 16;
clock_sg=clk_time[2]%16;//BCD*to*10
hour=clock_ss*10+ clock_sg; //用16求商和余进行"高4位"和"低4位"分离

P2=0;
P0=ledmap[clock_ss];
delay1(1);
P0=0X00;
P2=1;
P0=ledmap[clock_sg];//时个位
delay1(1);
P0=0X00;
P2=2;
P0=ledmap[10];//显示"-"数组里的 0x40
delay1(1);
P0=0X00;
P2=3;
P0=ledmap[clock_fs];//分十位
delay1(1);
P0=0X00;
P2=4;
P0=ledmap[clock_fg];//时个位
delay1(1);P0=0X00;
P2=5;
P0=ledmap[10];//显示"-"数组里的 0x40
delay1(1);
P0=0X00;
P2=6;
P0=ledmap[clock_ms];//秒十位
delay1(1);
P0=0X00;
P2=7;
P0=ledmap[clock_mg];
delay1(1);
P0=0X00;
}
void init_ds1302()
{
write_1302(0x8e,0x80); //WP=1 写保护,加上这个操作之后断电重新上电是正确的时间.
}
void get_ds1302()
{
unsigned char temp=0x81;
unsigned char i;
for(i=0;i<3;i++)//分别把秒分时数据读出分3次读好一次地址加2" temp+=2;"
{
clk_time[i]=read_1302(temp);
temp+=2;
}
led_disp();
}
//添加设置时间的代码  ,结合key
void set_ds1302(unsigned m,unsigned char f,unsigned char s)
{
write_1302(0x8e,0x00); //WP=0 写操作
/**/
write_1302(0x80,m);//0x80是写秒数据此处写进"01"秒
write_1302(0x82,f);//0x82是写分数据
write_1302(0x84,s);//0x84是写时数据

write_1302(0x8e,0x80); //WP=1 写保护
delay1(255);
}


串口部分程序没有比较好写,我便不贴上来了.程序往上位机传输数据时,我这里没有考虑负数.所以如果需要使用本程序需要注意这一点.

这个是下位机程序开发日志:

已经实现串口控制台灯的开关.

下面添加串口测温功能

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时间的设置,需要进行一下转码,方法如下:

/// <summary>
/// 这里进行编码转化 ,ds1302 每一位是16进制数据.如果你想设置成12点, 需要发送 0x12  .
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
private byte Transcoding(string code )
{
byte[] bit = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59};
return bit[int.Parse(code)<60 ? int.Parse(code):59] ;
}


亮点二,右下角动态显示温度

  这个代码直接用的这个作者的http://www.codeproject.com/Articles/9361/WeatherNotify ,有兴趣的可以直接到这里查看.

亮点三,c# hook

  代码参考第五条引用.原程序是有一点小错误,这里的更正方法如下:

  在globalKeyboardHook.cs 文件中定义的委托下面添加这一句 private keyboardHookProc _keyboardHookProc; //新添加的

  在 hook() 方法体中的代码更改为

/*
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
*/
//把引掉的部分更改为下面的
IntPtr hInstance = LoadLibrary("User32");
_keyboardHookProc = new keyboardHookProc(hookProc);
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

/article/5626604.html

http://bbs.csdn.net/topics/100169052

http://www.codeproject.com/Articles/19004/A-Simple-C-Global-Low-Level-Keyboard-Hook
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: