码农自己制作小风扇,妈妈再也用不担心我们夏天晚上被热醒了。
简要描述:
硬件线路连接=>硬件编程=>软件编程
客户端软件截图: 硬件编程源码:
/********************************************************************
* 文件名 : FanServer.c
* 创建人 : wu_zhuojun,2014年2月23日 北京
* 版本号 : 3.0
***********************************************************************/
#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
uchar LCD_ID_1[16] = {"I have a dream!"};
uchar LCD_ID_2[16] = {"Speed: 0"};
void delay(uchar x); //x*0.14MS
void delay1(int ms);
void beep();
sbit IRIN = P3^3; //红外接收器数据线
sbit BEEP = P1^5; //蜂鸣器驱动线
sbit RELAY= P1^4; //继电器驱动线
uchar IRCOM[7];
sbit E=P2^7; //1602使能引脚
sbit RW=P2^6; //1602读写引脚
sbit RS=P2^5; //1602数据/命令选择引脚
//==========直流电机参数定义 begin
sbit PWM = P1^0; //定义直流电机的控制端口
uchar PWM_ON = 0 ; //风扇档数
//==========直流电机参数定义 end
//==========FLASH参数定义 begin
sbit scl=P1^5; //24c08 SCL
sbit sda=P3^6; //24c08 SDA
//==========FLASH参数定义 End
/********************************************************************
* 名称 : delay()
* 功能 : 延时,延时时间大概为140US。
* 输入 : 无
* 输出 : 无
***********************************************************************/
void Delay2()
{
int i,j;
for(i=0; i<=10; i++)
for(j=0; j<=2; j++)
;
}
/********************************************************************
* 名称 : enable(uchar del)
* 功能 : 1602命令函数
* 输入 : 输入的命令值
* 输出 : 无
***********************************************************************/
void enable(uchar del)
{
P0 = del;
RS = 0;
RW = 0;
E = 0;
Delay2();
E = 1;
Delay2();
}
/********************************************************************
* 名称 : write(uchar del)
* 功能 : 1602写数据函数
* 输入 : 需要写入1602的数据
* 输出 : 无
***********************************************************************/
void write(uchar del)
{
P0 = del;
RS = 1;
RW = 0;
E = 0;
Delay2();
E = 1;
Delay2();
}
/********************************************************************
* 名称 : L1602_init()
* 功能 : 1602初始化,请参考1602的资料
* 输入 : 无
* 输出 : 无
***********************************************************************/
void L1602_init(void)
{
enable(0x01);
enable(0x38);
enable(0x0c);
enable(0x06);
enable(0xd0);
}
/********************************************************************
* 名称 : L1602_char(uchar hang,uchar lie,char sign)
* 功能 : 改变液晶中某位的值,如果要让第一行,第五个字符显示"b" ,调用该函数如下
L1602_char(1,5,'b')
* 输入 : 行,列,需要输入1602的数据
* 输出 : 无
***********************************************************************/
void L1602_char(uchar hang,uchar lie,char sign)
{
uchar a;
if(hang == 1) a = 0x80;
if(hang == 2) a = 0xc0;
a = a + lie - 1;
enable(a);
write(sign);
}
/********************************************************************
* 名称 : L1602_string(uchar hang,uchar lie,uchar *p)
* 功能 : 改变液晶中某位的值,如果要让第一行,第五个字符开始显示"ab cd ef" ,调用该函数如下
L1602_string(1,5,"ab cd ef;")
* 输入 : 行,列,需要输入1602的数据
* 输出 : 无
***********************************************************************/
void L1602_string(uchar hang,uchar lie,uchar *p)
{
uchar a;
if(hang == 1) a = 0x80;
if(hang == 2) a = 0xc0;
a = a + lie - 1;
enable(a);
while(1)
{
if(*p == '\0') break;
write(*p);
p++;
}
}
/********************************************************************
* 名称 : Com_Init()
* 功能 : 初始化串口程序,晶振11.0592, 波特率9600
* 输入 : 无
* 输出 : 无
***********************************************************************/
void Com_Init(void)
{
TMOD = 0x20;
PCON = 0x00;
SCON = 0x50;
TH1 = 0xFd;
TL1 = 0xFd;
TR1 = 1;
}
//风扇3档
void PWM_Speed3()
{
static uchar status = 0;
if(10 > status)
{
PWM = 1;//风扇转
}
else//(1000 == status)
{
PWM = 0;//风扇不转
status = 0;
}
status++;
}
//风扇2档
void PWM_Speed2()
{
static uchar status = 0;
if(4 > status)
{
PWM = 1;//风扇转
}
else//(1000 == status)
{
PWM = 0;//风扇不转
status = 0;
}
status++;
}
//风扇1档
void PWM_Speed1()
{
static uchar status = 0;
if(0 == status)
{
PWM = 1;
}
else if(1 == status)
{
PWM = 1;
}
else //2 == status
{
PWM = 0;
status = 0;
}
status++;
}
//风扇0档
void PWM_Speed0()
{
PWM = 0;//风扇不转
}
/********************************************************************
* 名称 : PWM_Contrl()
* 功能 : 控制风扇的档数
* 输入 :
* 输出 : 无
***********************************************************************/
void PWM_Contrl()
{
switch(PWM_ON)
{
case 0:
PWM_Speed0();
break;
case 1:
PWM_Speed1();
break;
case 2:
PWM_Speed2();
break;
case 3:
PWM_Speed3();
break;
default:
PWM_Speed0();
break;
}
}
/********************************************************************
* 名称 : flash()
* 功能 : 延时,时间为2个NOP,大概为2US
* 输入 : 无
* 输出 : 无
***********************************************************************/
void flash(void)
{
_nop_();
_nop_();
}
/********************************************************************
* 名称 : x24c02_init()
* 功能 : 24c02初始化子程序
* 输入 : 无
* 输出 : 无
***********************************************************************/
void x24c02_init(void)
{
scl = 1;
flash();
sda = 1;
flash();
}
/********************************************************************
* 名称 : start(void)
* 功能 : 启动I2C总线
* 输入 : 无
* 输出 : 无
***********************************************************************/
void start(void)
{
scl = 1;
flash();
sda = 1;
flash();
sda = 0;
flash();
scl = 0;
flash();
}
/********************************************************************
* 名称 : stop()
* 功能 : 停止I2C总线
* 输入 : 无
* 输出 : 无
***********************************************************************/
void stop()
{
scl = 0;
flash();
sda = 0;
flash();
scl = 1;
flash();
sda = 1;
flash();
}
/********************************************************************
* 名称 : writex()
* 功能 : 写一个字节
* 输入 : j(需要写入的值)
* 输出 : 无
***********************************************************************/
void writex(uchar j)
{
uchar i,temp;
temp = j;
for(i=0; i<8; i++)
{
scl = 0;
flash();
sda = (bit)(temp & 0x80);
flash();
scl = 1;
flash();
temp = temp << 1;
}
scl = 0;
flash();
}
/********************************************************************
* 名称 : readx()
* 功能 : 读一个字节
* 输入 : 无
* 输出 : 读出的值
***********************************************************************/
uchar readx(void)
{
uchar i, j, k = 0;
for(i=0; i<8; i++)
{
scl = 0;
flash();
if(sda == 1)
{
j = 1;
}
else j = 0;
k = (k << 1) | j;
scl = 1;
flash();
}
return(k);
}
/********************************************************************
* 名称 : ack()
* 功能 : I2C总线时钟
* 输入 : 无
* 输出 : 无
***********************************************************************/
void ack(void)
{
uchar i = 0;
scl = 1;
flash();
while((sda == 1) && (i < 255))
{
i++;
}
scl = 0;
flash();
}
/********************************************************************
* 名称 : x24c02_read()
* 功能 : 从24c02中读出值
* 输入 : address(要在这个地址读取值)
* 输出 : 从24c02中读出的值
***********************************************************************/
uchar x24c02_read(uchar address)
{
uchar i;
start();
writex(0xa0);
ack();
writex(address);
ack();
start();
writex(0xa1);
ack();
i = readx();
stop();
return(i);
}
/********************************************************************
* 名称 : x24c02_write()
* 功能 : 想24c02中写入数据
* 输入 : address(地址) , info(值)
* 输出 : 无
***********************************************************************/
void x24c02_write(uchar address, uchar info)
{
start();
writex(0xa0);
ack();
writex(address);
ack();
writex(info);
ack();
stop();
}
/********************************************************************
* 名称 : main()
* 功能 :
* 输入 : 无
* 输出 : 无
***********************************************************************/
main()
{
//=========初始化红外线控制 Begin=======//
IE = 0x84; //允许总中断中断,使能 INT1 外部中断
TCON = 0x10; //触发方式为脉冲负边沿触发
IRIN=1; //I/O口初始化
BEEP=1;
RELAY=1;
//=========初始化红外线控制 End=======//
delay1(10); //延时
L1602_init(); //初始化LCD
L1602_string(1, 1, LCD_ID_1); //设置 液晶显示屏 显示LCD_ID_1的内容
L1602_string(2, 1, LCD_ID_2); //设置 液晶显示屏 显示LCD_ID_2的内容
//=======初始化串口 Begin=======//
Com_Init();
//=======初始化串口 Begin=======//
//=======初始化FLASH Begin=======//
x24c02_init(); //初始化24C02
PWM_ON = x24c02_read(2); //读出保存的数据赋于sec
L1602_char(2,8,PWM_ON+'0');
//=======初始化FALSH End=======//
while(1)
{
PWM_Contrl();
}
} //end main
/********************************************************************
* 名称 : IR_IN interrupt 2 using 0
* 功能 : 接收红外线数据,并中断处理液晶屏显示
* 输入 : 无
* 输出 : 无
***********************************************************************/
void IR_IN() interrupt 2 using 0
{
//======初始化串口发送数据 变量 Begin=======//
uchar code strSpeed[][4] = {"0", "1", "2", "3" };
uchar *p = strSpeed[0]; //初始化为 0 档
//======初始化串口发送数据 变量 End=======//
unsigned char j,k,N=0;
EX1 = 0;
delay(15);
if (IRIN==1)
{
EX1 =1;
return;
}
//确认IR信号出现
while (!IRIN) //等IR变为高电平,跳过9ms的前导低电平信号。
{
delay(1);
}
for (j=0;j<4;j++) //收集四组数据
{
for (k=0;k<8;k++) //每组数据有8位
{
while (IRIN) //等 IR 变为低电平,跳过4.5ms的前导高电平信号。
{
delay(1);
}
while (!IRIN) //等 IR 变为高电平
{
delay(1);
}
while (IRIN) //计算IR高电平时长
{
delay(1);
N++;
if (N>=30)
{
EX1=1;
return;
} //if (N>=30) //0.14ms计数过长自动离开。
} //while (IRIN) //高电平计数完毕
IRCOM[j]=IRCOM[j] >> 1; //数据最高位补“0”
if (N>=8)
{
IRCOM[j] = IRCOM[j] | 0x80; //数据最高位补“1”
} //if (N>=8)
N=0;
}//for (k=0;k<8;k++)
}//for (j=0;j<4;j++)
if (IRCOM[2]!=~IRCOM[3])
{
EX1=1;
return;
}
IRCOM[5]=IRCOM[2] & 0x0F; //取键码的低四位
IRCOM[6]=IRCOM[2] >> 4; //右移4次,高四位变为低四位
if(IRCOM[5]>9)
{
IRCOM[5]=IRCOM[5]+0x37;
}
else
IRCOM[5]=IRCOM[5]+0x30;
if(IRCOM[6]>9)
{
IRCOM[6]=IRCOM[6]+0x37;
}
else
IRCOM[6]=IRCOM[6]+0x30;
//=======控制 液晶显示屏显示数据 Begin=========//
if( ('4' == IRCOM[6]) && ('5' == IRCOM[5])) //触发开关键
{
PWM_ON=0;//风扇关闭
//PWM_SLEEP = 0;
p = strSpeed[0]; //档速0
L1602_char(2,8,'0');
}
else if( ('0' == IRCOM[6]) && ('C' == IRCOM[5])) //触发1号键
{
PWM_ON=1;//风扇开启
//PWM_SLEEP=10;//风扇休眠时间
p = strSpeed[1]; //档速1
L1602_char(2,8,'1');
}
else if( ('1' == IRCOM[6]) && ('8' == IRCOM[5])) //触发2号键
{
PWM_ON=2;//风扇开启
//PWM_SLEEP=100;//风扇休眠时间
p = strSpeed[2]; //档速2
L1602_char(2,8,'2');
}
else if( ('5' == IRCOM[6]) && ('E' == IRCOM[5])) //触发3号键
{
PWM_ON=3;//风扇开启
//PWM_SLEEP=1000;//风扇休眠时间
p = strSpeed[3]; //档速3
L1602_char(2,8,'3');
}
//=======控制 液晶显示屏显示数据 End=========//
//=======发送串口数据 Begin=========//
while(1)
{
SBUF = *p;
while(!TI) //如果发送完毕,硬件会置位TI
{
_nop_();
}
p++;
if(*p == '\0') break; //在每个字符串的最后,会有一个'\0'
TI = 0; //TI清零
}
//=======发送串口数据 End=========//
//beep();
EX1 = 1;
//将风扇的档数记录
x24c02_write(2,PWM_ON); //在24c08的地址2中写入数据sec
}
/**********************************************************/
void beep()
{
unsigned char i;
for (i=0;i<100;i++)
{
delay(4);
BEEP=!BEEP; //BEEP取反
}
BEEP=1; //关闭蜂鸣器
}
/**********************************************************/
void delay(unsigned char x) //x*0.14MS
{
unsigned char i;
while(x--)
{
for (i = 0; i<13; i++) {}
}
}
/**********************************************************/
void delay1(int ms)
{
unsigned char y;
while(ms--)
{
for(y = 0; y<250; y++)
{
_nop_();
_nop_();
_nop_();
_nop_();
}
}
}
软件编程部分源码:
===================================================================================================
// FanDlg.h : 头文件
//
#pragma once
#define COM_RECVDATA WM_USER+1000//自定义消息
// CFanDlg 对话框
class CFanDlg : public CDialogEx
{
// 构造
public:
CFanDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
enum { IDD = IDD_FAN_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
private:
HANDLE hCom; //串口句柄
HANDLE hCommThread; //串口线程
public:
//获取串口句柄
HANDLE GetComHandle();
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
//打开串口
//返回值:TRUE 开启成功 FLASE 开启失败
BOOL OpenCom(CString strPort);
//接收到数据响应消息
afx_msg LRESULT OnRecvData(WPARAM wParam, LPARAM lParam);
public:
afx_msg void OnClose();
};
//声明 串口接收线程
extern DWORD WINAPI SerialPort1ThreadProcess(CFanDlg *pFan); ===================================================================================================
// FanDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "Fan.h"
#include "FanDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CFanDlg 对话框
CFanDlg::CFanDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CFanDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CFanDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CFanDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(COM_RECVDATA, &CFanDlg::OnRecvData)
ON_WM_CLOSE()
END_MESSAGE_MAP()
// CFanDlg 消息处理程序
BOOL CFanDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
this->SetWindowTextW(_T("风扇控制系统--客户端"));
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
BOOL bStatu = OpenCom(_T("COM4"));
//启动串口监视线程
DWORD threadID;
hCommThread = ::CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
(LPTHREAD_START_ROUTINE)SerialPort1ThreadProcess,
this, 0, &threadID);
if (hCommThread == NULL)
{
::AfxMessageBox(_T("创建串口1处理线程失败"));
::PostQuitMessage(0);
}
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
BOOL CFanDlg::OpenCom(CString strPort)
{
hCom=CreateFile(strPort,
GENERIC_READ|GENERIC_WRITE, //允许读和写
0, //独占方式
NULL,
OPEN_EXISTING, //打开而不是创建
0, //同步方式
NULL);
if(hCom==(HANDLE)-1)
{
AfxMessageBox(_T("打开COM失败!"));
return FALSE;
}
DCB wdcb;
GetCommState (hCom, &wdcb);
wdcb.BaudRate=9600;//波特率:9600,其他:不变
if(!SetCommState (hCom, &wdcb))
{
MessageBox(_T("串口设置出错!"));
return FALSE;
}
PurgeComm(hCom, PURGE_TXCLEAR);
return TRUE;
}
void CFanDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CFanDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CFanDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
//接收数据后(通过监听线程发来的用户自定义消息)显示
LRESULT CFanDlg::OnRecvData(WPARAM wParam, LPARAM lParam)
{
CString recvStr((char *)wParam);
this->GetDlgItem(IDC_EDT_FAN_SPEED)->SetWindowTextW(recvStr + _T("级风速"));
UpdateData(false);
return TRUE;
}
HANDLE CFanDlg::GetComHandle()
{
return hCom; //串口句柄
}
//以一个线程不同监控串口行接收的数据
DWORD WINAPI SerialPort1ThreadProcess(CFanDlg *pFan)
{
char str[101];
DWORD wCount; //读取的字节数
while(TRUE)
{
ReadFile(pFan->GetComHandle(),str, 100, &wCount, NULL);
if(wCount > 0) //收到数据
{
//发送消息给对话框主窗口,以进行接收内容的显示
str[wCount] = '\0';
::PostMessage(pFan->m_hWnd, COM_RECVDATA, (unsigned int) str, wCount);
}
Sleep((DWORD)0.1);
}
return TRUE;
}
void CFanDlg::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//CloseHandle(hCom); //程序退出时关闭串口
CDialogEx::OnClose();
}