这次我们来试着一步步的去掌握PC与单片机通过RS-232进行通讯和控制。

先说说我硬件的情况。我用的PC是个二手的IBM240小本本,十寸屏,赛扬400,机子很老了。但也有它的优点:1、串口,并口,PS鼠标口、USB口、PCM插槽全有。      调试硬件电路最好还是用真实串、并口好些,因为用USB转换的串、并口有时会出现兼容性上的问题,就会增加你调试上的复杂性。

下图为本人的IBM 240及各种接口图:

下图是PC的大小对比图

单片机还是我一步步做出来的那个了,USB-ISP编程线也是我前面秀过,好!现在我放上PC与单片机连接图:

用本本的好处就是调整方便,接口、器件都在旁边,假若是用台式机,你还得钻到桌子底下去插拔那些接口,而现在本本却又没有串、并口了。
     言归正传,单片机的RS-232串口通过9针串口线接到本本的串口上,单片机的ISP编程口通过USB-ISP编程器接到本本的USB口。

另外本本要接电源,单片机也要接5V电源,还有千万千要记得,本本是要插上鼠标才玩得转哦!

我将这个一步一步掌握串口的通讯与控制分为五步:

1、测试单片机与PC的串口连接是否正确好用。

2、用VB自己编写的程序替换掉串口调试器软件来接收单片机发送的数据。

3、掌握单片机端如何发送字符和数值数据。

4、掌握PC端程序如何接收发送字符和数值型数据。

5、做一个A/D转换(ADC0809)获取数据发送到PC,并在PC上显示实时趋势的例子。

我们要用到的四个软件:

1、USB接口编程软件:是PC机给单片机进行烧写编程用的

2、串口调试软件:用来测试单片机内串口电路、程序工作是否正常。

3、单片机程序的编程软件KEIL:用于编写单片机内的程序并生成HEX文件。

4、VB6.0:用于编写PC机上的应用程序

先进行第一步工作

在前面一篇《板子上最一个部件——RS232串口》讲过如何对单片机上的串口进行调试,我们还是先对这个连接进行测试,我们首先得确认连接正确,电路正常,才能进行后面程序编写和调试工作。
  依旧是那个最简单的程序,AT89S52从串口不停地发送“hello world!”

#include<at89x51.h>
#include<stdio.h>
void main(void)
{
  SCON=0x50; //串口方式1
  TMOD=0x20; //定时器1,定时方式为2
  PCON=0x80; //设定串口工作方式为1
  TCON=0x40; //设定时器1开始计数
  TH1=0xfd;  //设定波特率为19200
  TL1=0xfd;  //
  TI=1;
  TR1=1;     //启动定时器
  while(1)
  {
    printf("hello world!\n");
  }
}

把原先生成好的这个HEX文件用上面讲到的Progisp软件写到AT89S52里。

然后打开sscom32串口调试器,设定好串口号、波特率和数据位,按下“打开串口”钮,应该就能收到那个一行行的“hello world!”了。能看到这一行行的文字,说明电路、连接和程序都正常了。

如果是乱码,则要将单片机上的复位钮按一下。如果还是乱码,那一般就是波特率不对,晶振应为11.0592MHZ或串口接触不良,线过长等原因。如果跟本就收不到任何字符,就说明电路或连接有故障,或者程序有问题。那就要好好查查了。
  好!现在电路、连接、程序都可以正常工作。但在串口调试器接收框里看到接收到的一行行“hello world!”显示太快,不容易看出它一次一次发送的过程,不便于分析问题,我们得给它加点延时。

#include<at89x51.h>
#include<stdio.h>

void delay(void) //定义一个延时子程序
{
  unsigned int i;
  for (i=65535;i>0;i--);
}

void main(void) //主程序
{
  SCON=0x50; //串口方式1
  TMOD=0x20; //定时器1,定时方式为2
  PCON=0x80; //设定串口工作方式为1
  TCON=0x40; //设定时器1开始计数
  TH1=0xfd;  //设定波特率为19200
  TL1=0xfd;  //
  TI=1;
  TR1=1;     //启动定时器
  while(1)
  {
    printf("hello world!\n"); //向串口送出数据
  delay(); //调用延时
  delay();
  }

}

