这两天测试程序还发现一个bug就是如果客户端断开了,应该检测一下哪个断开了,数据就不应该发向那个连接,,,否则就会报错,然后模块会复位重启
所以加上这段代码
conn0:on("disconnection",function(sck,c) --print(sck) if sck == connect0 then connect0 = nil print("disconnect0") end if sck == connect1 then connect1 = nil print("disconnect1") end if sck == connect2 then connect2 = nil print("disconnect2") end if sck == connect3 then connect3 = nil print("disconnect3") end if sck == connect4 then connect4 = nil print("disconnect4") end end)
放一个全的
ConnectCnt = 0//连接计数 ConnectListenFlage=0//监听标志,只执行一次监听 UsartUsFlage = 0//允许串口数据发向网口 tmr.alarm(2, 1000, 1, function() if NetMode == 0 then//TCP Sever模式 if ConnectListenFlage == 0 then ConnectListenFlage = 1 srv:listen(ConnectPort,function(conn0) UsartUsFlage = 1 --print(conn0) if ConnectCnt == 0 then connect0 = conn0 if connect1 ~= nil then connect1:close() end connect1 = nil print("0-Connectd") end if ConnectCnt == 1 then connect1 = conn0 if connect2 ~= nil then connect2:close() end connect2 = nil print("1-Connectd") end if ConnectCnt == 2 then connect2 = conn0 if connect3 ~= nil then connect3:close() end connect3 = nil print("2-Connectd") end if ConnectCnt == 3 then connect3 = conn0 if connect4 ~= nil then connect4:close() end connect4 = nil print("3-Connectd") end if ConnectCnt == 4 then connect4 = conn0 if connect0 ~= nil then connect0:close() end connect0 = nil print("4-Connectd") end conn0:on("disconnection",function(sck,c) //断开连接函数 --print(sck) if sck == connect0 then connect0 = nil print("disconnect0") end if sck == connect1 then connect1 = nil print("disconnect1") end if sck == connect2 then connect2 = nil print("disconnect2") end if sck == connect3 then connect3 = nil print("disconnect3") end if sck == connect4 then connect4 = nil print("disconnect4") end end) conn0:on("receive",function(conn0,payload) uart.write(0,payload)end) --print(payload) end) ConnectCnt = ConnectCnt+1 if ConnectCnt == 5 then ConnectCnt = 0 end end) end end
串口函数里面
if UsartUsFlage == 1 then if NetMode == 0 then if connect0 ~= nil then connect0:send(RevData) end if connect1 ~= nil then connect1:send(RevData) end if connect2 ~= nil then connect2:send(RevData) end if connect3 ~= nil then connect3:send(RevData) end if connect4 ~= nil then connect4:send(RevData) end end
后面说发数据的时候碰到00会自动把00以前的发送到串口,这是因为使用的是print函数
可以把这个函数修改为uart.write(0,payload)就不会出现上面的问题了
截个图看一下
效果
如果想用手机做可以参考这篇文章,自己的协议规定在文章中
---恢复内容开始---
请问一下博客为什么又不能直接复制粘贴图片了呢............
先看8266的配置,8266我是用的Lua语言写的,,因为方便快捷....这次写的当然比以前完善......
关于WIFI模块可以看这几篇
最后加上一篇有人的WIFI模块,有人的做的也不错,当初项目就是用的有人的
自己的ESP8266是建立服务器
我的init.lua
lighton=0 pin = 4 gpio.mode(pin,gpio.OUTPUT) tmr.alarm(0,500,1,function() if lighton==0 then lighton=1 gpio.write(pin,1) else lighton=0 gpio.write(pin,0) end end) tmr.alarm(1, 3000, 0, function() print("dofile wifi.lua...") dofile("wifi.lua") end)
初始化呢,和我以前的一个地方不一样
tmr.alarm(1, 3000, 0, function()
print("dofile wifi.lua...")
dofile("wifi.lua")
我让模块3s以后再去加载的wifi.lua
说一下原因,,,因为我在wifi.lua里面设置的串口的数据直接发送到网口,,如果没有这个延时加载,一下子就执行了串口的数据直接发送到网口
那么下次想通过串口向模块发指令或者重新写入LUA程序就会出现问题,因为直接执行了串口的数据直接发送到网口,,有了这个延时我可以在复位的3s之前去操作模块了
再看一下wifi.lua
ConnectCnt = 0 wifi.setmode(wifi.STATIONAP) cfg={} cfg.ssid="HiWifi8266"--模块的WIFI信号名字 cfg.pwd="11223344"--密码 wifi.ap.config(cfg)--写入配置 wifi.sta.config("qqqqq","11223344")--模块连接的路由器名称和密码 wifi.sta.connect() tmr.alarm(2, 3000, 1, function()--3s检测一次是否连接上路由器 if wifi.sta.getip() ~= nil then tmr.stop(2) print("Connected, IP is "..wifi.sta.getip())--打印分得的IP end end) srv=net.createServer(net.TCP,28800)--创建TCP服务 tmr.alarm(3, 1000, 1, function()--1s进入一次看是否需要建立新的监听 if ConnectCnt == 0 then srv:listen(8080,function(conn0) print("Connect0") ConnectCnt = 1 if connect1 ~= nil then connect1:close() end connect0 = conn0 conn0:on("receive",function(conn0,payload) print(payload) end) end) end if ConnectCnt == 1 then srv:listen(8080,function(conn1) print("Connect1") ConnectCnt = 2 if connect2 ~= nil then connect2:close() end connect1 = conn1 conn1:on("receive",function(conn1,payload) print(payload) end) end) end if ConnectCnt == 2 then srv:listen(8080,function(conn2) print("Connect2") ConnectCnt = 3 if connect3 ~= nil then connect3:close() end connect2 = conn2 conn2:on("receive",function(conn2,payload) print(payload) end) end) end if ConnectCnt == 3 then srv:listen(8080,function(conn3) print("Connect3") ConnectCnt = 0 if connect0 ~= nil then connect0:close() end connect3 = conn3 conn3:on("receive",function(conn3,payload) print(payload) end) end) end end) uart.setup(0,9600,8,0,1,0) uart.on("data", function(data) if connect0 ~= nil then connect0:send(data) end if connect1 ~= nil then connect1:send(data) end if connect2 ~= nil then connect2:send(data) end if connect3 ~= nil then connect3:send(data) end end, 0)
这次的建立服务可以连接3个客户端,也不知道到底能连接多少个,,,没测试,,就先写了3个
注意看一下哈,我是这样设置的
if ConnectCnt == 0 then srv:listen(8080,function(conn0) print("Connect0") ConnectCnt = 1 if connect1 ~= nil then connect1:close() end connect0 = conn0 conn0:on("receive",function(conn0,payload) print(payload) end) end) end
conn0连接就把conn1关掉,
conn1连接就把conn2关掉,
conn2连接就把conn3关掉,
conn03接就把conn0关掉,
所以呢现在做的最多可以3个客户端连接,,,,改天试一试能不能做到65535个连接......看看最多能连接多少个
最后的串口发送就不用说了,,,只是在原先的基础上先判断了是不是 nil 然后再发送
那就测试一下
连接模块的无线
其实也可以不用连接模块的无线,,因为模块连接路由器了,所以电脑或者手机连接路由器后可以连接模块连接路由器后分得的IP来测试
就用这四个来测试,用串口助手看信息,,对了模块默认内部IP:192.168.4.1
然后我挨个点击连接
现在连接了三个
现在发送数据
好再连接一个
再看一下发送数据
wifi模块算是做好了
对了可以连接路由器测试
只不过IP地址是刚才模块连接路由器后分得的IP
连接路由器的好处是可以做到远程控制
现在做上位机部分
整体规划成这样
先写个函数获取本机的IP地址,然后填写到IP地址框
/// <获取本机 IP 地址> /// /// ///private void getIPAddress() { IPAddress[] hostipspool = Dns.GetHostAddresses(""); comboBoxIPAdress.Items.Clear(); foreach (IPAddress ipa in hostipspool) { if (ipa.AddressFamily == AddressFamily.InterNetwork) { comboBoxIPAdress.Items.Add(ipa.ToString()); comboBoxIPAdress.SelectedIndex = comboBoxIPAdress.Items.Count > 0 ? 0 : -1; } } }
程序加载的时候就搜索一下
private void Form1_Load(object sender, EventArgs e) { getIPAddress(); }
然后在点击下拉框IP地址的下拉框的时候再搜索一下
/// <点击IP下拉框> /// /// /// /// private void comboBoxIPAdress_DropDown(object sender, EventArgs e) { getIPAddress(); }
启动一下看效果
这两个IP是这两个的
现在写点击按钮连接函数
先定义一个连接线程,一个保存IP地址的变量,一个保存端口号的变量,一个连接断开的标志变量,还有TcpClient
public partial class Form1 : Form { private Thread ConnectThread;//连接线程 private IPAddress ipAddress;//ip地址 int Port = 0;//端口号 Boolean ConnectFlage = false;//连接标志 private TcpClient myTcpClient = null;// TcpClient public Form1() { InitializeComponent(); }
然后写个连接函数
/// <连接线程方法> /// /// private void ConnectMethod() { myTcpClient = new TcpClient(); //实例化myTcpClient try { myTcpClient.Connect(ipAddress, Port);//连接服务器 buttonConnect.Invoke(buttonConnectDelegate, "断开"); ConnectFlage = true; } catch (Exception) { //异常处理函数 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { myTcpClient.Close(); } catch { } } }
然后在点击事件中获取IP和端口号,然后启动连接任务
/// <连接点击事件> /// /// /// /// private void buttonConnect_Click(object sender, EventArgs e) { if (ConnectFlage == false) { if (string.IsNullOrEmpty(comboBoxIPAdress.Text) == false) { ipAddress = IPAddress.Parse(comboBoxIPAdress.Text);//获取IP地址 Port = Convert.ToInt32(textBoxPort.Text); //获取端口号 ConnectThread = new Thread(ConnectMethod); ConnectThread.Start(); } else { MessageBox.Show("请检查IP地址!", "提示"); } } else { //断开处理函数 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { myTcpClient.Close(); } catch { } } }
对了写个按钮的回调来显示按钮的连接和断开
Boolean ConnectFlage = false;//连接标志 private TcpClient myTcpClient = null;// TcpClient private delegate void ButtonConnectDelegate(string str);//定义连接按钮回调 ButtonConnectDelegate buttonConnectDelegate; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { buttonConnectDelegate = new ButtonConnectDelegate(buttonConnectMethod);//实例化 getIPAddress(); }
回调函数
/// <打开按钮回调函数> /// /// private void buttonConnectMethod(string str) { buttonConnect.Text = str; }
现在可以测试一下连接模块
对了这个现在模块好像是很长时间不和模块通信,模块就自动断开了服务,,,,好像为了低功耗吗....最后看一下怎么唤醒他,我现在是复位了一下
现在做一个函数来接收数据,然后把接收的数据显示到数据接收的显示框,数据框的回调函数也是必不可少的,还有定义一个接收任务
关于回调可以看一下
先定义一个networkstrem用来接收和发送网络数据流
其实C#的和JAVA的很类似
可以看一下
看一下现在的工程
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Windows.Forms; namespace TCPClient { public partial class Form1 : Form { private Thread ConnectThread;//连接线程 private IPAddress ipAddress;//ip地址 int Port = 0;//端口号 Boolean ConnectFlage = false;//连接标志 private TcpClient myTcpClient = null;// TcpClient private delegate void ButtonConnectDelegate(string str);//定义连接按钮回调 ButtonConnectDelegate buttonConnectDelegate; private delegate void ShowReMsgTcpDelegate(byte[] by);//定义显示TCP接收消息回调 private ShowReMsgTcpDelegate showReMsgTcpDelegate; private NetworkStream networkstrem = null;//网络数据流 private Thread ReceiveThread;//接收消息线程 public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { buttonConnectDelegate = new ButtonConnectDelegate(buttonConnectMethod);//实例化 showReMsgTcpDelegate = new ShowReMsgTcpDelegate(ShowReMsgTcpMethod);//实例化显示回调 getIPAddress(); } /// <获取本机 IP 地址> /// /// ///private void getIPAddress() { IPAddress[] hostipspool = Dns.GetHostAddresses(""); comboBoxIPAdress.Items.Clear(); foreach (IPAddress ipa in hostipspool) { if (ipa.AddressFamily == AddressFamily.InterNetwork) { comboBoxIPAdress.Items.Add(ipa.ToString()); comboBoxIPAdress.SelectedIndex = comboBoxIPAdress.Items.Count > 0 ? 0 : -1; } } } /// <点击IP下拉框> /// /// /// /// private void comboBoxIPAdress_DropDown(object sender, EventArgs e) { getIPAddress(); } /// <连接点击事件> /// /// /// /// private void buttonConnect_Click(object sender, EventArgs e) { if (ConnectFlage == false) { if (string.IsNullOrEmpty(comboBoxIPAdress.Text) == false) { ipAddress = IPAddress.Parse(comboBoxIPAdress.Text);//获取IP地址 Port = Convert.ToInt32(textBoxPort.Text); //获取端口号 ConnectThread = new Thread(ConnectMethod); ConnectThread.Start(); } else { MessageBox.Show("请检查IP地址!", "提示"); } } else { //断开处理函数 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { ReceiveThread.Abort(); } catch { } try { networkstrem.Dispose(); } catch { } try { myTcpClient.Close(); } catch { } } } /// <连接线程方法> /// /// private void ConnectMethod() { myTcpClient = new TcpClient(); //实例化myTcpClient try { myTcpClient.Connect(ipAddress, Port);//连接服务器 networkstrem = myTcpClient.GetStream();//获取数据流 ReceiveThread = new Thread(ReceiveDataMethod);//启动接收数据任务 ReceiveThread.Start(); buttonConnect.Invoke(buttonConnectDelegate, "断开"); ConnectFlage = true; } catch (Exception) { //异常处理函数 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { ReceiveThread.Abort(); }//销毁任务 catch { } try { networkstrem.Dispose(); }//释放资源 catch { } try { myTcpClient.Close(); }//关闭TCP catch { } } } /// <打开按钮回调函数> /// /// private void buttonConnectMethod(string str) { buttonConnect.Text = str; } /// <显示串口接收到的信息--回调函数> /// /// private void ShowReMsgTcpMethod(byte[] by) { string getMsg = string.Empty; if (checkBoxHexShow.Checked)//16进制显示 { getMsg = byteToHexStr(by); //用到函数byteToHexStr--字节数组转16进制字符串 } else { getMsg = new ASCIIEncoding().GetString(by); } textBoxDataRes.AppendText(getMsg); } /// <字节数组转16进制字符串> /// /// /// /// public static string byteToHexStr(byte[] bytes) { string returnStr = ""; try { if (bytes != null) { for (int i = 0; i < bytes.Length; i++) { returnStr += bytes[i].ToString("X2"); } } return returnStr; } catch (Exception) { return returnStr; } } /// <接收消息线程> /// /// private void ReceiveDataMethod() { int RecvCnt = 0; byte[] recvBytes = new byte[1024]; while (true) { try { if ((myTcpClient.Client.Poll(20, SelectMode.SelectRead)) && (myTcpClient.Client.Available == 0)) { myTcpClient.Close();//引发异常 } RecvCnt = networkstrem.Read(recvBytes, 0, recvBytes.Length); byte[] receive = new byte[RecvCnt];//设置缓冲区 RecvCnt 个字节 for (int i = 0; i < receive.Length; i++) { receive[i] = recvBytes[i]; } textBoxDataRes.Invoke(showReMsgTcpDelegate, receive); } catch (Exception) { //异常处理函数 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { ReceiveThread.Abort(); }//销毁任务 catch { } try { networkstrem.Dispose(); }//释放资源 catch { } try { myTcpClient.Close(); }//关闭TCP catch { } } } } } }
看一下显示回调函数
/// <显示串口接收到的信息--回调函数> /// /// private void ShowReMsgTcpMethod(byte[] by) { string getMsg = string.Empty; if (checkBoxHexShow.Checked)//16进制显示 { getMsg = byteToHexStr(by); //用到函数byteToHexStr--字节数组转16进制字符串 } else { getMsg = new ASCIIEncoding().GetString(by); } textBoxDataRes.AppendText(getMsg); }
这个函数 byteToHexStr(by); //用到函数byteToHexStr--字节数组转16进制字符串
/// <字节数组转16进制字符串> /// /// /// ///public static string byteToHexStr(byte[] bytes) { string returnStr = ""; try { if (bytes != null) { for (int i = 0; i < bytes.Length; i++) { returnStr += bytes[i].ToString("X2"); } } return returnStr; } catch (Exception) { return returnStr; } }
那么接收数据的函数
/// <接收消息线程> /// /// private void ReceiveDataMethod() { int RecvCnt = 0; byte[] recvBytes = new byte[1024]; while (true) { try { if ((myTcpClient.Client.Poll(20, SelectMode.SelectRead)) && (myTcpClient.Client.Available == 0)) { myTcpClient.Close();//引发异常 } RecvCnt = networkstrem.Read(recvBytes, 0, recvBytes.Length); byte[] receive = new byte[RecvCnt];//设置缓冲区 RecvCnt 个字节 for (int i = 0; i < receive.Length; i++) { receive[i] = recvBytes[i]; } textBoxDataRes.Invoke(showReMsgTcpDelegate, receive); } catch (Exception) { //异常处理函数 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { ReceiveThread.Abort(); }//销毁任务 catch { } try { networkstrem.Dispose(); }//释放资源 catch { } try { myTcpClient.Close(); }//关闭TCP catch { } } } }
里面的这里是判断TCP是不是断开了连接,,如果断开了执行myTcpClient.Close()就会引发异常
if ((myTcpClient.Client.Poll(20, SelectMode.SelectRead)) && (myTcpClient.Client.Available == 0))
{
myTcpClient.Close();//引发异常
}
现在连接模块然后用串口助手发数据
已经能接收到数据了,现在呢,做发送数据
/// <发送按钮点击事件> /// /// /// /// private void buttonSend_Click(object sender, EventArgs e) { if (!checkBoxHexSend.Checked)//字符发送 { byte[] sendbyte = Encoding.Default.GetBytes(textBoxSend.Text); try { networkstrem.Write(sendbyte, 0, sendbyte.Length); } catch (Exception) { MessageBox.Show("请检查连接", "提示!"); } } else//16形式进制发送 { byte[] sendbyte = strToToHexByte(textBoxSend.Text);//字符串转16进制格式,不够自动前面补零 try { networkstrem.Write(sendbyte, 0, sendbyte.Length); } catch (Exception) { MessageBox.Show("请检查连接", "提示!"); } } }
/// <字符串转16进制格式,不够自动前面补零> /// /// /// ///private static byte[] strToToHexByte(String hexString) { int i; bool Flag = false; hexString = hexString.Replace(" ", "");//清除空格 if ((hexString.Length % 2) != 0) { Flag = true; } if (Flag == true) { byte[] returnBytes = new byte[(hexString.Length + 1) / 2]; try { for (i = 0; i < (hexString.Length - 1) / 2; i++) { returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, '0'), 16); } catch { for (i = 0; i < returnBytes.Length; i++) { returnBytes[i] = 0; } MessageBox.Show("超过16进制范围A-F,已初始化为0", "提示"); } return returnBytes; } else { byte[] returnBytes = new byte[(hexString.Length) / 2]; try { for (i = 0; i < returnBytes.Length; i++) { returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } } catch { for (i = 0; i < returnBytes.Length; i++) { returnBytes[i] = 0; } MessageBox.Show("超过16进制范围A-F,已初始化为0", "提示"); } return returnBytes; } }
现在测试一下发送数据
对了
当点击这两个的时候,对应数据框里面的内容应该相应的变化
直接在点击函数里面设置
/// <发送Hex和字符串选择> /// /// /// /// private void checkBoxHexSend_CheckedChanged(object sender, EventArgs e) { if (checkBoxHexSend.Checked) { try { byte[] by = StringToByte(textBoxSend.Text); textBoxSend.Clear(); textBoxSend.BeginInvoke(showSeMsgTcpDelegate, by); } catch (Exception) { //MessageBox.Show(ex.ToString()); } } else { try { byte[] by = strToToHexByte(textBoxSend.Text); textBoxSend.Clear(); textBoxSend.BeginInvoke(showSeMsgTcpDelegate, by); } catch (Exception) { //MessageBox.Show(ex.ToString()); } } } /// <接收Hex和字符串选择> /// /// /// /// private void checkBoxHexShow_CheckedChanged(object sender, EventArgs e) { if (checkBoxHexShow.Checked) { try { byte[] by = StringToByte(textBoxDataRes.Text); textBoxDataRes.BeginInvoke(showReMsgTcpDelegate, by); } catch (Exception) { //MessageBox.Show(ex.ToString()); } } else { try { byte[] by = strToToHexByte(textBoxDataRes.Text); textBoxDataRes.Clear(); textBoxDataRes.BeginInvoke(showReMsgTcpDelegate, by); } catch (Exception) { //MessageBox.Show(ex.ToString()); } } }
其实在优化应是用回调来做,,,,等做下一个画波形图的时候在说,现在做界面看不出来卡顿
现在呢做的差不多了,,规定一下协议
上位机发送 0xaa,0x55,0x01,CRC//读取采集的电压数据 单片机返回 0xaa,0x55,0x01,采集电压数据AD0(4字节Float型),AD1,AD2,AD3,CRC//单片机返回 //上位机发送 0xaa,0x55,0x02,设置的输出电压(单字节),CRC//设置输出的电压
就写一个任务,,方便点用定时器吧,,然后如果判断连接上服务器了每隔100ms发送一次采集数据的命令
/// <定时发送事件> /// /// /// /// private void timer1_Tick(object sender, EventArgs e) { byte[] sendbyte = new byte[3]; sendbyte[0] = 0xaa; sendbyte[1] = 0x55; sendbyte[2] = 0x01; SerialSend(sendbyte); }
/// <网络发送数据函数> /// /// /// private void SerialSend(byte[] byt) { int crc = 0; byte[] sendbyte = new byte[byt.Length + 2]; for (int i = 0; i < byt.Length; i++) { sendbyte[i] = byt[i]; } crc = crc16_modbus(byt, byt.Length);//计算CRC byte[] Crcbyte = System.BitConverter.GetBytes(crc);//得到CRC sendbyte[sendbyte.Length - 2] = Crcbyte[0]; sendbyte[sendbyte.Length - 1] = Crcbyte[1]; try { networkstrem.Write(sendbyte, 0, sendbyte.Length); } catch (Exception) { //异常处理函数 ConnectFlage = false; timer1.Stop();//停止定时器 buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { ReceiveThread.Abort(); }//销毁任务 catch { } try { networkstrem.Dispose(); }//释放资源 catch { } try { myTcpClient.Close(); }//关闭TCP catch { } MessageBox.Show("请检查连接", "提示!"); } }
定时器呢在这里面启动
/// <打开按钮回调函数> /// /// private void buttonConnectMethod(string str) { if (str == "断开") { timer1.Enabled = true; timer1.Start();//启动定时器 } else { timer1.Enabled = false; timer1.Stop();//停止定时器 } buttonConnect.Text = str; }
现在的代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Windows.Forms; namespace TCPClient { public partial class Form1 : Form { private Thread ConnectThread;//连接线程 private IPAddress ipAddress;//ip地址 int Port = 0;//端口号 Boolean ConnectFlage = false;//连接标志 private TcpClient myTcpClient = null;// TcpClient private delegate void ButtonConnectDelegate(string str);//定义连接按钮回调 ButtonConnectDelegate buttonConnectDelegate; private delegate void ShowReMsgTcpDelegate(byte[] by);//定义显示TCP接收消息回调 private ShowReMsgTcpDelegate showReMsgTcpDelegate; private NetworkStream networkstrem = null;//网络数据流 private Thread ReceiveThread;//接收消息线程 private Thread ShowDataThread;//显示任务,点击Hex显示时 private delegate void ShowSeMsgTcpDelegate(byte[] by);//定义显示TCP发送消息回调 private ShowSeMsgTcpDelegate showSeMsgTcpDelegate; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { timer1.Stop();//启动定时器 buttonConnectDelegate = new ButtonConnectDelegate(buttonConnectMethod);//实例化 showReMsgTcpDelegate = new ShowReMsgTcpDelegate(ShowReMsgTcpMethod);//实例化显示回调 showSeMsgTcpDelegate = new ShowSeMsgTcpDelegate(showSeMsgTcpMethod);//实例化发送回调 getIPAddress(); } /// <获取本机 IP 地址> /// /// ///private void getIPAddress() { IPAddress[] hostipspool = Dns.GetHostAddresses(""); comboBoxIPAdress.Items.Clear(); foreach (IPAddress ipa in hostipspool) { if (ipa.AddressFamily == AddressFamily.InterNetwork) { comboBoxIPAdress.Items.Add(ipa.ToString()); comboBoxIPAdress.SelectedIndex = comboBoxIPAdress.Items.Count > 0 ? 0 : -1; } } } /// <点击IP下拉框> /// /// /// /// private void comboBoxIPAdress_DropDown(object sender, EventArgs e) { getIPAddress(); } /// <连接点击事件> /// /// /// /// private void buttonConnect_Click(object sender, EventArgs e) { if (ConnectFlage == false) { if (string.IsNullOrEmpty(comboBoxIPAdress.Text) == false) { ipAddress = IPAddress.Parse(comboBoxIPAdress.Text);//获取IP地址 Port = Convert.ToInt32(textBoxPort.Text); //获取端口号 ConnectThread = new Thread(ConnectMethod); ConnectThread.Start(); } else { MessageBox.Show("请检查IP地址!", "提示"); } } else { //断开处理函数 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { ReceiveThread.Abort(); } catch { } try { networkstrem.Dispose(); } catch { } try { myTcpClient.Close(); } catch { } } } /// <连接线程方法> /// /// private void ConnectMethod() { myTcpClient = new TcpClient(); //实例化myTcpClient try { myTcpClient.Connect(ipAddress, Port);//连接服务器 networkstrem = myTcpClient.GetStream();//获取数据流 ReceiveThread = new Thread(ReceiveDataMethod);//启动接收数据任务 ReceiveThread.Start(); buttonConnect.Invoke(buttonConnectDelegate, "断开"); ConnectFlage = true; } catch (Exception) { //异常处理函数 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { ReceiveThread.Abort(); }//销毁任务 catch { } try { networkstrem.Dispose(); }//释放资源 catch { } try { myTcpClient.Close(); }//关闭TCP catch { } } } /// <打开按钮回调函数> /// /// private void buttonConnectMethod(string str) { if (str == "断开") { timer1.Enabled = true; timer1.Start();//启动定时器 } else { timer1.Enabled = false; timer1.Stop();//停止定时器 } buttonConnect.Text = str; } /// <显示串口接收到的信息--回调函数> /// /// private void ShowReMsgTcpMethod(byte[] by) { string getMsg = string.Empty; if (checkBoxHexShow.Checked)//16进制显示 { getMsg = byteToHexStr(by); //用到函数byteToHexStr--字节数组转16进制字符串 } else { getMsg = new ASCIIEncoding().GetString(by); } textBoxDataRes.AppendText(getMsg); } private void showSeMsgTcpMethod(byte[] by) { string getMsg = string.Empty; if (checkBoxHexSend.Checked)//16进制显示 { getMsg = byteToHexStr(by); //用到函数byteToHexStr--字节数组转16进制字符串 } else { getMsg = new ASCIIEncoding().GetString(by); } textBoxSend.AppendText(getMsg); } /// <字节数组转16进制字符串> /// /// /// /// public static string byteToHexStr(byte[] bytes) { string returnStr = ""; try { if (bytes != null) { for (int i = 0; i < bytes.Length; i++) { returnStr += bytes[i].ToString("X2"); } } return returnStr; } catch (Exception) { return returnStr; } } /// <接收消息线程> /// /// private void ReceiveDataMethod() { int RecvCnt = 0; byte[] recvBytes = new byte[1024]; while (true) { try { if ((myTcpClient.Client.Poll(20, SelectMode.SelectRead)) && (myTcpClient.Client.Available == 0)) { myTcpClient.Close();//引发异常 } RecvCnt = networkstrem.Read(recvBytes, 0, recvBytes.Length); byte[] receive = new byte[RecvCnt];//设置缓冲区 RecvCnt 个字节 for (int i = 0; i < receive.Length; i++) { receive[i] = recvBytes[i]; } textBoxDataRes.Invoke(showReMsgTcpDelegate, receive); } catch (Exception) { //异常处理函数 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { ReceiveThread.Abort(); }//销毁任务 catch { } try { networkstrem.Dispose(); }//释放资源 catch { } try { myTcpClient.Close(); }//关闭TCP catch { } } } } /// <发送按钮点击事件> /// /// /// /// private void buttonSend_Click(object sender, EventArgs e) { if (!checkBoxHexSend.Checked)//字符发送 { byte[] sendbyte = Encoding.Default.GetBytes(textBoxSend.Text); try { networkstrem.Write(sendbyte, 0, sendbyte.Length); } catch (Exception) { MessageBox.Show("请检查连接", "提示!"); } } else//16形式进制发送 { byte[] sendbyte = strToToHexByte(textBoxSend.Text);//字符串转16进制格式,不够自动前面补零 try { networkstrem.Write(sendbyte, 0, sendbyte.Length); } catch (Exception) { MessageBox.Show("请检查连接", "提示!"); } } } /// <字符串转16进制格式,不够自动前面补零> /// /// /// /// private static byte[] strToToHexByte(String hexString) { int i; bool Flag = false; hexString = hexString.Replace(" ", "");//清除空格 if ((hexString.Length % 2) != 0) { Flag = true; } if (Flag == true) { byte[] returnBytes = new byte[(hexString.Length + 1) / 2]; try { for (i = 0; i < (hexString.Length - 1) / 2; i++) { returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, '0'), 16); } catch { for (i = 0; i < returnBytes.Length; i++) { returnBytes[i] = 0; } MessageBox.Show("超过16进制范围A-F,已初始化为0", "提示"); } return returnBytes; } else { byte[] returnBytes = new byte[(hexString.Length) / 2]; try { for (i = 0; i < returnBytes.Length; i++) { returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } } catch { for (i = 0; i < returnBytes.Length; i++) { returnBytes[i] = 0; } MessageBox.Show("超过16进制范围A-F,已初始化为0", "提示"); } return returnBytes; } } /// <字符串转换成字节数组> /// /// /// /// public static byte[] StringToByte(string stringToConvert) { return (new ASCIIEncoding()).GetBytes(stringToConvert); } /// <发送Hex和字符串选择> /// /// /// /// private void checkBoxHexSend_CheckedChanged(object sender, EventArgs e) { if (checkBoxHexSend.Checked) { try { byte[] by = StringToByte(textBoxSend.Text); textBoxSend.Clear(); textBoxSend.BeginInvoke(showSeMsgTcpDelegate, by); } catch (Exception) { //MessageBox.Show(ex.ToString()); } } else { try { byte[] by = strToToHexByte(textBoxSend.Text); textBoxSend.Clear(); textBoxSend.BeginInvoke(showSeMsgTcpDelegate, by); } catch (Exception) { //MessageBox.Show(ex.ToString()); } } } /// <接收Hex和字符串选择> /// /// /// /// private void checkBoxHexShow_CheckedChanged(object sender, EventArgs e) { if (checkBoxHexShow.Checked) { try { byte[] by = StringToByte(textBoxDataRes.Text); textBoxDataRes.BeginInvoke(showReMsgTcpDelegate, by); } catch (Exception) { //MessageBox.Show(ex.ToString()); } } else { try { byte[] by = strToToHexByte(textBoxDataRes.Text); textBoxDataRes.Clear(); textBoxDataRes.BeginInvoke(showReMsgTcpDelegate, by); } catch (Exception) { //MessageBox.Show(ex.ToString()); } } } /// <清除接收> /// /// /// /// private void buttonClearRecv_Click(object sender, EventArgs e) { textBoxDataRes.Clear(); } /// <清除发送> /// /// /// /// private void buttonClearSend_Click(object sender, EventArgs e) { textBoxSend.Clear(); } /// <设置DA输出> /// /// /// /// private void trackBarDAout_Scroll(object sender, EventArgs e) { textBoxDAout.Text = trackBarDAout.Value.ToString(); } /// /// /// /// /// /// private int crc16_modbus(byte[] modbusdata, int length) { int i, j; int crc = 0xffff; try { for (i = 0; i < length; i++) { crc ^= modbusdata[i]; for (j = 0; j < 8; j++) { if ((crc & 0x01) == 1) { crc = (crc >> 1) ^ 0xa001; } else { crc >>= 1; } } } } catch (Exception) { throw; } return crc; } /* modbusdata 要校验的数据 length 数据长度 返回值 1 正确 0 错误 */ private int crc16_flage(byte[] modbusdata, int length) { int Receive_CRC = 0, calculation = 0;//接收到的CRC,计算的CRC Receive_CRC = crc16_modbus(modbusdata, length); calculation = modbusdata[length + 1]; calculation <<= 8; calculation += modbusdata[length]; if (calculation != Receive_CRC) { return 0; } return 1; } /// <定时发送事件> /// /// /// /// private void timer1_Tick(object sender, EventArgs e) { byte[] sendbyte = new byte[3]; sendbyte[0] = 0xaa; sendbyte[1] = 0x55; sendbyte[2] = 0x01; SerialSend(sendbyte); } /// <网络发送数据函数> /// /// /// private void SerialSend(byte[] byt) { int crc = 0; byte[] sendbyte = new byte[byt.Length + 2]; for (int i = 0; i < byt.Length; i++) { sendbyte[i] = byt[i]; } crc = crc16_modbus(byt, byt.Length);//计算CRC byte[] Crcbyte = System.BitConverter.GetBytes(crc);//得到CRC sendbyte[sendbyte.Length - 2] = Crcbyte[0]; sendbyte[sendbyte.Length - 1] = Crcbyte[1]; try { networkstrem.Write(sendbyte, 0, sendbyte.Length); } catch (Exception) { //异常处理函数 ConnectFlage = false; timer1.Stop();//停止定时器 buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { ReceiveThread.Abort(); }//销毁任务 catch { } try { networkstrem.Dispose(); }//释放资源 catch { } try { myTcpClient.Close(); }//关闭TCP catch { } MessageBox.Show("请检查连接", "提示!"); } } } }
然后呢,设置一下这个好弄得
协议的规定
//上位机发送
0xaa,0x55,0x02,设置的输出电压(单字节),CRC//设置输出的电压
/// <设置DA输出> /// /// /// /// private void trackBarDAout_Scroll(object sender, EventArgs e) { byte[] sendbyte = new byte[4]; sendbyte[0] = 0xaa; sendbyte[1] = 0x55; sendbyte[2] = 0x02; sendbyte[3] = Convert.ToByte(trackBarDAout.Value); SerialSend(sendbyte); textBoxDAout.Text = trackBarDAout.Value.ToString(); }
只要滑动这个滑动条就把当前滑动条的数据发给Wifi模块
现在写接收数据的函数,然后显示在相应的位置
/// <显示串口接收到的信息--回调函数> /// /// private void ShowReMsgTcpMethod(byte[] by) { string getMsg = string.Empty; if (checkBoxHexShow.Checked)//16进制显示 { getMsg = byteToHexStr(by); //用到函数byteToHexStr--字节数组转16进制字符串 } else { getMsg = new ASCIIEncoding().GetString(by); } if (by[0] == 0xaa && by[1] == 0x55) { if (crc16_flage(by, by.Length - 2) == 1)//CRC校验正确 { if (by[2] == 0x01) { float f1 = BitConverter.ToSingle(by, 3); float f2 = BitConverter.ToSingle(by, 7); float f3 = BitConverter.ToSingle(by, 11); float f4 = BitConverter.ToSingle(by, 15); textBoxAD0.Text = f1.ToString(); textBoxAD1.Text = f2.ToString(); textBoxAD2.Text = f3.ToString(); textBoxAD3.Text = f4.ToString(); } } } textBoxDataRes.AppendText(getMsg); }
就加上这几句
if (by[0] == 0xaa && by[1] == 0x55) { if (crc16_flage(by, by.Length - 2) == 1)//CRC校验正确 { if (by[2] == 0x01) { float f1 = BitConverter.ToSingle(by, 3); float f2 = BitConverter.ToSingle(by, 7); float f3 = BitConverter.ToSingle(by, 11); float f4 = BitConverter.ToSingle(by, 15); textBoxAD0.Text = f1.ToString(); textBoxAD1.Text = f2.ToString(); textBoxAD2.Text = f3.ToString(); textBoxAD3.Text = f4.ToString(); } } }
关于FLoat的转换,先给大家一篇文章
链接:http://pan.baidu.com/s/1ck2m7O%20密码:rmsg
单片机程序中会做转换介绍,其实就是用共用体,,上位机呢就是用那个方法解析
现在测试一下,把数据220.5发给上位机
好了上位机测试没有问题了
我后来改了一些地方
协议变成了一条
0xaa,0x55,0x01,(设置的电压值),CRC16校验
/// <定时发送事件> /// /// /// /// private void timer1_Tick(object sender, EventArgs e) { byte[] sendbyte = new byte[4]; sendbyte[0] = 0xaa; sendbyte[1] = 0x55; sendbyte[2] = 0x01; sendbyte[3] = trackBarValue[0];//滑动条的值 SerialSend(sendbyte); }
滑动控件的事件
/// <设置DA输出> /// /// /// /// private void trackBarDAout_Scroll(object sender, EventArgs e) { trackBarValue[0] = Convert.ToByte(trackBarDAout.Value); textBoxDAout.Text = trackBarDAout.Value.ToString(); }
这里面加了个try,,是因为不能保证数据传输的时候没有错误的时候
/// <显示串口接收到的信息--回调函数> /// /// private void ShowReMsgTcpMethod(byte[] by) { string getMsg = string.Empty; try { if (checkBoxHexShow.Checked)//16进制显示 { getMsg = byteToHexStr(by); //用到函数byteToHexStr--字节数组转16进制字符串 } else { getMsg = new ASCIIEncoding().GetString(by); } if (by[0] == 0xaa && by[1] == 0x55) { if (crc16_flage(by, by.Length - 2) == 1)//CRC校验正确 { if (by[2] == 0x01) { float f1 = BitConverter.ToSingle(by, 3); float f2 = BitConverter.ToSingle(by, 7); float f3 = BitConverter.ToSingle(by, 11); float f4 = BitConverter.ToSingle(by, 15); textBoxAD0.Text = f1.ToString(); textBoxAD1.Text = f2.ToString(); textBoxAD2.Text = f3.ToString(); textBoxAD3.Text = f4.ToString(); } } } } catch (Exception) { } textBoxDataRes.AppendText(getMsg); }
现在做单片机的程序
程序呢说几个地方
typedef union Resolverf//51是大端模式,32默认小端模式 { float Data; unsigned char Data_Table[4]; }ResolverfData; typedef union ResolverLong { long Data; unsigned char Data_Table[4]; }ResolverLongData;
最后做完了发现了一个问题,这个模块刷入Lua语言后,把数据通过WIFI发给模块,模块内部默认的是遇到'\0'后认为是一条数据,,所以呢
当发送的byte数组里面的值是0的时候数据就会出现问题,直接把0以前的数据发过去了.
所以自己做了下设置
/// <网络发送数据函数> /// /// /// private void SerialSend(byte[] byt) { int crc = 0; byte[] sendbyte = new byte[byt.Length + 4];//加一个检测数据的,CRC,最后结尾判断CRC byte[] Change0 = new byte[1];//检测数据用 Change0[0] = 0xff;//初始化为FF,哪位数据为0就把相应的数据位变为1,检测的为变为0 for (int i = 0; i < byt.Length; i++) { sendbyte[i] = byt[i]; if (sendbyte[i] == 0x00) { sendbyte[i] = 0x01; switch(i) { case 0: Change0[0] &= 0xfe; break; case 1: Change0[0] &= 0xfd; break; case 2: Change0[0] &= 0xfb; break; case 3: Change0[0] &= 0xf7; break; case 4: Change0[0] &= 0xef; break; case 5: Change0[0] &= 0xdf; break; case 6: Change0[0] &= 0xbf; break; } } } sendbyte[sendbyte.Length-4] = Change0[0]; crc = crc16_modbus(sendbyte, sendbyte.Length-3);//计算CRC byte[] Crcbyte = System.BitConverter.GetBytes(crc);//得到CRC sendbyte[sendbyte.Length - 3] = Crcbyte[0]; sendbyte[sendbyte.Length - 2] = Crcbyte[1]; sendbyte[sendbyte.Length - 1] = 0xff; if (sendbyte[sendbyte.Length - 3] == 0x00) { sendbyte[sendbyte.Length - 3] = 0x01; sendbyte[sendbyte.Length - 1] = 0xF0; } if (sendbyte[sendbyte.Length - 2] == 0x00) { sendbyte[sendbyte.Length - 2] = 0x01; sendbyte[sendbyte.Length - 1] = 0x0F; } try { if (networkstrem.CanWrite) { networkstrem.Write(sendbyte, 0, sendbyte.Length); } } catch (Exception) { //异常处理函数 ConnectFlage = false; timer1.Stop();//停止定时器 buttonConnect.Invoke(buttonConnectDelegate, "连接"); try { ReceiveThread.Abort(); }//销毁任务 catch { } try { networkstrem.Dispose(); }//释放资源 catch { } try { myTcpClient.Close(); }//关闭TCP catch { } MessageBox.Show("请检查连接", "提示!"); } }
想法就是在数据的后面用一个字节来标识前面的数据哪些是0,对了如果数据是0,就把数据改为1,
0xaa, 0x55, 0x01, DA值, 标识前面哪些数据是0, CRC低位, CRC高位, 标识CRC有没有0
假如说 DA值是0,那么把数据变成0x01,然后标识下
0xaa,0x55,0x01,0x01,0xf7,CRC低位,CRC高位,0xFF(假设校验没有0)
如果CRC低位也是0那么
0xaa,0x55,0x01,0x01,0xf7,CRC低位,CRC高位,0xF0
如果CRC高位也是0那么
0xaa,0x55,0x01,0x01,0xf7,CRC低位,CRC高位,0x0F
其实可以用字符串格式发送,就不用麻烦了
单片机解析
/** * @brief 数据发送方法 * @param * @param None * @param None * @retval None * @example **/ void DataSendMethod() { if(UsartFlage == 1)//接收到数据 { if(UsartReceive[7] != 0xff)//CRC校验有0数据 { if(UsartReceive[7] == 0xf0)//看一下CRC低位是0还是高位是0 {UsartReceive[5] = 0;} if(UsartReceive[7] == 0x0f) {UsartReceive[6] = 0;} } if(crc16_flage(UsartReceive, UsartReadCntCopy-5))//校验数据 { if(UsartReceive[4]!=0xFF)//有0数据 { switch(UsartReceive[4])//找到哪一位是0数据,修改过来 { case 0xFE: UsartReceive[0] = 0; break;//0位 case 0xFD: UsartReceive[1] = 0; break;//1位 case 0xFB: UsartReceive[2] = 0; break;//2位 case 0xF7: UsartReceive[3] = 0; break;//3位 case 0xEF: UsartReceive[4] = 0; break;//4位 case 0xDF: UsartReceive[5] = 0; break;//5位 case 0xBF: UsartReceive[6] = 0; break;//6位 } } if(UsartReceive[0] == 0xaa && UsartReceive[1] == 0x55) { UsartFlage = 0; switch(UsartReceive[2]) { case 0x01: SetDAValue(); SendADValue(); break;//设置输出的电压,发送采集的电压 default: break; } } } else { UsartFlage = 0; } } }
一,关于大小端
关于大小端---数据的低位存在低地址,高位存在高地址就是小端模式
关于大小端---数据的低位存在高地址,高位存在低地址就是大端模式
传输float数据直接用共用体解决,可以看看上面的青智仪表的文章,,,这个用的挺多的,,几乎所有设备的浮点型数据都是这样存储的,,,,,,
二,51单片机使用printf,,,我程序里能使用printf,,不过说一点注意的地方
如果51想用printf发送数据,加上这个函数,其实也可以不加,,,不过必须在初始化的时候TI必须一直为 1 但是这样的话就会有问题,程序一直进入串口中断......会造成主程序卡卡卡卡.......
/* 用自带的printf 发送数据 */ char putchar(char c) { UartSend(c); return c; }
别忘了包含
#include "stdio.h" #include
可以看一下这篇文章
http://blog.csdn.net/googlemi/article/details/8996605
发送数据
printf("%d",(unsigned int)Value);注意Value如果是char 或者 unsigned char 这样的时候要加强制转换
可以看一下这篇
http://taihang604.blog.163.com/blog/static/20834216520125185579173/
三,关于51单片机里面的CRC16校验
记住:操作的数据,,定义的数据类型要定义成 unsigned char 或者 unsigned int 类型的,,否则会算错的
/** * @brief 计算CRC * @param *modbusdata:数据指针 * @param length:数据长度 * @param * @retval 计算的CRC值 * @example **/ unsigned int crc16_modbus(unsigned char *modbusdata, char length) { char i, j; unsigned int crc = 0xffff; for (i = 0; i < length; i++) { crc ^= modbusdata[i]; for (j = 0; j < 8; j++) { if ((crc & 0x01) == 1) { crc = (crc >> 1) ^ 0xa001; } else { crc >>= 1; } } } return crc; } /** * @brief 判断数据的CRC校验是否正确 * @param *modbusdata:数据指针 * @param length:数据长度 * @param * @retval 1 正确 0 错误 * @example **/ char crc16_flage(unsigned char *modbusdata, char length) { unsigned int Receive_CRC=0,calculation=0;//接收到的CRC,计算的CRC int i = 0; Receive_CRC = crc16_modbus(modbusdata, length); calculation = modbusdata[length+1]; calculation <<= 8; calculation += modbusdata[length]; if(calculation != Receive_CRC) { return 0; } return 1; }
四,51单片机的发送数据我是用中断发送的
只需要把准备好的数据调用这个函数
/** * @brief 串口发送函数中断函数 * @param * @param None * @param None * @retval None * @example **/ void UartSendTI(unsigned char *value,int DataLen) { UsartSendData = value; UsartSendDataCnt = DataLen; TI = 1; }
/** * @brief 串口发送函数中断函数 * @param * @param None * @param None * @retval None * @example **/ void UartSendTI(unsigned char *value,int DataLen) { UsartSendData = value; UsartSendDataCnt = DataLen; TI = 1; } void UARTInterrupt(void) interrupt 4 { if(RI) { RI=0; UsartReceive[UsartReadCnt]=SBUF;//接收串口数据 UsartReadCnt++; if(UsartReadCnt>8)//防止溢出 { UsartReadCnt = 0; } } if(TI) { TI = 0; if(UsartSendDataCnt>0) { SBUF = *UsartSendData++; UsartSendDataCnt--; } else { TI = 0; } } }
五,串口接收和以前一样用的空闲检测
void Timer0Interrupt(void) interrupt 1 { TH0 = (65536 - 1000)/256; TL0 = (65536 - 1000)%256; //TimeCnt ++; if (UsartReadCnt != 0)//如果接收到数据了 { if (UsartIdleCnt == UsartReadCnt)//1ms时间数据没了变化 { UsartReadCntCopy = UsartReadCnt; UsartReadCnt = 0;//清零数据个数 UsartIdleCnt = 0;//清零 UsartFlage = 1; } else { UsartIdleCnt = UsartReadCnt; } } }
说一下IIC,,因为当初一个学弟问过我,简要说一下
IIC协议规定发送数据的时候要先发一个起始信号,,,也就是告诉对方我开始和你通信了
函数原型: void Start_I2c(); 功能: 启动I2C总线,即发送I2C起始条件. ********************************************************************/ void Start_I2c() { SDA=1; /*发送起始条件的数据信号*/ _Nop(); SCL=1; _Nop(); /*起始条件建立时间大于4.7us,延时*/ _Nop(); _Nop(); _Nop(); _Nop(); SDA=0; /*发送起始信号*/ _Nop(); /* 起始条件锁定时间大于4μs*/ _Nop(); _Nop(); _Nop(); _Nop(); SCL=0; /*钳住I2C总线,准备发送或接收数据 */ _Nop(); _Nop(); }
然后呢,发送器件的地址,,因为可能挂接了好几个IIC设备,目的是找到我想与之通信的那个,
看一下发送数据,,,SCL=0;注意IIC规定只有SCL=0;的时候SDA上的数据才允许改变.....
void SendByte(unsigned char c) { unsigned char BitCnt; //SCL=0; 起始信号最后是SCL=0;所以不用写了 for(BitCnt=0;BitCnt<8;BitCnt++) /*要传送的数据长度为8位*/ { if((c<0x80)SDA=1; /*判断发送位*/ else SDA=0; _Nop(); SCL=1; /*置时钟线为高,通知被控器开始接收数据位*/ _Nop(); _Nop(); /*保证时钟高电平周期大于4μs*/ _Nop(); _Nop(); _Nop(); SCL=0; }
发送数据的时候先发的高位,,SCL为低电平的时候SDA准备数据,,,然后SCL为高电平,数据就传过去了(就是告诉对方你接收SDA上的电平数据哈),
接着
8位数据传完之后,主机(单片机)把SDA置为高电平,,,然后SCL置为高电平这个时候呢如果从机(PCF8591)确实接收完了8位数据,就会把SDA拉低
这就是从机的应答信号,说明数据传输成功,,,非应答就是不用管它
void SendByte(unsigned char c) { unsigned char BitCnt; //SCL=0; 起始信号最后是SCL=0;所以不用写了 for(BitCnt=0;BitCnt<8;BitCnt++) /*要传送的数据长度为8位*/ { if((c<0x80)SDA=1; /*判断发送位*/ else SDA=0; _Nop(); SCL=1; /*置时钟线为高,通知被控器开始接收数据位*/ _Nop(); _Nop(); /*保证时钟高电平周期大于4μs*/ _Nop(); _Nop(); _Nop(); SCL=0; } _Nop(); _Nop(); SDA=1; /*8位发送完后释放数据线,准备接收应答位*/ _Nop(); _Nop(); SCL=1; _Nop(); _Nop(); _Nop(); if(SDA==1)ack=0; else ack=1; /*判断是否接收到应答信号*/ SCL=0; _Nop(); _Nop(); }
接着就是发命令或者说数据了,,,,这个数据呢,就和芯片有关了,,看芯片资料才知道
源码链接
链接:http://pan.baidu.com/s/1qX8gfA8%20密码:8xiy
实物链接
https://item.taobao.com/item.htm?spm=686.1000925.0.0.4a155084sH7efy&id=557671967654
哎呀!为什么博客又不能直接复制粘贴图片了呢........这样的话每写一篇文章就会很耽误时间了.............