效果
 
如果想用手機做可以參考這篇文章,自己的協議規定在文章中
android 之TCP客戶端編程
---恢復內容開始---
請問一下博客為什么又不能直接復制粘貼圖片了呢............
先看8266的配置,8266我是用的Lua語言寫的,,因為方便快捷....這次寫的當然比以前完善......
關于WIFI模塊可以看這幾篇
ESP8266使用詳解NodeMCU初探ESP8266刷AT固件與nodemcu固件(一)Lua腳本語言入門(二)Lua腳本語言入門(三)Lua腳本語言入門(四)Lua腳本語言入門(五)Lua腳本語言入門ESP8266使用詳解--基于Lua腳本語言最后加上一篇有人的WIFI模塊,有人的做的也不錯,當初項目就是用的有人的
有人WIFI模塊使用詳解自己的ESP8266是建立服務器
我的init.lua
lighton=0pin = 4gpio.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) endend)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 = 0wifi.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 endend)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) endend)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
連接路由器的好處是可以做到遠程控制
android客服端+eps8266+單片機+路由器之遠程控制系統現在做上位機部分
整體規劃成這樣

先寫個函數獲取本機的IP地址,然后填寫到IP地址框
/// <獲取本機 IP 地址> /// /// </summary> /// <returns></returns> 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下拉框> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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(); }
然后寫個連接函數
/// <連接線程方法> /// /// </summary> 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和端口號,然后啟動連接任務
/// <連接點擊事件> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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(); }
回調函數
/// <打開按鈕回調函數> /// /// </summary> private void buttonConnectMethod(string str) { buttonConnect.Text = str; }
現在可以測試一下連接模塊

對了這個現在模塊好像是很長時間不和模塊通信,模塊就自動斷開了服務,,,,好像為了低功耗嗎....最后看一下怎么喚醒他,我現在是復位了一下
現在做一個函數來接收數據,然后把接收的數據顯示到數據接收的顯示框,數據框的回調函數也是必不可少的,還有定義一個接收任務
關于回調可以看一下
C#委托+回調詳解先定義一個networkstrem用來接收和發送網絡數據流
其實C#的和JAVA的很類似
可以看一下
android 之TCP客戶端編程
看一下現在的工程
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 地址> /// /// </summary> /// <returns></returns> 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下拉框> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void comboBoxIPAdress_DropDown(object sender, EventArgs e) { getIPAddress(); } /// <連接點擊事件> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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 { } } } /// <連接線程方法> /// /// </summary> 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 { } } } /// <打開按鈕回調函數> /// /// </summary> private void buttonConnectMethod(string str) { buttonConnect.Text = str; } /// <顯示串口接收到的信息--回調函數> /// /// </summary> 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進制字符串> /// /// </summary> /// <param name="bytes"></param> /// <returns></returns> public static string byteToHexStr(byte[] bytes) { string returnStr = ""; try { if (bytes != null) { for (int i = 0; i < bytes.Length; i++) { returnStr += bytes.ToString("X2"); } } return returnStr; } catch (Exception) { return returnStr; } } /// <接收消息線程> /// /// </summary> 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 = recvBytes; } textBoxDataRes.Invoke(showReMsgTcpDelegate, receive); } catch (Exception) { //異常處理函數 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "連接"); try { ReceiveThread.Abort(); }//銷毀任務 catch { } try { networkstrem.Dispose(); }//釋放資源 catch { } try { myTcpClient.Close(); }//關閉TCP catch { } } } } }}
看一下顯示回調函數
/// <顯示串口接收到的信息--回調函數> /// /// </summary> 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進制字符串> /// /// </summary> /// <param name="bytes"></param> /// <returns></returns> public static string byteToHexStr(byte[] bytes) { string returnStr = ""; try { if (bytes != null) { for (int i = 0; i < bytes.Length; i++) { returnStr += bytes.ToString("X2"); } } return returnStr; } catch (Exception) { return returnStr; } }
那么接收數據的函數
/// <接收消息線程> /// /// </summary> 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 = recvBytes; } 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();//引發異常
}
現在連接模塊然后用串口助手發數據

已經能接收到數據了,現在呢,做發送數據
/// <發送按鈕點擊事件> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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進制格式,不夠自動前面補零> /// /// </summary> /// <param name="hexString"></param> /// <returns></returns> 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 = 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 = 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 = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } } catch { for (i = 0; i < returnBytes.Length; i++) { returnBytes = 0; } MessageBox.Show("超過16進制范圍A-F,已初始化為0", "提示"); } return returnBytes; } }
現在測試一下發送數據

對了

當點擊這兩個的時候,對應數據框里面的內容應該相應的變化
直接在點擊函數里面設置
/// <發送Hex和字符串選擇> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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和字符串選擇> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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發送一次采集數據的命令