程序写好了,在KEIL里添加上延时语句后,重新生成HEX文件。再用Progisp将它写进AT89S52里,这时,你就可以看到串口调试器已经接收到大约一秒一次的“Hello world!”了。
  在这里说明一下,只要像最上面我图上给出的那样把单片机和PC连接好后,无论你往AT89S52里烧写程序,还是单片机连接到PC并向PC串口发送数据,都不用再插拔器件了,只需要在这几个程序间切换工作便可以了。(是不是很方便呢

这样第一项工作就完成了,确认电路的连接及单片机程序都工作正常。

下面要进行第二项工作:

目的:用我自己的PC程序把串口调试器软件替换掉。因为我最终要的是接收单片机上的数据,并将接收的数据在PC上进行处理、存储,而串口调试器只能接收固定的内容,你也不无法把收到的数据接管过来,仅仅能做连接测试而已。

我用的是最简单易用的Visual Basic 6.0。具体如何操作运用,网上有很多教程,也很容易上手。

打开VB6.0,新建一个工程,也就是要建立一个新的程序。

这是个标准的VB6.0界面,我们要进行串口的操作需要添加一个串口控件MSCOMM32.OCX,或许你的机子上有,也许没有,没有的在网上搜了下一个装在你c:\winnt\system32\ 。然后你用鼠标右键点击VB界面左侧的工具箱,弹出菜单后选部件,或者在上部的主菜单上点“工程”--“部件”,就会弹出如下界面:

在列出的项里找到Microsoft Comm Control 6.0。在前面的小方框里点上钩。注意看下面的提示栏里就告诉你这个控件的文件名和所在的目录。点“确定”钮,这时在VB主界面的工具箱里就会多出个小电话的控件图标了:

接下来点击这个控件图标,然后在 Form1的窗口上拉出个框(或者双击小电话图标)把图标放到Form1窗口上去。如下图:

如果你的图标放不上去并弹出如下提示框:

就说明你的VB6.0是简化版,不是正式安装的。解决方法如下:
首先把MSComm32.OCX拷进C:\WINNT\SYSTEM32\ (我的机器一开始并没有这个控件,我去网上下了一个,机器里面有此控件的此步不做!)(注:路径以我机器的winXP系统为例)
      然后点击 开始>运行>regsvr32 c:\winnt\system32\mscomm32.ocx 成功后,开始>运行>regedit,进入注册表,找到HKEY_CLASSES_ROOT\Licenses,然后新建一个项,命名为:“4250E830-6AC2-11cf-8ADB-00AA00C00905”,值为:“kjljvjjjoquqmjjjvpqqkqmqykypoqjquoun”。一切就OK了。

VB6.0设置正常后,我由简入繁地进行程序的编写。在窗体上先放上一个串口控件,一个文本框,一个按钮,一个定时器。如下图:

串口控件是单片机串口和PC串口进行通讯的桥梁;文本框用来显示我们收到的数据,按钮用来启动这个接收,定时器用来定时检查每一小段时间检查是否有串口数据收到。
我们先对串口控件进行属性设置,Commport是串口号设置,一般设置为1,Settings是对串口的波特率、有无奇偶校验,数据位数,停止位数进行设置,因为我的单片机程序上用的波特率是19200,所以在这儿我只对波特率进行调整,其它都是都用默认值。如下图:

对按钮控键进行设置:只是将“Caption”标题属性改为“接收数据”。
对定时器进行设置,将“Interval”间歇时间改为400。这样就是每400毫秒检查一次有无数据收到。
对文本框进行设置:将“MultiLine”多行显示设为“True”允许。
控件的属性设置完了,下面我们为程序写代码,先双击“接收数据”按钮。会弹出代码窗口,我了如下代码,如图:

上面就是我们编写的按钮事件代码。写完后我们在键盘上按“Shift”+“F7”,回到对象窗口。再双击那个定时器控件,界面就切换到定时器的代码窗口,我们写程序如下图:

现在我们的这个VB程序就写好了。接着就试着运行一下这个程序,且慢!我们还是要先启动串口调试器看看单片机是否还在不断的发送着“hello world!”,确认它还是在不停地显示着那行“hello world!”,就可以关了串口调试器。然后在VB6.0的主界面点击那个小三角的播放钮。我的程序就运行如下了:

第二项任务完成!我自己接管了接收的数据。点菜单的保存这项工程。

第三项任务:掌握单片机端如何发送字符和数值数据。

接下来我要做的是对AT89S52内串行数据的发送进行解和掌握,以便我们能随心所欲地将单片机获得的数值数据或字符数据发送给PC机进行处理或存储。

先来看看原来我写的AT89S52不断发送“hello world!”的那段程序。

#include<at89x51.h>
#include<stdio.h>
void delay(void) //定义一个延时子程序
{
  unsigned int i;
  for (i=65535;i>0;i--);
}

void main(void) //主程序
{
  SCON=0x50; //串口方式1
  TMOD=0x20; //定时器1,定时方式为2
  PCON=0x80; //设定串口工作方式为1
  TCON=0x40; //设定时器1开始计数
  TH1=0xfd;  //设定波特率为19200
  TL1=0xfd;  //
  TI=1;
  TR1=1;     //启动定时器
  while(1)
  {
    printf("hello world!\n"); //向串口送出数据
  delay(); //调用延时
  delay();
  }

}

上面的程序中除了设置串口的语句和延时语句外,负责向串口发送的语句只有一行,即“printf("helleo world!\n")”。学习过编程的一般都知道,print语句的作用是输出字符串的,但我们如果从单片机的A/D模块上获得了数据想发送到PC,应该怎么做呢?虽然你也可以先将这些数据转换成数字字符串,例如我们从一个8位的A/D模块上获得的数据是个数值从0-255的8位的数值,如果数值是1,那你得先将1这个数值转换成“1”字符对应的代码49(二进制110001,十六进制31H),再用printf语句发送出去。如果值是255,那你得先把它转换成3个字符“2”、“5”、“5”,再用printf发送出去。但这样既复杂又不规范,“1”是一个字符,“2”、“5”、“5”是三个字符,随着数值的不同,发送的数据的字节数据也不同,这样可不行。

我们还是先蹲蹲马步,了解一下单片机串口发送数据的实质:

上图是串口的发送时序示意图,最上面的TX表示的是单片机串口的发送线,,第二根CLK是内部时钟线,最下面的是发送标志信号TI。

我们以最常用的串行方式1,即10位异步通信方式来简单分析一下。它规定了1位起始位、8位数据位和1位停止位。其中第一位(起始位)和最后一位(停止位)是在你设定好串口的方式,打开串口后由芯片内串口模块自动插入的,不用人为加。

当你想通过串口发送数据时,只需要向AT89S52内的一个8位的特殊功能寄存器SBUF(99H)送入一个字节你想要发送给PC的数据,它就会自动连同起始位,数据位,停止位一起产生10位串行的电位信号送出。在第10个脉冲后将TX线的电位拉高,同时将标志位TI置1,告诉自己的程序发送结束。

接收方也是以规定好的相同的波特率时钟脉冲为基准,当某一个脉冲到来后检测到RX线上的电位被拉低,就知道对方开始发送数据了,然后从下一个脉冲起计数并在每个脉冲后检查RX线上的电位,若是高电位便记做1,低电位便记做0,如此得到8个位的数据,然后在第10个脉冲后,检测到RX线上的电位为1就知道这帧数据传送完毕。(注意:单片机的串行发送口(TX)和PC的接收口(RX)是通过串行线直接连接的,所以这两点的信号是相同的)

例如要发送“1”这个字符,代码是49(二进制110001,十六进制31H),串口发送时低位在前,如下图

归纳起来,若想发送数据只要向SBUF送一个字节的数据,然后等TI变为1后,就再发第二个字节依,此类推。

再说说字符和数值的关系,对于电路来说,它不知道什么是字符,什么是数值,只是按高低电位发送一帧帧的电信号。例如00000000代表0(00H),10101010代表170(AAH),11111111代表255(FF),但对于接收方PC就有不同了,大家都知道,电脑下载文本比下载一幅图像的数据量要小得多,原因就是文本只是用一个代码来代表一个将要显示的文字图像,而这个文字的图像数据就预先存在自己的电脑里,就是所说的字库。而你下载一幅图像,则需要每一个阵点的数据都得传送,所以数据量很大。西文也是一样的,也是用代码来代表一个需要显示的西文字符图像,这就是ACSII码。这样用一个字节的数(0-255)的范围就能代表所有的西文字符和常用符号了,例如用数值 65(十六进制为41H)代表“A”。用数值49(十六进制为31H)代表“1”,我只要向SBUF里输入值65,PC只要以字符方式接收,就会显示“A”字。如果以数值方式接收,变量的值就是65。这里面也包括有一些非字符的功能控制符号。例如13代表回车,10代表换行。

下面我们就来试试改写一下发送程序:试着用送数值和送字符两种方式发送。同样是“A”“B”“C”“D”四个字符。

#include<at89x51.h>
#include<stdio.h>
void delay(void) //定义一个延时子程序
{
  unsigned int i;
  for (i=65535;i>0;i--);
}

void main(void) //主程序
{
  SCON=0x50; //串口方式1
  TMOD=0x20; //定时器1,定时方式为2
  PCON=0x80; //设定串口工作方式为1
  TCON=0x40; //设定时器1开始计数
  TH1=0xfd;  //设定波特率为19200
  TL1=0xfd;  //
  TI=1;
  TR1=1;     //启动定时器
  while(1)
  {

SBUF=65;   //向SBUF内写入65的数值,也就是字符“A”的代码

while(TI==0);   //检测TI,当TI=0时,说明还没发送完,就循环等待。

TI=0;   //当TI=1时,就把TI的值置0,以便下一组发送。

SBUF=66;     //向SBUF送数值66.即字符“B”的代码

while(TI==0);

TI=0;

SBUF='C';     //向SBUF送字符“C”

while(TI==0);

TI=0;

SBUF='D";    //向SBUF送字符“D”

while(TI==0);

TI=0;

delay(); //调用延时
  delay();
  }

}

将程序编译生成HEX文件后写入AT89S52。打开我上面用VB6编好那个程序,点击“接收数据”钮。如下图:

它的确按我预想的执行了。只要是节字数据,无论是数值还是字符代友都是可以进行发送的。这是C语言的优点。

另外我们顺便看一下原先用printf函数发送生成HEX和直接写SBUF来成HEX的差别:


上图的提示显示了生成的代码共用了1120个字节。这是用printf函数发送的。

下图是直接写SBUF后的编译提示信息:

哈!直接操作串口缓冲寄存器只用了89个字节。这是直接进行底层操作的优势。

上面的程序发送“A”“B”“C”“D”四个数据,因为没有发送回车符,所以一次次的字符都是连续显示的。我们再修改一下,把要发送的ABCD这四个数据再加两个代表回车的控制字符数据定义到一个字节数组中变量中,再改用循环的方式来发送,程序如下:

#include<at89x51.h>
#include<stdio.h>
void delay(void) //定义一个延时子程序

  unsigned int i;
  for (i=65535;i>0;i--);
}

void main(void)

  unsigned char buf[  ]={65,66,'C','D',13,10};    //定义一个单字节数组最后两个数值13和10是回车符。
  unsigned char i;
  SCON=0x50; //串口方式1
  TMOD=0x20; //定时器1,定时方式为2
  PCON=0x80; //设定串口工作方式为1
  TCON=0x40; //设定时器1开始计数
  TH1=0xfd;  //设定波特率为19200
  TL1=0xfd;  //
  //TI=1;
  TR1=1;     //启动定时器
  while(1)
  {
    for(i=0;i<6;i++)
    { 
 SBUF=buf[i]; //向串口送出数据
 while(TI==0);
    TI=0;
    }
    delay(); //调用延时
    delay();
  }

}

下面是运行结果:


这样就和原先printf函数输出的效果一样了。

从上面程序我们知道了,如果我们想发送测量的数值数据,可以把用A/D模块获得的测量数据赋给buf[ ]数组里的变量,然后就可以进行发送处理了。字符直接用上面的发送方法就行了。

这样第三项工作也完成!(啊!累了,要歇歇,歇歇!

第四步:掌握PC端程序如何接收发送字符和数值型数据。

接下来我们来看看PC端的程序如何正常接收并处理收到的数据。字符没有问题,因为刚才就是显示的字符,但我主要是想看看采用字符方式接收对于0-255范围内那些非字符数值能否正常接收和处理。
打开VB6,调出我们原先编写的程序,在串口控件的属性中有个InputMode属性,如果是0就是以字符方式接收,如果是1就是以二进制方式接收。

原先我们就是用了缺省的字符方式。为了便于分析收到的数据,我们得分别修改一下单片机和PC里的程序,首先不能让单片机不停的发送,而是从PC机先向AT89S52发送一个字符“s”,AT89S52收到并确认是“s”字符后再发送一组数据,发完后停下,等待PC的下次请求。这样我们可以准确和稳定地看到这组数据的情况。

VB程序的修改如下:

我们在Command1+_Click事件的代码里添加了一行MSComm1.Output="s",也就是每当我按下“接收数据”钮时向AT89S52发送了一个"s"字符,然后清空文本框内容,然后启动定时器子程序Timer1每100毫秒检查一次有无收到数据,收到数据便显示出来。

对AT89S52内的程序做修改,程序循环检查有无收到数据,当的RI=1时便有数据收到,确认收到的数据为字符“s”时,便送出数组内数据。修改程序如下:
#include<at89x51.h>

void delay(void) //定义一个延时子程序

  unsigned int i;
  for (i=65535;i>0;i--);
}

void main(void)

  unsigned char buf[]={65,66,'C','D','E','F'};//定义一个单字节数组
  unsigned char i;
  SCON=0x50; //串口方式1
  TMOD=0x20; //定时器1,定时方式为2
  PCON=0x80; //设定串口工作方式为1
  TCON=0x40; //设定时器1开始计数
  TH1=0xfd;  //设定波特率为19200
  TL1=0xfd;  //
  //TI=1;
  TR1=1;     //启动定时器
  while(1)
  { if(RI==1)  //如果接收到数据则进入以下操作
    {if(SBUF=='s')   //如果收到的数据为“s”字符则进入发送操作
       {
         for(i=0;i<6;i++)//循环发送出数组buf[ ]的6个数据
             { 
        SBUF=buf[i]; //向串口送出数据
        while(TI==0);
        TI=0;
        delay(); //调用延时子程序
      }
    RI=0;//上面的发送操作完毕后将标志RI清0等待PC下次请求
        }
     }
  }
}
编译、生成HEX文件然后将文件写进AT89S52。我们运行VB6编写的Scom1程序。

现在我点击“接收数据”钮后,文本框内一个字符一个字符的依次显示出“ABCDEF”如下图:

当我再次点击“接收数据”钮时,文本框先清空,然后再重复上面的显示。达到我们的要求。PC机每请求一次,单片机就发送一次数据。

接下来我要做的是让文本框里不再显示“ABCDEF”这几个字符,而是要显示AT89S52发过来的数值,例如我发送65的值,文本框里就显示65,我发送255,文本框就显示255,这样我就能测试出用字符接收的方法能否将一个字节的值(0-255)都能正确接收和显示。我们把那句Text1.Text = Text1.Text + MSComm1.Input 。改成Text1.Text = Text1.Text + str(asc(MSComm1.Input )),就是把原先字符串变量MSComm1.Input先转换成ASCII值,再把这个值显示出来。改完了。我们运行下试试。


上图显示的确可以正确显示出数值,但这仅仅只是有相对应字符几个代码数值,我再来试试,非字符的ASCII值,看是否都能正确收到并显示。先修改AT89S52里我们原先发送的那些数组buf[ ]里的值,让它们部分不在字符范围里,看能不能正确接收到显示。我们将数组unsigned char buf[]={65,66,'C','D','E','F'};改写为既有字符也有非字符范围的数组unsigned char buf[]={0,1,2,'D',254,255};


从上图来看,后两个数据显示不对,254没有显示出来,255显示为63,经过修改AT89S52程序,把0-255的数值都发送一遍,发现大于128的数值几乎都不能正确接收。所以,得出结论:以字符方式接收数值数据是不可行的!

如下图:

接下来我改变串口控件的InputMode属性,将它的值改为1,即用二进制读取来试试。但二进制方式怎么读取呢,看了很多资料,也试了很多次,终于弄明白了,原来先要定义一个字节型的可变数组,这样当接收到数据时把接收到的一个或多个数值的首地址变量Mscomm1.input赋给这个字节数组名。于是你就可以运用这个字节数组里的变量了。

我先在VB里修改串口控件的InputMode属性,如下图:

然后我要在VB程序里先定义一个单字节Byte类型的数组,将收到的数据变量(Mscomm1.Input)赋给inbuff这个数组名,程序修改如下图:

AT89S52里程序,基本不动,只是等待PC机发来请求字符“s”。收到请求后,发送0-255的全部数值。
修改的程序如下:

#include<at89x51.h>

void delay(void) //定义一个延时子程序

  unsigned int i;
  for (i=15535;i>0;i--);
}

void main(void)

  unsigned char buf[]={3,4,5,'D',255,253};//定义一个单字节数组
  unsigned char i;
  SCON=0x50; //串口方式1
  TMOD=0x20; //定时器1,定时方式为2
  PCON=0x80; //设定串口工作方式为1
  TCON=0x40; //设定时器1开始计数
  TH1=0xfd;  //设定波特率为19200
  TL1=0xfd;  //
  //TI=1;
  TR1=1;     //启动定时器
  while(1)
  { if(RI==1)  //如果接收到数据则进入以下操作
    {if(SBUF=='s')   //如果收到的数据为“s”字符则进入发送操作
       {for(i=0;i<255;i++)//循环发送出数,这里做为调试,我们先发0-255的数值。


        SBUF=i; //向串口送出数据,这里不发buf [ ]数组的数据,而是直接发送循环体里的i 值(0-254)。
        while(TI==0);
        TI=0;
        delay(); //调用延时
    }

SBUF=255; //上面循环里没有包括255这个值,这里补发送一次

while(TI==0);
    TI=0;
    RI=0;//发送完毕将收到请求标志清0等待PC下次请求
     }
  }
  }

}

显示结果如下:

这样,所有的数值都是可以正确接收并显示了。

结论:要接收数值数据,串口控件必须修改为二进制的接收属性。即:InputMode=1

---上篇结束---

上一篇我已经将串口通讯的操作完成,这次我来连上ADC0809进行实际的测量,把数据传到PC来显示和处理。

连接示意图如下。

实际连接图如下:

单片机上我还是连上了LCD12864,让它同时也显示转换值。

单片机程序用了一个主程序adc0809m.c和两个子程序adc0809c.c   12864put.c  如下图左边:

下面我列出主程序:

#include <AT89X52.H>
#include <intrins.H>
#define uchar unsigned char
#define uint unsigned int
extern void LcmClear( void );       //清屏
extern void LcmInit( void );        //初始化
extern void LcmPutstr( uchar row,uchar y,uchar * str );  //在设定位置显示字符串
extern uchar adc0809conv(void);  //测量转换函数
extern void Delay(uint MS) ;    //延时函数
uchar * uchartostr(unsigned char unm);    //将char值转成字符串
uchar str[4];
uchar idata buff2[2];

//****************************
//将char值转成字符串函数
//****************************
uchar * uchartostr(uchar unm)
{  
 uchar x00,xx,x0,x,n;
 x00=unm/100;
 xx=unm%100;
 x0=xx/10;
 x=xx%10;
 n=0;
 if(x00!=0)
 {  str[n]=x00+48;  //值加48即为字符
    n++;
 }
 if(!(x00==0&x0==0))
 {  str[n]=x0+48;
    n++;
 }
 str[n]=x+48;
 n++;
 str[n]='\0';
   return str;
}

void Scomm_Receive( void ) interrupt 4 using 3   //定义一个串口中断函数
{
   if(RI==1)  //如果接收到串口数据则进入以下操作
    {if(SBUF=='s')   //如果收到的数据为“s”字符则进入发送操作
    {  SBUF=buff2[0]; //向串口送出测量数据
       while(TI==0);
       TI=0;
        }
 }
   RI=0;//发送完毕将收到请求标志清0等待PC下次请求

}

//****************
//    主函数
//****************

void Main( void )
{

/* T2 设置定时器2 */
  TR2=0x0;
  T2MOD=0x02;//0010(B) 设置T2为P1.0口输出方波模式
  C_T2=0;//用内部时钟计数
  TL2=0xfd;
  TH2=0xff;
  RCAP2L=0xfd;
  RCAP2H=0xff;
  TR2=1;
  //?------设置串口和T1------
  SCON=0x50; //串口方式1
  TMOD=0x20; //定时器1,定时方式为2
  PCON=0x80; //设定串口工作方式为1
  TCON=0x40; //设定时器1开始计数
  TH1=0xfd;  //设定波特率为19200
  TL1=0xfd;  //
  TR1=1;     //启动定时器

ES=1;//允许串行中断
  EA=1;//总中断允许

Delay(100);//延时一会

buff2[0]=adc0809conv();  //调用子程序进行测量转换
  LcmInit();  //初始化LCD
  LcmClear();  //LCD清屏
  LcmPutstr( 0,28,"ADC0809 TEST" );//向LCD输出信息
  LcmPutstr( 3,0,"Value:" );
  LcmPutstr( 3,40,uchartostr(buff2[0]) );//向LCD输出测量转换的值

LcmPutstr( 5,0,"BLOG:http://" );
  LcmPutstr( 6,18,"hi.baidu.com/txz01" );
  LcmPutstr( 7,8,"Email:TXZ001@139.com" );

while(1)        //进入循环,重复做下面的事
    {  buff2[0]=adc0809conv();   //调用子程序进行测量转换
         LcmPutstr(3,40,"          ");     //将上次LCD上显示的转换值清掉
   LcmPutstr( 3,40,uchartostr(buff2[0]) );   //显示这次新转换后的值
   Delay(800);
     
    }
}

PC机程序如下:

将单片机程序写进AT89S52并运行,PC机程序也运行,我测量的是一节电池的电压,测量结果如下:

在LCD上和PC上可以同步显示测量值,在PC上将转换值处理成实际的电压值。

看来一切都正常咯!

接下来我要在PC上显示出测量的实际趋势图,猛看了几天VB控件的书,本来想自己写个作趋势的程序,但感觉功能上不够强大,决定用MSChart控件,摆弄了几天,觉得这个控件还是蛮复杂的,网上系统介绍的也不多,看来有必要绍几句。

1、打开VB6软件,添加MSChart控件。还是在左侧的工具箱空白处点击鼠标右键,选择“部件”然后在弹出的窗口里选择如下图:

在“Microsoft Chart Control 6.0 (OLEDB) ”前的小框里画上钩,点击“应用”或“确定”,便会在工具箱中增加一个图表控件。如下图:


添加了控件以后,你就可以点击这个控件,在Form1窗体上用鼠标拉出个框将图表放上去。如果你放上去后,弹出警告框说没有认证,那么说明你没有完全安装,可能用的简化版,或绿色拷贝版。解决的办法有几个,但最好的还是用个企业版的完全安装一下。

  先来说说MSChart控件的使用,它里面的对象、方法,属性非常多,我只按我最常用最有效的说:上图已经将图表控件加到窗体上,在缺省的情况下它就是显示着这样一个柱状图,背景色和窗体Form1的颜色一致。为了看得清楚,我将MSChartr 背景色换掉如下图:

从上图,你可以看出,MSChart控件并不是一个单一的控件区域,在MSChart里,还包含着Plot(绘图)区域、Title(标题)区域、Legend(图例)区域和Footnote(脚注)区域。我们要分别去控制这些区域要多大、在什么位置显示,什么颜色。很不幸的的是,当你调整完了以后,你会发现它会自动回到某种大小和状态,因为在缺省设置时,它的自动布局是打开的,它就会动调整MSChart内Plot、的大小和宽高,让其空的Title、Footnote等都在合适的位置,但这一般不是我们所愿意的,我期望对设置多大它就显示多大,别乱跑,标题什么的我自然会留出位置显示上去。因此我先得关掉自动布局功能。

可以在Private Sub Form_Load()过程中写入语句

MSChart1.Plot.AutoLayout = False

这样运行时你设置好的图表大小,位置就不会变动了。

下面说说常用的MSChart的控制语句:

'*************MSChart控件区域的设置*****************

MSChart1.Width=8000  '设置MSChart控件区域的宽度

MSChart1.Height=6000 ''设置MSChart控件区域的高度

MSChart1.Top=Form1.Top '设置MSChart控件区域顶边的位置对齐Form1顶边

MSChart1.Left=Form1.Left  '设置MSChart控件区域左边的位置对齐Form1左边

'设置MSChart1控件区域的背景颜色
MSChart1.Backdrop.Fill.Style = VtFillStyleBrush
MSChart1.Backdrop.Fill.Brush.FillColor.Set 90, 90, 90

'************设置绘图区(Plot)的大小位置和背景色*****************

MSChart1.Plot.LocationRect.Min.Set 0, 0   '设置Plot区域左下角点在MSChart1中的位置
MSChart1.Plot.LocationRect.Max.Set MSChart1.Width, MSChart1.Height   '设置Plot区域右上角点在MSChart1中的位置

'设置作图区域的前景颜色
MSChart1.Plot.Backdrop.Fill.Style = VtFillStyleBrush
MSChart1.Plot.Backdrop.Fill.Brush.FillColor.Set 255, 255, 220

同样的,Title(标题) Legend(图例) Footnote(脚注)区域都可以用同样的方式设置他们的位置,大小,背景色,不过缺省情况下除了Plot 其它的都不显示,我们也不需要,这里不在说了

好!图表控件的大小、位置、背景色我们都设置好在窗体上,下面就将我们需要展现趋势图的数据赋给图表。

MSChart能作出2维和3维的条形、折线,饼图等十几种图表来,但我们用来作趋势图就用XY散点图就可以了。定义MSChart作散点图用如下语句:

MSChart1.chartType = VtChChartType2dXY '设置图形为二维散点图

它的赋值是这样的,先定义一个二维数组如Mydata(X,Y),将要作图的数据赋值给数组Mydata,再有下面的语句将Mydata数组赋值给MSChart:

MSChart1.ChartData = MyData '数据

下面我们就来实际做一个试试,假设我们X轴显示的是0-5V的电压,Y轴表示0-100mA电流,我们在这个范围里存一组数据,来看看MSChart能否按我们的要求作图:

Private Sub Form_Load()
Dim MyData(10, 1) As Double

'-----x轴坐标值-----Y轴坐标值----------

MyData(0, 0) = 0: MyData(0, 1) = 1 '本句代表了:第一点数据的X轴坐标为0,Y轴坐标为1

MyData(1, 0) = 3: MyData(1, 1) = 52

MyData(2, 0) = 6: MyData(2, 1) = 34

MyData(3, 0) = 9: MyData(3, 1) = 67

MyData(4, 0) = 12: MyData(4, 1) = 47

MyData(5, 0) = 15: MyData(5, 1) = 89

MyData(6, 0) = 18: MyData(6, 1) = 47

MyData(7, 0) = 21: MyData(7, 1) = 52

MyData(8, 0) = 24: MyData(8, 1) = 12

MyData(9, 0) = 27: MyData(9, 1) = 50

MyData(10, 0) = 30: MyData(10, 1) = 0

MSChart1.Plot.AutoLayout = False  '关闭自动布局

'******设置Plot的区域和MSChart1一样大*******

With MSChart1    
 .Plot.LocationRect.Min.Set 0, 0
 .Plot.LocationRect.Max.Set .Width, .Height
End With

MSChart1.chartType = VtChChartType2dXY '设置图形为二维散点图

MSChart1.ChartData = MyData '数据赋值

End Sub

这样我们来运行如下图:

这张图看来严重有问题啊,仔细看了一会发现有两个问题,

问题一:是自动设置了宽高比,它把X轴的值和Y的值做了成了刻度长度的对应关系。因此当Y最大值很大,而X最大值很小时,图形就会变得这么瘦高。我们一般作这种曲线图跟本不需要X轴和长度随Y值的长度而改变,也就是X的刻度不需要和Y值的刻度存在关系,刻度是我来设置的。所要关掉刻度线的宽高比

问题二:上图刻度线的最大范围是90,说明是它自动跟据Y最大值给出的,而我们需要的往往是个整数据如100 ,因此我们也要关掉刻度最大范围的自动功能。

MSChart1.Plot.UniformAxis = False  '关闭宽高比
MSChart1.Plot.Axis(VtChAxisIdY).ValueScale.Auto = False '关闭自动刻度范围
MSChart1.Plot.Axis(VtChAxisIdY).ValueScale.Maximum = 100 '设置Y值最大刻度为100

MSChart1.Plot.Axis(VtChAxisIdY).ValueScale.MajorDivision = 4  '设置主要刻度线为4根

。。。。。。。

最终经反复试了MSChart控件,觉得显示时闪烁的确太严重,还是自己写画图程序,程序如下:

Option Explicit
Public X0, Y0, Xmax, Ymax, KdnumY, KdnumX, YvalueMax, XvalueMax, YvalueMin, XvalueMin, CurrentRecord, ScomReceiveNullNum As Integer
Public CurrentValue As Single
Public temp, DataState As Integer
Public SaveFileName, OpenFileName As String
'X0  坐标原点的X值(Twip)
'Y0  坐标原点的Y值(Twip)
'Xmax  横坐标终点的X值(Twip)
'Ymax  纵坐标终点的Y值(Twip)
'KdnumX  横坐标的刻度线数量
'KdnumY  纵坐标的刻度线数量
'YvalueMax  纵坐标最大值
'XvalueMax  横坐标最大值
'YvalueMin  纵坐标最小值
'XvalueMin  横坐标最小值
'CurrentValue  当前值
'CurrentRecord  当前记录
'ScomReceiveNullNum  串口没有接收到数据的次数
'DataState    工作状态变量,0:为没有打开数据文件,1:为打开一个临时文件tmp.txz存入实时数据并显示实时数据趋势,2为打开一个历史数据文件并显示历史趋势

Private Sub Command1_Click() '接收串口数据子程序
Dim i, j As Integer
Dim tmpData
If MSComm1.PortOpen = False Then
   MSComm1.PortOpen = True  '如果串口是关闭的,就打开串口
End If

For i = 1 To 10
  MSComm1.Output = "s" '向单片机发出“s”字符,请求数据
  For j = 0 To 1000: Next j '延时一会
  If MSComm1.InBufferCount > 0 Then  '如果收到的数据量大于0
     tmpData = MSComm1.Input      '将收到的数据赋给数组变量inbuff
     ScomReceiveNullNum = 0
 Else
  ScomReceiveNullNum = ScomReceiveNullNum + 1  '将没有接收到数据的次数加1
  End If
Next i
   
If ScomReceiveNullNum >= 5 Then   '如果没有接收到串口数据的次数大于5次
     MsgBox "没有接收到串口数据,请检查串口连接!"
     MSComm1.PortOpen = False  '关闭串口
     ScomReceiveNullNum = 0
     DataState = 0
Else
    Text1.Text = ""     '清空文本框内容
    Text2.Text = ""
    Open App.Path & "\tmp.txz" For Random As #1 Len = Len(CurrentValue) '在当前目录下添加打开一个临时文件
    CurrentRecord = 1
    If Timer1.Enabled = False Then   '启用定时器
       Timer1.Enabled = True
    End If
    Command1.Enabled = False
    Command2.Enabled = True
    Command4.Enabled = True
    DataState = 1  '状态为接收并显示实时数据

End If

End Sub

Private Sub Command4_Click() '保存数据子程序
Dim RecodeNum As Long
Dim tmpData As Single
Dim i As Integer
  If DataState = 1 Then
         If SaveFileName = "" Then
            SaveFileName = CommonDialog1.FileName
         End If
         Open SaveFileName For Random As #2 Len = Len(CurrentValue)
         RecodeNum = LOF(1) / Len(CurrentValue)
         For i = 1 To RecodeNum
             Get #1, i, tmpData
             Put #2, i, tmpData
         Next i
         Close #2
  End If

End Sub

Private Sub Form_Load()

Dim SaveFileName As String
SaveFileName = ""   '清空需要存盘的数据文件名
OpenFileName = ""  '清空要打开的文件名变量
ScomReceiveNullNum = 0  '将串口没有接收到数据的次数清零
Command4.Enabled = False
Command2.Enabled = False
Command3.Enabled = False
Command1.Enabled = True
DataState = 0   '将数据状态置为0,没有打开数据文件

Text1.Text = ""     '清空文本框内容
Text2.Text = ""

SaveFileName = ""
X0 = Picture1.ScaleLeft + 700 '坐标原点初值
Y0 = Picture1.ScaleHeight - 400 '坐标原点初值
Xmax = Picture1.ScaleWidth - 400 '坐标终点初值
Ymax = Picture1.ScaleTop + 60 '坐标终点初值
KdnumY = 5 '垂直方向刻度的分段数
KdnumX = 6 '水平方向刻度的分段数
YvalueMin = 0 'Y方向上需要表征的最小值
XvalueMin = 0 'X方向上需要表征的最小值
YvalueMax = 5 'Y方向上需要表征的最大值
XvalueMax = 1200 'X方向上需要表征的最大值
drawKD   '调用画坐标刻度线子程序

End Sub

Private Sub Form_Unload(Cancel As Integer)
MsgBox "确认退出程序吗?"
End Sub

