目录
1、准备工作
1.1、软件
1.2、硬件
2、硬件连线
3、C 语言程序
4、测试
5、总结
我这里使用的 普中51-单核-A4 学习板,硬件电路已经基本都连接完了,而且它比较好的一点就是提供了 ttl 转 usb 的接口,可以用于配置 ESP8266,而且还提供了 3.3V & 5V 电源外接(注意: 烧录程序的时候还是不要外部供电,否则程序烧录不进去) 还是很贴心的,淘宝上价格 60 多块钱吧,作为我这种从事互联网纯软件开发过来试水51 单片机的程序员来说够用了。
硬件连线其实很简单,商家都会提供该款单片机学习板的电路原理图的,而且开发板都有预留单片机 I/O 的外部接口,只要依照这电路图找出 RXD/TXD I/O 口就行,普中51-单核-A4 这款单片机的 RXD、TXD 为 P30 和 P31 口,只要将 ESP8266 接通 3.3 V 电源,将 wifi 模块的 RX 和 TX 与单片机对应的口交叉相连就可以了,实际连线如下:
单片机左边连线从上到下分别是 3.3V 和 GND,右边连线从上到下依次是 P31(TXD)、P30(RXD) 口,具体查看手里单片机管脚图即可。
配置 ESP8266 我参考是 单片机+wifi 远程控制开关灯,讲得很详细,大家参考操作即可。
#include
#include
#include
#include
#include
#define uchar unsigned char
#define uint unsigned int
sbit LED1 = P2^0;
sbit LED2 = P2^1;
sbit LED3 = P2^2;
sbit LED4 = P2^3;
sbit LED5 = P2^4;
sbit LED6 = P2^5;
sbit LED7 = P2^6;
sbit LED8 = P2^7;
// 串口中断接收相关
uint scount = 0, maxLen = 25, tscount = 0;
uchar srdatas[25];
// 计时器 0, 多大代表有多少个 50ms(0 ~ 65535)
uint time0Count = 0;
// 串口接收数据已处理标志
bit sflag = 0;
// 校验连接发送标志(1-表示需要发送)
bit tsflag = 0;
void Delay2(unsigned long cnt);
void Tranfer(uchar *s);
void SysInit();
void SetWifi();
void dealReceiveData();
void dealReceiveLine(uchar* line, uint length);
void dealWifiConnectInfo();
// 10 进制 => 2 进制
//uchar* decimal2binary(uint val);
// 字符串转数字
// uint parseInt(uchar* str, uint len);
void Delay2(unsigned long cnt) {
long i;
for(i=0;i 2) {
// 出现了换行符, 处理接收到的行的数据
if(srdatas[scount - 2] == '\r' && srdatas[scount - 1] == '\n') {
dealReceiveLine(srdatas, scount - 2);
scount = 0;
}
}
}
// 执行之后即为串口接收到的数据已经处理
sflag = 1;
ES = 1;
}
void dealWifiConnectInfo() {
// 开始发送尝试重连
if(tsflag == 1) {
ET0 = 0;
printf("AT+CIPCLOSE=2\r\n");
Delay2(5);
printf("AT+CIPSTART=2,\"TCP\",\"115.29.109.104\",6520\r\n");
Delay2(10);
tsflag = 0;
ET0 = 1;
}
}
void dealReceiveLine(uchar* line, uint length) {
// bit hasCommand = 0;
uint i = 0, t = 0;
uchar command;
uchar newLine[25];
// 去除换行符
if(length > 3) {
for(i = 0; i < length; i++) {
if(line[i] != '\r' && line[i] != '\n') {
newLine[t] = line[i];
t++;
}
if(t >= 25) {
length = t;
break;
}
}
}
if(length > 3) {
// 处理开关控制指令
if(
newLine[0] == '+'
&& newLine[1] == 'I'
&& newLine[2] == 'P'
&& newLine[3] == 'D'
) {
i = 0;
while(i < length && newLine[i] != ':') {
i++;
}
// 存在有效位置
if(i < length) {
t = 0;
for(i = i + 1; i < length; i++) {
t++;
if(newLine[i] == '/') {
continue;
}
command = newLine[i] - 0x30;
switch(t) {
case 1:
LED1 = !command;
break;
case 2:
LED2 = !command;
break;
case 3:
LED3 = !command;
break;
case 4:
LED4 = !command;
break;
case 5:
LED5 = !command;
break;
case 6:
LED6 = !command;
break;
case 7:
LED7 = !command;
break;
case 8:
LED8 = !command;
break;
}
if(t >= 8) {
break;
}
}
}
}
/*
LED6 = ~LED6;
// 处理定时轮询的远程连接状态响应数据
if(tsflag == 1) {
LED7 = ~LED7;
if(
newLine[0] == 'A'
&& newLine[1] == 'L'
&& newLine[2] == 'R'
&& newLine[3] == 'E'
&& newLine[4] == 'A'
&& newLine[5] == 'D'
&& newLine[6] == 'Y'
) {
LED8 = 0;
} else {
LED8 = 1;
}
tsflag = 0;
}
*/
}
}
void SysInit() {
// 初始化定时器1, 配置波特率发生器
TH1 = 0xFD; //晶振11.0592mhz 波特率设为9600
TL1 = TH1;
TMOD |= 0x20; //定时器1方式2
SCON = 0x50; //串口接收使能
ES = 1; //串口中断使能
TR1 = 1; //定时器1使能
TI = 1; //发送中断标记位,必须设置
// 初始化定时器0, 做系统定时任务(11.0592MHz, 定时 50ms)
/* */
TH0 = 0x4C;
TL0 = 0x00;
TMOD |= 0x01; // 工作在方式2
TR0 = 1; // 定时器0使能
ET0 = 1;
//REN = 0; // 禁止串口接收数据
printf("begin init wifi...\r\n");
SetWifi();
printf("wifi inited...\r\n");
//REN = 1;
EA=1;
}
void SetWifi() {
Delay2(1000);
printf("AT+CIPMUX=1\r\n");
Delay2(1000);
printf("AT+CIPSERVER=1\r\n");
Delay2(1000);
printf("AT+CIPCLOSE=2\r\n");
Delay2(2000);
printf("AT+CIPSTART=2,\"TCP\",\"115.29.109.104\",6520\r\n");
Delay2(2000);
}
/**/
void Timer0() interrupt 1 {
uint t = 0;
ET0 = 0;
// 继续下一轮定时
TH0 = 0x4C;
TL0 = 0x00;
time0Count = (time0Count + 1) % 1200;
// 大概 30s 判断一下连接是否断开, 断开之后需要重连
if(time0Count % 600 == 0) {
tsflag = 1;
}
ET0 = 1;
}
void Usart() interrupt 4 {
if(RI == 1)
{
RI = 0;
srdatas[scount] = SBUF;
scount += 1;
if(scount >= maxLen) {
scount = 0;
}
sflag = 0;
}
}
void main() {
SysInit();
while(1) {
dealReceiveData();
dealWifiConnectInfo();
}
}
上述程序编写经过实际测试,没有问题,具体细节如下:
dealReceiveData 方法中延时使用的是定时器,没有用 Delay2(),因为如果在延时期间发生了中断,则 Delay2() 会死循环,具体原因不详,有知道的小伙伴麻烦留言解释一下,不胜感谢,其他需要注意的就是在处理串口接收到的数据的时候需要将串口关闭,处理完成之后开启,为什么呢?因为串口接收数据缓存是互斥量,不能在读取的数据处理的时候再改变该数据,其实就是一个变相加锁。在读取接收缓存的时候,不允许更改接收缓存的数据。
写了个 java 程序,每隔 1S 向 TCP Server 发送 0 ~ 255 的 二进制数据,以此控制小灯,程序采用 Java 编写,如下:
pulbic class LEDTests {
@Test
public void ledControlTest() throws InterruptedException {
try {
//创建Socket对象
Socket socket = new Socket("115.29.109.104", 6520);
//获取一个输出流,向服务端发送信息
OutputStream outputStream = socket.getOutputStream();
//将输出流包装成打印流
PrintWriter printWriter = new PrintWriter(outputStream);
char chs[] = { '0', '0', '0', '0', '0', '0', '0', '0' };
int t;
//根据输入输出流和服务端连接
for(int i = 0; i < 10000; i++) {
String binaryStr = Integer.toBinaryString((i + 1) % 256);
char[] tchs = binaryStr.toCharArray();
t = 0;
for(int j = 8 - tchs.length; j < 8; j++) {
if(j < (8 - tchs.length)) {
chs[j] = '0';
} else {
chs[j] = tchs[t++];
}
}
StringBuffer sb = new StringBuffer();
for (char ch : chs) {
sb.append(ch);
}
binaryStr = sb.toString();
// 发送数据后需要加上换行符
printWriter.print(binaryStr + "\r\n");
Thread.sleep(500);
printWriter.flush();
System.out.println("=> " + binaryStr);
}
//关闭输出流
socket.shutdownOutput();
printWriter.close();
outputStream.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
实际效果如下:
本来想扩展很多功能,比如说设备可以使用红外遥控器配置热点连接信息,超时重连时间,向服务器上传温湿度,传输信息加密,单片机间隔一段时间发送心跳包给服务器,点对点控制等等功能,不过想想挺麻烦的,哈市不做了!!!听说 arduino 、STM32 的 RAM 方便很多,等后面学了这些单片机再做这些功能吧,对于 51 这种微控制器还是不想折腾这么多功能了。