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的實驗板......哎,,,,,,,感覺太懶了

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM