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

2023-02-12,,

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

整体设计包含下位机程序和上位机程序.下位机用的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^;
sbit key_led=P3^;
unsigned int num;
unsigned char control=,set_time=;
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=; //定时器 0 中断允许 .
TR0=; // run the timer0 这个中断会让单片机查询中断向量表.
EA = ; //打开总中断
} //显示 open 字符
void disp_open()
{
unsigned int i=;
while(i--)
{
P2=;
P0=0x3f; //'O'
delay1();
P0=0X00; P2=;
P0=0x73; //'P'
delay1();
P0=0X00; P2=;
P0=0x79; //'E'
delay1();
P0=0X00; P2=;
P0=0x37; //'N'
delay1();
P0=0X00;
} }
// 显示close 字符
void disp_close()
{
unsigned int i=;
while(i--)
{
P2=;
P0=0x39; //'C'
delay1();
P0=0X00; P2=;
P0=0x38; //'L'
delay1();
P0=0X00; P2=;
P0=0x3f; //'O'
delay1();
P0=0X00; P2=;
P0=0x6d; //'S'
delay1();
P0=0X00; P2=;
P0=0x79; //'E'
delay1();
P0=0X00;
} } /*----------------------------------------------------------------------------------------
这是主函数部分
--------------------------------------------------------------------------------------*/
void main()
{
unsigned int TempH,TempL;
unsigned char H, L ;
jdq=;
init_timer();
init_ds1302();
UARTinit(); while()
{
if(flag_get) //定时读取当前温度
{ temp=ReadTemperature(); //这个函数8ms if(temp&0x8000)
{
temp=~temp; // 取反加1
temp +=;
}
TempH=temp>>;
TempL=temp&0x0F;
TempL=TempL*/;//小数近似处理 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=; } //这个循环用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=;
}
if(set_time)
{
if((time[]<0x60)||(time[]<0x60)&&(time[]<0x24))
{
set_ds1302(time[],time[],time[]); // s , m ,h
//不知为何时间定时器自动停止. ,数组越界
set_time=;
}
}
if(!key_led)
{
control=;
}
//添加时钟显示代码
get_ds1302(); //2.23ms
} //不进第一个if 时间为 2ms 进入 13ms 这里指周期
} /******************************************************************/
/* 定时器中断 */
/******************************************************************/
void tim(void) interrupt using //中断用于温度检测
{
TR0=; //关闭定时器
TH0=0x0f;//定时器重装值 定时器有没有中断标志位??,未清零?
TL0=0x00;
flag_get=;//标志位有效
} void UART_SER() interrupt
{
unsigned char Temp;
static unsigned char flag=;
if(RI)
{
RI=;
Temp=SBUF;
switch(Temp)
{
case 's':
control=;
break;
case 't':
//set_time=1;
flag=buff_size;
break;
case 'w':
TR0=;
break;
default:
break;
}
if(flag)
{
flag--;
time[flag]=Temp; //全局变量
if(time[]=='e')
{
time[]=0x11;
set_time=;
}
}
} }

18b20.c

 #include "reg52.h"
#include "18b20.h"
/******************************************************************/
/* 定义端口 */
/******************************************************************/
sbit DQ=P1^;//ds18b20 端口
unsigned int temp;
unsigned char flag_get= ; /******************************************************************/
/* 延时函数 */
/******************************************************************/
void delay(unsigned int i)//延时函数
{
while(i--);
} /******************************************************************/
/* 初始化 */
/******************************************************************/
void Init_DS18B20(void)
{
unsigned char x=;
DQ = ; //DQ复位
delay(); //稍做延时
DQ = ; //单片机将DQ拉低
delay(); //精确延时 大于 480us
DQ = ; //拉高总线
delay();
x=DQ; //稍做延时后 如果x=0则初始化成功 x=1则初始化失败
delay();
} /******************************************************************/
/* 读一个字节 */
/******************************************************************/
unsigned char ReadOneChar(void)
{
unsigned char i=;
unsigned char dat = ;
for (i=;i>;i--)
{
DQ = ; // 给脉冲信号
dat>>=;
DQ = ; // 给脉冲信号
if(DQ)
dat|=0x80;
delay();
}
return(dat);
} /******************************************************************/
/* 写一个字节 */
/******************************************************************/
void WriteOneChar(unsigned char dat)
{
unsigned char i=;
for (i=; i>; i--)
{
DQ = ;
DQ = dat&0x01;
delay();
DQ = ;
dat>>=;
}
delay();
} /******************************************************************/
/* 读取温度 */
/******************************************************************/
unsigned int ReadTemperature(void)
{
unsigned char a=;
unsigned int b=;
unsigned int t=;
Init_DS18B20();
WriteOneChar(0xCC); // 跳过读序号列号的操作
WriteOneChar(0x44); // 启动温度转换
delay();
Init_DS18B20();
WriteOneChar(0xCC); //跳过读序号列号的操作
WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度
a=ReadOneChar(); //低位
b=ReadOneChar(); //高位 b<<=;
t=a+b; return(t);
}

ds1302.c

 #include <reg52.h>