/// <定時發送事件> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void timer1_Tick(object sender, EventArgs e) { byte[] sendbyte = new byte[3]; sendbyte[0] = 0xaa; sendbyte[1] = 0x55; sendbyte[2] = 0x01; SerialSend(sendbyte); }
/// <網絡發送數據函數> /// /// </summary> /// <param name="sendbyte"></param> private void SerialSend(byte[] byt) { int crc = 0; byte[] sendbyte = new byte[byt.Length + 2]; for (int i = 0; i < byt.Length; i++) { sendbyte = byt; } 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("請檢查連接", "提示!"); } }
定時器呢在這里面啟動
/// <打開按鈕回調函數> /// /// </summary> 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 地址> /// /// </summary> /// <returns></returns> 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下拉框> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void comboBoxIPAdress_DropDown(object sender, EventArgs e) { getIPAddress(); } /// <連接點擊事件> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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 { } } } /// <連接線程方法> /// /// </summary> 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 { } } } /// <打開按鈕回調函數> /// /// </summary> private void buttonConnectMethod(string str) { if (str == "斷開") { timer1.Enabled = true; timer1.Start();//啟動定時器 } else { timer1.Enabled = false; timer1.Stop();//停止定時器 } buttonConnect.Text = str; } /// <顯示串口接收到的信息--回調函數> /// /// </summary> 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進制字符串> /// /// </summary> /// <param name="bytes"></param> /// <returns></returns> public static string byteToHexStr(byte[] bytes) { string returnStr = ""; try { if (bytes != null) { for (int i = 0; i < bytes.Length; i++) { returnStr += bytes.ToString("X2"); } } return returnStr; } catch (Exception) { return returnStr; } } /// <接收消息線程> /// /// </summary> 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 = recvBytes; } textBoxDataRes.Invoke(showReMsgTcpDelegate, receive); } catch (Exception) { //異常處理函數 ConnectFlage = false; buttonConnect.Invoke(buttonConnectDelegate, "連接"); try { ReceiveThread.Abort(); }//銷毀任務 catch { } try { networkstrem.Dispose(); }//釋放資源 catch { } try { myTcpClient.Close(); }//關閉TCP catch { } } } } /// <發送按鈕點擊事件> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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進制格式,不夠自動前面補零> /// /// </summary> /// <param name="hexString"></param> /// <returns></returns> 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 = 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 = 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 = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } } catch { for (i = 0; i < returnBytes.Length; i++) { returnBytes = 0; } MessageBox.Show("超過16進制范圍A-F,已初始化為0", "提示"); } return returnBytes; } } /// <字符串轉換成字節數組> /// /// </summary> /// <param name="stringToConvert"></param> /// <returns></returns> public static byte[] StringToByte(string stringToConvert) { return (new ASCIIEncoding()).GetBytes(stringToConvert); } /// <發送Hex和字符串選擇> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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和字符串選擇> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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()); } } } /// <清除接收> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonClearRecv_Click(object sender, EventArgs e) { textBoxDataRes.Clear(); } /// <清除發送> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonClearSend_Click(object sender, EventArgs e) { textBoxSend.Clear(); } /// <設置DA輸出> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void trackBarDAout_Scroll(object sender, EventArgs e) { textBoxDAout.Text = trackBarDAout.Value.ToString(); } /// <CRC 效驗 > /// /// </summary> /// <param name="modbusdata"></param> /// <param name="length"></param> /// <returns></returns> private int crc16_modbus(byte[] modbusdata, int length) { int i, j; int crc = 0xffff; try { for (i = 0; i < length; i++) { crc ^= modbusdata; 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; } /// <定時發送事件> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void timer1_Tick(object sender, EventArgs e) { byte[] sendbyte = new byte[3]; sendbyte[0] = 0xaa; sendbyte[1] = 0x55; sendbyte[2] = 0x01; SerialSend(sendbyte); } /// <網絡發送數據函數> /// /// </summary> /// <param name="sendbyte"></param> private void SerialSend(byte[] byt) { int crc = 0; byte[] sendbyte = new byte[byt.Length + 2]; for (int i = 0; i < byt.Length; i++) { sendbyte = byt; } 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("請檢查連接", "提示!"); } } }}
View Code
測試一下

然后呢,設置一下這個好弄得

協議的規定
//上位機發送
0xaa,0x55,0x02,設置的輸出電壓(單字節),CRC//設置輸出的電壓
/// <設置DA輸出> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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模塊
現在寫接收數據的函數,然后顯示在相應的位置
/// <顯示串口接收到的信息--回調函數> /// /// </summary> 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校驗
/// <定時發送事件> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> 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輸出> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void trackBarDAout_Scroll(object sender, EventArgs e) { trackBarValue[0] = Convert.ToByte(trackBarDAout.Value); textBoxDAout.Text = trackBarDAout.Value.ToString(); }
這里面加了個try,,是因為不能保證數據傳輸的時候沒有錯誤的時候
/// <顯示串口接收到的信息--回調函數> /// /// </summary> 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以前的數據發過去了.
所以自己做了下設置
/// <網絡發送數據函數> /// /// </summary> /// <param name="sendbyte"></param> 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 = byt; if (sendbyte == 0x00) { sendbyte = 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 <stdarg.h>
可以看一下這篇文章
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; 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<<BitCnt)&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<<BitCnt)&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
哎呀!為什么博客又不能直接復制粘貼圖片了呢........這樣的話每寫一篇文章就會很耽誤時間了.............
|