Private Sub Option1_Click()
If DataState = 2 Then '如果当时是显示历史趋势的状态
   If MsgBox("你确认要退出历史趋势显示吗?", vbYesNo, "提示") = vbYes Then
      Close #1
      Picture1.Cls  '清除画面
      drawKD     '重新调用画坐标和刻度线了程序
      Command1.Enabled = True
      Command2.Enabled = False
      Command4.Enabled = False
      Command3.Enabled = False
      DataState = 0

Else
      Option2.Value = True
      Option1.Value = False
   End If
Else
   If DataState = 0 Then
      Command1.Enabled = True
      Command2.Enabled = False
      Command4.Enabled = False
      Command3.Enabled = False
   End If
End If
End Sub

Private Sub Option2_Click()
Dim Response, tmpData, i As Integer
Dim RecodeNum As Long
If DataState = 1 Then
   Response = MsgBox("正在接收串口实时数据,需要停止并存盘数据吗?", vbYesNoCancel, "提示")
   If Response = vbCancel Then
      Option1.Value = True
      Option2.Value = False
      Exit Sub
   Else
      Command1.Enabled = False
      Command2.Enabled = False
      Command4.Enabled = False
      Command3.Enabled = True
      Timer1.Enabled = False
      MSComm1.PortOpen = False
      If Response = vbYes Then
         If SaveFileName = "" Then
            SaveFileName = CommonDialog1.FileName
         End If
         Open SaveFileName For Random As #2 Len = Len(CurrentValue)
         RecodeNum = LOF(1) / Len(CurrentValue)
         For i = 1 To RecodeNum
             Get #1, i, tmpData
             Put #2, i, tmpData
         Next i
         Close #2
         Close #1
         Kill App.Path & "\tmp.txz"
      End If
      DataState = 0
   End If
Else
   If (DataState = 0) And (Command3.Enabled = False) Then
      Command1.Enabled = False
      Command2.Enabled = False
      Command4.Enabled = False
      Command3.Enabled = True
   End If
   
End If
End Sub

Private Sub Timer1_Timer()
Dim inbuff() As Byte       '定义一个字节类型的数组
Dim i As Integer
Dim tmp As Single
Dim X1, X2, Y1, Y2, TemRecord, temp As Integer

MSComm1.Output = "s" '向单片机发出“s”字符,请求数据
For i = 20 To 0
Next
If MSComm1.InBufferCount > 0 Then  '如果收到的数据量大于0
   ScomReceiveNullNum = 0 '将串口没有收到数据的次数清零
   inbuff = MSComm1.Input      '将收到的数据赋给数组变量inbuff
   tmp = inbuff(0) * 0.02      '将数据值转换成电压值
   temp = inbuff(0)
   Text1.Text = Str(inbuff(0)) '将收到的数值数据转换成字符显示在文本框
   Text2.Text = Format(Str(tmp), "##0.00") '将电压值转换成字符形式显示在文本框
   
   CurrentValue = tmp   '将接收到的串口数据传给作图的当前值
  Put #1, CurrentRecord, CurrentValue
  If CurrentRecord <= XvalueMax Then '当记录数小于最大X表征值时,从左向右画出实时趋势
    If CurrentRecord = 1 Then
      Get #1, CurrentRecord, CurrentValue
    Else
      Get #1, CurrentRecord - 1, CurrentValue
    End If
    X1 = CInt(X0 + (CurrentRecord - 1) * Abs(Xmax - X0) / Abs(XvalueMax - XvalueMin))
    Y1 = CInt(Y0 - CurrentValue * Abs(Ymax - Y0) / Abs(YvalueMax - YvalueMin))
    Picture1.CurrentX = X1
    Picture1.CurrentY = Y1

Get #1, CurrentRecord, CurrentValue
    X2 = CInt(X0 + CurrentRecord * Abs(Xmax - X0) / Abs(XvalueMax - XvalueMin))
    Y2 = CInt(Y0 - CurrentValue * Abs(Ymax - Y0) / Abs(YvalueMax - YvalueMin))
    Picture1.Line -(X2, Y2)
    CurrentRecord = CurrentRecord + 1
  Else '当记录数大于X方向最大表征值时,将当前记录值显示在右侧坐标轴上,以前数据逐次左移
    Picture1.Cls
    temp = 0
    drawKD
    For TemRecord = (CurrentRecord - (XvalueMax - XvalueMin) + 1) To CurrentRecord
      Get #1, TemRecord - 1, CurrentValue
      X1 = CInt(X0 + (temp) * Abs(Xmax - X0) / Abs(XvalueMax - XvalueMin))
      Y1 = CInt(Y0 - CurrentValue * Abs(Ymax - Y0) / Abs(YvalueMax - YvalueMin))
      Picture1.CurrentX = X1
      Picture1.CurrentY = Y1

Get #1, TemRecord, CurrentValue
      X2 = CInt(X0 + (temp + 1) * Abs(Xmax - X0) / Abs(XvalueMax - XvalueMin))
      Y2 = CInt(Y0 - CurrentValue * Abs(Ymax - Y0) / Abs(YvalueMax - YvalueMin))
      Picture1.Line -(X2, Y2)
      temp = temp + 1
    Next TemRecord
    CurrentRecord = CurrentRecord + 1
  End If

Else  '如果没有接收到数据
   ScomReceiveNullNum = ScomReceiveNullNum + 1  '将没有接收到数据的次数加1
   If ScomReceiveNullNum >= 10 Then   '如果没有接收到串口数据的次数大于10次
     MsgBox "串口接收数据中断,请检查串口连接!"
     MSComm1.PortOpen = False
     Close #1
     Timer1.Enabled = False
     Command2.Enabled = False
     Command1.Enabled = True
     ScomReceiveNullNum = 0
     DataState = 0
   End If
   
End If

End Sub

Public Sub drawKD()  '画刻度线子程序,是以Twip为单的
Dim KdfY, KdfX, i As Integer
Dim FwfY, FwfX As Single

Picture1.Line (X0, Ymax)-(X0, Y0)   '左侧纵坐标线
Picture1.Line -(Xmax, Y0)      '底部横坐标线
Picture1.Line -(Xmax, Ymax)     '右侧纵坐标线