#include "ds1302.h" //===以下IO定义请根据您硬件的连接修改===
sbit T_RST=P3^;//ds1302-5
sbit T_IO=P3^;//ds1302-6
sbit T_CLK=P3^;//ds1302-7
sbit ACC0=ACC^;
sbit ACC7=ACC^;//累加器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[]; //秒,分,时寄存器初始值
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;//
for(i=;i>;i--)
{
T_IO=ACC0;
T_CLK=;
T_CLK=;
ACC=ACC>>;//
}
}
/******************DS1302:读取操作(下降沿)*****************/
unsigned char read_byte(void)
{
unsigned char i;
for(i=;i<;i++)//00000001 假设ACC=00000000
{
ACC=ACC>>;//01000000
T_CLK = ;
T_CLK = ;
ACC7 = T_IO;//
}
return(ACC); } /******************DS1302:写入数据(先送地址,再写数据)***************************/
void write_1302(unsigned char addr,unsigned char da)
{
T_RST=; //停止工作
T_CLK=;
T_RST=; //重新工作
write_byte(addr); //写入地址 write_byte(da);
T_RST=;
T_CLK=;
} /******************DS1302:读取数据(先送地址,再读数据)**************************/
unsigned char read_1302(unsigned char addr)
{
unsigned char temp;
T_RST=; //停止工作
T_CLK=;
T_RST=; //重新工作
write_byte(addr); //写入地址
temp=read_byte();
T_RST=;
T_CLK=; //停止工作
return(temp);
} /***********************延时程序=a*1ms**************************************/
void delay1(unsigned char a)
{
unsigned int i;
while(a-- !=)
{ // led_disp();
for(i=;i<;i++); //
}
} /***********************显示程序**********************************************/ /* DS1302秒,分,时寄存器是BCD码形式: 用16求商和余进行"高4位"和"低4位"分离 */
/****************************************************************************/
void led_disp()
{
clock_ms=clk_time[]/ ;
clock_mg=clk_time[]%; clock_fs=clk_time[]/ ;
clock_fg=clk_time[]%; mie=clock_fs*+ clock_fg; //这个有什么用? clock_ss=clk_time[]/ ;
clock_sg=clk_time[]%;//BCD*to*10
hour=clock_ss*+ clock_sg; //用16求商和余进行"高4位"和"低4位"分离 P2=;
P0=ledmap[clock_ss];
delay1();
P0=0X00;
P2=;
P0=ledmap[clock_sg];//时个位
delay1();
P0=0X00;
P2=;
P0=ledmap[];//显示"-"数组里的 0x40
delay1();
P0=0X00;
P2=;
P0=ledmap[clock_fs];//分十位
delay1();
P0=0X00;
P2=;
P0=ledmap[clock_fg];//时个位
delay1();P0=0X00;
P2=;
P0=ledmap[];//显示"-"数组里的 0x40
delay1();
P0=0X00;
P2=;
P0=ledmap[clock_ms];//秒十位
delay1();
P0=0X00;
P2=;
P0=ledmap[clock_mg];
delay1();
P0=0X00;
}
void init_ds1302()
{
write_1302(0x8e,0x80); //WP=1 写保护,加上这个操作之后断电重新上电是正确的时间.
}
void get_ds1302()
{
unsigned char temp=0x81;
unsigned char i;
for(i=;i<;i++)//分别把秒分时数据读出分3次读好一次地址加2" temp+=2;"
{
clk_time[i]=read_1302(temp);
temp+=;
}
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();
}

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

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

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

下面添加串口测温功能

2013.11.
目前系统有个奇怪的问题, 不能打开串口接收中断但是不发送 ,且发送不能少于2个byte .
当关闭ES 时不会出现这个问题 .
在初始化时 TI 置位会引用跳到串口接收中断部分 ,稍过后没有此问题. 每次发生 接收中断时系统运算减慢,过片刻正常. 2013.11.
设置日期的格式 't''h''m''s''e'
发送的数据 均为 0x00格式 比如说 设置为 :00:01 则发送 't''0x20''0x00''0x01''e' 关于首次初始化问题 思路是将1302的某个寄存器定义为是否首次开机检测标志,比如存入0xaa数值。
上电时读取1302的这个寄存器,如果是0xaa,说明不是首次,便不再初始化,否则初始化,并向开机定义的寄存器中写入0xaa。
我这里直接对写保护位写 1. 2013.11.
添加发送'w'获取温度 返回的数据 'a''H''L''e'
2013.11.
添加返回台灯状态 'c''e' 'o''e'
添加握手信息 开机是请求获取一次温度 ,
2013.11.
添加 修改 second
2013.11.
上述提到问题已经更正

上位机程序分析,程序界面如图:

上位机程序有几个我自认为的亮点. 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>") + ;
int monthIndex = html.IndexOf("<month>") + ;
int dayIndex = html.IndexOf("<day>") + ;
int hourIndex = html.IndexOf("<hour>") + ;
int miniteIndex = html.IndexOf("<minite>") + ;
int secondIndex = html.IndexOf("<second>") + ; 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)< ? int.Parse(code):] ;
}

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

  这个代码直接用的这个作者的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, );

如果想按下快捷键不对其它的程序产生影响,可以去掉这个方法体 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

电脑控制台灯(c# hook,显示室温,联网校正时间)的相关教程结束。

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

下载本文的Word格式文档,以方便收藏与打印。