博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#上位机串口控制12864显示
阅读量:6095 次
发布时间:2019-06-20

本文共 35529 字,大约阅读时间需要 118 分钟。

实现的效果

 

 

上面是用Proteus仿真的,,对了如果自己想用proteus仿真需要安装下面这个软件

 

再看一下实物显示效果

 

 

 

先做上位机部分...........

为了程序一启动就把电脑上能用的串口号显示在下拉框中

 

private void Form1_Load(object sender, EventArgs e)        {            string[] ComName = SerialPort.GetPortNames();//把可用的串口号存入comname            comboBoxCom.Items.AddRange(ComName);//添加到下拉框            comboBoxCom.SelectedIndex = comboBoxCom.Items.Count > 0 ? 0 : -1;//显示第一个        }

还有就是串口呢可能会随时改变,所以在用户点击下拉框的时候重新更新一下下拉框中的内容

private void comboBoxCom_DropDown(object sender, EventArgs e)        {            string[] ComName = SerialPort.GetPortNames();//把可用的串口号存入comname            comboBoxCom.Items.Clear();//先清除一下,防止重复添加            comboBoxCom.Items.AddRange(ComName);//添加到下拉框            comboBoxCom.SelectedIndex = comboBoxCom.Items.Count > 0 ? 0 : -1;//显示第一个        }

现在在波特率框中添加常用的波特率

现在的效果

然后放一个按钮用来打开和关闭串口

 现在就做打开和关闭串口部分,,,

 

/// 
<打开按钮事件>
/// /// ///
///
private void buttonOpen_Click(object sender, EventArgs e) { if (OpenFlage == false)//打开串口 { try { serialPort1.PortName = comboBoxCom.Text;//端口号 serialPort1.BaudRate = Convert.ToInt32(comboBoxBaud.Text);//波特率 serialPort1.Open();//打开串口 OpenFlage = true; } catch (Exception)//其余意外情况执行这里 { OpenFlage = false; MessageBox.Show("端口错误,请检查串口", "提示"); } } else//关闭串口 { try { OpenFlage = false; if (serialPort1.IsOpen)//判断串口是否打开,如果打开执行下一步操作 { serialPort1.Close(); } serialPort1.Close();//强制关闭 } catch (Exception) { } } }

对了按钮点击了打开串口,让它显示"关闭串口"

就用回调来显示

现在按钮事件就这样了

/// 
<打开按钮事件>
/// /// ///
///
private void buttonOpen_Click(object sender, EventArgs e) { if (OpenFlage == false)//打开串口 { try { serialPort1.PortName = comboBoxCom.Text; serialPort1.BaudRate = Convert.ToInt32(comboBoxBaud.Text); serialPort1.Open(); OpenFlage = true; buttonOpen.Invoke(buttonConnectDelegate,"关闭串口"); } catch (Exception)//其余意外情况执行这里 { OpenFlage = false; buttonOpen.Invoke(buttonConnectDelegate, "打开串口"); MessageBox.Show("端口错误,请检查串口", "提示"); } } else//关闭串口 { try { OpenFlage = false; buttonOpen.Invoke(buttonConnectDelegate, "打开串口"); if (serialPort1.IsOpen)//判断串口是否打开,如果打开执行下一步操作 { serialPort1.Close(); } serialPort1.Close();//强制关闭 } catch (Exception) { } } }

现在在多优化一下,我们在打开了串口的时候,我接着用去选择别的串口了,那么为了不去重复重新打开的按钮动作,我们就多加一点程序,,,,这个一会再说吧!现在看不出效果

现在写接收程序部分

放一个textbox

接收的文本框设置只读

接收的数据肯定会很多,,所以让他有上下的滚动条

 

然后界面又加了几个按钮和选择

现在接收数据

为了接收到一条完整的数据之后再去做处理,我就用个定时器用于检测接收是否空闲了一段时间,只要出现空闲说明接收到了一条完整的数据

设置的是10ms检测一次

看程序里面怎么做,,,其实和我的单片机检测空闲是一样的道理

定义一个链表用于存储数据,还有两个计数变量

List
SerialBuffer = new List
(1024);//串口接收数据缓存 int UsartReadCnt = 0;//串口接收到的数据个数 int UsartIdleCnt = 0;//空闲检测用

串口接收函数里面这样写

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)        {            byte[] SerialBuff = new byte[serialPort1.BytesToRead];//串口接收数据临时缓存            if (serialPort1.BytesToRead != 0)            {                try                {                    UsartReadCnt = serialPort1.Read(SerialBuff, 0, serialPort1.BytesToRead);                    SerialBuffer.AddRange(SerialBuff);                }                catch (Exception ex)                {                    MessageBox.Show(ex.ToString());                }            }        }

然后定时器里面

/// 
<串口空闲检测定时器>
/// /// ///
///
private void timer1_Tick(object sender, EventArgs e) { if (UsartReadCnt != 0)//如果接收到数据了 { if (UsartIdleCnt == UsartReadCnt)//10ms时间数据没了变化 { UsartReadCnt = 0;//清零数据个数 UsartIdleCnt = 0;//清零 byte[] ReadData = new byte[SerialBuffer.Count]; for (int i = 0; i < SerialBuffer.Count; i++) { ReadData[i] = SerialBuffer[i]; } SerialBuffer.RemoveRange(0, SerialBuffer.Count); } else { UsartIdleCnt = UsartReadCnt; } } }

现在定义个回调把数据显示出来

/// 
<显示串口接收到的信息>
/// /// private void ShowReMsgMethod(byte[] by) { }

private void ShowReMsgMethod(byte[] by)        {            string getMsg = " ";            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 = string.Empty; 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 checkBoxHexShow_CheckedChanged(object sender, EventArgs e)        {            if (checkBoxHexShow.Checked)            {                try                {                    byte[] by = StringToByte(textBoxDataRes.Text);                    textBoxDataRes.Clear();                    textBoxDataRes.BeginInvoke(showReMsgSerialDelegate, by);                }                catch (Exception ex)                {                    //MessageBox.Show(ex.ToString());                }            }            else            {                try                {                    byte[] by = strToToHexByte(textBoxDataRes.Text);                    textBoxDataRes.Clear();                    textBoxDataRes.BeginInvoke(showReMsgSerialDelegate, by);                }                catch (Exception ex)                {                    //MessageBox.Show(ex.ToString());                }            }

其实就一句话..........................

/// 
<字符串转换成字节数组>
/// /// ///
///
public static byte[] StringToByte(string stringToConvert) { return (new ASCIIEncoding()).GetBytes(stringToConvert); }

/// 
<字符串转16进制格式,不够自动前面补零(每两位组成一个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; } }

看效果

 

 

加一个功能,,,我已经电机打开一个串口了,然后呢想换一个

然而如果和第一次选择的一样就不切换了

 

写上以下代码

private void comboBoxCom_DropDownClosed(object sender, EventArgs e)        {            try            {                if (CopyPortName != comboBoxCom.SelectedItem.ToString())//与当前的不同才切换                {                    if (serialPort1.IsOpen)                    {                        serialPort1.Close();                        serialPort1.PortName = comboBoxCom.SelectedItem.ToString();                        serialPort1.BaudRate = Convert.ToInt32(comboBoxBaud.Text);                        serialPort1.Open(); CopyPortName = serialPort1.PortName;                    }                }            }            catch (Exception)//切换出现错误执行这里            {                OpenFlage = false;                buttonOpen.Invoke(buttonConnectDelegate, "打开串口");                MessageBox.Show("端口错误,请检查串口", "提示");            }        }

然后呢波特率也是如此

不过呢有点不同

不用关闭串口....

private void comboBoxBaud_DropDownClosed(object sender, EventArgs e)        {            try            {                if (CopyBaud != Convert.ToInt32(comboBoxBaud.SelectedItem.ToString()))//与当前的不同才切换                {                    serialPort1.BaudRate = Convert.ToInt32(comboBoxBaud.SelectedItem.ToString());                    CopyBaud = serialPort1.BaudRate;                }            }            catch (Exception)//切换出现错误执行这里            {                OpenFlage = false;                buttonOpen.Invoke(buttonConnectDelegate, "打开串口");                MessageBox.Show("端口错误,请检查串口", "提示");            }        }

干脆再便捷点....一启动软件就自动连接第一个串口号

private void InitConnect()        {            string[] ComName = SerialPort.GetPortNames();//把可用的串口号存入comname            comboBoxCom.Items.AddRange(ComName);//添加到下拉框            comboBoxCom.SelectedIndex = comboBoxCom.Items.Count > 0 ? 0 : -1;//显示第一个            if (comboBoxCom.SelectedIndex != -1)            {                try                {                    serialPort1.PortName = comboBoxCom.Text;                    serialPort1.BaudRate = Convert.ToInt32(comboBoxBaud.Text);                    serialPort1.Open();                    OpenFlage = true;                    CopyPortName = serialPort1.PortName;//记录COM口号                    CopyBaud = serialPort1.BaudRate;//记录波特率                    buttonOpen.Invoke(buttonConnectDelegate, "关闭串口");                }                catch (Exception)//其余意外情况执行这里                {                    OpenFlage = false;                    buttonOpen.Invoke(buttonConnectDelegate, "打开串口");                    MessageBox.Show("端口错误,请检查串口", "提示");                }            }        }
private void Form1_Load(object sender, EventArgs e)        {            buttonConnectDelegate = new ButtonConnectDelegate(buttonConnectMethod);//实例化            showReMsgSerialDelegate = new ShowReMsgSerialDelegate(ShowReMsgMethod);//实例化            InitConnect();        }

再便捷一点,让软件打开一个能用的串口号

private void InitConnect()        {            string[] ComName = SerialPort.GetPortNames();//把可用的串口号存入comname            comboBoxCom.Items.AddRange(ComName);//添加到下拉框            comboBoxCom.SelectedIndex = comboBoxCom.Items.Count > 0 ? 0 : -1;//显示第一个            if (comboBoxCom.SelectedIndex != -1)            {                for (int i = 0; i < comboBoxCom.Items.Count; i++)                {                    try                    {                        serialPort1.PortName = comboBoxCom.SelectedIndex.ToString();                        serialPort1.PortName = comboBoxCom.Text;                        serialPort1.BaudRate = Convert.ToInt32(comboBoxBaud.Text);                        serialPort1.Open();                        OpenFlage = true;                        CopyPortName = serialPort1.PortName;//记录COM口号                        CopyBaud = serialPort1.BaudRate;//记录波特率                        buttonOpen.Invoke(buttonConnectDelegate, "关闭串口");                        break;                    }                    catch (Exception)//其余意外情况执行这里                    {                        OpenFlage = false;                        buttonOpen.Invoke(buttonConnectDelegate, "打开串口");                        if (comboBoxCom.SelectedIndex < comboBoxCom.Items.Count - 1)                        {                            comboBoxCom.SelectedIndex++;                        }                        //MessageBox.Show("端口错误,请检查串口", "提示");                    }                }            }        }

 

 

 

再优化点,,就是软件关闭的时候释放用到的资源

private void Form1_FormClosed(object sender, FormClosedEventArgs e)        {            try            {                serialPort1.Dispose();            }            catch (Exception)            {            }        }

好,现在做发送部分

/// 
<发送数据按钮事件>
/// /// ///
///
private void buttonSend_Click(object sender, EventArgs e) { if (!checkBoxHexSend.Checked)//字符发送 { byte[] sendbyte = Encoding.Default.GetBytes(textBoxSend.Text); try { serialPort1.Write(sendbyte, 0, sendbyte.Length); } catch (Exception) { MessageBox.Show("请检查串口", "提示!"); } } else//16形式进制发送 { byte[] sendbyte = strToToHexByte(textBoxSend.Text); try { serialPort1.Write(sendbyte, 0, sendbyte.Length); } catch (Exception) { MessageBox.Show("请检查串口", "提示!"); } } }

/// 
<显示串口发送的信息>
/// /// ///
private void ShowSeMsgMethod(byte[] by) { string getMsg = string.Empty; if (checkBoxHexSend.Checked)//16进制发送 { getMsg = byteToHexStr(by); //用到函数byteToHexStr } else { getMsg = new ASCIIEncoding().GetString(by); } textBoxSend.AppendText(getMsg); }

其实和接收数据的文本框一样的处理

private void checkBoxHexSend_CheckedChanged(object sender, EventArgs e)        {            if (checkBoxHexSend.Checked)            {                try                {                    byte[] by = StringToByte(textBoxSend.Text);                    textBoxSend.Clear();                    textBoxSend.BeginInvoke(showSeMsgSerialDelegate, by);                }                catch (Exception)                {                    //MessageBox.Show(ex.ToString());                }            }            else            {                try                {                    byte[] by = strToToHexByte(textBoxSend.Text);                    textBoxSend.Clear();                    textBoxSend.BeginInvoke(showSeMsgSerialDelegate, by);                }                catch (Exception)                {                    //MessageBox.Show(ex.ToString());                }            }        }

再加一项功能,,就是说在串口意外断开的时候能够检测出来

加入下面这个函数

/// 
<检测串口是否断开>
/// /// ///
protected override void WndProc(ref Message m) { if (m.Msg == 0x0219) { if (m.WParam.ToInt32() == 0x8004) { if (OpenFlage == true)//确定串口一开始是打开的 { if (!serialPort1.IsOpen)//是当前串口意外关闭 { OpenFlage = false; try { buttonOpen.Invoke(buttonConnectDelegate, "打开串口"); /*重新添加一下串口号*/ string[] ComName = SerialPort.GetPortNames();//把可用的串口号存入comname comboBoxCom.Items.Clear();//先清除一下,防止重复添加 comboBoxCom.Items.AddRange(ComName);//添加到下拉框 comboBoxCom.SelectedIndex = comboBoxCom.Items.Count > 0 ? 0 : -1;//显示第一个 serialPort1.Dispose();//释放资源 } catch (Exception) { } } } } } base.WndProc(ref m); }

到这里只是做了一个串口助手

其余的呢就简单了

看现在的界面

对了我规定了协议,,第一个字节代表命令,01代表后面是汉字数据,02代表正弦波数据,03矩形波数据,,04三角波数据

数据的最后两位是CRC16校验

显示汉字部分

 

/// 
<发送显示的汉字>
/// /// ///
///
private void buttonSendChinese_Click(object sender, EventArgs e) { byte[] sendby = Encoding.Default.GetBytes(textBoxChinese.Text.ToString()); byte[] sendbyte = new byte[sendby.Length + 1]; sendbyte[0] = 0x01; for (int i = 0; i < sendby.Length; i++) { sendbyte[i+1] = sendby[i]; } 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 { serialPort1.Write(sendbyte, 0, sendbyte.Length); } catch (Exception) { MessageBox.Show("请检查串口", "提示!"); } }

正弦波以及其余波形的方案

 

byte[] sendbyte = new byte[3];            if(trackBarSinFlage == 1)//正弦波            {                trackBarSinCnt++;                if (trackBarSinCnt>=5)                {                    trackBarSinFlage = 0;                    trackBarSinCnt = 0;                    sendbyte[0] = 0x02;                    sendbyte[1] = Convert.ToByte(trackBarSinF.Value);//正弦波F                    sendbyte[2] = Convert.ToByte(trackBarSinH.Value);//正弦波H                    SerialSend(sendbyte);                }            }

这段代码放在了定时器2里面,,,我这样做的,只要拖动滑块后500Ms没在改变滑块的值,那么就把当前滑块的值发给单片机,让单片机显示出来

我没有做成一直发给单片机的,,因为12864本身刷新整个界面就慢,,一直发也没什么用.............

 

其余的亲们看源码吧!

 

 

 

现在做做下位机--单片机程序

由于单片机程序太多了,所以就事先做好了底层的了,,,就先看一看

直接贴上来把

#define _12864_C_#include "include.h"#include "12864.h"/*** @brief  延时us函数* @param  Time 延时微秒数* @param  None* @param  None* @retval None* @example **/void DelayUs(int Time)   //误差 -0.173611111111us{    while(Time --)    {        _nop_();  }}void Init12864(){    WriteCom(0x30);// 基本指令(DL=1)    WriteCom(0x30);// 基本指令(RE=0)    WriteCom(0x0C);// 打开整体显示(不显示光标)    WriteCom(0x01);// RAM地址归零    DelayUs(2000);        WriteCom(0x06);// 游标自动加一}void CRAM_OFF()//关闭显示{    WriteCom(0x30);     //DL=1:8-BIT interface    WriteCom(0x30);     //RE=0:basic instruction    WriteCom(0x08);      //Display OFF,Cursor OFF,Cursor position blink OFF    WriteCom(0x01);     //CLEAR    DelayUs(2000);}void CRAM_ON()//打开显示{    WriteCom(0x30);// 基本指令(DL=1)    WriteCom(0x30);// 基本指令(RE=0)    WriteCom(0x0C);// 打开整体显示(不显示光标)}/*** @brief  向12864内写入数据* @param  Data 数据* @param  None* @param  None* @retval None* @example **/void WriteData(char Data){    RS = 1;//数据    RW = 0;//写入    E  = 0;//使能拉低    DelayUs(1);    Port = Data;    DelayUs(1);    E = 1;    DelayUs(1);    E = 0;    DelayUs(80);}/*** @brief  向12864内写入命令* @param  Com  命令* @param  None* @param  None* @retval None* @example **/void WriteCom(char Com){    E  = 0;//使能拉低    RS = 0;//命令    RW = 0;//写入    DelayUs(1);    Port = Com;    DelayUs(1);    E = 1;    DelayUs(1);    E = 0;    DelayUs(80);}/*** @brief  从12864内读出数据* @param  None* @param  None* @param  None* @retval None* @example 读出的数据**/char ReadData(){    char Data;    Port = 0xff;    RS = 1; //数据    RW = 1; //读取    E  = 1;    Data=Port;//读取数据    E  = 0;    DelayUs(80);    return(Data);   }/*** @brief  显示图片* @param  char  *img* @param  None* @param  None* @retval None* @example **/void DisplayImage(char  *img)//横向取膜{    char i,j;      WriteCom(0x36); //图形方式      for(i=0;i<32;i++)    {         WriteCom(0x80+i);        WriteCom(0x80);                  for(j=0;j<16;j++)         {            WriteData(*img++);        }    }         for(i=0;i<32;i++)  {         WriteCom(0x80+i);        WriteCom(0x88);        for(j=0;j<16;j++)         {            WriteData(*img++);        }    }}/*** @brief  在指定位置画一个点* @param  char x,char y, char Flage* @param  None* @param  None* @retval None* @example **/void DrawPoint(char x,char y, char Flage){        char x_dyte,x_byte; //定义列地址的字节位,及在字节中的哪1位     char y_dyte,y_byte; //定义为上下两个屏(取值为0,1),行地址(取值为0~31)    char GDRAM_hbit,GDRAM_lbit;        WriteCom(0x36); //绘图模式命令    /***X,Y坐标互换,即普通的X,Y坐标***/    x_dyte=y/16; //计算在16个字节中的哪一个    x_byte=y&0x0f; //计算在该字节中的哪一位    y_dyte=x/32; //0为上半屏,1为下半屏    y_byte=x&0x1f; //计算在0~31当中的哪一行    WriteCom(0x80+y_byte); //设定行地址(y坐标)    WriteCom(0x80+x_dyte+8*y_dyte); //设定列地址(x坐标),并通过8*y_Dyte选定上下屏  DelayUs(1);        ReadData();    GDRAM_hbit=ReadData();//读取当前显示高8位数据    GDRAM_lbit=ReadData();//读取当前显示低8位数据      if(Flage == 1)    {        WriteCom(0x80+y_byte); //设定行地址(y坐标)      WriteCom(0x80+x_dyte+8*y_dyte); //设定列地址(x坐标),并通过8*y_Dyte选定上下屏        DelayUs(1);                if(x_byte<8) //判断其在高8位,还是在低8位        {            WriteData(GDRAM_hbit|(0X01<<(7-x_byte)));   //显示GDRAM区高8位数据            WriteData(GDRAM_lbit); //显示GDRAM区低8位数据         }        else        {            WriteData(GDRAM_hbit);            WriteData(GDRAM_lbit|(0x01<<(15-x_byte)));        }  }            else    {        WriteData((0x00)); //清除GDRAM区高8位数据        WriteData((0x00)); //清除GDRAM区低8位数据    }}/*** @brief  八点画圆* @param  char x,char y,char xc,char yc* @param  None* @param  None* @retval None* @example **/void plotC(char x,char y,char xc,char yc) {     DrawPoint(xc+x,yc+y,1);     DrawPoint(xc+x,yc-y,1);     DrawPoint(xc-x,yc+y,1);     DrawPoint(xc-x,yc-y,1);     DrawPoint(xc+y,yc+x,1);     DrawPoint(xc+y,yc-x,1);     DrawPoint(xc-y,yc+x,1);     DrawPoint(xc-y,yc-x,1); } /*** @brief  在指定位置画一个半径为R的圆* @param  char x0,char y0, char r* @param  None* @param  None* @retval None* @example **/void DrawCircle(char xc,char yc, char r){    char x,y,d;     y=r;     d=3-(r<<1);     x=0;     while(x<=y)     {         plotC(x,y,xc,yc);         if(d < 0)         {      d+=(x<<2)+6;     }        else         {             d+=((x-y)<<2)+10;             y=y-1;         }         x=x+1;     } }/*** @brief  显示汉字* @param  x:行号, y:列号, k:个数, *p:数据* @param  None* @param  None* @retval None* @example **/void Chinese(char x,char y,char k,char *p){    char hang=0,out=0,i=0;    y=y-1;    switch(x)    {        case 1:hang=0x80;break;        case 2:hang=0x90;break;        case 3:hang=0x88;break;        case 4:hang=0x98;break;            }    out=hang+y;    WriteCom(out);     for(i=0;i
= 32) break; if(i%f==0) { for(j=32-h;j<=32+h;j++) DrawPoint(j,i,1); if(flag==0) flag=1; else flag=0; } else { if(flag==0) j=32-h; else j=32+h; DrawPoint(j,i,1); } }}/*** @brief 画一条线* @param int x0, int y0,起点* @param int x1, int y1,终点* @param None* @retval None* @example **/void DrawLine(char x0, char y0, char x1, char y1){ char x,y; char dx;// = abs(x1 - x0); char dy;// = abs(y1 - y0); if(y0==y1) { if(x0<=x1) { x=x0; } else { x=x1; x1=x0; } while(x <= x1) { DrawPoint(x,y0,1); x++; } return; } else if(y0>y1) { dy=y0-y1; } else { dy=y1-y0; } if(x0==x1) { if(y0<=y1) { y=y0; } else { y=y1; y1=y0; } while(y <= y1) { DrawPoint(x0,y,1); y++; } return; } else if(x0 > x1) { dx=x0-x1; x = x1; x1 = x0; y = y1; y1 = y0; } else { dx=x1-x0; x = x0; y = y0; } if(dx == dy) { while(x < x1) { x++; if(y>y1) { y--; } else { y++; } DrawPoint(x,y,1); } } else { DrawPoint(x, y,1); if(y < y1) { if(dx > dy) { char p = dy * 2 - dx; char twoDy = 2 * dy; char twoDyMinusDx = 2 * (dy - dx); while(x < x1) { x++; if(p < 0) { p += twoDy; } else { y++; p += twoDyMinusDx; } DrawPoint(x, y,1); } } else { char p = dx * 2 - dy; char twoDx = 2 * dx; char twoDxMinusDy = 2 * (dx - dy); while(y < y1) { y++; if(p < 0) { p += twoDx; } else { x++; p+= twoDxMinusDy; } DrawPoint(x, y,1); } } } else { if(dx > dy) { char p = dy * 2 - dx; char twoDy = 2 * dy; char twoDyMinusDx = 2 * (dy - dx); while(x < x1) { x++; if(p < 0) { p += twoDy; } else { y--; p += twoDyMinusDx; } DrawPoint(x, y,1); } } else { char p = dx * 2 - dy; char twoDx = 2 * dx; char twoDxMinusDy = 2 * (dx - dy); while(y1 < y) { y--; if(p < 0) { p += twoDx; } else { x++; p+= twoDxMinusDy; } DrawPoint(x, y,1); } } } }} /*** @brief 显示三角波* @param char f,char h,频率,幅值* @param None* @param None* @retval None* @example **/void TriWave(char f,char h)//显示三角波{ char i,j=0,flag=0; char x1,x2; for(i=0;i<127;i++) { if(i%f==0) { if(flag==0) { x1 = i; flag=1; j++; } else { x2 = i; flag=0; } if(flag == 1) { if(j>=2) { DrawLine(32+h,x2,32-h,x1); } } else { DrawLine(32-h,x1,32+h,x2); } } }}
View Code

 

 

#ifndef __12864_H_#define __12864_H_#include 
#ifndef _12864_C_#define _12864_C_ extern#else#define _12864_C_#endifsbit RS = P3^7;//数据\命令选择sbit RW = P3^6;//读\写sbit E = P3^5;//使能sfr Port = 0xA0;void DelayUs(int Time);void Init12864();void WriteData(char Data);void WriteCom(char Com);char ReadData();void DisplayImage(char *img);void CRAM_OFF();void CRAM_ON();void DrawPoint(char x,char y, char Flage);void DrawCircle(char x0,char y0, char r);void Chinese(char x,char y,char k,char *p);void ClearGDRAM(void);void ClearDDRAM();void fsin(char f,char h);void RecWave(char f,char h);//显示矩形波void DrawLine(char x0, char y0, char x1, char y1);void TriWave(char f,char h);//显示三角波#endif
View Code

 

#define _USART_C_#include "include.h"#include "usart.h"bit  UsartFlage = 0;char  UsartReadCnt = 0;char  UsartReadCntCopy = 0;char UsartReceive[50] = {
0};void InitUART(long Baud){ if(Baud == 115200) { SCON=0x50; //串口工作方式1,8位UART,波特率可变 TH2=0xFF; TL2=0xFD; //波特率:115200 晶振=11.0592MHz RCAP2H=0xFF; RCAP2L=0xFD; //16位自动再装入值 /*****************/ TCLK=1; RCLK=1; C_T2=0; EXEN2=0; //波特率发生器工作方式 /*****************/ TR2=1 ; //定时器2开始 } else { TMOD |= 0x20; SCON = 0x50; switch(Baud) { case 2400 :TH1 = 0xF4;TL1 = TH1;PCON = 0x00;break; case 4800 :TH1 = 0xFA;TL1 = TH1;PCON = 0x00;break; case 9600 :TH1 = 0xFD;TL1 = TH1;PCON = 0x00;break; case 14400 :TH1 = 0xFE;TL1 = TH1;PCON = 0x00;break; case 19200 :TH1 = 0xFD;TL1 = TH1;PCON = 0x80;break; default : TH1 = 0xFD;TL1 = TH1;PCON = 0x00;break; } EA = 1; ES = 1; TR1 = 1; }}void UartSend(unsigned char value) { ES=0; //关闭串口中断 TI=0; //清发送完毕中断请求标志位 SBUF=value; //发送 while(TI==0); //等待发送完毕 TI=0; //清发送完毕中断请求标志位 ES=1; //允许串口中断 }void UARTInterrupt(void) interrupt 4{ if(RI) { RI=0; UsartReceive[UsartReadCnt]=SBUF;//接收串口数据 UsartReadCnt++; }}
View Code

 

#ifndef __USART_H_#define __USART_H_#ifndef _USART_C_#define _USART_C_ extern#else#define _USART_C_#endif_USART_C_ bit  UsartFlage;_USART_C_ char  UsartReadCnt;_USART_C_ char UsartReceive[50];_USART_C_ char  UsartReadCntCopy;void InitUART(long Baud);void UartSend(unsigned char value);#endif
View Code

#define _TIME_C_#include "include.h"#include "time.h"int UsartIdleCnt =0 ;int TimeCnt = 0;int TimeDelay = 0;void DelayS(int s){    TimeCnt = 0;    TimeDelay = s;    while(TimeDelay>0);}//定时器初始化void InitTimer0(void){    TMOD |= 0x01;    TH0 = (65536 - 5000)/256;    TL0 = (65536 - 5000)%256;    EA = 1;    ET0 = 1;    TR0 = 1;}void Timer0Interrupt(void) interrupt 1{    TH0 = (65536 - 5000)/256;    TL0 = (65536 - 5000)%256;        TimeCnt ++;        if(TimeCnt >= 200)    {        TimeCnt = 0;        TimeDelay --;  }        if (UsartReadCnt != 0)//如果接收到数据了    {            if (UsartIdleCnt == UsartReadCnt)//1ms时间数据没了变化            {                UsartReadCntCopy = UsartReadCnt;                UsartReadCnt = 0;//清零数据个数                UsartIdleCnt = 0;//清零                UsartFlage = 1;            }            else            {                UsartIdleCnt = UsartReadCnt;            }    }}
View Code

#ifndef __TIME_H_#define __TIME_H_#ifndef _TIME_C_#define _TIME_C_ extern#else#define _TIME_C_#endifvoid InitTimer0(void);void DelayS(int s);#endif
View Code

算了剩下的不贴了,反正后面有源码.......

说几个地方吧

程序风格呢,还是自己习惯的风格.....

串口接收和上位机一样的道理

在定时器里面做的判断是否接收到一个完整的数据

 

串口的配置呢加入了115200的,因为印象深刻......

void InitUART(long Baud){    if(Baud == 115200)    {    SCON=0x50; //串口工作方式1,8位UART,波特率可变          TH2=0xFF;                   TL2=0xFD;    //波特率:115200 晶振=11.0592MHz         RCAP2H=0xFF;           RCAP2L=0xFD; //16位自动再装入值        /*****************/        TCLK=1;           RCLK=1;           C_T2=0;           EXEN2=0; //波特率发生器工作方式        /*****************/        TR2=1 ; //定时器2开始  }    else    {        TMOD |= 0x20;        SCON = 0x50;    switch(Baud)        {      case 2400 :TH1 = 0xF4;TL1 = TH1;PCON = 0x00;break;            case 4800 :TH1 = 0xFA;TL1 = TH1;PCON = 0x00;break;            case 9600 :TH1 = 0xFD;TL1 = TH1;PCON = 0x00;break;            case 14400 :TH1 = 0xFE;TL1 = TH1;PCON = 0x00;break;            case 19200 :TH1 = 0xFD;TL1 = TH1;PCON = 0x80;break;            default : TH1 = 0xFD;TL1 = TH1;PCON = 0x00;break;    }        EA = 1;      ES = 1;      TR1 = 1;  }}

这个控制显示正弦波的函数 h呢很容易看出来是控制这个波形的高度,,,,,那个3.14和f共同决定了周期(其实就是点数),,f越大这个函数的图像越拉伸,,,,,

 

void TriWave(char f,char h)//显示三角波{    char i,j=0,flag=0;    char x1,x2;    for(i=0;i<127;i++)    {        if(i%f==0)        {            if(flag==0)            {                x1 = i;                flag=1;                j++;            }            else            {                x2 = i;                flag=0;            }            if(flag == 1)            {                if(j>=2)                {                    DrawLine(32+h,x2,32-h,x1);                }            }            else            {                DrawLine(32-h,x1,32+h,x2);            }        }    }}

这个三角波函数是当初自己造的......其实就是画线.....

上面的 f 很容易看出来就是控制拐点的,,每隔 f 个点拐一下,

x1 和 x2是记录当前的 i  的值,关于那个 j 是由于 i 是从 0 开始的 如果不限制一下,那么第一根先就会是这样

 最后看一下主函数

 

#define _MAIN_C_#include "include.h"#include "main.h"void main(){    unsigned int CRC=0;    InitTimer0();//初始化定时器    InitUART(9600);//初始化串口    Init12864();//初始化12864    CRAM_OFF();//关闭显示    DisplayImage(Image);//显示图片    CRAM_ON();//打开显示        DelayS(1);    ClearGDRAM();//清除界面        Init12864();//初始化12864        for(CRC = 17;CRC<127;CRC+=23)    {        DrawCircle(32,CRC, 16);//画5个圆    }    while(1)    {        if(UsartFlage == 1)        {            UsartFlage = 0;                        if(crc16_flage(UsartReceive,UsartReadCntCopy-2))//判断CRC正确与否            {                ClearGDRAM();//清除界面                Init12864();//初始化12864                switch(UsartReceive[0])                {                    case 1 : Chinese(1,1,(UsartReadCntCopy-3)/2,&UsartReceive[1]); break;//显示汉字                    case 2 : fsin(UsartReceive[1],UsartReceive[2]); break;//显示正弦波                    case 3 : RecWave(UsartReceive[1],UsartReceive[2]); break;//显示锯齿波                    case 4 : TriWave(UsartReceive[1],UsartReceive[2]); break;//显示三角波                    default : break;                }            }        }    }}

主函数呢,没什么说的....

源码地址

链接:密码:ix66

实物链接

关于为什么要有实物了,,因为确实有人用到实物,,,,能满足的就一定要满足,而且好多元器件放着就浪费了.....

记得当初一个朋友学8266,竟然用了1个多月才能正常通信,,,那时候其实就想着应该做一个实物供朋友使用,这样的话就不能耽搁这么长时间了...

想想这都过去5个多月了,,我还没有去做8266的实验板......哎,,,,,,,感觉太懒了

 

转载地址:http://qxwza.baihongyu.com/

你可能感兴趣的文章
在使用EF开发时候,遇到 using 语句中使用的类型必须可隐式转换为“System.IDisposable“ 这个问题。...
查看>>
PHP使用DES进行加密和解密
查看>>
Oracle 如何提交手册Cluster Table事务
查看>>
BeagleBone Black第八课板:建立Eclipse编程环境
查看>>
在服务器上用Fiddler抓取HTTPS流量
查看>>
文件类似的推理 -- 超级本征值(super feature)
查看>>
【XCode7+iOS9】http网路连接请求、MKPinAnnotationView自定义图片和BitCode相关错误--备用...
查看>>
各大公司容器云的技术栈对比
查看>>
记一次eclipse无法启动的排查过程
查看>>
【转】jmeter 进行java request测试
查看>>
读书笔记--MapReduce 适用场景 及 常见应用
查看>>
SignalR在Xamarin Android中的使用
查看>>
走过电竞之路的程序员
查看>>
Eclipse和MyEclipse使用技巧--Eclipse中使用Git-让版本管理更简单
查看>>
[转]响应式表格jQuery插件 – Responsive tables
查看>>
8个3D视觉效果的HTML5动画欣赏
查看>>
C#如何在DataGridViewCell中自定义脚本编辑器
查看>>
【linux】crontab定时命令
查看>>
必须知道的八大种排序算法【java实现】(三) 归并排序算法、堆排序算法详解...
查看>>
python错误类型
查看>>