实现的效果

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

再看一下实物显示效果

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

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

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

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

 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 > ? : -;//显示第一个
}

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

现在的效果

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

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

/// <打开按钮事件>
///
/// </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>();//串口接收数据缓存
int UsartReadCnt = ;//串口接收到的数据个数
int UsartIdleCnt = ;//空闲检测用

串口接收函数里面这样写

 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] SerialBuff = new byte[serialPort1.BytesToRead];//串口接收数据临时缓存
if (serialPort1.BytesToRead != )
{
try
{
UsartReadCnt = serialPort1.Read(SerialBuff, , 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 != )//如果接收到数据了
{
if (UsartIdleCnt == UsartReadCnt)//10ms时间数据没了变化
{
UsartReadCnt = ;//清零数据个数
UsartIdleCnt = ;//清零
byte[] ReadData = new byte[SerialBuffer.Count];
for (int i = ; i < SerialBuffer.Count; i++)
{
ReadData[i] = SerialBuffer[i];
}
SerialBuffer.RemoveRange(, 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 = ; 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 % ) != )
{
Flag = true;
}
if (Flag == true)
{
byte[] returnBytes = new byte[(hexString.Length + ) / ]; try
{
for (i = ; i < (hexString.Length - ) / ; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * , ), );
}
returnBytes[returnBytes.Length - ] = Convert.ToByte(hexString.Substring(hexString.Length - , ).PadLeft(, ''), ); }
catch
{
for (i = ; i < returnBytes.Length; i++)
{
returnBytes[i] = ;
}
MessageBox.Show("超过16进制范围A-F,已初始化为0", "提示");
}
return returnBytes;
}
else
{
byte[] returnBytes = new byte[(hexString.Length) / ];
try
{
for (i = ; i < returnBytes.Length; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * , ), );
}
}
catch
{
for (i = ; i < returnBytes.Length; i++)
{
returnBytes[i] = ;
}
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 > ? : -;//显示第一个 if (comboBoxCom.SelectedIndex != -)
{
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 > ? : -;//显示第一个 if (comboBoxCom.SelectedIndex != -)
{
for (int i = ; 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 - )
{
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, , sendbyte.Length); }
catch (Exception) { MessageBox.Show("请检查串口", "提示!"); }
}
else//16形式进制发送
{
byte[] sendbyte = strToToHexByte(textBoxSend.Text);
try { serialPort1.Write(sendbyte, , 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 > ? : -;//显示第一个 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 + ]; sendbyte[] = 0x01;
for (int i = ; i < sendby.Length; i++)
{
sendbyte[i+] = sendby[i];
} SerialSend(sendbyte);
}
/// <串口发送数据函数>
///
/// </summary>
/// <param name="sendbyte"></param>
private void SerialSend(byte[] byt)
{
int crc = ; byte[] sendbyte = new byte[byt.Length + ]; for (int i = ; 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 - ] = Crcbyte[];
sendbyte[sendbyte.Length - ] = Crcbyte[]; try
{
serialPort1.Write(sendbyte, , sendbyte.Length);
}
catch (Exception)
{
MessageBox.Show("请检查串口", "提示!");
}
}

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

 byte[] sendbyte = new byte[];
if(trackBarSinFlage == )//正弦波
{
trackBarSinCnt++;
if (trackBarSinCnt>=)
{
trackBarSinFlage = ;
trackBarSinCnt = ;
sendbyte[] = 0x02;
sendbyte[] = Convert.ToByte(trackBarSinF.Value);//正弦波F
sendbyte[] = 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();
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();
} 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 = ;//数据
RW = ;//写入
E = ;//使能拉低
DelayUs();
Port = Data;
DelayUs();
E = ;
DelayUs();
E = ;
DelayUs();
} /**
* @brief 向12864内写入命令
* @param Com 命令
* @param None
* @param None
* @retval None
* @example
**/
void WriteCom(char Com)
{
E = ;//使能拉低
RS = ;//命令
RW = ;//写入
DelayUs();
Port = Com;
DelayUs();
E = ;
DelayUs();
E = ;
DelayUs();
} /**
* @brief 从12864内读出数据
* @param None
* @param None
* @param None
* @retval None
* @example 读出的数据
**/
char ReadData()
{
char Data;
Port = 0xff;
RS = ; //数据
RW = ; //读取
E = ;
Data=Port;//读取数据
E = ;
DelayUs();
return(Data);
} /**
* @brief 显示图片
* @param char *img
* @param None
* @param None
* @retval None
* @example
**/
void DisplayImage(char *img)//横向取膜
{
char i,j; WriteCom(0x36); //图形方式 for(i=;i<;i++)
{
WriteCom(0x80+i);
WriteCom(0x80); for(j=;j<;j++)
{
WriteData(*img++);
}
} for(i=;i<;i++)
{
WriteCom(0x80+i);
WriteCom(0x88);
for(j=;j<;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个字节中的哪一个
x_byte=y&0x0f; //计算在该字节中的哪一位
y_dyte=x/; //0为上半屏,1为下半屏
y_byte=x&0x1f; //计算在0~31当中的哪一行
WriteCom(0x80+y_byte); //设定行地址(y坐标)
WriteCom(0x80+x_dyte+*y_dyte); //设定列地址(x坐标),并通过8*y_Dyte选定上下屏
DelayUs(); ReadData();
GDRAM_hbit=ReadData();//读取当前显示高8位数据
GDRAM_lbit=ReadData();//读取当前显示低8位数据 if(Flage == )
{
WriteCom(0x80+y_byte); //设定行地址(y坐标)
WriteCom(0x80+x_dyte+*y_dyte); //设定列地址(x坐标),并通过8*y_Dyte选定上下屏
DelayUs(); if(x_byte<) //判断其在高8位,还是在低8位
{
WriteData(GDRAM_hbit|(0X01<<(-x_byte))); //显示GDRAM区高8位数据
WriteData(GDRAM_lbit); //显示GDRAM区低8位数据
}
else
{
WriteData(GDRAM_hbit);
WriteData(GDRAM_lbit|(0x01<<(-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,);
DrawPoint(xc+x,yc-y,);
DrawPoint(xc-x,yc+y,);
DrawPoint(xc-x,yc-y,);
DrawPoint(xc+y,yc+x,);
DrawPoint(xc+y,yc-x,);
DrawPoint(xc-y,yc+x,);
DrawPoint(xc-y,yc-x,);
} /**
* @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=-(r<<);
x=;
while(x<=y)
{
plotC(x,y,xc,yc);
if(d < )
{
d+=(x<<)+;
}
else
{
d+=((x-y)<<)+;
y=y-;
}
x=x+;
}
} /**
* @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=,out=,i=;
y=y-;
switch(x)
{
case :hang=0x80;break;
case :hang=0x90;break;
case :hang=0x88;break;
case :hang=0x98;break;
}
out=hang+y;
WriteCom(out);
for(i=;i<k*;i++)
{
switch(i)
{
case :WriteCom(0x90);break;
case :WriteCom(0x88);break;
case :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=;j<;j++)
{
WriteCom(i++);
WriteCom(0x80);
for(k=;k<;k++)
{
WriteData(0x00);
}
} i=0x80;
for(j=;j<;j++)
{
WriteCom(i++);
WriteCom(0x88);
for(k=;k<;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();
} /**
* @brief 正弦波
* @param None
* @param None
* @param None
* @retval None
* @example
**/
void fsin(char f,char h)
{
char i,j;
for(i=;i<;i++)
{
j=-h*sin(i*3.14/f);
DrawPoint(j,i,);
}
} /**
* @brief 矩形波
* @param None
* @param None
* @param None
* @retval None
* @example
**/
void RecWave(char f,char h)
{
char i,j,flag=; for(i=;i<;i++)
{
if(f <= ) break;
if(h >= ) break; if(i%f==)
{
for(j=-h;j<=+h;j++)
DrawPoint(j,i,);
if(flag==)
flag=;
else
flag=;
}
else
{
if(flag==)
j=-h;
else
j=+h;
DrawPoint(j,i,);
}
}
} /**
* @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,);
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,);
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,);
}
}
else
{
DrawPoint(x, y,);
if(y < y1)
{
if(dx > dy)
{
char p = dy * - dx;
char twoDy = * dy;
char twoDyMinusDx = * (dy - dx);
while(x < x1)
{
x++;
if(p < )
{
p += twoDy;
}
else
{
y++;
p += twoDyMinusDx;
}
DrawPoint(x, y,);
}
}
else
{
char p = dx * - dy;
char twoDx = * dx;
char twoDxMinusDy = * (dx - dy);
while(y < y1)
{
y++;
if(p < )
{
p += twoDx;
}
else
{
x++;
p+= twoDxMinusDy;
}
DrawPoint(x, y,);
}
}
}
else
{
if(dx > dy)
{
char p = dy * - dx;
char twoDy = * dy;
char twoDyMinusDx = * (dy - dx);
while(x < x1)
{
x++;
if(p < )
{
p += twoDy;
}
else
{
y--;
p += twoDyMinusDx;
}
DrawPoint(x, y,);
}
}
else
{
char p = dx * - dy;
char twoDx = * dx;
char twoDxMinusDy = * (dx - dy);
while(y1 < y)
{
y--;
if(p < )
{
p += twoDx;
}
else
{
x++;
p+= twoDxMinusDy;
}
DrawPoint(x, y,);
}
}
}
}
} /**
* @brief 显示三角波
* @param char f,char h,频率,幅值
* @param None
* @param None
* @retval None
* @example
**/
void TriWave(char f,char h)//显示三角波
{
char i,j=,flag=;
char x1,x2;
for(i=;i<;i++)
{
if(i%f==)
{
if(flag==)
{
x1 = i;
flag=;
j++;
}
else
{
x2 = i;
flag=;
}
if(flag == )
{
if(j>=)
{
DrawLine(+h,x2,-h,x1);
}
}
else
{
DrawLine(-h,x1,+h,x2);
}
}
}
}

#ifndef __12864_H_
#define __12864_H_
#include <REGX52.H> #ifndef _12864_C_
#define _12864_C_ extern
#else
#define _12864_C_
#endif sbit RS = P3^;//数据\命令选择
sbit RW = P3^;//读\写
sbit E = P3^;//使能 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

#define _USART_C_

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

#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[];
_USART_C_ char UsartReadCntCopy; void InitUART(long Baud);
void UartSend(unsigned char value); #endif

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

#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

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

说几个地方吧

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

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

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

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

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

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

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

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

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

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

最后看一下主函数

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

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

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

实物链接

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

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

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

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

C#上位机串口控制12864显示的更多相关文章

  1. 13-ESP8266 SDK开发基础入门篇--上位机串口控制 Wi-Fi输出PWM的占空比,IEEE754规约

    https://www.cnblogs.com/yangfengwu/p/11100552.html 这节做个上位机控制Wi-Fi引脚输出的PWM占空比信号,灯的亮度就可以用上位机控制了 大家可以自己 ...

  2. 14-ESP8266 SDK开发基础入门篇--上位机串口控制 Wi-Fi输出PWM的占空比,调节LED亮度,8266程序编写

    https://www.cnblogs.com/yangfengwu/p/11102026.html 首先规定下协议  ,CRC16就不加了哈,最后我会附上CRC16的计算程序,大家有兴趣自己加上 上 ...

  3. 15-ESP8266 SDK开发基础入门篇--上位机串口控制 Wi-Fi输出PWM的占空比,调节LED亮度,上位机程序编写

    https://www.cnblogs.com/yangfengwu/p/11104167.html 先说一下整体思路哈.. 咱滑动的时候 会进入这个,然后咱呢不直接从这个里面写发送 因为这样的话太快 ...

  4. ROS常用库(二) Serial库(单片机和上位机串口通讯)

    比如我们做了个单片机,在win里面用串口调试助手接收和下发数据,那么在ubuntu里用ros怎么实现?换个说法,怎么实现上位机和下位机的通讯? 首先,用python自带的库就可以实现这个功能. 安装p ...

  5. VS2008基于对话框的MFC上位机串口通信(C++实现)简单例程

    首先,在 vs2008 环境下创建 MFC 运用程序 设置项目名称为 ComTest(这个地方随意命名,根据个人习惯),点击确定后,点击下一步 出现如下界面 选择"基于对话框"模式 ...

  6. 基于Arduino和python的串口通信和上位机控制

    引言 经常的时候我们要实现两个代码之间的通信,比如说两个不同不同人写的代码要对接,例如将python指令控制Arduino控件的开关,此处使用串口通信是非常方便的,下面笔者将结合自己踩过的坑来讲述下自 ...

  7. 9-ESP8266 SDK开发基础入门篇--编写串口上位机软件

    https://www.cnblogs.com/yangfengwu/p/11087613.html 页面修改成这样子             现在看串口发送数据 点击点亮 发送0xaa 0x55 0 ...

  8. C# 简易的串口监视上位机实现

    实现上位机和下位机之间的通信,通常使用的是串口通信,接下来实现一个通过上位机和串口调试助手来完成串口通信测试. 首先创建一个WInfrom窗体应用工程文件,创建过程可参考https://www.cnb ...

  9. [自娱自乐] 4、超声波测距模块DIY笔记(四)——终结篇·基于C#上位机软件开发

    前言 上一节我们已经基本上把超声波硬件的发射和接收模块全部做好了,接下来我们着手开发一个软硬结合的基于C#的平面定位软件! 目录 一.整体思路 二.效果提前展示 2-1.软件部分展示 2-2.硬件部分 ...

随机推荐

  1. 常见License错误代码

    2017-06-2816:32:40 -1 找不到许可文件. -2 无效的许可文件语 -3 没有用于此功能的 -4 已达到许可的用户 -5 不存在此功能. -6 许可文件中没有 TCP/IP 端口号, ...

  2. [leetcode-438-Find All Anagrams in a String]

    Given a string s and a non-empty string p, find all the start indices of p's anagrams in s.Strings c ...

  3. 【Android Developers Training】 101. 显示快速联系人挂件(Quick Contact Badge)

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  4. SVN仓库迁移到Git遇到的两个问题和解决办法

    OS: CentOS 7.0 准备: git svn git-svn sudo yum install git sudo yum install subversion sudo yum install ...

  5. 浅析ConcurrentHashMap

    一.导论 这些天一直在看关于多线程和高并发的书籍,也对jdk中的并发措施了解了些许,看到concurrentHashMap的时候感觉知识点很乱,有必要写篇博客整理记录一下. 当资源在多线程下共享时会产 ...

  6. Eclipse添加struts2

    参照:http://jingyan.baidu.com/article/915fc414fd94fb51394b208e.html 一.插件下载:http://struts.apache.org/do ...

  7. 如何删除 SQL Server 表中的重复行

    第一种:有主键的重复行,就是说主键不重复,但是记录的内容重复比如人员表tab ,主键列id,身份证编号idcard当身份证重复的时候,保留最小id值的记录,其他删除delete a from tab ...

  8. java定时任务的实现方式

    在本文里,我会给大家介绍2种不同的实现方法:1.普通thread实现2.ScheduledExecutorService实现 一:实现普通的thread: 首先是创建thread然后就是一直让whil ...

  9. Python基础之常用模块(一)

    模块本质就是一个.py文件,在安装目录下的lib文件夹下可以看到 模块分为三个部分:内置模块(存在于解释器中),第三方模块(lib文件夹下),自定义模块(自己定义的) 1.time模块 import ...

  10. SSM整合学习笔记

    对学习Spring+Spring MVC+Mybatis过程中出现的问题解决办法汇总 出现的问题 1.pom.xml报错 右键项目->maven->update project 因此每次更 ...