KdfY = CInt((Abs(Ymax - Y0)) / KdnumY)   '纵坐标每格刻度的特维数
KdfX = CInt((Abs(Xmax - X0)) / KdnumX)   '横坐标每格刻度的行维数
FwfY = (YvalueMax - YvalueMin) / KdnumY  '纵坐标表征值每格刻度对应的变量改变值
FwfX = (XvalueMax - XvalueMin) / KdnumX  '横坐标表征值每格刻度对应的变量改变值
For i = 1 To KdnumY + 1
Picture1.DrawStyle = vbSolid
Picture1.Line (X0 - 90, (i - 1) * KdfY + Ymax)-(X0, (i - 1) * KdfY + Ymax)
Picture1.CurrentX = X0 - 550
Picture1.CurrentY = (i - 1) * KdfY + Ymax - 90
Picture1.Print Format(YvalueMax - (i - 1) * FwfY, "##0.0")
Picture1.Line (Xmax, (i - 1) * KdfY + Ymax)-(Xmax + 90, (i - 1) * KdfY + Ymax)
Picture1.DrawStyle = vbDot
Picture1.Line (X0, (i - 1) * KdfY + Ymax)-(Xmax, (i - 1) * KdfY + Ymax)

Next i
Picture1.DrawStyle = vbSolid
For i = 1 To KdnumX + 1
Picture1.CurrentX = Xmax - (i - 1) * KdfX
Picture1.CurrentY = Y0
Picture1.Line -(Xmax - (i - 1) * KdfX, Y0 + 90)
Picture1.CurrentX = Xmax - (i - 1) * KdfX - 120
Picture1.CurrentY = Y0 + 100
Picture1.Print Format(XvalueMax - (i - 1) * FwfX, "##0.#")
Next i
End Sub

下面要测量的是一个电解电容器的充放电过程中,我们测量电容器两端的电压不断获得数据来作出趋势图,被测量电路是很简单的如下图:

ADC0809测量的是电解电容的充放电过程,实时趋势图:

作图区慢慢填满后,图形会慢慢向左挪动,实时点一直落在纵坐标右侧线上。

后来又添加了保存数据功能,查看保存的历史数据趋势功能。界面如下图:

自制单片机之十七……PC与单片机RS-232串口的通讯和控制的更多相关文章

  1. PC和FPGA间的串口通信实现

    应用笔记 V1.0 2015/03/26 PC和FPGA间的串口通信实现   概述   本文将介绍PC和FPGA间的串口通信实现的基本思路和Verilog代码,对于通信而言,收发双方都要有相应的控制. ...

  2. PC和单片机通过MODBUS RTU通信

    最近研究了一下MODBUS通信,在STC12C5A60S2单片机上实现了MODBUS协议的部分功能,方便上位机从单片机系统上获取数据,比如由单片机获取的温度.湿度.或者控制信号的状态等.有了MODBU ...

  3. 9-51单片机ESP8266学习-AT指令(单片机采集温湿度数据通过8266发送给AndroidTCP客户端显示)

    http://www.cnblogs.com/yangfengwu/p/8798512.html 补充:今天答应了一个朋友写一下如果单片机发过的是字符串应该怎么解析,答应了今天写,哦哦哦是明天了,闲话 ...

  4. 8-51单片机ESP8266学习-AT指令(单片机采集温湿度数据通过8266发送给C#TCP客户端显示)

    http://www.cnblogs.com/yangfengwu/p/8785516.html 先写单片机端的程序 先把源码和资料链接放到这里 链接: https://pan.baidu.com/s ...

  5. 单片机DIY制作-基于STM32单片机甲醛二氧化碳温度湿度采集系统

    基于STM32单片机甲醛二氧化碳温度湿度采集系统 实践制作DIY-GC008-甲醛二氧化碳温度湿度采集系统 一.功能说明: 基于STM32单片机设计-甲醛二氧化碳温度湿度采集系统 二.功能介绍: 1. ...

  6. 普通PC通过USB转485串口 ModBus-RTU通信协议控制伺服电机

    一.RS485通信 RS485 是半双工通信(2 线制),可以一点对多点进行组网,而且 RS485 是用缆线两端的电压差值来表示传递信号,这与 RS232 电气特性大不一样.RS485 仅仅规定了接收 ...

  7. Network基础(三):网线的制作、交换机基本命令模式、交换机命令行基本配置、交换机的密码设置

    一.网线的制作 目标: 在常见的计算机网络中,网线主要用来连接计算机与交换机(或宽带路由器).交换机与交换机.交换机与路由器,以及需要连网的其他各种设备.网线的制作与测试是作为网络管理员的一个入门技能 ...

  8. 在KEIL下查看单片机编程内存使用情况

    原文链接:https://blog.csdn.net/D_azzle/article/details/83410141 截至到目前为止,本人接触单片机也有将近一年的时间.这一年以来也接触过了很具代表性 ...

  9. 51单片机-PC数据传输 温度 距离 监控系统设计

    >_<:功能概述: 通过串口PC和单片机通信,可以询问单片机测得的温度,可以询问声呐测距的测量距离,同时把测量温度显示在数码管上. >_<:PC部分 这里com.cpp和com ...

随机推荐

  1. Qt入门(6)——Qt的界面布局

    Qt提供四种布局: VBoxLayout:垂直布局 HBoxLayout:水平布局 GridLayout:二维布局. FormLayout: 窗体布局

  2. [LeetCode] 116. Populating Next Right Pointers in Each Node 解决思路

    Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *nex ...

  3. sql语句相关操作

    create user test identified by test default tablespace users temporary tablespace temp quota 3M on u ...

  4. POJ1017 packets

    Packets Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 48911   Accepted: 16570 Descrip ...

  5. java--字节数组输入、输出流

    在java网络编程中,字节数组很重要,它可以传输任何资料(文本,音频,视频,图片等),因此掌握字节数组和其它数据类型的相互转化尤为重要. 示例代码: package com.lky.util; imp ...

  6. 数据库版本管理工具Flyway(4.0.3)---介绍(译文)

    Flyway Evolve your Database Schema easily and reliably across all your instances 简单的.可靠的升级(发展)你的数据库模 ...

  7. 认识ptrace函数

    认识ptrace函数 这是man对于ptrace这个系统调用的解释 http://man7.org/linux/man-pages/man2/ptrace.2.html #include <sy ...

  8. nexus4/5/6/7/9/10设备谷歌安卓5.1.1系统底包下载

    https://developers.google.com/android/nexus/images http://www.inexus.co/thread-18488-1-1.html

  9. Linux(CentOS 5.5) Redis安装

    一,什么是redis redis是一个key-value存储系统. 和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset ...

  10. speex的基本编码和解码流程

    最近在研究speex的编码和解码流程 之前在IM上用到的都是发语音片段,这个很简单,只需要找到googlecode上gauss的代码,然后套一下就可以用了. 不过googlecode要关闭,有人将他导 ...