zoukankan      html  css  js  c++  java
  • C#上位机串口控制12864显示

    实现的效果

     

     

    上面是用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;//显示第一个
            }

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

    现在的效果

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

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

    /// <打开按钮事件>
            /// 
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            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)
                    {
                    }
                    
                }
            }

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

    就用回调来显示

    现在按钮事件就这样了

    /// <打开按钮事件>
            /// 
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            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<byte> SerialBuffer = new List<byte>(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());
                    }
                }
            }

    然后定时器里面

    /// <串口空闲检测定时器>
            /// 
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            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;
                    }
                }
            }


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

    /// <显示串口接收到的信息>
            /// 
            /// </summary>
            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进制字符串>
            /// 
            /// </summary>
            /// <param name="bytes"></param>
            /// <returns></returns>
            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());
                    }
                }

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

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

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


    好,现在做发送部分

    /// <发送数据按钮事件>
            /// 
            /// </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 { 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("请检查串口", "提示!"); }
                }
            }

    /// <显示串口发送的信息>
            /// 
            /// </summary>
            /// <param name="by"></param>
            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());
                    }
                }
            }

    再加一项功能,,就是说在串口意外断开的时候能够检测出来
    加入下面这个函数

            /// <检测串口是否断开>
            /// 
            /// </summary>
            /// <param name="m"></param>
            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校验

    显示汉字部分

    /// <发送显示的汉字>
            /// 
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            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);
            }
    /// <串口发送数据函数>
            /// 
            /// </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[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<k*2;i++) 
        {
            switch(i)
          {
                case 16:WriteCom(0x90);break;
                case 32:WriteCom(0x88);break;
                case 48:WriteCom(0x98);break;     
          }
            WriteData(*p);
            p++;
        }
    }
    
    /**
    * @brief  清除液晶GDRAM中的随机数据
    * @param  None
    * @param  None
    * @param  None
    * @retval None
    * @example 
    **/
    void ClearGDRAM(void)
    {
      char i,j,k;
    
        WriteCom(0x34);  //打开扩展指令集
        i=0x80;
        for(j=0;j<32;j++)
        {  
            WriteCom(i++);
            WriteCom(0x80);
            for(k=0;k<16;k++)
            { 
              WriteData(0x00); 
          }
        }
    
        i=0x80;
        for(j=0;j<32;j++)
        {
            WriteCom(i++);
            WriteCom(0x88);
            for(k=0;k<16;k++)
            { 
              WriteData(0x00); 
          }
        }
        WriteCom(0x30); //回到基本指令集    
    } 
    
    /**
    * @brief  
    * @param  None
    * @param  None
    * @param  None
    * @retval None
    * @example 
    **/
    void ClearDDRAM()
    {
        WriteCom(0x30);     //DL=1:8-BIT interface
        WriteCom(0x30);     //RE=0:basic instruction
        WriteCom(0x0C);      //Display ON,Cursor OFF,Cursor position blink OFF
        WriteCom(0x01);     //CLEAR
        DelayUs(5000);
    }
    
    /**
    * @brief  正弦波
    * @param  None
    * @param  None
    * @param  None
    * @retval None
    * @example 
    **/
    void fsin(char f,char h)
    {
      char i,j;
      for(i=0;i<127;i++)
        {
            j=32-h*sin(i*3.14/f);
            DrawPoint(j,i,1);
        }
    }
    
    /**
    * @brief  矩形波
    * @param  None
    * @param  None
    * @param  None
    * @retval None
    * @example 
    **/
    void RecWave(char f,char h)
    {
      char i,j,flag=0;
        
      for(i=0;i<127;i++)
      {
            if(f <= 0) break;
            if(h >= 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 <REGX52.H>
    
    #ifndef _12864_C_
    #define _12864_C_ extern
    #else
    #define _12864_C_
    #endif
    
    sbit 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_
    #endif
    
    void 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;
                    }
                }
            }
        }
    }

    主函数呢,没什么说的....
    源码地址

    链接:http://pan.baidu.com/s/1miiLiGC%20密码:ix66

    实物链接

    https://item.taobao.com/item.htm?id=556782600668

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

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

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

  • 相关阅读:
    决策树
    Caffe:深入分析(怎么训练)
    Caffe深度学习计算框架
    Caffe参数交换源码分析
    Net的网络层的构建(源码分析)
    FineTuning机制的分析
    Caffe::Snapshot的运行过程
    AdaBoost算法
    SVM支持向量机
    SMO序列最小最优化算法
  • 原文地址:https://www.cnblogs.com/yangfengwu/p/7341056.html
Copyright © 2011-2022 走看看